# intermediate CA certificate 中间证书
Date: 2024-10-12


#### **中间证书**

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

可以看到验证结果 Verify return code: 0 (ok)

证书链条，就是我们手工创建的证书。

```
----
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 格式

1. **首先，将 CRT 转换为 PKCS12 格式**（此时需要设置一个密码）：
    
    ```
    openssl pkcs12 -export -in rootCA.crt -inkey rootCA.key -out rootCA.p12 -name rootCA
    Enter Export Password:
    Verifying - Enter Export Password:
    
    ```
    
    在此过程中，系统会要求您输入导出文件的密码。
2. **将 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[陳聯鳳](https://www.facebook.com/groups/474930279315210/user/100003830764002/?__tn__=-UC*F)

