我对HTTPS/SSL/TLS相当陌生,我有点困惑,当使用证书进行身份验证时,客户端究竟应该呈现什么。

I'm writing a Java client that needs to do a simple POST of data to a particular URL. That part works fine, the only problem is it's supposed to be done over HTTPS. The HTTPS part is fairly easy to handle (either with HTTPclient or using Java's built-in HTTPS support), but I'm stuck on authenticating with client certificates. I've noticed there's already a very similar question on here, which I haven't tried out with my code yet (will do so soon enough). My current issue is that - whatever I do - the Java client never sends along the certificate (I can check this with PCAP dumps).

我想知道客户端在使用证书进行身份验证时到底应该向服务器提供什么(特别是对于Java -如果这很重要的话)?这是一个JKS文件,还是PKCS#12?里面应该有什么;只有客户端证书,还是密钥?如果是,是哪个钥匙?关于所有不同类型的文件、证书类型等,有相当多的困惑。

正如我之前说过的,我是HTTPS/SSL/TLS的新手,所以我也希望能了解一些背景知识(不必是一篇文章;我将满足于好文章的链接)。


当前回答

对于那些只想建立双向身份验证(服务器和客户端证书)的人来说,这两个链接的组合将使您达到目的:

双向认证设置:

https://linuxconfig.org/apache-web-server-ssl-authentication

你不需要使用他们提到的openssl配置文件;只使用

$ openssl genrsa -des3 -out ca.key 4096 $ openssl req -new -x509 -days 365 -key ca.key out ca.crt . out

生成您自己的CA证书,然后通过以下方式生成并签署服务器和客户端密钥:

$ openssl genrsa -des3 -out服务器。关键的4096 $ openssl req -new -key服务器。输入-out server.csr $ openssl x509 -req -days 365 -in server。csr -CA ca.crt -CAkey ca.key -set_serial 100 -out server.crt

and

$ openssl genrsa -des3 -out客户端关键的4096 $ openssl req -new -key客户端。输出client.csr $ openssl x509 -req -days 365 -in客户端csr -CA ca.crt -CAkey ca.key -set_serial 101 -out client.crt

其余的请按照链接中的步骤进行操作。Chrome的证书管理方法与上面提到的firefox的证书管理方法相同。

接下来,通过以下方式设置服务器:

https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-14-04

请注意,您已经创建了服务器的.crt和.key,因此您不必再执行这一步。

其他回答

给定一个包含证书和私钥的p12文件(例如,由openssl生成),下面的代码将对特定的HttpsURLConnection使用该文件:

    KeyStore keyStore = KeyStore.getInstance("pkcs12");
    keyStore.load(new FileInputStream(keyStorePath), keystorePassword.toCharArray());
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(keyStore, keystorePassword.toCharArray());
    SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(kmf.getKeyManagers(), null, null);
    SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();

    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    connection.setSSLSocketFactory(sslSocketFactory);

SSLContext需要一些时间来初始化,因此可能需要缓存它。

对于那些只想建立双向身份验证(服务器和客户端证书)的人来说,这两个链接的组合将使您达到目的:

双向认证设置:

https://linuxconfig.org/apache-web-server-ssl-authentication

你不需要使用他们提到的openssl配置文件;只使用

$ openssl genrsa -des3 -out ca.key 4096 $ openssl req -new -x509 -days 365 -key ca.key out ca.crt . out

生成您自己的CA证书,然后通过以下方式生成并签署服务器和客户端密钥:

$ openssl genrsa -des3 -out服务器。关键的4096 $ openssl req -new -key服务器。输入-out server.csr $ openssl x509 -req -days 365 -in server。csr -CA ca.crt -CAkey ca.key -set_serial 100 -out server.crt

and

$ openssl genrsa -des3 -out客户端关键的4096 $ openssl req -new -key客户端。输出client.csr $ openssl x509 -req -days 365 -in客户端csr -CA ca.crt -CAkey ca.key -set_serial 101 -out client.crt

其余的请按照链接中的步骤进行操作。Chrome的证书管理方法与上面提到的firefox的证书管理方法相同。

接下来,通过以下方式设置服务器:

https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-14-04

请注意,您已经创建了服务器的.crt和.key,因此您不必再执行这一步。

我已经用Spring Boot用双向SSL(客户端和服务器证书)连接到银行。所以在这里描述我所有的步骤,希望它能帮助到别人(我发现了最简单的工作解决方案):

生成证书请求:

生成私钥: openssl genrsa -des3 -passout pass:MY_PASSWORD -out user。关键的2048 生成证书请求: Openssl req -new -key user。退出用户。csr -passin pass:MY_PASSWORD

保持用户。密钥(和密码)和发送证书请求到银行

Receive 2 certificate: my client root certificate user.pem and bank root certificate: bank.crt Create Java keystore (enter key password and set keystore password): openssl pkcs12 -export -in user.pem -inkey user.key -out keystore.p12 -name clientId -CAfile ca.crt -caname root Don't pay attention on output: unable to write 'random state'. Java PKCS12 keystore.p12 created. Add into keystore bank.crt (for simplicity I've used one keystore): keytool -import -alias bankca -file bank.crt -keystore keystore.p12 -storepass MY_PASS Check keystore certificates by: keytool -list -keystore keystore.p12 Ready for Java code:) I've used Spring Boot RestTemplate with add org.apache.httpcomponents.httpcore dependency: @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; }

我认为这里的修复是密钥库类型,pkcs12(pfx)总是有私钥和JKS类型可以存在没有私钥。除非您在代码中指定或通过浏览器选择证书,否则服务器无法知道它在另一端代表客户端。

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