Android HTTP请求使用客户端证书。

14

我正在尝试使用此代码向服务器发出客户端证书身份验证请求:

try {
    /*** CA Certificate ***/

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = getResources().openRawResource(R.raw.caserver);
    Certificate ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());

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

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

    /*** Client Certificate ***/

    KeyStore keyStore12 = KeyStore.getInstance("PKCS12");
    InputStream certInput12 = getResources().openRawResource(R.raw.p12client);
    keyStore12.load(certInput12, "123456key".toCharArray());

    // Create a KeyManager that uses our client cert
    String algorithm = KeyManagerFactory.getDefaultAlgorithm();
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
    kmf.init(keyStore12, null);


    /*** SSL Connection ***/

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

    URL url = new URL("https://myurl/test.json");
    HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
    urlConnection.setSSLSocketFactory(context.getSocketFactory());

    System.out.println("Weeeeeeeeeee");
    InputStream in = urlConnection.getInputStream(); // this throw exception
}
catch (Exception e) {
    e.printStackTrace();
}

当执行到最后一行代码InputStream in = urlConnection.getInputStream();时,我遇到了以下异常。

System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

我花了很多时间尝试修复这个错误,但是找不到任何信息。当我使用客户端证书使用Web浏览器进行相同请求时,一切正常。

有人能帮忙吗?提前感谢。

编辑

我按照以下步骤生成证书:

> openssl req -config openssl.cnf -new -x509 -extensions v3_ca -days 3650 -keyout private/caserver.key -out certs/caserver.crt
> openssl req -config openssl.cnf -new -nodes -keyout private/client.key -out client.csr -days 1095
> openssl ca -config openssl.cnf -cert certs/caserver.crt -policy policy_anything -out certs/client.crt -infiles csr/client.csr
> openssl pkcs12 -export -clcerts -in certs/client.crt  -inkey private/client.key -out p12client.p12

我的代码中使用caserver.crt和p12client.p12。


@Piyush 我已经用新代码更新了问题。 - rjurado01
1
您的信任/密钥库中有哪些证书(根证书和中间证书)?服务器发送了哪些服务器证书(叶子证书和中间证书)? - Robert
@Robert 我更新了问题,并附上了我所遵循的生成证书的步骤。 - rjurado01
如果我用curl发送请求,它就能工作:curl -v -s -k --key client.key --cert client.crt https://myurl/test.json - rjurado01
你解决了吗? - Choletski
显示剩余6条评论
2个回答

0

您似乎正在关注客户端证书和可能存在的问题,但我认为错误与服务器证书有关。

您有InputStream caInput = getResources().openRawResource(R.raw.caserver);,它以一个可以验证服务器证书有效性的CA证书作为输入(例如,caserver可能是一个DER文件)。由于您的代码表示您想信任该CA,因此问题可能是该文件不是该CA的正确证书。

或者,它可能确实是该CA的证书。但是,该CA可能没有直接签署您的服务器证书。通常,存在一条信任链,其中一个CA可能会签署,然后另一个CA信任该CA,依此类推,一直到根CA或其他您信任的CA。

那么,为什么同一网站,使用相同的服务器证书,在浏览器中可以正常工作?您的浏览器可能具有更大的可信CA集合,因此能够验证服务器。而您的Android应用程序可能不信任信任链中的一个或多个中间CA。因此,“找不到认证路径的信任锚点”。

你可以怎么做呢?请参考谷歌的指南,了解如何处理未知CA或缺失中间CA等情况。


0
我不知道为什么输入流无法从“Assets”文件夹中读取证书。我之前也遇到了同样的问题。为了解决这个问题,我将证书放在“raw”文件夹中,并通过访问它来解决。
InputStream caInput = getResources().openRawResource(R.raw.mycertificate);

并且运行良好!


我进行更改,但是出现了以下异常:System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: 找不到证书路径的信任锚点。 - rjurado01
你是否在使用你的服务器证书? - Piyush
当然可以,我会在问题中更新我所遵循的生成证书的步骤。 - rjurado01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接