7

我的 Android 上有一个非常具体的 SSL 问题。如果我尝试通过代码访问特定网站,则会收到以下错误:

SSL handshake failure: Failure in SSL library, usually a protocol error
error:140773F2:SSL routines:SSL23_GET_SERVER_HELLO: sslv3 alert unexpected message (external/openssl/ssl/s23_cInt.c:500 0xaf076228:0x00000000)

无论构建如何,我都会得到这个......我已经在 API 级别 1.5、1.6、2.2 和 4.0 上尝试过,并且每次都得到相同的结果。

经过一些故障排除后,我尝试通过浏览器访问该网站,但出现以下错误:

Data connectivity problem
A secure connection could not be established.

事情是这样的……该网站在 Windows 浏览器上打开得很好(在 Firefox、IE 和 Chrome 上测试)。它在使用与 Android 相同的 webkit 的 iOS 设备上也可以正常打开,这很奇怪。该网站也可以在 Opera Mini 浏览器上正常运行。

这是网站。

我尝试通过将客户端证书添加到密钥库并忽略无效的客户端证书来解决问题,但没有结果。但是,证书本身似乎不是问题。

我陷入了僵局。任何人都可以就我如何让它发挥作用提供任何指导吗?

4

4 回答 4

4

我找到了解决方案(感谢 Nikolay 为我指明了正确的方向)。

问题有两个……第一,它返回了一个 Android 不喜欢的站点证书,第二,它只启用了 SSLv3(而不是 TLS)。

这是我的解决方案。首先,我必须创建一个自定义套接字工厂类:

public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("SSLv3");

public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
    super(truststore);

    TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    sslContext.init(null, new TrustManager[] { tm }, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    S.setEnabledProtocols(new String[] {"SSLv3"});
    return S;
}

@Override
public Socket createSocket() throws IOException {
    SSLSocket S = (SSLSocket) sslContext.getSocketFactory().createSocket();
    S.setEnabledProtocols(new String[] {"SSLv3"});
    return S;
}

}

其次,我在我的代码中定义了这个自定义的 HttpClient:

public HttpClient getNewHttpClient() {
    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);
    } catch (Exception e) {
        return new DefaultHttpClient();
    }
}

第三,我调用了自定义的HttpClient并解析了结果:

public String test(String URIString) {
    HttpClient httpClient = getNewHttpClient();
    String result = "";
    URI uri;
    try {
        uri = new URI(URIString);
    } catch (URISyntaxException e1) {
        return "ERROR";
    }
    HttpHost host = new HttpHost(uri.getHost(), 443, uri.getScheme());
    HttpPost httppost = new HttpPost(uri.getPath());
    try {
        HttpResponse response = httpClient.execute(host, httppost);
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(
                    response.getEntity().getContent()
                )
            );
            String line = null;
            while ((line = reader.readLine()) != null){
              result += line + "\n";
            }

            return result;
    } catch (ClientProtocolException e) {
        return "ERROR";
    } catch (IOException e) {
        return "ERROR";
    }
}
于 2012-06-25T17:26:07.017 回答
3

你是如何访问这个网站的?通过安卓浏览器?网络视图?还是 HttpClient/HTTPSURLConnection?它似乎只响应 SSL3,您必须强制您的客户端使用它。

于 2012-06-22T03:18:01.407 回答
0

经过两天的努力,这个最终解决方案对我有用-

    private OkHttpClient.Builder getUnsafeOkHttpClient() {
    try {
        // Create a trust manager that does not validate certificate chains
        final TrustManager[] trustAllCerts = new X509TrustManager[]{new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
            public X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        }};

        // Install the all-trusting trust manager
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        kmf.init(keyStore, "password".toCharArray());
        sslContext.init(kmf.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
                .allEnabledTlsVersions()
                .allEnabledCipherSuites()
                .build();
        ConnectionSpec spec1 = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT)
                .build();

        builder.connectionSpecs(Arrays.asList(spec,spec1));

        return builder;
    } catch (Exception e) {
        Logger.e("[DownloadService][getUnsafeOkHttpClient] :", false);
       return new OkHttpClient.Builder();

    }
}
于 2020-03-17T10:20:56.470 回答
-2

使用它并调用此方法 HttpsTrustManager.allowAllSSL()

它将解决问题并且对我来说工作正常。

公共类 HttpsTrustManager 实现 X509TrustManager {

private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};

@Override 
public void checkClientTrusted( 
        java.security.cert.X509Certificate[] x509Certificates, String s)
        throws java.security.cert.CertificateException {

} 

@Override 
public void checkServerTrusted( 
        java.security.cert.X509Certificate[] x509Certificates, String s)
        throws java.security.cert.CertificateException {

} 

public boolean isClientTrusted(X509Certificate[] chain) {
    return true; 
} 

public boolean isServerTrusted(X509Certificate[] chain) { 
    return true; 
} 

@Override 
public X509Certificate[] getAcceptedIssuers() {
    return _AcceptedIssuers; 
} 

public static void allowAllSSL() { 
    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

        @Override 
        public boolean verify(String arg0, SSLSession arg1) {
            return true; 
        } 

    }); 

    SSLContext context = null;
    if (trustManagers == null) {
        trustManagers = new TrustManager[]{new HttpsTrustManager()};
    } 

    try { 
        context = SSLContext.getInstance("TLS");
        context.init(null, trustManagers, new SecureRandom());
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    } 

    HttpsURLConnection.setDefaultSSLSocketFactory(context
            .getSocketFactory());
} 

}

于 2016-03-11T11:14:40.213 回答