我正在尝试使用 Apache HTTPClient 实现客户端和服务器身份验证,并使用自签名证书对其进行测试。我试图在这里遵循几个教程和类似问题的答案,但没有成功。我试图尽可能详细地说明我一直在做的所有步骤,希望有人能指出我做错了什么:
req.conf
为配置创建了一个文件[req] prompt=no distinguished_name = req_distinguished_name [ req_distinguished_name ] O=selfSignedO CN=selfSignedCn DC=selfSignedDc
生成的服务器私钥和自签名证书
openssl req \ -config req.conf \ -x509 \ -newkey rsa:4096 \ -keyout server/server-private-key.pem \ -out server/server.crt \ -days 3650 \ -nodes
创建了包含上一步中创建的私钥和证书的 PKCS12 密钥库
openssl pkcs12 \ -export \ -out server/server-key-store.p12 \ -inkey server/server-private-key.pem \ -in server/server.crt
假设我使用的密码是
123456
生成客户端私钥和证书签名请求
openssl req \ -config req.conf \ -new \ -newkey rsa:4096 \ -out client/client-request.csr \ -keyout client/client-private-key.pem \ -nodes
使用服务器的私钥和证书对客户端的证书签名请求进行签名
openssl x509 \ -req \ -days 360 \ -in client/client-request.csr \ -CA server/server.crt \ -CAkey server/server-private-key.pem \ -CAcreateserial \ -out client/client-signed-cert.crt \ -sha256
创建了一个 PKCS12 密钥库,其中包含上一步中创建的客户端私钥和证书证书。
openssl pkcs12 \ -export \ -out client/client-keystore.p12 \ -inkey client/client-private-key.pem \ -in client/client-signed-cert.crt \ -certfile server/server.crt
我们
123456
再次用作密码。生成的包含客户端签名证书的服务器信任库
keytool \ -import \ -trustcacerts \ -alias root \ -file client/client-signed-cert.crt \ -keystore server/server-trust-store.jks
密码?
123456
卷曲正在工作,但仅限于
-k
curl -k \ --cert client/client-signed-cert.crt \ --key client/client-private-key.pem \ https://localhost:443:/my/endpoint
没有
-k
我得到错误:curl: (60) SSL certificate problem: self signed certificate More details here: https://curl.haxx.se/docs/sslcerts.html curl performs SSL certificate verification by default, using a "bundle" of Certificate Authority (CA) public keys (CA certs). If the default bundle file isn't adequate, you can specify an alternate file using the --cacert option. If this HTTPS server uses a certificate signed by a CA represented in the bundle, the certificate verification probably failed due to a problem with the certificate (it might be expired, or the name might not match the domain name in the URL). If you'd like to turn off curl's verification of the certificate, use the -k (or --insecure) option. HTTPS-proxy has similar options --proxy-cacert and --proxy-insecure.
已配置的 Apache HTTPClient:
private HttpClient createClient() throws Exception { String keyPassword = "123456"; KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(resourceAsStream("/client/client-key-store.p12"), keyPassword.toCharArray()); SSLContext sslContext = new SSLContextBuilder() .setProtocol("TLSv1.2") .loadKeyMaterial(ks, keyPassword.toCharArray()) .loadTrustMaterial(null, new TrustSelfSignedStrategy()) .build() return HttpClients.custom() .setSSLContext(sslContext) .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); }
(构造是通过多种方法完成的,我在这里挤成一个,所以如果有什么奇怪或丢失的东西,请告诉我,也许我错误地粘贴了一些东西。)
但是当尝试发送与 Curl 相同的请求时,我得到:
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2038)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1135)
at sun.security.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1779)
at sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:124)
at sun.security.ssl.Handshaker.sendChangeCipherSpec(Handshaker.java:1156)
at sun.security.ssl.ClientHandshaker.sendChangeCipherAndFinish(ClientHandshaker.java:1266)
at sun.security.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:1178)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:348)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:394)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)