中间证书
中间证书的使用是随着公共密钥基础设施(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.