Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed local issuer determination for OCSP Staple, issue #3773 #4355

Merged
merged 2 commits into from Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions server/certstore/certstore.go
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
2 changes: 1 addition & 1 deletion server/client.go
Expand Up @@ -33,6 +33,7 @@ import (
"time"

"github.com/klauspost/compress/s2"

"github.com/nats-io/jwt/v2"
)

Expand Down Expand Up @@ -212,7 +213,6 @@ const (
DuplicateServerName
MinimumVersionRequired
ClusterNamesIdentical
FailedOCSPPeerVerification
)

// Some flags passed to processMsgResults
Expand Down
2 changes: 0 additions & 2 deletions server/monitor.go
Expand Up @@ -2401,8 +2401,6 @@ func (reason ClosedState) String() string {
return "Minimum Version Required"
case ClusterNamesIdentical:
return "Cluster Names Identical"
case FailedOCSPPeerVerification:
return "Failed OCSP Peer Verification"
}

return "Unknown State"
Expand Down
98 changes: 72 additions & 26 deletions server/ocsp.go
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
@@ -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-----