Merge branch 'master' of https://github.com/moparisthebest/Conversations into moparisthebest-master

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/utils/DNSHelper.java     | 121 +-
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java | 221 +++-
2 files changed, 226 insertions(+), 116 deletions(-)

Detailed changes

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

@@ -19,6 +19,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Random;
 import java.util.TreeMap;
+import java.util.Map;
 import java.util.regex.Pattern;
 
 import de.measite.minidns.Client;
@@ -57,7 +58,7 @@ public class DNSHelper {
 		if (!b.containsKey("values")) {
 			Log.d(Config.LOGTAG,"all dns queries failed. provide fallback A record");
 			ArrayList<Parcelable> values = new ArrayList<>();
-			values.add(createNamePortBundle(host,5222));
+			values.add(createNamePortBundle(host, 5222, false));
 			b.putParcelableArrayList("values",values);
 		}
 		return b;
@@ -96,57 +97,73 @@ public class DNSHelper {
 		return servers;
 	}
 
-	public static Bundle queryDNS(String host, InetAddress dnsServer) {
-		Bundle bundle = new Bundle();
-		try {
-			client.setTimeout(Config.PING_TIMEOUT * 1000);
-			String qname = "_xmpp-client._tcp." + host;
-			Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
-			DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
-
-			TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<>();
-			TreeMap<String, ArrayList<String>> ips4 = new TreeMap<>();
-			TreeMap<String, ArrayList<String>> ips6 = new TreeMap<>();
-
-			for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
-				for (Record rr : rrset) {
-					Data d = rr.getPayload();
-					if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
-						SRV srv = (SRV) d;
-						if (!priorities.containsKey(srv.getPriority())) {
-							priorities.put(srv.getPriority(),new ArrayList<SRV>());
-						}
-						priorities.get(srv.getPriority()).add(srv);
+	private static class TlsSrv {
+		private final SRV srv;
+		private final boolean tls;
+
+		public TlsSrv(SRV srv, boolean tls) {
+			this.srv = srv;
+			this.tls = tls;
+		}
+	}
+
+	private static void fillSrvMaps(final String qname, final InetAddress dnsServer, final Map<Integer, List<TlsSrv>> priorities, final Map<String, List<String>> ips4, final Map<String, List<String>> ips6, final boolean tls) throws IOException {
+		final DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress());
+		for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) {
+			for (Record rr : rrset) {
+				Data d = rr.getPayload();
+				if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) {
+					SRV srv = (SRV) d;
+					if (!priorities.containsKey(srv.getPriority())) {
+						priorities.put(srv.getPriority(),new ArrayList<TlsSrv>());
 					}
-					if (d instanceof A) {
-						A a = (A) d;
-						if (!ips4.containsKey(rr.getName())) {
-							ips4.put(rr.getName(), new ArrayList<String>());
-						}
-						ips4.get(rr.getName()).add(a.toString());
+					priorities.get(srv.getPriority()).add(new TlsSrv(srv, tls));
+				}
+				if (d instanceof A) {
+					A a = (A) d;
+					if (!ips4.containsKey(rr.getName())) {
+						ips4.put(rr.getName(), new ArrayList<String>());
 					}
-					if (d instanceof AAAA) {
-						AAAA aaaa = (AAAA) d;
-						if (!ips6.containsKey(rr.getName())) {
-							ips6.put(rr.getName(), new ArrayList<String>());
-						}
-						ips6.get(rr.getName()).add("[" + aaaa.toString() + "]");
+					ips4.get(rr.getName()).add(a.toString());
+				}
+				if (d instanceof AAAA) {
+					AAAA aaaa = (AAAA) d;
+					if (!ips6.containsKey(rr.getName())) {
+						ips6.put(rr.getName(), new ArrayList<String>());
 					}
+					ips6.get(rr.getName()).add("[" + aaaa.toString() + "]");
 				}
 			}
+		}
+	}
+
+	public static Bundle queryDNS(String host, InetAddress dnsServer) {
+		Bundle bundle = new Bundle();
+		try {
+			client.setTimeout(Config.PING_TIMEOUT * 1000);
+			final String qname = "_xmpp-client._tcp." + host;
+			final String tlsQname = "_xmpps-client._tcp." + host;
+			Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host);
+
+			final Map<Integer, List<TlsSrv>> priorities = new TreeMap<>();
+			final Map<String, List<String>> ips4 = new TreeMap<>();
+			final Map<String, List<String>> ips6 = new TreeMap<>();
+
+			fillSrvMaps(qname, dnsServer, priorities, ips4, ips6, false);
+			fillSrvMaps(tlsQname, dnsServer, priorities, ips4, ips6, true);
 
-			ArrayList<SRV> result = new ArrayList<>();
-			for (ArrayList<SRV> s : priorities.values()) {
+			final List<TlsSrv> result = new ArrayList<>();
+			for (final List<TlsSrv> s : priorities.values()) {
 				result.addAll(s);
 			}
 
-			ArrayList<Bundle> values = new ArrayList<>();
+			final ArrayList<Bundle> values = new ArrayList<>();
 			if (result.size() == 0) {
 				DNSMessage response;
 				try {
 					response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress());
 					for (int i = 0; i < response.getAnswers().length; ++i) {
-						values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload()));
+						values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
 					}
 				} catch (SocketTimeoutException e) {
 					Log.d(Config.LOGTAG,"ignoring timeout exception when querying A record on "+dnsServer.getHostAddress());
@@ -154,37 +171,38 @@ public class DNSHelper {
 				try {
 					response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
 					for (int i = 0; i < response.getAnswers().length; ++i) {
-						values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload()));
+						values.add(createNamePortBundle(host, 5222, response.getAnswers()[i].getPayload(), false));
 					}
 				} catch (SocketTimeoutException e) {
 					Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
 				}
-				values.add(createNamePortBundle(host,5222));
+				values.add(createNamePortBundle(host, 5222, false));
 				bundle.putParcelableArrayList("values", values);
 				return bundle;
 			}
-			for (SRV srv : result) {
+			for (final TlsSrv tlsSrv : result) {
+				final SRV srv = tlsSrv.srv;
 				if (ips6.containsKey(srv.getName())) {
-					values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6));
+					values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6, tlsSrv.tls));
 				} else {
 					try {
 						DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress());
 						for (int i = 0; i < response.getAnswers().length; ++i) {
-							values.add(createNamePortBundle(srv.getName(), srv.getPort(), response.getAnswers()[i].getPayload()));
+							values.add(createNamePortBundle(srv.getName(), srv.getPort(), response.getAnswers()[i].getPayload(), tlsSrv.tls));
 						}
 					} catch (SocketTimeoutException e) {
 						Log.d(Config.LOGTAG,"ignoring timeout exception when querying AAAA record on "+dnsServer.getHostAddress());
 					}
 				}
 				if (ips4.containsKey(srv.getName())) {
-					values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4));
+					values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4, tlsSrv.tls));
 				} else {
 					DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress());
 					for(int i = 0; i < response.getAnswers().length; ++i) {
-						values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload()));
+						values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload(), tlsSrv.tls));
 					}
 				}
-				values.add(createNamePortBundle(srv.getName(), srv.getPort()));
+				values.add(createNamePortBundle(srv.getName(), srv.getPort(), tlsSrv.tls));
 			}
 			bundle.putParcelableArrayList("values", values);
 		} catch (SocketTimeoutException e) {
@@ -195,28 +213,31 @@ public class DNSHelper {
 		return bundle;
 	}
 
-	private static Bundle createNamePortBundle(String name, int port) {
+	private static Bundle createNamePortBundle(String name, int port, final boolean tls) {
 		Bundle namePort = new Bundle();
 		namePort.putString("name", name);
+		namePort.putBoolean("tls", tls);
 		namePort.putInt("port", port);
 		return namePort;
 	}
 
-	private static Bundle createNamePortBundle(String name, int port, TreeMap<String, ArrayList<String>> ips) {
+	private static Bundle createNamePortBundle(String name, int port, Map<String, List<String>> ips, final boolean tls) {
 		Bundle namePort = new Bundle();
 		namePort.putString("name", name);
+		namePort.putBoolean("tls", tls);
 		namePort.putInt("port", port);
 		if (ips!=null) {
-			ArrayList<String> ip = ips.get(name);
+			List<String> ip = ips.get(name);
 			Collections.shuffle(ip, new Random());
 			namePort.putString("ip", ip.get(0));
 		}
 		return namePort;
 	}
 
-	private static Bundle createNamePortBundle(String name, int port, Data data) {
+	private static Bundle createNamePortBundle(String name, int port, Data data, final boolean tls) {
 		Bundle namePort = new Bundle();
 		namePort.putString("name", name);
+		namePort.putBoolean("tls", tls);
 		namePort.putInt("port", port);
 		if (data instanceof A) {
 			namePort.putString("ip", data.toString());

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

@@ -20,7 +20,7 @@ import org.xmlpull.v1.XmlPullParserException;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
+import java.lang.reflect.Method;
 import java.math.BigInteger;
 import java.net.ConnectException;
 import java.net.IDN;
@@ -247,6 +247,7 @@ public class XmppConnection implements Runnable {
 				}
 				Log.d(Config.LOGTAG,account.getJid().toBareJid()+": connect to "+destination+" via TOR");
 				socket = SocksSocketFactory.createSocketOverTor(destination,account.getPort());
+				startXmpp();
 			} else if (DNSHelper.isIp(account.getServer().toString())) {
 				socket = new Socket();
 				try {
@@ -254,13 +255,12 @@ public class XmppConnection implements Runnable {
 				} catch (IOException e) {
 					throw new UnknownHostException();
 				}
+				startXmpp();
 			} else {
-				final Bundle result = DNSHelper.getSRVRecord(account.getServer(),mXmppConnectionService);
+				final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
 				final ArrayList<Parcelable>values = result.getParcelableArrayList("values");
-				int i = 0;
-				boolean socketError = true;
-				while (socketError && values.size() > i) {
-					final Bundle namePort = (Bundle) values.get(i);
+				for(Iterator<Parcelable> iterator = values.iterator(); iterator.hasNext();) {
+					final Bundle namePort = (Bundle) iterator.next();
 					try {
 						String srvRecordServer;
 						try {
@@ -271,48 +271,55 @@ public class XmppConnection implements Runnable {
 						}
 						final int srvRecordPort = namePort.getInt("port");
 						final String srvIpServer = namePort.getString("ip");
+						// if tls is true, encryption is implied and must not be started
+						features.encryptionEnabled = namePort.getBoolean("tls");
 						final InetSocketAddress addr;
 						if (srvIpServer != null) {
 							addr = new InetSocketAddress(srvIpServer, srvRecordPort);
 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
 									+ ": using values from dns " + srvRecordServer
-									+ "[" + srvIpServer + "]:" + srvRecordPort);
+									+ "[" + srvIpServer + "]:" + srvRecordPort + " tls: " + features.encryptionEnabled);
 						} else {
 							addr = new InetSocketAddress(srvRecordServer, srvRecordPort);
 							Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
 									+ ": using values from dns "
-									+ srvRecordServer + ":" + srvRecordPort);
+									+ srvRecordServer + ":" + srvRecordPort + " tls: " + features.encryptionEnabled);
 						}
-						socket = new Socket();
-						socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
-						socketError = false;
+
+						if (!features.encryptionEnabled) {
+							socket = new Socket();
+							socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
+						} else {
+							final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
+							socket = tlsFactoryVerifier.factory.createSocket();
+
+							if (socket == null) {
+								throw new IOException("could not initialize ssl socket");
+							}
+
+							setSSLSocketSecurity((SSLSocket) socket);
+							this.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
+							this.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
+
+							socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
+
+							if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
+								Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
+								throw new SecurityException();
+							}
+						}
+
+						if(startXmpp())
+							break; // successfully connected to server that speaks xmpp
 					} catch (final Throwable e) {
 						Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() +"("+e.getClass().getName()+")");
-						i++;
+						if (!iterator.hasNext()) {
+							throw new UnknownHostException();
+						}
 					}
 				}
-				if (socketError) {
-					throw new UnknownHostException();
-				}
-			}
-			final OutputStream out = socket.getOutputStream();
-			tagWriter.setOutputStream(out);
-			final InputStream in = socket.getInputStream();
-			tagReader.setInputStream(in);
-			tagWriter.beginDocument();
-			sendStartStream();
-			Tag nextTag;
-			while ((nextTag = tagReader.readTag()) != null) {
-				if (nextTag.isStart("stream")) {
-					processStream();
-					break;
-				} else {
-					throw new IOException("unknown tag on connect");
-				}
-			}
-			if (socket.isConnected()) {
-				socket.close();
 			}
+			processStream();
 		} catch (final IncompatibleServerException e) {
 			this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
 		} catch (final SecurityException e) {
@@ -344,6 +351,99 @@ public class XmppConnection implements Runnable {
 		}
 	}
 
+	/**
+	 * Starts xmpp protocol, call after connecting to socket
+	 * @return true if server returns with valid xmpp, false otherwise
+	 * @throws IOException Unknown tag on connect
+	 * @throws XmlPullParserException Bad Xml
+	 * @throws NoSuchAlgorithmException Other error
+     */
+	private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
+		tagWriter.setOutputStream(socket.getOutputStream());
+		tagReader.setInputStream(socket.getInputStream());
+		tagWriter.beginDocument();
+		sendStartStream();
+		Tag nextTag;
+		while ((nextTag = tagReader.readTag()) != null) {
+			if (nextTag.isStart("stream")) {
+				return true;
+			} else {
+				throw new IOException("unknown tag on connect");
+			}
+		}
+		if (socket.isConnected()) {
+			socket.close();
+		}
+		return false;
+	}
+
+	private void setSNIHost(final SSLSocketFactory factory, final SSLSocket socket, final String hostname) {
+		if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+			((android.net.SSLCertificateSocketFactory) factory).setHostname(socket, hostname);
+		} else {
+			try {
+				socket.getClass().getMethod("setHostname", String.class).invoke(socket, hostname);
+			} catch (Throwable e) {
+				// ignore any error, we just can't set the hostname...
+			}
+		}
+	}
+
+	private void setAlpnProtocol(final SSLSocketFactory factory, final SSLSocket socket, final String protocol) {
+		try {
+			if (factory instanceof android.net.SSLCertificateSocketFactory && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+				// can't call directly because of @hide?
+				//((android.net.SSLCertificateSocketFactory)factory).setAlpnProtocols(new byte[][]{protocol.getBytes("UTF-8")});
+				android.net.SSLCertificateSocketFactory.class.getMethod("setAlpnProtocols", byte[][].class).invoke(socket, new Object[]{new byte[][]{protocol.getBytes("UTF-8")}});
+			} else {
+				final Method method = socket.getClass().getMethod("setAlpnProtocols", byte[].class);
+				// the concatenation of 8-bit, length prefixed protocol names, just one in our case...
+				// http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4
+				final byte[] protocolUTF8Bytes = protocol.getBytes("UTF-8");
+				final byte[] lengthPrefixedProtocols = new byte[protocolUTF8Bytes.length + 1];
+				lengthPrefixedProtocols[0] = (byte) protocol.length(); // cannot be over 255 anyhow
+				System.arraycopy(protocolUTF8Bytes, 0, lengthPrefixedProtocols, 1, protocolUTF8Bytes.length);
+				method.invoke(socket, new Object[]{lengthPrefixedProtocols});
+			}
+		} catch (Throwable e) {
+			// ignore any error, we just can't set the alpn protocol...
+		}
+	}
+
+	private static class TlsFactoryVerifier {
+		private final SSLSocketFactory factory;
+		private final HostnameVerifier verifier;
+
+		public TlsFactoryVerifier(final SSLSocketFactory factory, final HostnameVerifier verifier) throws IOException {
+			this.factory = factory;
+			this.verifier = verifier;
+			if (factory == null || verifier == null) {
+				throw new IOException("could not setup ssl");
+			}
+		}
+	}
+
+	private TlsFactoryVerifier getTlsFactoryVerifier() throws NoSuchAlgorithmException, KeyManagementException, IOException {
+		final SSLContext sc = SSLContext.getInstance("TLS");
+		MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
+		KeyManager[] keyManager;
+		if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
+			keyManager = new KeyManager[]{mKeyManager};
+		} else {
+			keyManager = null;
+		}
+		sc.init(keyManager, new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()}, mXmppConnectionService.getRNG());
+		final SSLSocketFactory factory = sc.getSocketFactory();
+		final HostnameVerifier verifier;
+		if (mInteractive) {
+			verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
+		} else {
+			verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
+		}
+
+		return new TlsFactoryVerifier(factory, verifier);
+	}
+
 	@Override
 	public void run() {
 		try {
@@ -605,53 +705,42 @@ public class XmppConnection implements Runnable {
 		tagWriter.writeTag(startTLS);
 	}
 
+	private void setSSLSocketSecurity(final SSLSocket sslSocket) throws NoSuchAlgorithmException {
+		final String[] supportProtocols;
+		final Collection<String> supportedProtocols = new LinkedList<>(
+				Arrays.asList(sslSocket.getSupportedProtocols()));
+		supportedProtocols.remove("SSLv3");
+		supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
+
+		sslSocket.setEnabledProtocols(supportProtocols);
+
+		final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
+				sslSocket.getSupportedCipherSuites());
+		//Log.d(Config.LOGTAG, "Using ciphers: " + Arrays.toString(cipherSuites));
+		if (cipherSuites.length > 0) {
+			sslSocket.setEnabledCipherSuites(cipherSuites);
+		}
+	}
+
 	private void switchOverToTls(final Tag currentTag) throws XmlPullParserException, IOException {
 		tagReader.readTag();
 		try {
-			final SSLContext sc = SSLContext.getInstance("TLS");
-			MemorizingTrustManager trustManager = this.mXmppConnectionService.getMemorizingTrustManager();
-			KeyManager[] keyManager;
-			if (account.getPrivateKeyAlias() != null && account.getPassword().isEmpty()) {
-				keyManager = new KeyManager[]{ mKeyManager };
-			} else {
-				keyManager = null;
-			}
-			sc.init(keyManager,new X509TrustManager[]{mInteractive ? trustManager : trustManager.getNonInteractive()},mXmppConnectionService.getRNG());
-			final SSLSocketFactory factory = sc.getSocketFactory();
-			final HostnameVerifier verifier;
-			if (mInteractive) {
-				verifier = trustManager.wrapHostnameVerifier(new XmppDomainVerifier());
-			} else {
-				verifier = trustManager.wrapHostnameVerifierNonInteractive(new XmppDomainVerifier());
-			}
+			final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
 			final InetAddress address = socket == null ? null : socket.getInetAddress();
 
-			if (factory == null || address == null || verifier == null) {
+			if (address == null) {
 				throw new IOException("could not setup ssl");
 			}
 
-			final SSLSocket sslSocket = (SSLSocket) factory.createSocket(socket,address.getHostAddress(), socket.getPort(),true);
+			final SSLSocket sslSocket = (SSLSocket) tlsFactoryVerifier.factory.createSocket(socket, address.getHostAddress(), socket.getPort(), true);
 
 			if (sslSocket == null) {
 				throw new IOException("could not initialize ssl socket");
 			}
 
-			final String[] supportProtocols;
-			final Collection<String> supportedProtocols = new LinkedList<>(
-					Arrays.asList(sslSocket.getSupportedProtocols()));
-			supportedProtocols.remove("SSLv3");
-			supportProtocols = supportedProtocols.toArray(new String[supportedProtocols.size()]);
-
-			sslSocket.setEnabledProtocols(supportProtocols);
-
-			final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites(
-					sslSocket.getSupportedCipherSuites());
-			//Log.d(Config.LOGTAG, "Using ciphers: " + Arrays.toString(cipherSuites));
-			if (cipherSuites.length > 0) {
-				sslSocket.setEnabledCipherSuites(cipherSuites);
-			}
+			setSSLSocketSecurity(sslSocket);
 
-			if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) {
+			if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), sslSocket.getSession())) {
 				Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed");
 				throw new SecurityException();
 			}