生成证书请求:
我对HTTPS/SSL/TLS
相当陌生,对于使用证书进行身份验证时客户端应该呈现的确切内容有些困惑。
我正在编写一个Java客户端,它需要对特定的POST
执行简单的URL
数据。那部分工作正常,唯一的问题是应该在HTTPS
上完成。 HTTPS
部分相当容易处理(使用HTTPclient
或使用Java内置的HTTPS
支持),但是我一直坚持使用客户端证书进行身份验证。我注意到这里已经存在一个非常类似的问题,我还没有尝试使用我的代码(很快就会这样做)。我当前的问题是-无论执行什么操作-Java客户端都不会发送证书(我可以使用PCAP
转储检查此证书)。
我想知道在使用证书进行身份验证时,客户端到底应该向服务器呈现什么(特别是对于Java,如果这很重要的话?)?这是JKS
文件还是PKCS#12
?里面应该有什么?只是客户证书还是密钥?如果是这样,哪个键?关于所有不同类型的文件,证书类型等,都有很多混淆。
正如我刚接触HTTPS/SSL/TLS
之前所说的那样,所以我也希望了解一些背景信息(不必是散文;我会选择好文章的链接)。
[客户端的密钥库是
PKCS#12格式包含]的文件>客户端的公共
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)设置完全正常工作的示例的完整演练。问题/备注/提示
客户端证书认证
只能由服务器强制执行。
-Djavax.net.debug=ssl
相似,但是如果您对Java SSL调试输出感到不满意,它的结构也更清晰,并且可以说更容易解释。-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"));
client
我已通过Spring Boot使用双向SSL(客户端和服务器证书)连接到银行。因此,请在这里描述我的所有步骤,希望对您有所帮助(我发现了最简单的解决方案):
生成证书请求:
生成私钥:
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
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; }
生成证书请求: