В нашем приложении, когда приложение общается с сервером, я проверил дополнительную проверку сертификатов (с открытым ключом) (против MITMA). Для связи с сервером я использую HttpClient. Также у меня есть прокси-сервер в производстве, поэтому мне нужно использовать SNI. Прежде чем мы опубликуем приложение для производства, мы проверим его в другой среде (TEST env). Для TEST env у нас есть только самозаверяющий сертификат, поэтому его использование используется только для тестирования, и мы не хотим покупать новый сертификат только для этого случая.Пользовательский SSLSocketFactory не использует настраиваемый trustmanager
Для его реализации я создал настраиваемый SSLSocketFactory (org.apache.http.conn.ssl.SSLSocketFactory). Но проблема в том, что он не работает для самозаверяющих сертификатов. Я установил пользовательский trustManager (IgnoreCertificatesTrustManager) в sslSocketFactory (SSLCertificateSocketFactory). Если я использую следующий код:
SSLContext sslContext = SSLContext.getInstance(SSLSocketFactory.TLS);
sslContext.init(null, new TrustManager[] { new IgnoreCertificatesTrustManager() }, null);
sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
проверка самостоятельных опалены сертификаты работов (игнорировать проверку в TrustManager (IgnoreCertificatesTrustManager)). Но код не поддерживает решение SNI. Что я сделал не так?
Спасибо.
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;
}
}
Вам это не нужно. Просто инициализируйте 'SSLContext' с помощью своего доверительного менеджера. Странное определение 'isSecure().' Что делает 'isConnected()' связано с этим? – EJP
Вы уверены, что используете версию HTTPClient, которая поддерживает SNI? Обычно Android поставляется со старой версией Apache HTTPClient, которая не поддерживает SNI. –
Если вы хотите поддерживать * самозаверяющие сертификаты и обычные CA-корневые сертификаты из того же 'SSLContext', вам нужно создать подходящий' TrustManager', который может выполнять логическую логику. См. Мой «TrustManagerBuilder» в моей [CWAC-Security library] (https://github.com/commonsguy/cwac-security/). – CommonsWare