Java HTTPS客户端证书认证

我对HTTPS / SSL / TLS相当陌生,我对使用证书进行身份验证时客户端应该呈现的内容感到困惑。

我正在编写一个Java客户端,需要对特定的URL执行简单的POST数据POST。 这部分工作正常,唯一的问题是它应该通过HTTPS完成。 HTTPS部分相当容易处理(使用HTTPclient或使用Java的内置HTTPS支持),但我坚持使用客户端证书进行身份验证。 我注意到在这里已经有一个非常类似的问题,我还没有用我的代码尝试过(将尽快完成)。 我目前的问题是 - 无论我做什么 - Java客户端永远不会发送证书(我可以使用PCAP转储来检查此问题)。

我想知道当使用证书进行身份验证时,客户端应该向服务器提供什么(专门用于Java - 如果这很重要)? 这是一个JKS文件,还是PKCS#12? 他们应该有什么; 只是客户端证书或密钥? 如果是这样,哪个键? 关于所有不同类型的文件,证书类型等都存在相当多的混淆。

正如我之前所说的,我是HTTPS / SSL / TLS的新手,所以我希望能够获得一些背景信息(不一定是散文;我会解决链接到好文章的问题)。


最后设法解决所有问题,所以我会回答我自己的问题。 这些是我用来解决特定问题的设置/文件;

客户端的密钥库是一个包含PKCS#12格式的文件

  • 客户的公共证书(在这种情况下由自签名的CA签署)
  • 客户的私钥
  • 为了生成它,我使用了OpenSSL的pkcs12命令;

    openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "Whatever"
    

    提示:确保您获得最新的OpenSSL, 而不是版本0.9.8h,因为这似乎受到一个不允许您正确生成PKCS#12文件的错误的影响。

    当服务器明确请求客户端进行身份验证时,Java客户端将使用此PKCS#12文件将客户端证书呈现给服务器。 请参阅TLS上的维基百科文章,了解客户端证书身份验证协议实际如何工作的概述(也解释了为什么我们需要此处的客户端私钥)。

    客户端的信任库是一个简单的JKS格式文件,包含 证书中间CA证书 。 这些CA证书将确定您将被允许与哪些端点通信,在这种情况下,它将允许您的客户端连接到任何一个服务器提供由其中一个信任库的CA签名的证书。

    为了生成它,你可以使用标准的Java keytool,例如;

    keytool -genkey -dname "cn=CLIENT" -alias truststorekey -keyalg RSA -keystore ./client-truststore.jks -keypass whatever -storepass whatever
    keytool -import -keystore ./client-truststore.jks -file myca.crt -alias myca
    

    使用此信任库,您的客户端将尝试与提供由myca.crt标识的CA签名的证书的所有服务器进行完整的SSL握手。

    以上文件仅限客户使用。 当你想要设置服务器时,服务器也需要自己的密钥和信任库文件。 在这个网站上可以找到为Java客户端和服务器(使用Tomcat)设置完整工作示例的很好的演练。

    问题/备注/提示

  • 客户端证书认证只能由服务器执行。
  • (重要!)当服务器请求客户端证书(作为TLS握手的一部分)时,它还会提供一个可信CA的列表作为证书请求的一部分。 当你希望出示身份验证的客户端证书没有被这些CA之一签名时,它根本不会被呈现(在我看来,这是奇怪的行为,但我相信这是有原因的)。 这是我的问题的主要原因,因为另一方没有正确配置他们的服务器来接受我的自签名客户端证书,并且我们认为问题在于我没有在请求中正确提供客户端证书。
  • 获取Wireshark。 它具有很好的SSL / HTTPS数据包分析功能,对于调试和发现问题将有很大的帮助。 它类似于-Djavax.net.debug=ssl但是如果您对Java SSL调试输出不舒服,则更具结构化和(可以说)更易于解释。
  • 完全可以使用Apache httpclient库。 如果您想使用httpclient,只需将目标URL替换为HTTPS等效项并添加以下JVM参数(对于任何其他客户端而言都是相同的,无论您想要通过HTTP / HTTPS发送/接收数据的库是什么) :

    -Djavax.net.debug=ssl
    -Djavax.net.ssl.keyStoreType=pkcs12
    -Djavax.net.ssl.keyStore=client.p12
    -Djavax.net.ssl.keyStorePassword=whatever
    -Djavax.net.ssl.trustStoreType=jks
    -Djavax.net.ssl.trustStore=client-truststore.jks
    -Djavax.net.ssl.trustStorePassword=whatever

  • 其他答案显示了如何全局配置客户端证书。 但是,如果要以编程方式为特定连接定义客户端密钥,而不是跨JVM上运行的每个应用程序全局定义它,则可以像这样配置自己的SSLContext:

    String keyPassphrase = "";
    
    KeyStore keyStore = KeyStore.getInstance("PKCS12");
    keyStore.load(new FileInputStream("cert-key-pair.pfx"), keyPassphrase.toCharArray());
    
    SSLContext sslContext = SSLContexts.custom()
            .loadKeyMaterial(keyStore, null)
            .build();
    
    HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
    HttpResponse response = httpClient.execute(new HttpGet("https://example.com"));
    

    他们的JKS文件只是一个证书和密钥对的容器。 在客户端身份验证方案中,密钥的各个部分将位于此处:

  • 客户的商店将包含客户的私钥和公钥对。 它被称为密钥库
  • 服务器的商店将包含客户端的公钥 。 它被称为信任库
  • 信任库和密钥库的分离不是强制性的,但建议使用。 它们可以是相同的物理文件。

    要设置两个商店的文件系统位置,请使用以下系统属性:

    -Djavax.net.ssl.keyStore=clientsidestore.jks
    

    并在服务器上:

    -Djavax.net.ssl.trustStore=serversidestore.jks
    

    要将客户端的证书(公钥)导出到文件中,以便将其复制到服务器中,请使用

    keytool -export -alias MYKEY -file publicclientkey.cer -store clientsidestore.jks
    

    要将客户端的公钥导入到服务器的密钥库中,请使用(如上所述,这已由服务器管理员完成)

    keytool -import -file publicclientkey.cer -store serversidestore.jks
    
    链接地址: http://www.djcxy.com/p/5269.html

    上一篇: Java HTTPS client certificate authentication

    下一篇: Android MediaRecorder: Difference between AmrWb and ThreeGpp OutputFormat