introduced code to verify omemo device keys with x509 certificates.

Daniel Gultsch created

cleaned up TrustKeysActivity to automatically close if there is nothing to do

Change summary

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java  | 86 
src/main/java/eu/siacs/conversations/entities/Account.java               |  3 
src/main/java/eu/siacs/conversations/generator/IqGenerator.java          | 10 
src/main/java/eu/siacs/conversations/parser/IqParser.java                | 29 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java |  9 
src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java           | 80 
src/main/java/eu/siacs/conversations/utils/CryptoHelper.java             | 28 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java            | 10 
8 files changed, 190 insertions(+), 65 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -1,10 +1,10 @@
 package eu.siacs.conversations.crypto.axolotl;
 
 import android.security.KeyChain;
-import android.security.KeyChainException;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.util.Log;
+import android.util.Pair;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.whispersystems.libaxolotl.AxolotlAddress;
@@ -20,11 +20,9 @@ import org.whispersystems.libaxolotl.state.PreKeyRecord;
 import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
 import org.whispersystems.libaxolotl.util.KeyHelper;
 
-import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.Security;
 import java.security.Signature;
-import java.security.SignatureException;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -43,12 +41,13 @@ import eu.siacs.conversations.parser.IqParser;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
 import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
 import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 import eu.siacs.conversations.xmpp.jid.Jid;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 
-public class AxolotlService {
+public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 
 	public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
 	public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
@@ -71,6 +70,15 @@ public class AxolotlService {
 	private int numPublishTriesOnEmptyPep = 0;
 	private boolean pepBroken = false;
 
+	@Override
+	public void onAdvancedStreamFeaturesAvailable(Account account) {
+		if (account.getXmppConnection().getFeatures().pep()) {
+			publishBundlesIfNeeded(true, false);
+		} else {
+			Log.d(Config.LOGTAG,account.getJid().toBareJid()+": skipping OMEMO initialization");
+		}
+	}
+
 	private static class AxolotlAddressMap<T> {
 		protected Map<String, Map<Integer, T>> map;
 		protected final Object MAP_LOCK = new Object();
@@ -402,7 +410,6 @@ public class AxolotlService {
 			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) {
@@ -565,6 +572,50 @@ public class AxolotlService {
 		axolotlStore.setFingerprintTrust(fingerprint, trust);
 	}
 
+	private void verifySessionWithPEP(final XmppAxolotlSession session, final IdentityKey identityKey) {
+		final AxolotlAddress address = session.getRemoteAddress();
+		try {
+			IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveVerificationForDevice(Jid.fromString(address.getName()), address.getDeviceId());
+			mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+				@Override
+				public void onIqPacketReceived(Account account, IqPacket packet) {
+					Pair<X509Certificate[],byte[]> verification = mXmppConnectionService.getIqParser().verification(packet);
+					if (verification != null) {
+						try {
+							Signature verifier = Signature.getInstance("sha256WithRSA");
+							verifier.initVerify(verification.first[0]);
+							verifier.update(identityKey.serialize());
+							if (verifier.verify(verification.second)) {
+								try {
+									mXmppConnectionService.getMemorizingTrustManager().getNonInteractive().checkClientTrusted(verification.first, "RSA");
+									Log.d(Config.LOGTAG, "verified session with x.509 signature");
+									setFingerprintTrust(session.getFingerprint(), XmppAxolotlSession.Trust.TRUSTED);
+								} catch (Exception e) {
+									Log.d(Config.LOGTAG,"could not verify certificate");
+								}
+							}
+						} catch (Exception e) {
+							Log.d(Config.LOGTAG, "error during verification " + e.getMessage());
+						}
+					} else {
+						Log.d(Config.LOGTAG, " unable to parse verification");
+					}
+					finishBuildingSessionsFromPEP(address);
+				}
+			});
+		} catch (InvalidJidException e) {
+			finishBuildingSessionsFromPEP(address);
+		}
+	}
+
+	private void finishBuildingSessionsFromPEP(final AxolotlAddress address) {
+		AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
+		if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
+				&& !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) {
+			mXmppConnectionService.keyStatusUpdated();
+		}
+	}
+
 	private void buildSessionFromPEP(final AxolotlAddress address) {
 		Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.toString());
 		if (address.getDeviceId() == getOwnDeviceId()) {
@@ -576,13 +627,6 @@ public class AxolotlService {
 					Jid.fromString(address.getName()), address.getDeviceId());
 			Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket);
 			mXmppConnectionService.sendIqPacket(account, bundlesPacket, new OnIqPacketReceived() {
-				private void finish() {
-					AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
-					if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
-							&& !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) {
-						mXmppConnectionService.keyStatusUpdated();
-					}
-				}
 
 				@Override
 				public void onIqPacketReceived(Account account, IqPacket packet) {
@@ -594,7 +638,7 @@ public class AxolotlService {
 						if (preKeyBundleList.isEmpty() || bundle == null) {
 							Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet);
 							fetchStatusMap.put(address, FetchStatus.ERROR);
-							finish();
+							finishBuildingSessionsFromPEP(address);
 							return;
 						}
 						Random random = new Random();
@@ -602,7 +646,7 @@ public class AxolotlService {
 						if (preKey == null) {
 							//should never happen
 							fetchStatusMap.put(address, FetchStatus.ERROR);
-							finish();
+							finishBuildingSessionsFromPEP(address);
 							return;
 						}
 
@@ -617,17 +661,21 @@ public class AxolotlService {
 							XmppAxolotlSession session = new XmppAxolotlSession(account, axolotlStore, address, bundle.getIdentityKey().getFingerprint().replaceAll("\\s", ""));
 							sessions.put(address, session);
 							fetchStatusMap.put(address, FetchStatus.SUCCESS);
+							if (Config.X509_VERIFICATION) {
+								verifySessionWithPEP(session, bundle.getIdentityKey());
+							} else {
+								finishBuildingSessionsFromPEP(address);
+							}
 						} catch (UntrustedIdentityException | InvalidKeyException e) {
 							Log.e(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error building session for " + address + ": "
 									+ e.getClass().getName() + ", " + e.getMessage());
 							fetchStatusMap.put(address, FetchStatus.ERROR);
+							finishBuildingSessionsFromPEP(address);
 						}
-
-						finish();
 					} else {
 						fetchStatusMap.put(address, FetchStatus.ERROR);
 						Log.d(Config.LOGTAG, getLogprefix(account) + "Error received while building session:" + packet.findChild("error"));
-						finish();
+						finishBuildingSessionsFromPEP(address);
 					}
 				}
 			});
@@ -699,9 +747,9 @@ public class AxolotlService {
 		return newSessions;
 	}
 
-	public boolean hasPendingKeyFetches(Conversation conversation) {
+	public boolean hasPendingKeyFetches(Account account, Contact contact) {
 		AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
-		AxolotlAddress foreignAddress = new AxolotlAddress(conversation.getJid().toBareJid().toString(), 0);
+		AxolotlAddress foreignAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
 		return fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
 				|| fetchStatusMap.getAll(foreignAddress).containsValue(FetchStatus.PENDING);
 

src/main/java/eu/siacs/conversations/entities/Account.java 🔗

@@ -297,6 +297,9 @@ public class Account extends AbstractEntity {
 	public void initAccountServices(final XmppConnectionService context) {
 		this.mOtrService = new OtrService(context, this);
 		this.axolotlService = new AxolotlService(this, context);
+		if (xmppConnection != null) {
+			xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
+		}
 	}
 
 	public OtrService getOtrService() {

src/main/java/eu/siacs/conversations/generator/IqGenerator.java 🔗

@@ -137,9 +137,13 @@ public class IqGenerator extends AbstractGenerator {
 
 	public IqPacket retrieveBundlesForDevice(final Jid to, final int deviceid) {
 		final IqPacket packet = retrieve(AxolotlService.PEP_BUNDLES+":"+deviceid, null);
-		if(to != null) {
-			packet.setTo(to);
-		}
+		packet.setTo(to);
+		return packet;
+	}
+
+	public IqPacket retrieveVerificationForDevice(final Jid to, final int deviceid) {
+		final IqPacket packet = retrieve(AxolotlService.PEP_VERIFICATION+":"+deviceid, null);
+		packet.setTo(to);
 		return packet;
 	}
 

src/main/java/eu/siacs/conversations/parser/IqParser.java 🔗

@@ -3,6 +3,7 @@ package eu.siacs.conversations.parser;
 import android.support.annotation.NonNull;
 import android.util.Base64;
 import android.util.Log;
+import android.util.Pair;
 
 import org.whispersystems.libaxolotl.IdentityKey;
 import org.whispersystems.libaxolotl.InvalidKeyException;
@@ -10,6 +11,10 @@ import org.whispersystems.libaxolotl.ecc.Curve;
 import org.whispersystems.libaxolotl.ecc.ECPublicKey;
 import org.whispersystems.libaxolotl.state.PreKeyBundle;
 
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -204,6 +209,30 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
 		return preKeyRecords;
 	}
 
+	public Pair<X509Certificate[],byte[]> verification(final IqPacket packet) {
+		Element item = getItem(packet);
+		Element verification = item != null ? item.findChild("verification",AxolotlService.PEP_PREFIX) : null;
+		Element chain = verification != null ? verification.findChild("chain") : null;
+		Element signature = verification != null ? verification.findChild("signature") : null;
+		if (chain != null && signature != null) {
+			List<Element> certElements = chain.getChildren();
+			X509Certificate[] certificates = new X509Certificate[certElements.size()];
+			try {
+				CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+				int i = 0;
+				for(Element cert : certElements) {
+					certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(),Base64.DEFAULT)));
+					++i;
+				}
+				return new Pair<>(certificates,Base64.decode(signature.getContent(),Base64.DEFAULT));
+			} catch (CertificateException e) {
+				return null;
+			}
+		} else {
+			return null;
+		}
+	}
+
 	public PreKeyBundle bundle(final IqPacket bundle) {
 		Element bundleItem = getItem(bundle);
 		if(bundleItem == null) {

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -60,6 +60,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.PgpEngine;
+import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Blockable;
@@ -256,7 +257,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 			mMessageArchiveService.executePendingQueries(account);
 			mJingleConnectionManager.cancelInTransmission();
 			syncDirtyContacts(account);
-			account.getAxolotlService().publishBundlesIfNeeded(true, false);
 		}
 	};
 	private OnStatusChanged statusListener = new OnStatusChanged() {
@@ -459,7 +459,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 		final String action = intent == null ? null : intent.getAction();
 		boolean interactive = false;
 		if (action != null) {
-			Log.d(Config.LOGTAG, "action: " + action);
 			switch (action) {
 				case ConnectivityManager.CONNECTIVITY_ACTION:
 					if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
@@ -760,6 +759,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 		connection.setOnBindListener(this.mOnBindListener);
 		connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
 		connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
+		AxolotlService axolotlService = account.getAxolotlService();
+		if (axolotlService != null) {
+			connection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
+		}
 		return connection;
 	}
 
@@ -1066,8 +1069,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 				public void run() {
 					Log.d(Config.LOGTAG, "restoring roster");
 					for (Account account : accounts) {
-						databaseBackend.readRoster(account.getRoster());
 						account.initAccountServices(XmppConnectionService.this);
+						databaseBackend.readRoster(account.getRoster());
 					}
 					getBitmapCache().evictAll();
 					Looper.prepare();

src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java 🔗

@@ -8,6 +8,7 @@ import android.widget.Button;
 import android.widget.CompoundButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import org.whispersystems.libaxolotl.IdentityKey;
 
@@ -16,6 +17,7 @@ import java.util.Map;
 import java.util.Set;
 
 import eu.siacs.conversations.R;
+import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
@@ -27,11 +29,11 @@ import eu.siacs.conversations.xmpp.jid.Jid;
 public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdated {
 	private Jid accountJid;
 	private Jid contactJid;
-	private boolean hasOtherTrustedKeys = false;
-	private boolean hasPendingFetches = false;
+
 	private boolean hasNoTrustedKeys = true;
 
 	private Contact contact;
+	private Account mAccount;
 	private TextView keyErrorMessage;
 	private LinearLayout keyErrorMessageCard;
 	private TextView ownKeysTitle;
@@ -50,10 +52,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
 		@Override
 		public void onClick(View v) {
 			commitTrusts();
-			Intent data = new Intent();
-			data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
-			setResult(RESULT_OK, data);
-			finish();
+			finishOk();
 		}
 	};
 
@@ -157,11 +156,11 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
 			foreignKeysTitle.setText(contactJid.toString());
 			foreignKeysCard.setVisibility(View.VISIBLE);
 		}
-		if(hasPendingFetches) {
+		if(hasPendingKeyFetches()) {
 			setFetching();
 			lock();
 		} else {
-			if (!hasForeignKeys && !hasOtherTrustedKeys) {
+			if (!hasForeignKeys && hasNoOtherTrustedKeys()) {
 				keyErrorMessageCard.setVisibility(View.VISIBLE);
 				keyErrorMessage.setText(R.string.error_no_keys_to_trust);
 				ownKeys.removeAllViews(); ownKeysCard.setVisibility(View.GONE);
@@ -172,12 +171,13 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
 		}
 	}
 
-	private void getFingerprints(final Account account) {
-		Set<IdentityKey> ownKeysSet = account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED);
-		Set<IdentityKey> foreignKeysSet = account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact);
+	private boolean reloadFingerprints() {
+		AxolotlService service = this.mAccount.getAxolotlService();
+		Set<IdentityKey> ownKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED);
+		Set<IdentityKey> foreignKeysSet = service.getKeysWithTrust(XmppAxolotlSession.Trust.UNDECIDED, contact);
 		if (hasNoTrustedKeys) {
-			ownKeysSet.addAll(account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED));
-			foreignKeysSet.addAll(account.getAxolotlService().getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact));
+			ownKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED));
+			foreignKeysSet.addAll(service.getKeysWithTrust(XmppAxolotlSession.Trust.UNTRUSTED, contact));
 		}
 		for(final IdentityKey identityKey : ownKeysSet) {
 			if(!ownKeysToTrust.containsKey(identityKey)) {
@@ -189,39 +189,55 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
 				foreignKeysToTrust.put(identityKey.getFingerprint().replaceAll("\\s", ""), false);
 			}
 		}
+		return ownKeysSet.size() + foreignKeysSet.size() > 0;
 	}
 
 	@Override
 	public void onBackendConnected() {
 		if ((accountJid != null) && (contactJid != null)) {
-			final Account account = xmppConnectionService
-				.findAccountByJid(accountJid);
-			if (account == null) {
+			this.mAccount = xmppConnectionService.findAccountByJid(accountJid);
+			if (this.mAccount == null) {
 				return;
 			}
-			this.contact = account.getRoster().getContact(contactJid);
+			this.contact = this.mAccount.getRoster().getContact(contactJid);
 			ownKeysToTrust.clear();
 			foreignKeysToTrust.clear();
-			getFingerprints(account);
-
-			if(account.getAxolotlService().getNumTrustedKeys(contact) > 0) {
-				hasOtherTrustedKeys = true;
-			}
-			Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, false);
-			if(account.getAxolotlService().hasPendingKeyFetches(conversation)) {
-				hasPendingFetches = true;
-			}
-
+			reloadFingerprints();
 			populateView();
 		}
 	}
 
+	private boolean hasNoOtherTrustedKeys() {
+		return mAccount == null || mAccount.getAxolotlService().getNumTrustedKeys(contact) == 0;
+	}
+
+	private boolean hasPendingKeyFetches() {
+		return mAccount != null && contact != null && mAccount.getAxolotlService().hasPendingKeyFetches(mAccount,contact);
+	}
+
+
 	@Override
 	public void onKeyStatusUpdated() {
-		final Account account = xmppConnectionService.findAccountByJid(accountJid);
-		hasPendingFetches = false;
-		getFingerprints(account);
-		refreshUi();
+		boolean keysToTrust = reloadFingerprints();
+		if (keysToTrust || hasPendingKeyFetches() || hasNoOtherTrustedKeys()) {
+			refreshUi();
+		} else {
+			runOnUiThread(new Runnable() {
+				@Override
+				public void run() {
+					Toast.makeText(TrustKeysActivity.this, "Nothing to do", Toast.LENGTH_SHORT).show();
+					finishOk();
+				}
+			});
+
+		}
+	}
+
+	private void finishOk() {
+		Intent data = new Intent();
+		data.putExtra("choice", getIntent().getIntExtra("choice", ConversationActivity.ATTACHMENT_CHOICE_INVALID));
+		setResult(RESULT_OK, data);
+		finish();
 	}
 
 	private void commitTrusts() {
@@ -248,7 +264,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate
 	}
 
 	private void lockOrUnlockAsNeeded() {
-		if (!hasOtherTrustedKeys && !foreignKeysToTrust.values().contains(true)){
+		if (hasNoOtherTrustedKeys() && !foreignKeysToTrust.values().contains(true)){
 			lock();
 		} else {
 			unlock();

src/main/java/eu/siacs/conversations/utils/CryptoHelper.java 🔗

@@ -1,16 +1,21 @@
 package eu.siacs.conversations.utils;
 
+import android.util.Log;
 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 org.bouncycastle.jce.PrincipalUtil;
 
 import java.security.SecureRandom;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.security.cert.X509Extension;
 import java.text.Normalizer;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
@@ -137,11 +142,26 @@ public final class CryptoHelper {
 		}
 	}
 
-	public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException {
+	public static Pair<Jid,String> extractJidAndName(X509Certificate certificate) throws CertificateEncodingException, InvalidJidException, CertificateParsingException {
+		Collection<List<?>> alternativeNames = certificate.getSubjectAlternativeNames();
+		List<String> emails = new ArrayList<>();
+		if (alternativeNames != null) {
+			for(List<?> san : alternativeNames) {
+				Integer type = (Integer) san.get(0);
+				if (type == 1) {
+					emails.add((String) san.get(1));
+				}
+			}
+		}
 		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());
+		if (emails.size() == 0) {
+			emails.add(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);
+		if (emails.size() >= 1) {
+			return new Pair<>(Jid.fromString(emails.get(0)), name);
+		} else {
+			return null;
+		}
 	}
 }

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -947,11 +947,10 @@ public class XmppConnection implements Runnable {
 							}
 						}
 						disco.put(jid, info);
-						if (account.getServer().equals(jid)) {
+						if ((jid.equals(account.getServer()) || jid.equals(account.getJid().toBareJid()))
+								&& disco.containsKey(account.getServer())
+								&& disco.containsKey(account.getJid().toBareJid())) {
 							enableAdvancedStreamFeatures();
-							for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
-								listener.onAdvancedStreamFeaturesAvailable(account);
-							}
 						}
 					} else {
 						Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not query disco info for "+jid.toString());
@@ -969,6 +968,9 @@ public class XmppConnection implements Runnable {
 			Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list");
 			this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
 		}
+		for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
+			listener.onAdvancedStreamFeaturesAvailable(account);
+		}
 	}
 
 	private void sendServiceDiscoveryItems(final Jid server) {