In our app when the app communicate with the server I made additional check of certificates (public key pinning) (against MITMA). To communicate with the server I use HttpClient. Also I have some proxy server in production, so I need to use SNI. Before we publish the app to production we check it in another environment (TEST env). For TEST env we have only self-signed certificate, cause its using for testing only and we don’t want to buy a new certificate just for this case.
To implement it I created the custom SSLSocketFactory (org.apache.http.conn.ssl.SSLSocketFactory). But the problem is it doesn't work for self-signed certificates. I set the custom trustManager (IgnoreCertificatesTrustManager) to sslSocketFactory (SSLCertificateSocketFactory). If I use the following code:
SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS); sslContext.init(null, new TrustManager[] { new IgnoreCertificatesTrustManager() }, null); sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); the check of self-singed certificates works (ignore the check in TrustManager (IgnoreCertificatesTrustManager)). But the code doesn't support the SNI solution. What did I do wrong?
Thanks.
private class CustomSSLSocketFactory extends SSLSocketFactory { public CustomSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, CertificateException { super(null); } // Plain TCP/IP (layer below TLS) @Override public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException { return null; } @Override public Socket createSocket() throws IOException { return null; } @Override public boolean isSecure(Socket s) throws IllegalArgumentException { if (s instanceof SSLSocket) { return ((SSLSocket) s).isConnected(); } return false; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { SSLSocket sslSocket = null; // if (isProduction()) { if (autoClose) { // we don't need the plainSocket socket.close(); } // create and connect SSL socket, but don't do hostname/certificate verification yet SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0, null); // NOT works! sslSocketFactory.setTrustManagers(new TrustManager[] { new IgnoreCertificatesTrustManager() }); // ---- sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, host, port, autoClose); // enable TLSv1.1/1.2 if available (see https://github.com/rfc2822/davdroid/issues/229 ) sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols()); // set up SNI before the handshake if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { logger.debug("Setting SNI hostname"); sslSocketFactory.setHostname(sslSocket, host); } else { logger.debug("No documented SNI support on Android <4.2, trying with reflection"); try { java.lang.reflect.Method setHostnameMethod = sslSocket.getClass().getMethod("setHostname", String.class); setHostnameMethod.invoke(sslSocket, host); } catch (Exception e) { logger.error("SNI not useable", e); } } // } else { // try { // SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS); // sslContext.init(null, new TrustManager[] { new IgnoreCertificatesTrustManager() }, null); // sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); // } catch (java.security.NoSuchAlgorithmException e) { // throw new IOException(e); // } catch (KeyManagementException e) { // throw new IOException(e); // } // } // verify certificate SSLSession session = sslSocket.getSession(); X509Certificate[] certificates = (X509Certificate[]) session.getPeerCertificates(); if (!checkPublicKey(certificates)) { throw new IOException("SSL_HANDSHAKE_FAILED"); } return sslSocket; } }
SSLContextwith your trust manager. Strange definition ofisSecure().What doesisConnected()have to do with it?SSLContext, you will need to craft a suitableTrustManagerthat can do the boolean logic. See myTrustManagerBuilderin my CWAC-Security library.EJPI need SNI to check public key of certificates. Also isConnected() I think you are right. It is my mistake. It should be return true. I will check it.Steffen UllrichI found that httpClient supports it from Android API 17. Since Android 2.3, SNI is available in the OpenSSL implementation, so I can use it via reflection.CommonsWareI would not want to add check of self-signed certificates (in our department we haven't access to them, i can't get keystore). I want to ignore them for NON-prod environment