中间证书
中间证书的使用是随着公共密钥基础设施(PKI)的发展而逐渐普及。
早期数字证书体系中根证书直接签发终端(客户端)证书,这种方式存在的问题
- 根证书安全风险: 一旦根证书泄露整个信任体系就会崩溃;
- 证书管理的灵活性:根证书通常有大型认证机构管理,工作不能合理分发;
- 证书撤销机制:仅用根证书签发,一旦撤销将影响整个信任链;
相关标准
X.509 证书标准:
X.509 v3 证书标准中, 引入了更多扩展字段,使得中间证书可以更有效地运作。它允许通过设置 basicConstraints
扩展来定义证书是否是一个证书颁发机构(CA)证书,并引入路径长度约束(pathlen),以确保中间证书不能签发超出其级别的证书。
X.509 v3 是证书链中的关键标准,它允许将证书分层,使得根证书签发中间证书,中间证书再签发终端证书。这一机制在 1996 年被纳入 X.509 v3 标准。
互联网工程任务组 (IETF) 标准:RFC 5280:
RFC 5280 是互联网标准的一个重要文档,它详细说明了证书路径验证的标准,定义了如何使用 X.509 证书和证书吊销列表(CRL)来建立信任链。
X.509 v3 侧重于定义证书和结构, RFC 5280 侧重于在互联网应用中的使用,包括如何处理证书路径验证和证书撤销。
记得刚工作时正赶上银行收单系统一机一密改造,为每个终端生成终端密钥,与PKI体系不同,当时的pos和收单系统终端大多用的还是对称加密体系。
收单终端签到会根据其对应终端主密钥生成工作密钥,用于保护用户密码和用于报文校验。最早的终端密钥用的都是各省一个, 后来为每个终端生成一个终端主密钥, 防止主密钥泄露导致全省密钥泄露。
其实大体改造思路和中间正式类似。
实际使用
看下实际网站证书使用情况
# openssl s_client -connect baidu.com:443 -showcerts CONNECTED(00000003) depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA verify return:1 depth=1 C = US, O = DigiCert Inc, CN = DigiCert Secure Site Pro CN CA G3 verify return:1 depth=0 C = CN, ST = \E5\8C\97\E4\BA\AC\E5\B8\82, O = "BeiJing Baidu Netcom Science Technology Co., Ltd", CN = www.baidu.cn verify return:1 --- Certificate chain 0 s:C = CN, ST = \E5\8C\97\E4\BA\AC\E5\B8\82, O = "BeiJing Baidu Netcom Science Technology Co., Ltd", CN = www.baidu.cn i:C = US, O = DigiCert Inc, CN = DigiCert Secure Site Pro CN CA G3 -----BEGIN CERTIFICATE----- ....PMdeTgwOzn2nJhPcJFZ5yUGPZ8thorOYjMLHQ4DKsy7mnGMnKLwmz17L/0Tx 8GU= -----END CERTIFICATE----- 1 s:C = US, O = DigiCert Inc, CN = DigiCert Secure Site Pro CN CA G3 i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA -----BEGIN CERTIFICATE----- ....+AgqbDB91mdnxIr1MuTtHGn/Q1YE2x9OSTb2f2k0ArhFxFdcjKTfLLewG1xD -----END CERTIFICATE----- --- Server certificate subject=C = CN, ST = \E5\8C\97\E4\BA\AC\E5\B8\82, O = "BeiJing Baidu Netcom Science Technology Co., Ltd", CN = www.baidu.cn issuer=C = US, O = DigiCert Inc, CN = DigiCert Secure Site Pro CN CA G3 ---
- 根证书(depth=2):由 DigiCert 签发,是整个信任链的起点,受操作系统或浏览器信任。
- 中间证书(depth=1):DigiCert 的中间 CA,用于签发百度的终端服务器证书。
- 服务器证书(depth=0):这是百度的具体服务器证书,表明它属于
www.baidu.cn
,并由 DigiCert 的中间 CA 签发。
一些问题
实际应用中间证书遇到问题主要配上的一些问题。
比如说类似下面的报错。
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alert.createSSLException(Alert.java:131) at sun.security.ssl.TransportContext.fatal(TransportContext.java:331) at sun.security.ssl.TransportContext.fatal(TransportContext.java:274) at sun.security.ssl.TransportContext.fatal(TransportContext.java:269) at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:654) at sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:473) at sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:369) at sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:377) at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444) at sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:422) at sun.security.ssl.TransportContext.dispatch(TransportContext.java:182) at sun.security.ssl.SSLTransport.decode(SSLTransport.java:152) at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1401) at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1309) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:440) at SSLClient.main(SSLClient.java:34) Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:456) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:323) at sun.security.validator.Validator.validate(Validator.java:271) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:315) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:223) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129) at sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:638) ... 11 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:148) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:129) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:451) ... 17 more
Traceback (most recent call last): File "client.py", line 18, in <module> wrapped_socket.connect((server_address, server_port)) File "/usr/lib64/python3.8/ssl.py", line 1342, in connect self._real_connect(addr, False) File "/usr/lib64/python3.8/ssl.py", line 1333, in _real_connect self.do_handshake() File "/usr/lib64/python3.8/ssl.py", line 1309, in do_handshake self._sslobj.do_handshake() ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)f
复现问题
使用openssl可以创建证书,搭建环境验证一下
创建openssl.cnf
# openssl.cnf [ req ] default_bits = 2048 distinguished_name = req_distinguished_name prompt = no req_extensions = v3_req [ req_distinguished_name ] C = US ST = California L = San Francisco O = MyCompany OU = MyDept CN = localhost # 根CA扩展配置 [ v3_ca_root ] basicConstraints = critical, CA:TRUE, pathlen:1 keyUsage = critical, keyCertSign, cRLSign # 中间CA扩展配置 [ v3_ca_intermediate ] basicConstraints = critical, CA:TRUE, pathlen:0 keyUsage = critical, keyCertSign, cRLSign # 服务器证书扩展配置 [ v3_req ] basicConstraints = CA:FALSE keyUsage = critical, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth
生成根CA证书
# 生成根CA私钥 openssl genrsa -out rootCA.key 2048 # 生成根CA证书 openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt -config openssl.cnf -subj "/C=US/ST=California/L=San Francisco/O=MyCompany/OU=RootCA/CN=MyRootCA" -extensions v3_ca_root
生成中间CA证书
# 生成中间CA私钥 openssl genrsa -out intermediateCA.key 2048 # 生成中间CA证书签署请求 (CSR) openssl req -new -key intermediateCA.key -out intermediateCA.csr -config openssl.cnf -subj "/C=US/ST=California/L=San Francisco/O=MyCompany/OU=IntermediateCA/CN=MyIntermediateCA" # 生成中间CA证书 openssl x509 -req -in intermediateCA.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out intermediateCA.crt -days 500 -sha256 -extensions v3_ca_intermediate -extfile openssl.cnf
生成服务器证书
# 生成服务器私钥 openssl genrsa -out server.key 2048 # 生成服务器证书签署请求 (CSR) openssl req -new -key server.key -out server.csr -config openssl.cnf -subj "/C=US/ST=California/L=San Francisco/O=MyCompany/OU=Server/CN=localhost" # 生成服务器证书 openssl x509 -req -in server.csr -CA intermediateCA.crt -CAkey intermediateCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extensions v3_req -extfile openssl.cnf
验证证书链,
openssl verify -CAfile rootCA.crt -untrusted intermediateCA.crt server.crt
server.crt: OK
启动 OpenSSL 服务器
openssl s_server -accept 4433 -cert server.crt -key server.key -CAfile intermediateCA.crt -www
客户端验证
openssl s_client -connect localhost:4433 -CAfile rootCA.crt -showcerts
输出结果
CONNECTED(00000003) Can't use SSL_get_servername depth=2 C = US, ST = California, L = San Francisco, O = MyCompany, OU = RootCA, CN = MyRootCA verify return:1 depth=1 C = US, ST = California, L = San Francisco, O = MyCompany, OU = IntermediateCA, CN = MyIntermediateCA verify return:1 depth=0 C = US, ST = California, L = San Francisco, O = MyCompany, OU = Server, CN = localhost verify return:1 --- Certificate chain 0 s:C = US, ST = California, L = San Francisco, O = MyCompany, OU = Server, CN = localhost i:C = US, ST = California, L = San Francisco, O = MyCompany, OU = IntermediateCA, CN = MyIntermediateCA -----BEGIN CERTIFICATE----- MIIDtjCCAp6gAwIBAgIUXRjYuUAjaXYOsO4Jejyk8HH6gxgwDQYJKoZIhvcNAQEL BQAwgYIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH DA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKDAlNeUNvbXBhbnkxFzAVBgNVBAsMDklu dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4XDTI0MTAx MTE1MDMzMVoXDTI2MDIyMzE1MDMzMVowczELMAkGA1UEBhMCVVMxEzARBgNVBAgM CkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU15 Q29tcGFueTEPMA0GA1UECwwGU2VydmVyMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRiaafyXKpH7oA2h5O2QaHm/k+ 1qcM+ooiDLaB/VtFU5qlcx78Tf+YKdamVLZXNCaech1xtsELUBMxc7QPJcsBceRA KSetptL8nHsnzoy7MGR7i1ODV5mz4oAQFohT9SMvdHo/y4WflZYDY1g/Vjgsv0qn pA9EfUYfPkumR39WFcSIK8l/iUzCwVRrDZ2mehcGU5ekxWBVVchISw8P8QofldI2 YhHDFUJphLcYAYRgcESsGzdK1Ev2/luV9MsyKlcBBZK+p5XOulmKILHwhsb/e89t /bT79oSGuoMbUE3kcAai3Pz03qyH7dpNDozHYnCqJ9RqPQlq+RK/n7AnFZJvAgMB AAGjMjAwMAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsG AQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQB3u8VjA0UoGQms1CLuVBfhAh2/7KBH 2QyFz6lT9FFZivKtm4uP7tmWYgUx6sRRriugxD4h9IwMF/Eh/b/FD9yy2xhE4bFE OcBgQpACXzaZcyNBF6muG6hMYsgB4U1DD6+L6bXFKwer65QvmRdodd6HxRz7r90h bgGK/z1gSWvRgC7oYP/YRRoTsTkEc6WePcXMswPU0ss2qyJ4RaBEab4UsYD8VaDR UZB2p64Mbe14+Lfe2d7Tq+2oZUwLLSqbqpLJaSj8/aTFX8ENc0dhudW8yAey6yBo WucFYAIlAqeh7b9onIwfu39LU5Qr6y55xXMIv84sGQDdo8ntWdefH60l -----END CERTIFICATE----- 1 s:C = US, ST = California, L = San Francisco, O = MyCompany, OU = IntermediateCA, CN = MyIntermediateCA i:C = US, ST = California, L = San Francisco, O = MyCompany, OU = RootCA, CN = MyRootCA -----BEGIN CERTIFICATE----- MIIDqTCCApGgAwIBAgIUHyJzgt2DD8Ymr9bkQMPVBhLrt4AwDQYJKoZIhvcNAQEL BQAwcjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM DVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU15Q29tcGFueTEPMA0GA1UECwwGUm9v dENBMREwDwYDVQQDDAhNeVJvb3RDQTAeFw0yNDEwMTExNTAxMTZaFw0yNjAyMjMx NTAxMTZaMIGCMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG A1UEBwwNU2FuIEZyYW5jaXNjbzESMBAGA1UECgwJTXlDb21wYW55MRcwFQYDVQQL DA5JbnRlcm1lZGlhdGVDQTEZMBcGA1UEAwwQTXlJbnRlcm1lZGlhdGVDQTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKfUmPuPeSHszYUoVyw7m4kaQfYf ZjapPB/UKPr3VVLq64LQoDskJq/MF1gSKW3UxDoG5jCoDzPpLhbR68vGiPOirIOs bRfNJGXivvEBkv1NeDJUjvWsjXqCYdl0aIDQxfCA1iJDV7Mq5/qmPhee+8y7dUbD 1Ujx2WKKyDX+0jZNHto1cWSIWRZqqESG0RGHxez0c2UbG3Mgr401uvEPq2bcD4dy 9ociGzVAryyiXCipXvqyIE30bHQNgRSrxz/999d3uZFcNIrkrdycFD/Z+gfrzjI6 oDoopesc3XYOMYCfS/G27mvRFCR230KI2i+sN4jc85aCi1y29yFoAS+JpbUCAwEA AaMmMCQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI hvcNAQELBQADggEBAIskEpqFr8JRGFq9Bp4BNtR8TmGu3c+0gAzPIjeNGb34RbfH qYKGvWgUO4xN5liBzwkZCryDpn3SWhWydUETa+6z0p5r9lmegYoCa1P9l/0kSORJ XaLrw8hiSrSz5mkRxesHt6Y1QhBRSVJSNnm0jN9uafFlpmPWtX6OH+GWwqJjBqM+ D26PlrIPwmb+j/M7XRc/dQw0kWvmIR2hh+4wFb+5Y4Ew/XzSLXVCsOIaVNI8MZ49 DAvi7gd15o4hsHzP1IYct97VAdTwSGNwtQG1IGGxX76/t681kGdDqgkHePXLdraB 1f3TVbuyyc5CzuRoxz36ngEqygPFpK6NLjnT5Eo= -----END CERTIFICATE----- --- Server certificate subject=C = US, ST = California, L = San Francisco, O = MyCompany, OU = Server, CN = localhost issuer=C = US, ST = California, L = San Francisco, O = MyCompany, OU = IntermediateCA, CN = MyIntermediateCA --- No client certificate CA names sent Peer signing digest: SHA256 Peer signature type: RSA-PSS Server Temp Key: X25519, 253 bits --- SSL handshake has read 2456 bytes and written 369 bytes Verification: OK --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 2048 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 0 (ok) --- --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Session-ID: 2EC761154438F854423BD876391EE5876CB898267045F43DC6CA14AF3B90FCB6 Session-ID-ctx: Resumption PSK: CA7F44075220AA22B83B0E2CAA1559812BCAB3A5D8E46190D6885C8C8BCAF13EDBD53DE945511DA854CEFE81BEF55EC4 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 7200 (seconds) TLS session ticket: 0000 - 32 2b ef 01 94 00 9f 82-78 95 75 42 c2 60 00 c3 2+......x.uB.`.. 0010 - f3 d5 f1 80 79 c3 e5 1c-90 ed 32 a3 c5 58 b1 1b ....y.....2..X.. 0020 - 26 b3 b3 bf 7b fc 51 cc-e0 7c 47 7e 9e 62 45 2b &...{.Q..|G~.bE+ 0030 - da 16 7b a8 b6 4f aa 7a-38 47 0d dd e1 d4 ae 6f ..{..O.z8G.....o 0040 - 12 7d b2 f1 ca 4e 98 3d-66 0b c7 3a 28 bc e7 31 .}...N.=f..:(..1 0050 - 6f af 0a d4 56 6f 45 6b-f5 fa 0f 2e 35 c6 6a d6 o...VoEk....5.j. 0060 - 36 a3 9f f7 3d ba f9 f2-ae b0 6a b6 db fc fe 0b 6...=.....j..... 0070 - 9e 98 d8 d4 04 97 be 94-3b eb 3b 7a f7 43 10 da ........;.;z.C.. 0080 - 42 32 2f 21 f4 7b 82 bb-68 7c ed cb 25 5a ff 19 B2/!.{..h|..%Z.. 0090 - 8f 3f 78 e7 a1 73 34 58-8e f1 82 b0 e9 62 c8 7d .?x..s4X.....b.} 00a0 - 4c da 9e ea a2 41 40 e0-f5 27 ed ff 9d d0 49 dd L....A@..'....I. 00b0 - 42 cf 3c d0 a7 19 c9 fd-6d 7d fb 80 7a 6f 98 de B.<.....m}..zo.. Start Time: 1728685261 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no Max Early Data: 0 --- read R BLOCK --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Session-ID: D22D61BF964422B64B281B55817C7F612DEDEF2FC8E8DAC0A7A6A81D8AA299CA Session-ID-ctx: Resumption PSK: 9D7264E4F4DCAD28200D7D91AAC9CDA47E57A349D5015F2CD8B58937AEFF6BB9F5182113B069CB26A22C0AF985EC42D0 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 7200 (seconds) TLS session ticket: 0000 - 32 2b ef 01 94 00 9f 82-78 95 75 42 c2 60 00 c3 2+......x.uB.`.. 0010 - fc 7d 9d b6 ab 54 a9 db-fc b4 19 cb b7 51 80 1a .}...T.......Q.. 0020 - 7e 8f f4 b4 9a 46 9f 6f-65 e3 e0 d2 2e 72 63 8b ~....F.oe....rc. 0030 - 6c 7a 3a 59 c7 63 84 65-89 c2 f9 39 cd a2 a5 e8 lz:Y.c.e...9.... 0040 - cb 0a 66 e6 25 ef d9 15-64 92 df a3 52 7d b4 0d ..f.%...d...R}.. 0050 - 10 2e 2b 1c 06 59 bc 36-93 a8 60 9e fe 14 cb 86 ..+..Y.6..`..... 0060 - 4d ba 72 c9 44 30 cc 92-cd e0 52 a9 3c 87 2e 56 M.r.D0....R.<..V 0070 - f0 fd ce f3 0a 61 8a e5-54 63 9f 12 0a e7 f2 f1 .....a..Tc...... 0080 - 35 5b b4 ee 3b 84 61 2c-e8 f6 a1 58 1d 23 6e da 5[..;.a,...X.#n. 0090 - 61 a6 a5 17 80 60 8b 1f-77 4a b1 37 9d fb 16 39 a....`..wJ.7...9 00a0 - 97 90 a7 a9 e3 9a 67 12-a8 3f 76 1d 6a 7a 27 0e ......g..?v.jz'. 00b0 - 27 2a cf c8 7e b6 fc a5-52 95 8f 4a 58 65 46 99 '*..~...R..JXeF. Start Time: 1728685261 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no Max Early Data: 0 --- read R BLOCK
---- depth=2 C = US, ST = California, L = San Francisco, O = MyCompany, OU = RootCA, CN = MyRootCA verify return:1 depth=1 C = US, ST = California, L = San Francisco, O = MyCompany, OU = IntermediateCA, CN = MyIntermediateCA verify return:1 depth=0 C = US, ST = California, L = San Francisco, O = MyCompany, OU = Server, CN = localhost
使用代理配置
实际使用中如果是互联网环境一般会将证书链配置到DMZ代理服务或区的负载设备上,代理负载将卸载ssl,请求转发到应用服务。
真实环境一般客户都一般不需要加载CA,使用系统内置的 CA 证书来验证服务器证书,如果中间证书客户端没安装,也可以在服务配置证书链。
下面使用nginx配置,java客户端验证一下。
user root; worker_processes 1; error_log logs/error.log debug; #pid logs/nginx.pid; events { worker_connections 1024; } http { log_format quic '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" "$http3"'; access_log logs/access.log quic; server { listen 4433 ssl; server_name localhost; # 替换为你的域名 ssl_certificate /root/ssl/certs/server_chain.crt; # 证书链文件 #ssl_certificate /root/ssl/certs/server.crt; # 证书链文件 ssl_trusted_certificate /root/ssl/certs/intermediateCA.crt; # 中间 CA 证书 ssl_certificate_key /root/ssl/certs/server.key; # 服务器私钥文件 ssl_protocols TLSv1.2 TLSv1.3; # 设置支持的TLS协议版本 ssl_ciphers 'HIGH:!aNULL:!MD5'; # 设置加密套件 location / { root /root/nginx-quic/html; # 设置网站根目录 index index.html index.htm; # 设置默认首页 } } }
JAVA客户端,Java 默认使用 JKS 格式,但如果你的证书文件使用的是 PEM 或其他格式,需要先转换为 JKS 格式
- 首先,将 CRT 转换为 PKCS12 格式(此时需要设置一个密码):
openssl pkcs12 -export -in rootCA.crt -inkey rootCA.key -out rootCA.p12 -name rootCA Enter Export Password: Verifying - Enter Export Password:
在此过程中,系统会要求您输入导出文件的密码。
- 将 PKCS12 转换为 JKS:使用
keytool
命令将 PKCS12 转换为 JKS:keytool -importkeystore -srckeystore rootCA.p12 -srcstoretype PKCS12 -destkeystore rootCA.jks -deststoretype JKS
在此过程中,系统会要求您输入导出文件的密码。
java客户端代码
SSLClient.java,
import javax.net.ssl.*; import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; public class SSLClient { public static void main(String[] args) { String caCertPath = "/root/ssl/certs/rootCA.jks"; String password = "111111"; String host = "localhost"; int port = 4433; try { // 加载 KeyStore KeyStore keyStore = KeyStore.getInstance("JKS"); try (InputStream keyStoreStream = new FileInputStream(caCertPath)) { keyStore.load(keyStoreStream, password.toCharArray()); } // 创建 TrustManagerFactory TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); // 创建 SSLContext SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), null); // 创建 SSL Socket SSLSocketFactory socketFactory = sslContext.getSocketFactory(); try (SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(host, port)) { sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"}); // 发送请求 sslSocket.startHandshake(); sslSocket.getOutputStream().write("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n".getBytes()); sslSocket.getOutputStream().flush(); // 读取响应 byte[] response = new byte[4096]; int bytesRead = sslSocket.getInputStream().read(response); System.out.println(new String(response, 0, bytesRead)); } } catch (Exception e) { e.printStackTrace(); } } }
javac SSLClient.java
如果nginx只配置了服务器证书,那么要求客户端需要安装中间证书。
如果客户都没有安装中间证书,可以合并证书, 服务证书再上面, 做为服务端证书。
cat server.crt intermediateCA.crt > server_chain.crt
启动nginx, 执行java客户端返回。
[root@iZ8vbd88lmglnbsnad85q3Z certs]# java SSLClient HTTP/1.1 200 OK Server: nginx/1.25.4 Date: Fri, 11 Oct 2024 22:41:39 GMT Content-Type: text/html Content-Length: 1406 Last-Modified: Sun, 28 Jan 2024 02:07:47 GMT Connection: keep-alive ETag: "65b5b6f3-57e" Accept-Ranges: bytes <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web
如果直接使用服务端证书
#ssl_certificate /root/ssl/certs/server_chain.crt;# 证书链文件 ssl_certificate /root/ssl/certs/server.crt; # 证书链文件
直接报错, unable to find valid certification path to requested target
[root@iZ8vbd88lmglnbsnad85q3Z certs]# java SSLClient javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target ... 11 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:148) at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:129) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:451) ... 17 mored
图片from陳聯鳳
Comments are closed.