自三星设备上安装Android 5.0.2或5.1.1以来,当连接旧路由器“FRITZ!Box 7170”的网页界面时,我的Android应用会收到错误消息。
javax.net.ssl.SSLProtocolException:SSL握手已中止: ssl = 0xaecc7e00:SSL库失败,通常是协议错误 错误:14082174:SSL例程:SSL3_CHECK_CERT_AND_ALGORITHM:获取通道 ccs之前的ID(external / openssl / ssl / s3_clnt.c:3632 0xaf0e1679:00000000)
如果我使用Firefox浏览器连接相同的界面:
ssl_error_weak_server_ephemeral_dh_key
我认为这是因为Deffie-Hellmann关键长度不安全?
怎么避免这个?我正在使用HTTPClient
建立连接。
答案 0 :(得分:0)
我遇到同样的问题。
原因是三星安全更新,它更改了SSLSocketFactory给出的默认密码套件数组。如果您使用带有Android M的nexus设备,您将看到此错误消息
ssl_error_weak_server_ephemeral_dh_key
在三星设备上它是
SSL3_CHECK_CERT_AND_ALGORITHM
我的解决方案是覆盖密码套件数组。这是我的SSLSocketFactory,我用它来创建ssl套接字。
public class SpeedportSSLSocketFactory extends SSLSocketFactory {
private final static Logger logger = Logger.getLogger(SpeedportSSLSocketFactory.class);
/**
* the order of ciphers in this list is important here e.g. TLS_DHE_* must not stay above TLS_RSA_*
*/
private static final String[] APPROVED_CIPHER_SUITES = new String[]{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
};
private SSLSocketFactory factory;
public SpeedportSSLSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null, new TrustManager[]{
// accepts certs with valid but expired key chain (incl. root cert)
new ExpiredSpeedportTrustManager()}, new java.security.SecureRandom());
factory = sslcontext.getSocketFactory();
} catch (Exception ex) {
logger.error("Cannot create SpeedportSSLSocketFactory", ex);
}
}
// dirty
private void injectHostname(InetAddress address, String host) {
try {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(address, host);
} catch (Exception ignored) {
logger.error("Cannot inject hostName");
}
}
public static SocketFactory getDefault() {
return new SpeedportSSLSocketFactory();
}
public Socket createSocket() throws IOException {
return factory.createSocket();
}
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return factory.createSocket(socket, host, port, autoClose);
}
public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException {
return factory.createSocket(addr, port, localAddr, localPort);
}
public Socket createSocket(InetAddress inaddr, int i) throws IOException {
return factory.createSocket(inaddr, i);
}
public Socket createSocket(String host, int port, InetAddress localAddr, int localPort) throws IOException {
return factory.createSocket(host, port, localAddr, localPort);
}
public Socket createSocket(String host, int port) throws IOException {
InetAddress addr = InetAddress.getByName(host);
injectHostname(addr, host);
Socket socket = factory.createSocket(addr, port);
((SSLSocket) socket).setEnabledCipherSuites(getSupportedCipherSuites());
return socket;
}
@Override
public String[] getDefaultCipherSuites() {
return APPROVED_CIPHER_SUITES;
}
@Override
public String[] getSupportedCipherSuites() {
return APPROVED_CIPHER_SUITES;
}
}
最后两个方法覆盖默认密码套件。我不确定,你需要覆盖两者。
密码套件数组中的顺序也非常重要