Skip to content

Commit

Permalink
Fixed local issuer determination for OCSP Staple [Issue #3773](#3773)
Browse files Browse the repository at this point in the history
  • Loading branch information
tbeets committed Jul 26, 2023
1 parent 99dc115 commit 73c00d4
Show file tree
Hide file tree
Showing 7 changed files with 1,330 additions and 45 deletions.
11 changes: 11 additions & 0 deletions server/certstore/certstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package certstore

import (
"crypto"
"crypto/x509"
"io"
"runtime"
"strings"
Expand Down Expand Up @@ -82,6 +83,16 @@ func ParseCertMatchBy(certMatchBy string) (MatchByType, error) {
return certMatchByType, nil
}

func GetLeafIssuer(leaf *x509.Certificate, vOpts x509.VerifyOptions) (issuer *x509.Certificate) {
chains, err := leaf.Verify(vOpts)
if err != nil || len(chains) == 0 {
issuer = nil
} else {
issuer = chains[0][1]
}
return
}

// credential provides access to a public key and is a crypto.Signer.
type credential interface {
// Public returns the public key corresponding to the leaf certificate.
Expand Down
98 changes: 72 additions & 26 deletions server/ocsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
"time"

"golang.org/x/crypto/ocsp"

"github.com/nats-io/nats-server/v2/server/certstore"
)

const (
Expand Down Expand Up @@ -389,7 +391,7 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni
}

// TODO: Add OCSP 'responder_cert' option in case CA cert not available.
issuers, err := getOCSPIssuer(caFile, cert.Certificate)
issuer, err := getOCSPIssuer(caFile, cert.Certificate)
if err != nil {
return nil, nil, err
}
Expand All @@ -402,7 +404,7 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni
certFile: certFile,
stopCh: make(chan struct{}, 1),
Leaf: cert.Leaf,
Issuer: issuers[len(issuers)-1],
Issuer: issuer,
}

// Get the certificate status from the memory, then remote OCSP responder.
Expand Down Expand Up @@ -449,7 +451,7 @@ func (srv *Server) NewOCSPMonitor(config *tlsConfigKind) (*tls.Config, *OCSPMoni

chain := s.VerifiedChains[0]
leaf := chain[0]
parent := issuers[len(issuers)-1]
parent := issuer

resp, err := ocsp.ParseResponseForCert(oresp, leaf, parent)
if err != nil {
Expand Down Expand Up @@ -833,37 +835,81 @@ func parseCertPEM(name string) ([]*x509.Certificate, error) {
return x509.ParseCertificates(pemBytes)
}

// getOCSPIssuer returns a CA cert from the given path. If the path is empty,
// then this checks a given cert chain. If both are empty, then it returns an
// error.
func getOCSPIssuer(issuerCert string, chain [][]byte) ([]*x509.Certificate, error) {
var issuers []*x509.Certificate
var err error
switch {
case len(chain) == 1 && issuerCert == _EMPTY_:
err = fmt.Errorf("ocsp ca required in chain or configuration")
case issuerCert != _EMPTY_:
issuers, err = parseCertPEM(issuerCert)
case len(chain) > 1 && issuerCert == _EMPTY_:
issuers, err = x509.ParseCertificates(chain[1])
default:
err = fmt.Errorf("invalid ocsp ca configuration")
// getOCSPIssuerLocally determines a leaf's issuer from locally configured certificates
func getOCSPIssuerLocally(trustedCAs []*x509.Certificate, certBundle []*x509.Certificate) (*x509.Certificate, error) {
var vOpts x509.VerifyOptions
var leaf *x509.Certificate
trustedCAPool := x509.NewCertPool()

// Require Leaf as first cert in bundle
if len(certBundle) > 0 {
leaf = certBundle[0]
} else {
return nil, fmt.Errorf("invalid ocsp ca configuration")
}
if err != nil {
return nil, err

// Allow Issuer to be configured as second cert in bundle
if len(certBundle) > 1 {
// The operator may have misconfigured the cert bundle
issuerCandidate := certBundle[1]
err := issuerCandidate.CheckSignature(leaf.SignatureAlgorithm, leaf.RawTBSCertificate, leaf.Signature)
if err != nil {
return nil, fmt.Errorf("invalid issuer configuration: %w", err)
} else {
return issuerCandidate, nil
}
}

if len(issuers) == 0 {
return nil, fmt.Errorf("no issuers found")
// Operator did not provide the Leaf Issuer in cert bundle second position
// so we will attempt to create at least one ordered verified chain from the
// trusted CA pool.

// Specify CA trust store to validator; if unset, system trust store used
if len(trustedCAs) > 0 {
for _, ca := range trustedCAs {
trustedCAPool.AddCert(ca)
}
vOpts.Roots = trustedCAPool
}

return certstore.GetLeafIssuer(leaf, vOpts), nil
}

// getOCSPIssuer determines an issuer certificate from the cert (bundle) or the file-based CA trust store
func getOCSPIssuer(caFile string, chain [][]byte) (*x509.Certificate, error) {
var issuer *x509.Certificate
var trustedCAs []*x509.Certificate
var certBundle []*x509.Certificate
var err error

// FIXME(tgb): extend if pluggable CA store provider added to NATS (i.e. other than PEM file)

// Non-system default CA trust store passed
if caFile != _EMPTY_ {
trustedCAs, err = parseCertPEM(caFile)
if err != nil {
return nil, fmt.Errorf("failed to parse ca_file: %v", err)
}
}

for _, issuer := range issuers {
if !issuer.IsCA {
return nil, fmt.Errorf("%s invalid ca basic constraints: is not ca", issuer.Subject)
// Specify bundled intermediate CA store
for _, certBytes := range chain {
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, fmt.Errorf("failed to parse cert: %v", err)
}
certBundle = append(certBundle, cert)
}

issuer, err = getOCSPIssuerLocally(trustedCAs, certBundle)
if err != nil || issuer == nil {
return nil, fmt.Errorf("no issuers found")
}

return issuers, nil
if !issuer.IsCA {
return nil, fmt.Errorf("%s invalid ca basic constraints: is not ca", issuer.Subject)
}
return issuer, nil
}

func ocspStatusString(n int) string {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
3c:c4:82:66:f8:5d:a6:b6:c7:66:e1:b2:01:3f:e0:72:fc:72:61:33
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=WA, L=Tacoma, O=Testnats, CN=Intermediate CA 1
Validity
Not Before: May 1 19:33:37 2023 GMT
Not After : Apr 28 19:33:37 2033 GMT
Subject: C=US, ST=WA, L=Tacoma, O=Testnats, CN=TestServer1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:af:26:5c:50:c0:fa:62:b5:fd:3d:c1:9e:26:51:
58:62:04:37:b0:b5:6a:9b:6a:e3:22:3c:cd:ee:3c:
e7:8b:d3:e2:4c:08:1a:4d:63:c1:81:20:f4:53:a5:
5d:2f:d2:71:d8:af:e3:26:95:b4:27:14:46:7f:e2:
0a:73:12:a7:0e:ff:99:5a:29:f5:d0:65:96:b1:d1:
96:7f:0c:43:b8:71:f2:4b:21:e1:97:6c:1b:01:e5:
38:1a:39:44:72:d5:19:20:87:fe:90:4f:3b:97:f2:
7d:bd:57:97:4d:9d:56:50:89:5b:79:29:7a:3a:13:
97:08:61:c2:0c:a6:02:49:c9:8a:41:ab:8e:9f:25:
c9:33:18:f8:92:64:58:04:cc:a3:9d:cf:d4:d2:bd:
20:ab:8b:9d:55:df:fb:5b:23:ac:95:12:fa:6f:07:
93:3f:0e:03:86:c4:9b:25:06:21:9b:03:96:32:b8:
e0:0f:63:e2:1d:34:d1:41:35:19:09:c1:a0:dc:26:
b9:c8:66:fa:87:67:22:6e:0c:a6:e7:0f:24:64:b1:
4f:84:05:ef:ad:8e:1b:f2:f4:38:87:d3:e3:48:a5:
82:e0:66:89:1d:92:9a:59:67:a4:1d:03:6f:4d:a5:
fb:3b:c0:0b:73:a7:ab:8f:b4:10:25:8e:69:42:76:
82:5f
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
43:16:E6:03:AF:37:B2:7B:BD:B3:C8:A2:9C:95:D7:FA:32:F8:9E:6F
X509v3 Authority Key Identifier:
B5:91:6E:4F:64:B7:16:84:76:F9:B4:BE:99:CE:60:95:98:1A:8E:9D
X509v3 Basic Constraints: critical
CA:FALSE
Netscape Cert Type:
SSL Client, SSL Server
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 CRL Distribution Points:
Full Name:
URI:http://127.0.0.1:18888/intermediate1_crl.der
Authority Information Access:
OCSP - URI:http://127.0.0.1:18888/
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
a3:87:9f:05:e4:38:61:f7:c4:5b:17:13:4b:2c:9d:a2:4d:e6:
ad:93:54:c5:a3:00:27:0b:5c:45:c5:bd:f8:b6:a7:5a:2a:ec:
dc:9b:59:8a:c7:59:e7:b9:86:f7:27:be:45:0d:d9:86:76:cf:
00:71:ad:aa:cc:73:50:8c:68:63:b0:e2:3a:59:dd:85:fa:0d:
f0:82:51:05:79:e6:d5:0e:0b:bb:ed:23:65:8f:d0:8b:01:df:
86:74:bc:3a:22:90:e4:59:44:91:d5:44:d8:21:4d:4e:10:72:
0a:12:2e:4a:20:5f:15:e7:16:0b:6f:76:f3:04:1f:da:44:50:
3b:c3:b3:0f:fa:05:cf:6e:64:9c:65:e2:0d:38:28:31:c3:c3:
b6:66:ef:80:d3:c4:5f:e9:f9:01:e9:ce:e6:99:46:a0:9d:ce:
90:63:77:d2:85:21:d7:88:32:55:38:fe:10:07:69:cd:c8:06:
b7:6f:49:98:bf:cd:be:4f:ab:44:ea:78:af:ab:01:c8:3e:fa:
d9:54:bc:59:28:db:03:9b:1c:ee:e4:c3:ed:f3:97:30:c6:40:
33:76:84:40:b2:b8:4d:b4:ca:a9:2d:d1:4d:17:92:ea:c0:c9:
cb:f6:b1:d7:d3:c7:e6:75:15:00:ff:c7:d9:54:63:27:19:5c:
96:a5:e5:d9
-----BEGIN CERTIFICATE-----
MIIEYjCCA0qgAwIBAgIUPMSCZvhdprbHZuGyAT/gcvxyYTMwDQYJKoZIhvcNAQEL
BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMQ8wDQYDVQQHDAZUYWNvbWEx
ETAPBgNVBAoMCFRlc3RuYXRzMRowGAYDVQQDDBFJbnRlcm1lZGlhdGUgQ0EgMTAe
Fw0yMzA1MDExOTMzMzdaFw0zMzA0MjgxOTMzMzdaMFQxCzAJBgNVBAYTAlVTMQsw
CQYDVQQIDAJXQTEPMA0GA1UEBwwGVGFjb21hMREwDwYDVQQKDAhUZXN0bmF0czEU
MBIGA1UEAwwLVGVzdFNlcnZlcjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCvJlxQwPpitf09wZ4mUVhiBDewtWqbauMiPM3uPOeL0+JMCBpNY8GBIPRT
pV0v0nHYr+MmlbQnFEZ/4gpzEqcO/5laKfXQZZax0ZZ/DEO4cfJLIeGXbBsB5Tga
OURy1Rkgh/6QTzuX8n29V5dNnVZQiVt5KXo6E5cIYcIMpgJJyYpBq46fJckzGPiS
ZFgEzKOdz9TSvSCri51V3/tbI6yVEvpvB5M/DgOGxJslBiGbA5YyuOAPY+IdNNFB
NRkJwaDcJrnIZvqHZyJuDKbnDyRksU+EBe+tjhvy9DiH0+NIpYLgZokdkppZZ6Qd
A29Npfs7wAtzp6uPtBAljmlCdoJfAgMBAAGjggEkMIIBIDAdBgNVHQ4EFgQUQxbm
A683snu9s8iinJXX+jL4nm8wHwYDVR0jBBgwFoAUtZFuT2S3FoR2+bS+mc5glZga
jp0wDAYDVR0TAQH/BAIwADARBglghkgBhvhCAQEEBAMCBsAwDgYDVR0PAQH/BAQD
AgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA9BgNVHR8ENjA0MDKg
MKAuhixodHRwOi8vMTI3LjAuMC4xOjE4ODg4L2ludGVybWVkaWF0ZTFfY3JsLmRl
cjAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly8xMjcuMC4wLjE6
MTg4ODgvMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsF
AAOCAQEAo4efBeQ4YffEWxcTSyydok3mrZNUxaMAJwtcRcW9+LanWirs3JtZisdZ
57mG9ye+RQ3ZhnbPAHGtqsxzUIxoY7DiOlndhfoN8IJRBXnm1Q4Lu+0jZY/QiwHf
hnS8OiKQ5FlEkdVE2CFNThByChIuSiBfFecWC2928wQf2kRQO8OzD/oFz25knGXi
DTgoMcPDtmbvgNPEX+n5AenO5plGoJ3OkGN30oUh14gyVTj+EAdpzcgGt29JmL/N
vk+rROp4r6sByD762VS8WSjbA5sc7uTD7fOXMMZAM3aEQLK4TbTKqS3RTReS6sDJ
y/ax19PH5nUVAP/H2VRjJxlclqXl2Q==
-----END CERTIFICATE-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
3c:d7:16:fb:15:99:81:4e:53:f8:80:7c:b6:7c:77:a6:06:a4:3e:ea
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=WA, L=Tacoma, O=Testnats, CN=Root CA
Validity
Not Before: May 1 19:01:43 2023 GMT
Not After : Apr 28 19:01:43 2033 GMT
Subject: C=US, ST=WA, L=Tacoma, O=Testnats, CN=Intermediate CA 2
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:da:5f:ff:1d:f7:8d:1a:9e:9a:f3:2b:68:8f:c1:
0c:33:06:41:00:c9:3e:e4:1a:e1:e0:70:6a:f5:2f:
ad:df:f3:e9:99:ed:c5:d7:aa:93:13:37:ff:47:aa:
f3:c5:89:f7:b7:ad:3a:47:e5:9c:4e:9f:8c:e2:41:
ed:a4:7c:9d:88:32:ae:f5:8a:84:9f:0c:18:a0:b3:
fe:8e:dc:2a:88:6a:f5:2f:9c:86:92:fa:7b:6e:b3:
5a:78:67:53:0b:21:6c:0d:6c:80:1a:0e:1e:ee:06:
c4:d2:e7:24:c6:e5:74:be:1e:2e:17:55:2b:e5:9f:
0b:a0:58:cc:fe:bf:53:37:f7:dc:95:88:f4:77:a6:
59:b4:b8:7c:a2:4b:b7:6a:67:aa:84:dc:29:f1:f9:
d7:89:05:4d:0b:f3:8b:2d:52:99:57:ed:6f:11:9e:
af:28:a3:61:44:c2:ec:6e:7f:9f:3d:0b:dc:f7:19:
6d:14:8a:a5:b8:b6:29:02:34:90:b4:96:c1:cb:a7:
42:46:97:cf:8d:59:fd:17:b1:a6:27:a7:7b:8a:47:
6f:fa:03:24:1c:12:25:ee:34:d6:5c:da:45:98:23:
30:e1:48:c9:9a:df:37:aa:1b:70:6c:b2:0f:95:39:
d6:6d:3e:25:20:a8:07:2c:48:57:0c:99:52:cb:89:
08:41
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
75:55:E2:8E:E7:AD:A5:DD:80:3D:C9:33:0B:2C:A2:57:77:ED:15:AC
X509v3 Authority Key Identifier:
C3:12:42:BA:A9:D8:4D:E0:C3:3E:BA:D7:47:41:A6:09:2F:6D:B4:E1
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Key Usage: critical
Digital Signature, Certificate Sign, CRL Sign
X509v3 CRL Distribution Points:
Full Name:
URI:http://127.0.0.1:8888/root_crl.der
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888/
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
1f:c6:fc:1c:a1:a5:6d:76:f0:7d:28:1f:e1:15:ab:86:e0:c3:
dd:a0:17:96:0a:c0:16:32:52:37:a4:b6:ad:24:d7:fd:3c:01:
34:3b:a9:a2:ea:81:05:e7:06:5f:a3:af:7b:fa:b2:a9:c3:63:
89:bb:0c:70:48:e9:73:cc:33:64:cd:b3:71:88:d1:d1:a1:5a:
22:a6:ed:03:46:8e:9a:c0:92:37:46:9b:e5:37:78:a5:43:d5:
46:99:1b:34:40:27:8f:95:dd:c6:9a:55:d9:60:25:8d:b8:e9:
6e:c9:b3:ee:e8:f0:d9:11:ef:4e:ae:1e:03:70:03:60:66:fd:
ab:b0:f4:74:b6:27:7c:7a:96:9d:86:58:5f:5c:d3:04:ab:16:
57:12:53:51:c7:93:ca:0b:4e:67:27:2d:b7:20:79:b6:b7:8c:
e7:c3:d9:25:5e:25:63:cf:93:f0:6e:31:c0:d5:4f:05:1c:8d:
14:1b:6a:d5:01:b6:7a:09:6f:38:f3:e5:e2:5a:e4:e2:42:d5:
8a:8d:de:ef:73:25:85:3c:e3:a9:ef:f7:f7:23:4f:d3:27:c2:
3a:c6:c0:6f:2a:9b:1e:fe:fc:31:73:10:e1:08:62:98:2b:6d:
2f:cc:ab:dd:3a:65:c2:00:7f:29:18:32:cd:8f:56:a9:1d:86:
f1:5e:60:55
-----BEGIN CERTIFICATE-----
MIIECTCCAvGgAwIBAgIUPNcW+xWZgU5T+IB8tnx3pgakPuowDQYJKoZIhvcNAQEL
BQAwUDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMQ8wDQYDVQQHDAZUYWNvbWEx
ETAPBgNVBAoMCFRlc3RuYXRzMRAwDgYDVQQDDAdSb290IENBMB4XDTIzMDUwMTE5
MDE0M1oXDTMzMDQyODE5MDE0M1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldB
MQ8wDQYDVQQHDAZUYWNvbWExETAPBgNVBAoMCFRlc3RuYXRzMRowGAYDVQQDDBFJ
bnRlcm1lZGlhdGUgQ0EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANpf/x33jRqemvMraI/BDDMGQQDJPuQa4eBwavUvrd/z6ZntxdeqkxM3/0eq88WJ
97etOkflnE6fjOJB7aR8nYgyrvWKhJ8MGKCz/o7cKohq9S+chpL6e26zWnhnUwsh
bA1sgBoOHu4GxNLnJMbldL4eLhdVK+WfC6BYzP6/Uzf33JWI9HemWbS4fKJLt2pn
qoTcKfH514kFTQvziy1SmVftbxGeryijYUTC7G5/nz0L3PcZbRSKpbi2KQI0kLSW
wcunQkaXz41Z/Rexpiene4pHb/oDJBwSJe401lzaRZgjMOFIyZrfN6obcGyyD5U5
1m0+JSCoByxIVwyZUsuJCEECAwEAAaOB0DCBzTAdBgNVHQ4EFgQUdVXijuetpd2A
PckzCyyiV3ftFawwHwYDVR0jBBgwFoAUwxJCuqnYTeDDPrrXR0GmCS9ttOEwEgYD
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwMwYDVR0fBCwwKjAooCag
JIYiaHR0cDovLzEyNy4wLjAuMTo4ODg4L3Jvb3RfY3JsLmRlcjAyBggrBgEFBQcB
AQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly8xMjcuMC4wLjE6ODg4OC8wDQYJKoZI
hvcNAQELBQADggEBAB/G/ByhpW128H0oH+EVq4bgw92gF5YKwBYyUjektq0k1/08
ATQ7qaLqgQXnBl+jr3v6sqnDY4m7DHBI6XPMM2TNs3GI0dGhWiKm7QNGjprAkjdG
m+U3eKVD1UaZGzRAJ4+V3caaVdlgJY246W7Js+7o8NkR706uHgNwA2Bm/auw9HS2
J3x6lp2GWF9c0wSrFlcSU1HHk8oLTmcnLbcgeba3jOfD2SVeJWPPk/BuMcDVTwUc
jRQbatUBtnoJbzjz5eJa5OJC1YqN3u9zJYU846nv9/cjT9MnwjrGwG8qmx7+/DFz
EOEIYpgrbS/Mq906ZcIAfykYMs2PVqkdhvFeYFU=
-----END CERTIFICATE-----
Loading

0 comments on commit 73c00d4

Please sign in to comment.