add menu item in account details to renew certificate
This commit is contained in:
parent
09816b61df
commit
7be331bbb2
|
@ -1,5 +1,7 @@
|
||||||
package eu.siacs.conversations.crypto.axolotl;
|
package eu.siacs.conversations.crypto.axolotl;
|
||||||
|
|
||||||
|
import android.security.KeyChain;
|
||||||
|
import android.security.KeyChainException;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -18,7 +20,12 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord;
|
||||||
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
||||||
import org.whispersystems.libaxolotl.util.KeyHelper;
|
import org.whispersystems.libaxolotl.util.KeyHelper;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -46,6 +53,7 @@ public class AxolotlService {
|
||||||
public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
|
public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
|
||||||
public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
|
public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
|
||||||
public static final String PEP_BUNDLES = PEP_PREFIX + ".bundles";
|
public static final String PEP_BUNDLES = PEP_PREFIX + ".bundles";
|
||||||
|
public static final String PEP_VERIFICATION = PEP_PREFIX + ".verification";
|
||||||
|
|
||||||
public static final String LOGPREFIX = "AxolotlService";
|
public static final String LOGPREFIX = "AxolotlService";
|
||||||
|
|
||||||
|
@ -242,11 +250,11 @@ public class AxolotlService {
|
||||||
return this.pepBroken;
|
return this.pepBroken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void regenerateKeys() {
|
public void regenerateKeys(boolean wipeOther) {
|
||||||
axolotlStore.regenerate();
|
axolotlStore.regenerate();
|
||||||
sessions.clear();
|
sessions.clear();
|
||||||
fetchStatusMap.clear();
|
fetchStatusMap.clear();
|
||||||
publishBundlesIfNeeded(true);
|
publishBundlesIfNeeded(true, wipeOther);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOwnDeviceId() {
|
public int getOwnDeviceId() {
|
||||||
|
@ -380,7 +388,43 @@ public class AxolotlService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void publishBundlesIfNeeded(final boolean announceAfter) {
|
public void publishDeviceVerificationAndBundle(final SignedPreKeyRecord signedPreKeyRecord,
|
||||||
|
final Set<PreKeyRecord> preKeyRecords,
|
||||||
|
final boolean announceAfter,
|
||||||
|
final boolean wipe) {
|
||||||
|
try {
|
||||||
|
IdentityKey axolotlPublicKey = axolotlStore.getIdentityKeyPair().getPublicKey();
|
||||||
|
PrivateKey x509PrivateKey = KeyChain.getPrivateKey(mXmppConnectionService, account.getPrivateKeyAlias());
|
||||||
|
X509Certificate[] chain = KeyChain.getCertificateChain(mXmppConnectionService, account.getPrivateKeyAlias());
|
||||||
|
Signature verifier = Signature.getInstance("sha256WithRSA");
|
||||||
|
verifier.initSign(x509PrivateKey,mXmppConnectionService.getRNG());
|
||||||
|
verifier.update(axolotlPublicKey.serialize());
|
||||||
|
byte[] signature = verifier.sign();
|
||||||
|
IqPacket packet = mXmppConnectionService.getIqGenerator().publishVerification(signature, chain, getOwnDeviceId());
|
||||||
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": publish verification for device "+getOwnDeviceId());
|
||||||
|
Log.d(Config.LOGTAG,"verification : "+packet.toString());
|
||||||
|
mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
|
||||||
|
@Override
|
||||||
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
|
publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announceAfter, wipe);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (KeyChainException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
Log.d(Config.LOGTAG,"no such algo "+e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (java.security.InvalidKeyException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (SignatureException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publishBundlesIfNeeded(final boolean announce, final boolean wipe) {
|
||||||
if (pepBroken) {
|
if (pepBroken) {
|
||||||
Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... ");
|
Log.d(Config.LOGTAG, getLogprefix(account) + "publishBundlesIfNeeded called, but PEP is broken. Ignoring... ");
|
||||||
return;
|
return;
|
||||||
|
@ -470,27 +514,16 @@ public class AxolotlService {
|
||||||
|
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
|
if (account.getPrivateKeyAlias() == null) {
|
||||||
signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
|
publishDeviceBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
|
||||||
preKeyRecords, getOwnDeviceId());
|
} else {
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish);
|
publishDeviceVerificationAndBundle(signedPreKeyRecord, preKeyRecords, announce, wipe);
|
||||||
mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
|
}
|
||||||
@Override
|
|
||||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
|
||||||
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. ");
|
|
||||||
if (announceAfter) {
|
|
||||||
Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
|
|
||||||
publishOwnDeviceIdIfNeeded();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current");
|
Log.d(Config.LOGTAG, getLogprefix(account) + "Bundle " + getOwnDeviceId() + " in PEP was current");
|
||||||
if (announceAfter) {
|
if (wipe) {
|
||||||
|
wipeOtherPepDevices();
|
||||||
|
} else if (announce) {
|
||||||
Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
|
Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
|
||||||
publishOwnDeviceIdIfNeeded();
|
publishOwnDeviceIdIfNeeded();
|
||||||
}
|
}
|
||||||
|
@ -503,6 +536,32 @@ public class AxolotlService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishDeviceBundle(SignedPreKeyRecord signedPreKeyRecord,
|
||||||
|
Set<PreKeyRecord> preKeyRecords,
|
||||||
|
final boolean announceAfter,
|
||||||
|
final boolean wipe) {
|
||||||
|
IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundles(
|
||||||
|
signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
|
||||||
|
preKeyRecords, getOwnDeviceId());
|
||||||
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ": Bundle " + getOwnDeviceId() + " in PEP not current. Publishing: " + publish);
|
||||||
|
mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
|
||||||
|
@Override
|
||||||
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
|
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
||||||
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Successfully published bundle. ");
|
||||||
|
if (wipe) {
|
||||||
|
wipeOtherPepDevices();
|
||||||
|
} else if (announceAfter) {
|
||||||
|
Log.d(Config.LOGTAG, getLogprefix(account) + "Announcing device " + getOwnDeviceId());
|
||||||
|
publishOwnDeviceIdIfNeeded();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while publishing bundle: " + packet.findChild("error"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isContactAxolotlCapable(Contact contact) {
|
public boolean isContactAxolotlCapable(Contact contact) {
|
||||||
Jid jid = contact.getJid().toBareJid();
|
Jid jid = contact.getJid().toBareJid();
|
||||||
return hasAny(contact) ||
|
return hasAny(contact) ||
|
||||||
|
@ -774,7 +833,7 @@ public class AxolotlService {
|
||||||
plaintextMessage = message.decrypt(session, getOwnDeviceId());
|
plaintextMessage = message.decrypt(session, getOwnDeviceId());
|
||||||
Integer preKeyId = session.getPreKeyId();
|
Integer preKeyId = session.getPreKeyId();
|
||||||
if (preKeyId != null) {
|
if (preKeyId != null) {
|
||||||
publishBundlesIfNeeded(false);
|
publishBundlesIfNeeded(false, false);
|
||||||
session.resetPreKeyId();
|
session.resetPreKeyId();
|
||||||
}
|
}
|
||||||
} catch (CryptoFailedException e) {
|
} catch (CryptoFailedException e) {
|
||||||
|
|
|
@ -2,16 +2,20 @@ package eu.siacs.conversations.generator;
|
||||||
|
|
||||||
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.whispersystems.libaxolotl.IdentityKey;
|
import org.whispersystems.libaxolotl.IdentityKey;
|
||||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||||
import org.whispersystems.libaxolotl.state.PreKeyRecord;
|
import org.whispersystems.libaxolotl.state.PreKeyRecord;
|
||||||
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
|
||||||
|
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
|
@ -173,6 +177,23 @@ public class IqGenerator extends AbstractGenerator {
|
||||||
return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item);
|
return publish(AxolotlService.PEP_BUNDLES+":"+deviceId, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IqPacket publishVerification(byte[] signature, X509Certificate[] certificates, final int deviceId) {
|
||||||
|
final Element item = new Element("item");
|
||||||
|
final Element verification = item.addChild("verification", AxolotlService.PEP_PREFIX);
|
||||||
|
final Element chain = verification.addChild("chain");
|
||||||
|
for(int i = 0; i < certificates.length; ++i) {
|
||||||
|
try {
|
||||||
|
Element certificate = chain.addChild("certificate");
|
||||||
|
certificate.setContent(Base64.encodeToString(certificates[i].getEncoded(), Base64.DEFAULT));
|
||||||
|
certificate.setAttribute("index",i);
|
||||||
|
} catch (CertificateEncodingException e) {
|
||||||
|
Log.d(Config.LOGTAG, "could not encode certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verification.addChild("signature").setContent(Base64.encodeToString(signature, Base64.DEFAULT));
|
||||||
|
return publish(AxolotlService.PEP_VERIFICATION+":"+deviceId, item);
|
||||||
|
}
|
||||||
|
|
||||||
public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
|
public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
|
||||||
final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
|
final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
|
||||||
final Element query = packet.query("urn:xmpp:mam:0");
|
final Element query = packet.query("urn:xmpp:mam:0");
|
||||||
|
|
|
@ -30,6 +30,7 @@ import android.security.KeyChainException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.LruCache;
|
import android.util.LruCache;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import net.java.otr4j.OtrException;
|
import net.java.otr4j.OtrException;
|
||||||
import net.java.otr4j.session.Session;
|
import net.java.otr4j.session.Session;
|
||||||
|
@ -37,21 +38,17 @@ import net.java.otr4j.session.SessionID;
|
||||||
import net.java.otr4j.session.SessionImpl;
|
import net.java.otr4j.session.SessionImpl;
|
||||||
import net.java.otr4j.session.SessionStatus;
|
import net.java.otr4j.session.SessionStatus;
|
||||||
|
|
||||||
import org.bouncycastle.asn1.x500.RDN;
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name;
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle;
|
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||||
import org.bouncycastle.asn1.x500.style.IETFUtils;
|
import org.bouncycastle.asn1.x500.style.IETFUtils;
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
|
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
|
||||||
import org.bouncycastle.jce.PrincipalUtil;
|
|
||||||
import org.bouncycastle.jce.X509Principal;
|
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
import org.openintents.openpgp.util.OpenPgpApi;
|
||||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.cert.CertificateEncodingException;
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.security.cert.CertificateParsingException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -175,7 +172,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
mMessageArchiveService.executePendingQueries(account);
|
mMessageArchiveService.executePendingQueries(account);
|
||||||
mJingleConnectionManager.cancelInTransmission();
|
mJingleConnectionManager.cancelInTransmission();
|
||||||
syncDirtyContacts(account);
|
syncDirtyContacts(account);
|
||||||
account.getAxolotlService().publishBundlesIfNeeded(true);
|
account.getAxolotlService().publishBundlesIfNeeded(true, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
|
private final OnMessageAcknowledged mOnMessageAcknowledgedListener = new OnMessageAcknowledged() {
|
||||||
|
@ -1307,17 +1304,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
|
X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
|
||||||
PrivateKey key = KeyChain.getPrivateKey(XmppConnectionService.this, alias);
|
Pair<Jid,String> info = CryptoHelper.extractJidAndName(chain[0]);
|
||||||
X500Name x500name = new JcaX509CertificateHolder(chain[0]).getSubject();
|
if (findAccountByJid(info.first) == null) {
|
||||||
String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue());
|
Account account = new Account(info.first, "");
|
||||||
String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
|
|
||||||
Jid jid = Jid.fromString(email);
|
|
||||||
if (findAccountByJid(jid) == null) {
|
|
||||||
Account account = new Account(jid, "");
|
|
||||||
account.setPrivateKeyAlias(alias);
|
account.setPrivateKeyAlias(alias);
|
||||||
account.setOption(Account.OPTION_DISABLED, true);
|
account.setOption(Account.OPTION_DISABLED, true);
|
||||||
createAccount(account);
|
createAccount(account);
|
||||||
callback.onAccountCreated(account);
|
callback.onAccountCreated(account);
|
||||||
|
try {
|
||||||
|
getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
callback.informUser(R.string.certificate_chain_is_not_trusted);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
callback.informUser(R.string.account_already_exists);
|
callback.informUser(R.string.account_already_exists);
|
||||||
}
|
}
|
||||||
|
@ -1338,6 +1336,34 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateKeyInAccount(final Account account, final String alias) {
|
||||||
|
Log.d(Config.LOGTAG,"update key in account "+alias);
|
||||||
|
try {
|
||||||
|
X509Certificate[] chain = KeyChain.getCertificateChain(XmppConnectionService.this, alias);
|
||||||
|
Pair<Jid, String> info = CryptoHelper.extractJidAndName(chain[0]);
|
||||||
|
if (account.getJid().toBareJid().equals(info.first)) {
|
||||||
|
account.setPrivateKeyAlias(alias);
|
||||||
|
databaseBackend.updateAccount(account);
|
||||||
|
try {
|
||||||
|
getMemorizingTrustManager().getNonInteractive().checkClientTrusted(chain, "RSA");
|
||||||
|
} catch (CertificateException e) {
|
||||||
|
showErrorToastInUi(R.string.certificate_chain_is_not_trusted);
|
||||||
|
}
|
||||||
|
account.getAxolotlService().regenerateKeys(true);
|
||||||
|
} else {
|
||||||
|
showErrorToastInUi(R.string.jid_does_not_match_certificate);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (KeyChainException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InvalidJidException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (CertificateEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateAccount(final Account account) {
|
public void updateAccount(final Account account) {
|
||||||
this.statusListener.onStatusChanged(account);
|
this.statusListener.onStatusChanged(account);
|
||||||
databaseBackend.updateAccount(account);
|
databaseBackend.updateAccount(account);
|
||||||
|
|
|
@ -7,6 +7,8 @@ import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.security.KeyChain;
|
||||||
|
import android.security.KeyChainAliasCallback;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -34,6 +36,7 @@ import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
|
import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
|
||||||
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
||||||
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
|
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
@ -46,7 +49,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
import eu.siacs.conversations.xmpp.pep.Avatar;
|
import eu.siacs.conversations.xmpp.pep.Avatar;
|
||||||
|
|
||||||
public class EditAccountActivity extends XmppActivity implements OnAccountUpdate,
|
public class EditAccountActivity extends XmppActivity implements OnAccountUpdate,
|
||||||
OnKeyStatusUpdated, OnCaptchaRequested {
|
OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast {
|
||||||
|
|
||||||
private AutoCompleteTextView mAccountJid;
|
private AutoCompleteTextView mAccountJid;
|
||||||
private EditText mPassword;
|
private EditText mPassword;
|
||||||
|
@ -107,7 +110,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
final Jid jid;
|
final Jid jid;
|
||||||
try {
|
try {
|
||||||
if (Config.DOMAIN_LOCK != null) {
|
if (Config.DOMAIN_LOCK != null) {
|
||||||
jid = Jid.fromParts(mAccountJid.getText().toString(),Config.DOMAIN_LOCK,null);
|
jid = Jid.fromParts(mAccountJid.getText().toString(), Config.DOMAIN_LOCK, null);
|
||||||
} else {
|
} else {
|
||||||
jid = Jid.fromString(mAccountJid.getText().toString());
|
jid = Jid.fromString(mAccountJid.getText().toString());
|
||||||
}
|
}
|
||||||
|
@ -182,7 +185,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
&& mAccount.getStatus() != Account.State.ONLINE
|
&& mAccount.getStatus() != Account.State.ONLINE
|
||||||
&& mFetchingAvatar) {
|
&& mFetchingAvatar) {
|
||||||
startActivity(new Intent(getApplicationContext(),
|
startActivity(new Intent(getApplicationContext(),
|
||||||
ManageAccountActivity.class));
|
ManageAccountActivity.class));
|
||||||
finish();
|
finish();
|
||||||
} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
|
} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
|
||||||
if (!mFetchingAvatar) {
|
if (!mFetchingAvatar) {
|
||||||
|
@ -201,6 +204,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
public void onAccountUpdate() {
|
public void onAccountUpdate() {
|
||||||
refreshUi();
|
refreshUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
|
private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -318,7 +322,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getShareableUri() {
|
protected String getShareableUri() {
|
||||||
if (mAccount!=null) {
|
if (mAccount != null) {
|
||||||
return mAccount.getShareableUri();
|
return mAccount.getShareableUri();
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
|
@ -369,7 +373,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
|
final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCheckedChanged(final CompoundButton buttonView,
|
public void onCheckedChanged(final CompoundButton buttonView,
|
||||||
final boolean isChecked) {
|
final boolean isChecked) {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
mPasswordConfirm.setVisibility(View.VISIBLE);
|
mPasswordConfirm.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
|
@ -393,6 +397,10 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
|
final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
|
||||||
final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
|
final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
|
||||||
final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
|
final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
|
||||||
|
final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
|
||||||
|
|
||||||
|
renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
|
||||||
|
|
||||||
if (mAccount != null && mAccount.isOnlineAndConnected()) {
|
if (mAccount != null && mAccount.isOnlineAndConnected()) {
|
||||||
if (!mAccount.getXmppConnection().getFeatures().blocking()) {
|
if (!mAccount.getXmppConnection().getFeatures().blocking()) {
|
||||||
showBlocklist.setVisible(false);
|
showBlocklist.setVisible(false);
|
||||||
|
@ -445,11 +453,12 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
|
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
|
||||||
if (this.mAccount != null) {
|
if (this.mAccount != null) {
|
||||||
if (this.mAccount.getPrivateKeyAlias() != null) {
|
if (this.mAccount.getPrivateKeyAlias() != null) {
|
||||||
this.mPassword.setHint(R.string.authenticate_with_certificate);
|
this.mPassword.setHint(R.string.authenticate_with_certificate);
|
||||||
if (this.mInitMode) {
|
if (this.mInitMode) {
|
||||||
this.mPassword.requestFocus();
|
this.mPassword.requestFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} updateAccountInformation(true);
|
updateAccountInformation(true);
|
||||||
}
|
}
|
||||||
} else if (this.xmppConnectionService.getAccounts().size() == 0) {
|
} else if (this.xmppConnectionService.getAccounts().size() == 0) {
|
||||||
if (getActionBar() != null) {
|
if (getActionBar() != null) {
|
||||||
|
@ -489,10 +498,24 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
case R.id.action_clear_devices:
|
case R.id.action_clear_devices:
|
||||||
showWipePepDialog();
|
showWipePepDialog();
|
||||||
break;
|
break;
|
||||||
|
case R.id.action_renew_certificate:
|
||||||
|
renewCertificate();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void renewCertificate() {
|
||||||
|
KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void alias(String alias) {
|
||||||
|
if (alias != null) {
|
||||||
|
xmppConnectionService.updateKeyInAccount(mAccount, alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateAccountInformation(boolean init) {
|
private void updateAccountInformation(boolean init) {
|
||||||
if (init) {
|
if (init) {
|
||||||
if (Config.DOMAIN_LOCK != null) {
|
if (Config.DOMAIN_LOCK != null) {
|
||||||
|
@ -517,7 +540,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
|
if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
|
||||||
this.mStats.setVisibility(View.VISIBLE);
|
this.mStats.setVisibility(View.VISIBLE);
|
||||||
this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
|
this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
|
||||||
.getLastSessionEstablished()));
|
.getLastSessionEstablished()));
|
||||||
Features features = this.mAccount.getXmppConnection().getFeatures();
|
Features features = this.mAccount.getXmppConnection().getFeatures();
|
||||||
if (features.rosterVersioning()) {
|
if (features.rosterVersioning()) {
|
||||||
this.mServerInfoRosterVersion.setText(R.string.server_info_available);
|
this.mServerInfoRosterVersion.setText(R.string.server_info_available);
|
||||||
|
@ -528,7 +551,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
this.mServerInfoCarbons.setText(R.string.server_info_available);
|
this.mServerInfoCarbons.setText(R.string.server_info_available);
|
||||||
} else {
|
} else {
|
||||||
this.mServerInfoCarbons
|
this.mServerInfoCarbons
|
||||||
.setText(R.string.server_info_unavailable);
|
.setText(R.string.server_info_unavailable);
|
||||||
}
|
}
|
||||||
if (features.mam()) {
|
if (features.mam()) {
|
||||||
this.mServerInfoMam.setText(R.string.server_info_available);
|
this.mServerInfoMam.setText(R.string.server_info_available);
|
||||||
|
@ -570,21 +593,21 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
|
this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
|
||||||
this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
|
this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
|
||||||
this.mOtrFingerprintToClipboardButton
|
this.mOtrFingerprintToClipboardButton
|
||||||
.setVisibility(View.VISIBLE);
|
.setVisibility(View.VISIBLE);
|
||||||
this.mOtrFingerprintToClipboardButton
|
this.mOtrFingerprintToClipboardButton
|
||||||
.setOnClickListener(new View.OnClickListener() {
|
.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(final View v) {
|
public void onClick(final View v) {
|
||||||
|
|
||||||
if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
|
if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
EditAccountActivity.this,
|
EditAccountActivity.this,
|
||||||
R.string.toast_message_otr_fingerprint,
|
R.string.toast_message_otr_fingerprint,
|
||||||
Toast.LENGTH_SHORT).show();
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.mOtrFingerprintBox.setVisibility(View.GONE);
|
this.mOtrFingerprintBox.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
@ -627,7 +650,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
boolean hasKeys = false;
|
boolean hasKeys = false;
|
||||||
keys.removeAllViews();
|
keys.removeAllViews();
|
||||||
for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
|
for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
|
||||||
if(ownFingerprint.equals(fingerprint)) {
|
if (ownFingerprint.equals(fingerprint)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boolean highlight = fingerprint.equals(messageFingerprint);
|
boolean highlight = fingerprint.equals(messageFingerprint);
|
||||||
|
@ -661,7 +684,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
mAccount.getAxolotlService().regenerateKeys();
|
mAccount.getAxolotlService().regenerateKeys(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
|
@ -753,4 +776,13 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onShowErrorToast(final int resId) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
package eu.siacs.conversations.utils;
|
package eu.siacs.conversations.utils;
|
||||||
|
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import org.bouncycastle.asn1.x500.style.BCStyle;
|
||||||
|
import org.bouncycastle.asn1.x500.style.IETFUtils;
|
||||||
|
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.text.Normalizer;
|
import java.text.Normalizer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -9,6 +18,8 @@ import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
|
||||||
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
|
||||||
public final class CryptoHelper {
|
public final class CryptoHelper {
|
||||||
public static final String FILETRANSFER = "?FILETRANSFERv1:";
|
public static final String FILETRANSFER = "?FILETRANSFERv1:";
|
||||||
|
@ -125,4 +136,12 @@ public final class CryptoHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException {
|
||||||
|
X500Name x500name = new JcaX509CertificateHolder(certificate).getSubject();
|
||||||
|
//String xmpp = IETFUtils.valueToString(x500name.getRDNs(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.8.5"))[0].getFirst().getValue());
|
||||||
|
String email = IETFUtils.valueToString(x500name.getRDNs(BCStyle.EmailAddress)[0].getFirst().getValue());
|
||||||
|
String name = IETFUtils.valueToString(x500name.getRDNs(BCStyle.CN)[0].getFirst().getValue());
|
||||||
|
return new Pair<>(Jid.fromString(email),name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,13 @@
|
||||||
android:title="@string/show_block_list"
|
android:title="@string/show_block_list"
|
||||||
android:showAsAction="never" />
|
android:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_renew_certificate"
|
||||||
|
android:title="@string/action_renew_certificate"
|
||||||
|
android:visible="false"
|
||||||
|
android:showAsAction="never" />
|
||||||
|
/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_server_info_show_more"
|
android:id="@+id/action_server_info_show_more"
|
||||||
android:title="@string/server_info_show_more"
|
android:title="@string/server_info_show_more"
|
||||||
|
|
|
@ -524,10 +524,13 @@
|
||||||
<string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string>
|
<string name="pref_away_when_screen_off_summary">Marks your resource as away when the screen is turned off</string>
|
||||||
<string name="pref_xa_on_silent_mode">Not available in silent mode</string>
|
<string name="pref_xa_on_silent_mode">Not available in silent mode</string>
|
||||||
<string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when phone is in silent mode</string>
|
<string name="pref_xa_on_silent_mode_summary">Marks your resource as not available when phone is in silent mode</string>
|
||||||
<string name="action_add_account_from_key">Add account from key</string>
|
<string name="action_add_account_from_key">Add account from certificate</string>
|
||||||
<string name="unable_to_parse_certificate">Unable to parse certificate</string>
|
<string name="unable_to_parse_certificate">Unable to parse certificate</string>
|
||||||
<string name="authenticate_with_certificate">Leave empty to authenticate w/ certificate</string>
|
<string name="authenticate_with_certificate">Leave empty to authenticate w/ certificate</string>
|
||||||
<string name="captcha_ocr">Captcha text</string>
|
<string name="captcha_ocr">Captcha text</string>
|
||||||
<string name="captcha_required">Captcha required</string>
|
<string name="captcha_required">Captcha required</string>
|
||||||
<string name="captcha_hint">enter the text from the image</string>
|
<string name="captcha_hint">enter the text from the image</string>
|
||||||
|
<string name="certificate_chain_is_not_trusted">Certificate chain is not trusted</string>
|
||||||
|
<string name="jid_does_not_match_certificate">Jabber ID does not match certificate</string>
|
||||||
|
<string name="action_renew_certificate">Renew certificate</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue