|
19 | 19 |
|
20 | 20 | 传输层安全协议: |
21 | 21 | https://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E5%8D%94%E8%AD%B0 |
| 22 | +
|
| 23 | + ── mTLS additions ─────────────────────────────────────────────────────────── |
| 24 | + [MTLS-1] SetCACertificate(Pointer, Integer) override added. |
| 25 | + Loads a CA certificate from a memory buffer and registers it with |
| 26 | + the SSL context for client-certificate verification (mTLS server |
| 27 | + mode). Uses BIO_new_mem_buf + PEM_read_bio_X509 to parse the PEM |
| 28 | + data, then calls SSL_CTX_add_client_CA (populates the CA list sent |
| 29 | + to clients in the CertificateRequest handshake message) and |
| 30 | + X509_STORE_add_cert (adds the cert to the trust store used to |
| 31 | + verify the presented certificate chain). |
| 32 | + Companion overloads (TBytes / string / file) inherited from the |
| 33 | + base class (Net.CrossSslSocket.Base) mirror the SetCertificate |
| 34 | + overload family. |
| 35 | +
|
| 36 | + [MTLS-2] SetVerifyPeer(Boolean) override added. |
| 37 | + When AVerify=True, sets SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
| 38 | + so the server demands a valid client certificate. |
| 39 | + When AVerify=False, reverts to SSL_VERIFY_NONE (default). |
| 40 | + Must be called AFTER SetCACertificate — the trust store must be |
| 41 | + populated before verify mode is enabled. |
22 | 42 | } |
23 | 43 |
|
24 | 44 | interface |
@@ -158,6 +178,19 @@ TCrossOpenSslSocket = class(TCrossSslSocketBase) |
158 | 178 |
|
159 | 179 | procedure SetCertificate(const ACertBuf: Pointer; const ACertBufSize: Integer); overload; override; |
160 | 180 | procedure SetPrivateKey(const APKeyBuf: Pointer; const APKeyBufSize: Integer); overload; override; |
| 181 | + |
| 182 | + { ── MTLS-1: load a CA certificate (PEM buffer) for client-cert verification. |
| 183 | + Calls SSL_CTX_add_client_CA to register the CA name in the |
| 184 | + CertificateRequest list the server sends to clients during the |
| 185 | + handshake, and X509_STORE_add_cert to populate the trust store used to |
| 186 | + verify the certificate chain presented by the client. } |
| 187 | + procedure SetCACertificate(const ACACertBuf: Pointer; const ACACertBufSize: Integer); overload; override; |
| 188 | + |
| 189 | + { ── MTLS-2: enable / disable client-certificate verification. |
| 190 | + AVerify=True → SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
| 191 | + (handshake fails without a valid client cert) |
| 192 | + AVerify=False → SSL_VERIFY_NONE (default) } |
| 193 | + procedure SetVerifyPeer(const AVerify: Boolean); override; |
161 | 194 | end; |
162 | 195 |
|
163 | 196 | implementation |
@@ -888,6 +921,70 @@ procedure TCrossOpenSslSocket.SetPrivateKey(const APKeyBuf: Pointer; |
888 | 921 | TSSLTools.SetPrivateKey(FSslCtx, APKeyBuf, APKeyBufSize); |
889 | 922 | end; |
890 | 923 |
|
| 924 | +{ ── MTLS-1: load a CA certificate (PEM buffer) into the SSL context ────────── |
| 925 | + Registers the certificate with both the CertificateRequest CA list (sent to |
| 926 | + clients in the TLS handshake to indicate which CAs the server trusts) and |
| 927 | + the X509 trust store (used to verify the certificate chain presented by the |
| 928 | + client). Must be called before SetVerifyPeer(True). } |
| 929 | +procedure TCrossOpenSslSocket.SetCACertificate(const ACACertBuf: Pointer; |
| 930 | + const ACACertBufSize: Integer); |
| 931 | +var |
| 932 | + LBio: PBIO; |
| 933 | + LCACert: PX509; |
| 934 | + LStore: PX509_STORE; |
| 935 | +begin |
| 936 | + if not Ssl or (FSslCtx = nil) then Exit; |
| 937 | + |
| 938 | + // Wrap the caller's buffer in a read-only memory BIO — no copy is made. |
| 939 | + LBio := BIO_new_mem_buf(ACACertBuf, ACACertBufSize); |
| 940 | + if LBio = nil then |
| 941 | + raise ESsl.Create('SetCACertificate: BIO_new_mem_buf failed'); |
| 942 | + try |
| 943 | + // Parse the PEM-encoded X.509 certificate. |
| 944 | + LCACert := PEM_read_bio_X509(LBio, nil, nil, nil); |
| 945 | + if LCACert = nil then |
| 946 | + raise ESsl.Create( |
| 947 | + 'SetCACertificate: PEM_read_bio_X509 failed — ' + |
| 948 | + 'ensure the buffer contains a valid PEM certificate'); |
| 949 | + try |
| 950 | + // Register the CA name in the CertificateRequest list. |
| 951 | + SSL_CTX_add_client_CA(FSslCtx, LCACert); |
| 952 | + |
| 953 | + // Add the cert to the trust store for chain verification. |
| 954 | + LStore := SSL_CTX_get_cert_store(FSslCtx); |
| 955 | + if Assigned(LStore) then |
| 956 | + X509_STORE_add_cert(LStore, LCACert); |
| 957 | + // X509_STORE_add_cert returns 0 if the cert is already in the store |
| 958 | + // (duplicate) — this is benign, so we do not raise on <= 0 here. |
| 959 | + finally |
| 960 | + // Decrement our local ref-count. The context and store hold their own. |
| 961 | + X509_free(LCACert); |
| 962 | + end; |
| 963 | + finally |
| 964 | + BIO_free(LBio); |
| 965 | + end; |
| 966 | +end; |
| 967 | + |
| 968 | +{ ── MTLS-2: enable or disable mandatory client-certificate verification ────── |
| 969 | + SSL_VERIFY_NONE — no client certificate is requested (default). |
| 970 | + SSL_VERIFY_PEER — request and verify, but allow connections |
| 971 | + without a cert (or with an invalid cert). |
| 972 | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT — combined with PEER, requires the client |
| 973 | + to present a valid cert; handshake fails |
| 974 | + otherwise. |
| 975 | + This implementation uses PEER | FAIL_IF_NO_PEER_CERT for AVerify=True so |
| 976 | + the server demands a valid mTLS handshake. } |
| 977 | +procedure TCrossOpenSslSocket.SetVerifyPeer(const AVerify: Boolean); |
| 978 | +begin |
| 979 | + if not Ssl or (FSslCtx = nil) then Exit; |
| 980 | + |
| 981 | + if AVerify then |
| 982 | + SSL_CTX_set_verify(FSslCtx, |
| 983 | + SSL_VERIFY_PEER or SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nil) |
| 984 | + else |
| 985 | + SSL_CTX_set_verify(FSslCtx, SSL_VERIFY_NONE, nil); |
| 986 | +end; |
| 987 | + |
891 | 988 | procedure TCrossOpenSslSocket.TriggerConnected(const AConnection: ICrossConnection); |
892 | 989 | var |
893 | 990 | LConnection: TCrossOpenSslConnection; |
|
0 commit comments