簡體   English   中英

SSL 握手失敗 Android 5.1

[英]SSL Handshake failed Android 5.1

由於三星設備上的 Android 5.0.2 或 5.1.1 在連接舊路由器“FRITZ!Box 7170”的 Web 界面時,我的 Android 應用程序收到錯誤消息。

javax.net.ssl.SSLProtocolException:SSL握手中止:ssl = 0xaecc7e00:SSL庫失敗,通常是協議錯誤錯誤:14082174:SSL例程:SSL3_CHECK_CERT_AND_ALGORITHM:在ccs之前獲得通道ID(外部/openssl/ssl/s3_clnt.c :3632 0xaf0e1679:0x00000000)

如果我用 Firefox 瀏覽器連接相同的界面:

ssl_error_weak_server_ephemeral_dh_key

我認為這是因為不安全的 Diffie-Hellman 密鑰長度? 如何避免這種情況? 我正在使用HTTPClient建立連接。

我有同樣的問題。

原因是三星安全更新,它改變了 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;
}

}

最后兩種方法覆蓋默認密碼套件。 我不確定,你需要覆蓋兩者。

cipher suites 數組中的順序也很重要

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM