简体   繁体   English

带有客户端证书的 Android Http 请求

[英]Android Http request with Client Certificate

I'm trying to make a request to a server with a client certificate authentication with this code:我正在尝试使用以下代码向具有客户端证书身份验证的服务器发出请求:

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();
}

I obtain the next exception when the execution reach the last line InputStream in = urlConnection.getInputStream();当执行到达最后一行InputStream in = urlConnection.getInputStream();时,我获得下一个异常InputStream in = urlConnection.getInputStream(); . .

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

I have spent lots of hours trying to fix this error but I can't find any information.我花了很多时间试图修复这个错误,但我找不到任何信息。 When I make the same request using a web browser with the client certificate, all is ok.当我使用带有客户端证书的 Web 浏览器发出相同的请求时,一切正常。

Any help?有什么帮助吗? Thanks in advance.提前致谢。

Edit编辑

I follow this steps to generate certificates:我按照以下步骤生成证书:

> 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

In my code I use caserver.crt and p12client.p12.在我的代码中,我使用 caserver.crt 和 p12client.p12。

I don't know why input stream unable to read certificate from Assets folder.我不知道为什么输入流无法从Assets文件夹读取证书。 I had the same problem.我有同样的问题。 To overcome , i have put certificate in raw folder and access it through为了克服,我已将证书放在raw文件夹中并通过它访问

InputStream caInput = getResources().openRawResource(R.raw.mycertificate);

and worked well !并且运行良好!

You appear to be focusing on the client certificate and possible problems there, but I think the error is related to the server certificate.您似乎专注于客户端证书和可能存在的问题,但我认为该错误与服务器证书有关。

You have InputStream caInput = getResources().openRawResource(R.raw.caserver);你有InputStream caInput = getResources().openRawResource(R.raw.caserver); which takes as input, a CA certificate of a CA that can verify that the server certificate is valid (caserver may be a DER file, for example).它以 CA 的 CA 证书作为输入,可以验证服务器证书是否有效(例如,caserver 可能是一个 DER 文件)。 As your code is saying you want to trust that CA.正如您的代码所说,您希望信任该 CA。

So, the problem may be that this file is not a correct certificate for that CA.因此,问题可能在于该文件不是该 CA 的正确证书。

Or, it may really be the certificate for that CA.或者,它可能真的是该 CA 的证书。 But that CA might not have signed your server certificate directly.但是该 CA 可能没有直接签署您的服务器证书。 Often, there is a chain of trust, where one CA might sign, then that CA is trusted by another CA, and so on, all the way up to a root CA or other CA that you trust.通常,存在一条信任链​​,其中一个 CA 可能会签名,然后该 CA 被另一个 CA 信任,依此类推,一直到根 CA 或您信任的其他 CA。

So, why does the same web site, with same server certificate, work from the browser?那么,为什么具有相同服务器证书的同一个网站可以在浏览器中运行? Your browser may have a larger set of CAs that it trusts, so is able to authenticate the server.您的浏览器可能拥有更多信任的 CA,因此能够对服务器进行身份验证。 Whereas your android app may not trust one or more intermediate CAs in the chain of trust.而您的 android 应用程序可能不信任信任链中的一个或多个中间 CA。 Therefore, "Trust anchor for certification path not found."因此,“未找到认证路径的信任锚”。

What can you do about it?你能做些什么呢? See google's guide on what to do with cases of unknown CA or missing intermediate CA, etc.有关如何处理未知 CA 或缺少中间 CA 等的情况,请参阅google 指南

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM