@Override public boolean verify(String hostname, SSLSession session) { if (defaultVerifier != null) { return defaultVerifier.verify(hostname, session); } return false; }
protected boolean verify(String hostname, SSLSession session, boolean interactive) { LOGGER.log( Level.FINE, "hostname verifier for " + hostname + ", trying default verifier first"); // if the default verifier accepts the hostname, we are done if (defaultVerifier.verify(hostname, session)) { LOGGER.log(Level.FINE, "default verifier accepted " + hostname); return true; } // otherwise, we check if the hostname is an alias for this cert in our keystore try { X509Certificate cert = (X509Certificate) session.getPeerCertificates()[0]; // Log.d(TAG, "cert: " + cert); if (cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) { LOGGER.log(Level.FINE, "certificate for " + hostname + " is in our keystore. accepting."); return true; } else { LOGGER.log( Level.FINE, "server " + hostname + " provided wrong certificate, asking user."); if (interactive) { return interactHostname(cert, hostname); } else { return false; } } } catch (Exception e) { e.printStackTrace(); return false; } }
/** * Return an {@code SSLSocket} that is not only connected but has also passed hostname * verification. * * @param hostnameVerifier Used to verify the hostname we connected to is an acceptable match for * the peer certificate chain of the SSLSession. */ public SSLSocket verifySecureSocketHostname(HostnameVerifier hostnameVerifier) throws IOException { if (!hostnameVerifier.verify(address.uriHost, unverifiedSocket.getSession())) { throw new IOException("Hostname '" + address.uriHost + "' was not verified"); } sslSocket = unverifiedSocket; return sslSocket; }
// Server identity checking is done according to RFC 2818: HTTP over TLS // Section 3.1 Server Identity private void checkURLSpoofing(HostnameVerifier hostnameVerifier) throws IOException { // // Get authenticated server name, if any // boolean done = false; String host = url.getHost(); // if IPv6 strip off the "[]" if (host != null && host.startsWith("[") && host.endsWith("]")) { host = host.substring(1, host.length() - 1); } Certificate[] peerCerts = null; try { // get the subject's certificate peerCerts = session.getPeerCertificates(); X509Certificate peerCert; if (peerCerts[0] instanceof java.security.cert.X509Certificate) { peerCert = (java.security.cert.X509Certificate) peerCerts[0]; } else { throw new SSLPeerUnverifiedException(""); } HostnameChecker checker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS); checker.match(host, peerCert); // if it doesn't throw an exception, we passed. Return. return; } catch (SSLPeerUnverifiedException e) { // // client explicitly changed default policy and enabled // anonymous ciphers; we can't check the standard policy // // ignore } catch (java.security.cert.CertificateException cpe) { // ignore } // Assume the peerCerts are already cloned. String cipher = session.getCipherSuite(); if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) { return; } else if ((hostnameVerifier != null) && (hostnameVerifier.verify(host, session))) { return; } serverSocket.close(); session.invalidate(); throw new IOException("HTTPS hostname wrong: should be <" + url.getHost() + ">"); }
@Override public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException { String peerHost = this.conn.getRequestProperty("Host"); if (peerHost == null) peerHost = host; Log.i(TAG, "customized createSocket. host: " + peerHost); InetAddress address = plainSocket.getInetAddress(); if (autoClose) { // we don't need the plainSocket plainSocket.close(); } // create and connect SSL socket, but don't do hostname/certificate verification yet SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0); SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(address, port); // enable TLSv1.1/1.2 if available ssl.setEnabledProtocols(ssl.getSupportedProtocols()); // set up SNI before the handshake if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { Log.i(TAG, "Setting SNI hostname"); sslSocketFactory.setHostname(ssl, peerHost); } else { Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection"); try { java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class); setHostnameMethod.invoke(ssl, peerHost); } catch (Exception e) { Log.w(TAG, "SNI not useable", e); } } // verify hostname and certificate SSLSession session = ssl.getSession(); if (!hostnameVerifier.verify(peerHost, session)) throw new SSLPeerUnverifiedException("Cannot verify hostname: " + peerHost); Log.i( TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() + " using " + session.getCipherSuite()); return ssl; }
/** * Lightweight version of SSLCertificateSocketFactory.verifyHostname, which provides this service * but is not in the public API. * * <p>Verify the hostname of the certificate used by the other end of a connected socket. You MUST * call this if you did not supply a hostname to SSLCertificateSocketFactory.createSocket(). It is * harmless to call this method redundantly if the hostname has already been verified. * * <p>Wildcard certificates are allowed to verify any matching hostname, so "foo.bar.example.com" * is verified if the peer has a certificate for "*.example.com". * * @param socket An SSL socket which has been connected to a server * @param hostname The expected hostname of the remote server * @throws IOException if something goes wrong handshaking with the server * @throws SSLPeerUnverifiedException if the server cannot prove its identity */ private static void verifyHostname(Socket socket, String hostname) throws IOException { // The code at the start of OpenSSLSocketImpl.startHandshake() // ensures that the call is idempotent, so we can safely call it. SSLSocket ssl = (SSLSocket) socket; ssl.startHandshake(); SSLSession session = ssl.getSession(); if (session == null) { throw new SSLException("Cannot verify SSL socket without session"); } // TODO: Instead of reporting the name of the server we think we're connecting to, // we should be reporting the bad name in the certificate. Unfortunately this is buried // in the verifier code and is not available in the verifier API, and extracting the // CN & alts is beyond the scope of this patch. if (!HOSTNAME_VERIFIER.verify(hostname, session)) { throw new SSLPeerUnverifiedException( "Certificate hostname not useable for server: " + hostname); } }
/** * Verify the hostname of the certificate used by the other end of a connected socket. You MUST * call this if you did not supply a hostname to {@link #createSocket()}. It is harmless to call * this method redundantly if the hostname has already been verified. * * <p>Wildcard certificates are allowed to verify any matching hostname, so "foo.bar.example.com" * is verified if the peer has a certificate for "*.example.com". * * @param socket An SSL socket which has been connected to a server * @param hostname The expected hostname of the remote server * @throws IOException if something goes wrong handshaking with the server * @throws SSLPeerUnverifiedException if the server cannot prove its identity * @hide */ public static void verifyHostname(Socket socket, String hostname) throws IOException { if (!(socket instanceof SSLSocket)) { throw new IllegalArgumentException("Attempt to verify non-SSL socket"); } if (!isSslCheckRelaxed()) { // The code at the start of OpenSSLSocketImpl.startHandshake() // ensures that the call is idempotent, so we can safely call it. SSLSocket ssl = (SSLSocket) socket; ssl.startHandshake(); SSLSession session = ssl.getSession(); if (session == null) { throw new SSLException("Cannot verify SSL socket without session"); } if (!HOSTNAME_VERIFIER.verify(hostname, session)) { throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname); } } }
/** * Establishes and verifies a TLS connection to a (TCP-)connected SSLSocket: - set TLS parameters * like allowed protocols and ciphers - set SNI host name - verify host name - verify certificate * * @param socket unconnected SSLSocket * @param host host name for SNI * @throws java.io.IOException if the connection could not be established. */ private void establishAndVerify(SSLSocket socket, String host) throws IOException { setTlsParameters(socket); setSniHostname(socket, host); // TLS handshake, throws an exception for untrusted certificates socket.startHandshake(); // verify hostname and certificate SSLSession session = socket.getSession(); if (!hostnameVerifier.verify(host, session)) { // throw exception for invalid host names throw new SSLPeerUnverifiedException(host); } Log.i( TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() + " using " + session.getCipherSuite()); }
@Override public boolean verify(String s, SSLSession sslSession) { return verifier.verify(s, sslSession); }
@Override public void verify(String host, SSLSocket ssl) throws IOException { if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure"); }
@Override public boolean verify(String hostname, SSLSession session) { return hostnameVerifier.verify(hostname, session); }