SSLHandshakeException CertPathValidatorException 在 Android 2.3 上但不在 Android 4 上

Posted

技术标签:

【中文标题】SSLHandshakeException CertPathValidatorException 在 Android 2.3 上但不在 Android 4 上【英文标题】:SSLHandshakeException CertPathValidatorException on Android 2.3 but not on Android 4 【发布时间】:2014-05-22 08:32:21 【问题描述】:

我遇到的问题是 SSLHandshakeException 仅出现在任何 android 2.3 手机上,但不会出现在较新的(Android 4+)手机上。

为了接受服务器的证书,我从我的资源中读取了保存的证书,并将其与用于此连接的 TrustManager 连接。

Google 推荐了这种做法(2014 年 4 月 9 日):http://developer.android.com/training/articles/security-ssl.html

异常堆栈跟踪输出:

04-09 07:25:15.739: W/ShopLoader(2079): 
    javax.net.ssl.SSLHandshakeException: 
        java.security.cert.CertPathValidatorException: 
            Trust anchor for certification path not found.: 
                java.security.cert.CertPathValidatorException: 
                    Trust anchor for certification path not found.

我的代码:

private static InputStream getSecureStream(String urlString) throws KeyStoreException, KeyManagementException, NoSuchAlgorithmException, IOException, CertificateException 
    Context appCtx = MyBackgroundService.instance.context;
    Resources res = appCtx.getResources();
    InputStream is = res.openRawResource(res.getIdentifier("de_ssl_2014", "raw", appCtx.getPackageName()));

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = new BufferedInputStream(is);
    Certificate ca;
    try 
        ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
     finally 
        caInput.close();
    

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);

    // Create a TrustManager that trusts the CAs in our KeyStore
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);

    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, tmf.getTrustManagers(), null);

    // Tell the URLConnection to use a SocketFactory from our SSLContext
    URL url = new URL(urlString);
    HttpsURLConnection urlConnection =
        (HttpsURLConnection)url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());
    InputStream in = urlConnection.getInputStream();
    return in;

异常前的输出:

04-09 07:58:46.869: I/System.out(3282): ca=CN=api.xxxxxxxx.de, OU=Domain Control Validated - RapidSSL(R), OU=See www.rapidssl.com/resources/cps (c)13, OU=GT40709841, OID.2.5.4.5=0JfY9NNUOGmytnt1WE//sOqJj7JzTtCg

OpenSSL 连接返回:

openssl s_client -connect api.xxxxxxxx.de:443
CONNECTED(00000003)
depth=1 /C=US/O=GeoTrust, Inc./CN=RapidSSL CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/serialNumber=0JfY9NNUOGmytnt1WE//sOqJj7JzTtCg/OU=GT40709841/OU=See www.rapidssl.com/resources/cps (c)13/OU=Domain Control Validated - RapidSSL(R)/CN=api.xxxxxxxx.de
   i:/C=US/O=GeoTrust, Inc./CN=RapidSSL CA
 1 s:/C=US/O=GeoTrust, Inc./CN=RapidSSL CA
   i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFJjCCBA6gAwIBAgIDD2BUMA0GCSqGSIb3DQEBCwUAMDwxCzAJBgNVBAYTAlVT

[...]

jIhiyCMrPZ9VU6QqWQ7tslmtR54SpINwCzFVE6ySWC9CY8m8+PtWyfDDPwWzuJLO
UlxESqqQXD7iZJequBUoiLYCQTc7kofp/LU=
-----END CERTIFICATE-----
subject=/serialNumber=0JfY9NNUOGmytnt1WE//sOqJj7JzTtCg/OU=GT40709841/OU=See www.rapidssl.com/resources/cps (c)13/OU=Domain Control Validated - RapidSSL(R)/CN=api.xxxxxxxx.de
issuer=/C=US/O=GeoTrust, Inc./CN=RapidSSL CA
---
No client certificate CA names sent
---
SSL handshake has read 3129 bytes and written 316 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: C037859AE2DDF571DCC6D7C0C6C7D22CE34E7A3DC1BE6BB5E286B66A3EAA5492
    Session-ID-ctx:
    Master-Key: FE598D2380B14A0C73B6FBFBFB51C977579AE12CB37077769922D0E90C4AF5487B43EBC02433F1CAA6134CF60F4EBB34
    Key-Arg   : None
    Start Time: 1397029699
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---
closed

【问题讨论】:

***.com/questions/21183043/… 没有成功。同样的错误:( 使用 BouncyCastle KeyStore 而不是直接加载证书也没有改变任何东西。 【参考方案1】:

首先将您的keystoretrustore 复制到assets 文件夹,然后尝试使用此代码(获取方法):

KeyStore keyStore_manager = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore_manager.load(_context.getAssets().open(your_keystore),password.toCharArray());
KeyStore keyStore_trust = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore_trust.load(_context.getAssets().open(your_trustore),password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore_manager, password.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore_trust);
HttpsURLConnection.setDefaultHostnameVerifier(new NullHostNameVerifier());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(),tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL url = new URL(your_url);
HttpsURLConnection urlConnection = (HttpsURLConnection) requestedUrl.openConnection();
if (urlConnection instanceof HttpsURLConnection)

    ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sslContext
            .getSocketFactory());

urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(1500);
urlConnection.connect();
String data = "";
int http_status = urlConnection.getResponseCode();
if (http_status == 200)

//read data sent from server
    InputStream response = new BufferedInputStream(

    urlConnection.getInputStream());

    int bytesRead = -1;
    byte[] buffer = new byte[30 * 1024];
    while ((bytesRead = response.read(buffer)) > 0)
    
        data = new String(buffer, 0, bytesRead);
    


urlConnection.disconnect();

【讨论】:

我没有使用信任管理器,因为我没有任何信任管理器,也跳过了 NullHostNameVerifier,因为我没有 NullHostNameVerifier,而且我不想整合不同的主机名验证。最后也没有用。 :(

以上是关于SSLHandshakeException CertPathValidatorException 在 Android 2.3 上但不在 Android 4 上的主要内容,如果未能解决你的问题,请参考以下文章

Intellij IDEA 许可证激活 (SSLHandshakeException)

Scala:如何忽略“SSLHandshakeException”

文件下载 SSLHandshakeException

SSLHandshakeException 无法验证用户身份

SSLHandshakeException:不存在主题替代名称

服务帐户引发 - SSLHandshakeException