Java HTTPS客户端证书认证

问题描述 投票:212回答:7

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

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

我想知道在使用证书进行身份验证时,客户端到底应该向服务器呈现什么(特别是对于Java,如果这很重要的话?)?这是JKS文件还是PKCS#12?里面应该有什么?只是客户证书还是密钥?如果是这样,哪个键?关于所有不同类型的文件,证书类型等,都有很多混淆。

正如我刚接触HTTPS/SSL/TLS之前所说的那样,所以我也希望了解一些背景信息(不必是散文;我会选择好文章的链接)。

java ssl https client-certificates
7个回答
226
投票

[客户端的密钥库是

PKCS#12格式包含]的文件>客户端的

    公共

证书(在这种情况下,此证书由自签名CA签署)
  • 客户端的私有
  • 密钥
  • 例如,我使用OpenSSL的pkcs12命令生成它;openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "Whatever"

    Tip:
    确保获得最新的OpenSSL,

    not版本0.9.8h,因为它似乎存在一个错误,使您无法正确生成PKCS#12文件。

    当服务器明确要求客户端进行身份验证时,Java客户端将使用此PKCS#12文件向服务器提供客户端证书。有关客户端证书身份验证协议实际工作原理的概述,请参见Wikipedia article on TLS(在此处还说明了为什么需要客户端的私钥)。

    客户端的信任库

    是一个简单的

    JKS格式

    文件,其中包含root中间CA证书。这些CA证书将确定您将被允许与哪些端点进行通信,在这种情况下,它将允许您的客户端连接到提供由信任库的CA之一签名的证书的任何服务器。例如,可以使用标准的Java键盘工具生成它;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握手。

    以上文件仅适用于客户端。当您还想设置服务器时,服务器需要自己的密钥和信任库文件。在this website上可以找到有关为Java客户端和服务器(使用Tomcat)设置完全正常工作的示例的完整演练。

    问题/备注/提示

      客户端证书认证
    只能由服务器强制执行。

  • 重要!)当服务器请求客户端证书时(作为TLS握手的一部分),它还将提供受信任CA的列表作为证书请求的一部分。当您希望提供用于身份验证的客户端证书由这些CA之一签名时,根本不会显示(我认为这是很奇怪的行为,但是我确信这是有原因的)它)。这是导致我出现问题的主要原因,因为另一方未正确配置其服务器以接受我的自签名客户端证书,并且我们认为问题出在我端,原因是未在请求中正确提供客户端证书。
  • 获取Wireshark。它具有出色的SSL / HTTPS数据包分析功能,将有助于调试和发现问题。它与-Djavax.net.debug=ssl相似,但是如果您对Java SSL调试输出感到不满意,它的结构也更清晰,并且可以说更容易解释。
  • 完全可以使用Apache httpclient库。如果要使用httpclient,只需用等效的HTTPS替换目标URL,然后添加以下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"));

  • 56
    投票

    30
    投票

    client


    10
    投票

    6
    投票
    双向身份验证设置:

    0
    投票

    我已通过Spring Boot使用双向SSL(客户端和服务器证书)连接到银行。因此,请在这里描述我的所有步骤,希望对您有所帮助(我发现了最简单的解决方案):

    生成证书请求:

    1. 生成私钥:

      openssl genrsa -des3 -passout pass:MY_PASSWORD -out user.key 2048
    • 生成证书申请:

      openssl req -new -key user.key -out user.csr -passin pass:MY_PASSWORD
      
    • 保留user.key(和密码)并将证书请求user.csr发送到银行以获得我的证书
    接收2证书:我的客户根证书clientId.crt和银行根证书:bank.crt

  • 创建Java密钥库(输入密钥密码并设置密钥库密码):
  • openssl pkcs12 -export -in clientId.crt -inkey user.key -out keystore.p12 -name clientId -CAfile ca.crt -caname root

  • 不关注输出:unable to write 'random state'。 Java PKCS12 keystore.p12已创建。
  • 添加到密钥库bank.crt中(为简单起见,我使用一个密钥库):

    keytool -import -alias banktestca -file banktestca.crt -keystore keystore.p12 -storepass javaops

  • 您可以通过以下方式检查密钥库证书:
  • keytool -list -keystore keystore.p12

    准备使用Java代码:)我已经使用Spring Boot RestTemplate进行连接:

    @Bean("sslRestTemplate")
    public RestTemplate sslRestTemplate() throws Exception {
      char[] storePassword = appProperties.getSslStorePassword().toCharArray();
      URL keyStore = new URL(appProperties.getSslStore());
    
      SSLContext sslContext = new SSLContextBuilder()
            .loadTrustMaterial(keyStore, storePassword)
      // use storePassword twice (with key password do not work)!!
            .loadKeyMaterial(keyStore, storePassword, storePassword) 
            .build();
    
      // Solve "Certificate doesn't match any of the subject alternative names"
      SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    
      CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
      HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
      RestTemplate restTemplate = new RestTemplate(factory);
      // restTemplate.setMessageConverters(List.of(new Jaxb2RootElementHttpMessageConverter()));
      return restTemplate;
    }
    

    0
    投票

    生成证书请求:

    © www.soinside.com 2019 - 2024. All rights reserved.