WIP: NATS Server + OCSP Support

Signed-off-by: Waldemar Quevedo <wally@synadia.com>
Signed-off-by: Jaime Piña <jaime@synadia.com>
This commit is contained in:
Waldemar Quevedo
2021-04-26 14:20:25 -07:00
parent be5445e916
commit 46eccd7658
15 changed files with 1722 additions and 3 deletions

2
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/nats-io/nats.go v1.10.1-0.20210419223411-20527524c393 github.com/nats-io/nats.go v1.10.1-0.20210419223411-20527524c393
github.com/nats-io/nkeys v0.3.0 github.com/nats-io/nkeys v0.3.0
github.com/nats-io/nuid v1.0.1 github.com/nats-io/nuid v1.0.1
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
) )

2
go.sum
View File

@@ -27,6 +27,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -15,15 +15,20 @@ package server
import ( import (
"bytes" "bytes"
"crypto/sha256"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"encoding/pem"
"fmt" "fmt"
"golang.org/x/crypto/ocsp"
"io" "io"
"io/ioutil"
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"strconv" "strconv"
@@ -1645,6 +1650,107 @@ func computeRTT(start time.Time) time.Duration {
return rtt return rtt
} }
func (srv *Server) fetchOCSPStaples(tlsConfig *tls.Config) error {
opts := srv.getOpts()
// FIXME: Need to do this for all types of connections.
// FIXME: Add option for the state directory.
dir, err := ioutil.TempDir("", "nats-staples-")
if err != nil {
return err
}
srv.stateDir = dir
// Parse and decode CA certificate to verify OCSP signatures.
caCert := opts.OCSPConfig.CaFile
data, err := ioutil.ReadFile(caCert)
if err != nil {
return err
}
block, _ := pem.Decode(data)
if block == nil {
return fmt.Errorf("Error decoding CA certificate")
}
issuer, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return err
}
if !issuer.IsCA {
return fmt.Errorf("Invalid CA certificate: %s", caCert)
}
for _, cert := range tlsConfig.Certificates {
leaf := cert.Leaf
// Take the URL.
ocspServer := leaf.OCSPServer[0]
if ocspServer == "" {
return fmt.Errorf("Missing OCSP Url in certificate")
}
ocspRequest, err := ocsp.CreateRequest(leaf, issuer, nil)
if err != nil {
return err
}
ocspRequestReader := bytes.NewReader(ocspRequest)
resp, err := http.Post(ocspServer, "application/ocsp-request", ocspRequestReader)
if err != nil {
return err
}
defer resp.Body.Close()
ocspResponseBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
ocspResponse, err := ocsp.ParseResponse(ocspResponseBytes, issuer)
if err != nil {
return err
}
switch ocspResponse.Status {
case ocsp.Good:
// Store the certificate that can be served to the user on connect.
sha := sha256.New()
sha.Write(leaf.Raw)
digest := fmt.Sprintf("%x", sha.Sum(nil))
stapleFile := filepath.Join(dir, digest)
srv.Debugf("Storing OCSP staple at %s", stapleFile)
err := ioutil.WriteFile(stapleFile, ocspResponseBytes, 0644)
if err != nil {
return fmt.Errorf("Error storing OCSP staple: %s", err)
}
case ocsp.Revoked:
return fmt.Errorf("Error fetching OCSP staples, certificate is revoked")
default:
// FIXME: Handle unknown state certificates.
continue
}
}
// Setup the callback to server the staples to the user.
tlsConfig.GetCertificate = func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
// TODO: Multi cert?
// TODO: Race condition
for _, cert := range tlsConfig.Certificates {
leaf := cert.Leaf
sha := sha256.New()
sha.Write(leaf.Raw)
digest := fmt.Sprintf("%x", sha.Sum(nil))
stapleFile := filepath.Join(dir, digest)
data, err := ioutil.ReadFile(stapleFile)
if err != nil {
return nil, err
}
cert.OCSPStaple = data
// Return the first cert.
return &cert, nil
}
return nil, fmt.Errorf("No certs")
}
return nil
}
// processConnect will process a client connect op. // processConnect will process a client connect op.
func (c *client) processConnect(arg []byte) error { func (c *client) processConnect(arg []byte) error {
supportsHeaders := c.srv.supportsHeaders() supportsHeaders := c.srv.supportsHeaders()

View File

@@ -225,6 +225,7 @@ type Options struct {
TLSKey string `json:"-"` TLSKey string `json:"-"`
TLSCaCert string `json:"-"` TLSCaCert string `json:"-"`
TLSConfig *tls.Config `json:"-"` TLSConfig *tls.Config `json:"-"`
OCSPConfig *OCSPConfig `json:"-"`
AllowNonTLS bool `json:"-"` AllowNonTLS bool `json:"-"`
WriteDeadline time.Duration `json:"-"` WriteDeadline time.Duration `json:"-"`
MaxClosedClients int `json:"-"` MaxClosedClients int `json:"-"`
@@ -469,6 +470,13 @@ type TLSConfigOpts struct {
Timeout float64 Timeout float64
Ciphers []uint16 Ciphers []uint16
CurvePreferences []tls.CurveID CurvePreferences []tls.CurveID
OCSP *OCSPConfig
}
// OCSPConfig represents the options of OCSP stapling options.
type OCSPConfig struct {
MustStaple bool
CaFile string
} }
var tlsUsage = ` var tlsUsage = `
@@ -827,6 +835,12 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error
o.TLSTimeout = tc.Timeout o.TLSTimeout = tc.Timeout
o.TLSMap = tc.Map o.TLSMap = tc.Map
// TODO: Make this DRY for leafnode, gateway, routes, websockets, mqtt...
o.OCSPConfig = tc.OCSP
if o.OCSPConfig != nil {
// Need to keep state of the CA directory to verify OCSP signatures.
o.OCSPConfig.CaFile = tc.CaFile
}
case "allow_non_tls": case "allow_non_tls":
o.AllowNonTLS = v.(bool) o.AllowNonTLS = v.(bool)
case "write_deadline": case "write_deadline":
@@ -3432,6 +3446,13 @@ func parseTLS(v interface{}, isClientCtx bool) (t *TLSConfigOpts, retErr error)
at = mv at = mv
} }
tc.Timeout = at tc.Timeout = at
case "ocsp":
switch v := mv.(type) {
case bool:
tc.OCSP = &OCSPConfig{MustStaple: v}
default:
return nil, &configErr{tk, fmt.Sprintf("error parsing ocsp config: unsupported type %T", v)}
}
default: default:
return nil, &configErr{tk, fmt.Sprintf("error parsing tls config, unknown field [%q]", mk)} return nil, &configErr{tk, fmt.Sprintf("error parsing tls config, unknown field [%q]", mk)}
} }
@@ -3732,7 +3753,6 @@ func GenTLSConfig(tc *TLSConfigOpts) (*tls.Config, error) {
} }
config.ClientCAs = pool config.ClientCAs = pool
} }
return &config, nil return &config, nil
} }

View File

@@ -250,6 +250,9 @@ type Server struct {
// For out of resources to not log errors too fast. // For out of resources to not log errors too fast.
rerrMu sync.Mutex rerrMu sync.Mutex
rerrLast time.Time rerrLast time.Time
// stateDir used for caching things like OCSP staples.
stateDir string
} }
type nodeInfo struct { type nodeInfo struct {
@@ -1480,6 +1483,16 @@ func (s *Server) Start() {
s.Noticef("Using configuration file: %s", opts.ConfigFile) s.Noticef("Using configuration file: %s", opts.ConfigFile)
} }
// Check if need to get staples.
if opts.OCSPConfig != nil {
s.Noticef("OCSP Stapling Configuration: must_staple=%v", opts.OCSPConfig.MustStaple)
err := s.fetchOCSPStaples(opts.TLSConfig)
if err != nil {
s.Fatalf("Error getting OCSP staples: %v", err)
return
}
}
hasOperators := len(opts.TrustedOperators) > 0 hasOperators := len(opts.TrustedOperators) > 0
if hasOperators { if hasOperators {
s.Noticef("Trusted Operators") s.Noticef("Trusted Operators")

View File

@@ -0,0 +1,33 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,126 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=California, L=San Francisco, O=Example CA, OU=IT, CN=ca.example
Validity
Not Before: Apr 29 22:27:06 2021 GMT
Not After : Jul 28 22:27:06 2021 GMT
Subject: C=US, ST=California, L=San Francisco, O=Example Widgets Ltd, OU=IT, CN=NATS Client
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:a6:48:c5:e0:d0:74:d0:bd:d3:22:6f:a5:19:20:
9c:a2:7b:d8:cd:7a:69:1a:5f:8f:61:b6:a3:59:92:
90:a6:32:2a:66:31:ff:a5:10:69:ae:6b:b7:5f:d9:
f1:00:f4:6f:af:8f:f4:31:24:33:bb:a7:ed:4a:ca:
43:c3:d9:69:24:c0:04:85:53:df:e9:63:93:82:65:
33:90:c4:3a:3f:ee:7c:f3:67:71:97:21:ac:97:0c:
db:b3:ac:89:6f:ee:f7:d2:cb:aa:92:57:99:3d:f0:
6f:84:a9:f7:fa:52:b4:53:46:df:8d:95:6a:1c:18:
30:f3:df:a2:46:b5:db:ff:f5:ce:1c:52:b8:05:0d:
22:f5:40:18:1d:5b:c7:98:7d:eb:58:da:74:b8:05:
05:8b:3a:aa:5f:8d:98:dc:7e:5b:fc:eb:b9:44:0a:
93:ff:b5:ff:31:8b:2c:cd:cd:29:37:58:4f:bf:ce:
ac:0f:b3:bc:c4:89:71:b4:61:a7:f8:00:bd:c2:10:
f8:97:3b:ec:e7:b1:00:4a:25:68:f8:2e:00:15:74:
bb:7c:0d:eb:c5:2e:87:f2:75:96:07:c4:22:fa:a3:
a7:9e:f1:89:e3:d0:54:ae:c1:a0:ed:1f:a6:ed:fc:
c8:36:08:97:bb:7f:c4:26:4a:7d:a2:17:86:86:6b:
18:4c:48:bf:ec:12:c7:98:19:09:61:8e:2b:cd:2f:
ca:ae:a3:21:8a:c8:e4:eb:36:fe:e7:69:32:e0:28:
31:0f:b1:a9:ec:d2:ce:87:bc:5c:99:b8:ec:19:d7:
03:53:38:1a:95:46:0b:2b:3e:6e:fe:48:70:10:b9:
b6:cd:8f:f0:e3:8a:41:ce:cb:9b:96:15:c6:6c:09:
bd:9e:ef:9f:50:3b:ea:78:2f:40:a6:0d:1d:f9:98:
c5:90:ed:2e:ac:47:44:32:22:d7:2d:de:6d:31:07:
c0:ef:94:d5:b7:4d:0d:8a:07:86:02:16:5c:57:c5:
d2:ce:d3:57:45:66:0f:bb:b5:f0:89:e6:54:c6:27:
01:fa:b4:41:85:92:ed:be:68:59:b2:91:ed:7c:49:
68:04:dc:f2:da:70:05:6c:2d:60:79:0c:7f:2b:70:
1f:50:0c:e6:99:5d:75:1a:d9:b0:2a:a2:d5:44:69:
9d:9a:e0:e7:c4:10:36:4f:e0:04:a8:de:8b:b5:12:
9b:d4:3f:33:4c:5d:cb:0d:dc:1e:44:b5:94:13:9b:
7a:4c:69:92:bc:9f:20:cb:58:be:66:3f:45:1f:37:
61:63:e7:ab:d7:81:ef:b4:14:7c:0f:56:bc:a1:f9:
b9:8d:46:46:96:a9:0a:b6:42:28:70:e0:20:71:ef:
0c:ca:69
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888
TLS Feature:
status_request
X509v3 Subject Alternative Name:
DNS:localhost, DNS:client.localhost
Signature Algorithm: sha256WithRSAEncryption
0c:71:0f:85:e8:22:48:59:e1:29:52:e2:06:f6:87:94:38:df:
96:5e:46:25:2a:a1:37:cd:8c:62:79:a5:a5:db:6f:4a:a8:7f:
e4:f9:7f:e4:80:d3:2c:8a:86:8d:2c:97:6b:d2:b5:21:75:a1:
1d:de:69:7a:e7:cb:2c:14:cf:ba:e7:e7:01:b6:f4:52:f7:d7:
ae:85:fa:66:b2:25:df:6a:ef:98:30:3c:fc:30:4b:62:30:a9:
37:b6:45:9e:5d:b7:b0:45:b2:88:e3:2a:c0:91:eb:2d:35:1f:
d6:80:45:6f:88:1b:63:13:b4:85:59:69:67:a3:5d:18:da:a3:
ec:65:30:6e:3a:a7:76:c6:e5:a3:0d:ea:53:6d:e6:1d:99:d2:
d1:26:0f:a5:b3:5c:e4:a6:96:62:51:f6:67:70:c1:9c:c5:09:
e9:e1:e0:0c:b4:ed:be:9b:3b:4e:8b:c9:65:55:09:28:87:50:
29:e6:28:66:2c:9e:66:cf:d0:c4:47:85:b0:00:51:1b:8b:4f:
56:b5:69:f3:8c:30:13:e8:13:d3:d1:3a:15:2e:36:16:cd:4c:
40:4c:16:99:a2:f6:96:f1:b7:4a:05:ff:54:37:05:06:9b:06:
50:50:66:ae:b4:b1:81:17:c6:54:84:d4:d3:88:1c:4a:99:4d:
10:ff:38:c7:8b:7f:e8:5e:d0:5d:d3:4c:3e:a6:7e:13:cf:87:
49:d0:fa:50:8a:10:5b:c9:89:50:3f:d8:ca:8f:11:5f:f6:62:
03:7d:06:2a:59:b2:f7:0f:b6:5d:40:5e:5e:67:f8:68:45:f2:
d4:d8:67:b1:6d:3d:06:89:ac:c8:45:47:97:77:2f:9d:95:d7:
e2:c3:61:ad:07:df:b9:fd:3d:b3:f1:d1:19:88:07:03:c6:7d:
37:f0:34:18:1c:f5:85:c3:d4:03:70:19:c3:8a:bd:76:d9:7e:
e4:49:f7:62:b7:f3:81:b4:c9:a3:e6:28:e6:7c:43:d8:7d:30:
37:1d:1b:15:72:52:ae:6c:94:6e:2d:83:74:49:91:59:e1:aa:
04:96:68:29:a1:ca:8b:23:c9:7d:93:1a:5b:d7:bb:4a:32:98:
13:c9:64:5a:cc:3d:ca:dc:66:fe:11:2c:b4:98:32:1f:45:f4:
79:76:b7:f0:60:4d:fa:ae:b3:b9:37:00:29:c4:8f:51:51:32:
94:6a:6c:fc:b7:37:4d:46:40:9c:67:92:4b:63:dc:84:bd:6d:
17:e5:ca:ae:b9:cb:6f:20:ab:e3:e5:12:5f:0a:2c:91:a6:43:
b5:2b:c1:a8:d1:41:42:a8:23:e6:57:d4:46:e4:84:1a:58:11:
b9:10:f4:65:19:dd:57:bf
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,126 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=California, L=San Francisco, O=Example CA, OU=IT, CN=ca.example
Validity
Not Before: Apr 29 22:27:06 2021 GMT
Not After : Jul 28 22:27:06 2021 GMT
Subject: C=US, ST=California, L=San Francisco, O=Example Widgets Ltd, OU=IT, CN=NATS Server
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:b2:95:25:59:11:6a:42:42:f0:31:e7:49:f7:a0:
8f:fc:2f:33:b6:43:4d:3b:98:d6:c3:06:a9:b0:dc:
a9:c0:ee:77:e2:27:8c:8a:cd:be:0d:4d:45:64:d4:
be:17:9e:a9:c0:5f:b2:21:42:5c:2c:a0:09:32:2a:
01:75:1d:e7:05:62:33:31:9d:96:fe:fa:44:c3:0a:
08:50:80:dc:0e:26:dd:c5:c6:5a:c6:66:c7:f6:4c:
db:2e:12:36:a7:a0:1e:63:ba:66:92:e5:d6:af:c0:
fb:da:2c:01:fe:6b:91:1e:1d:2d:e7:19:27:56:69:
3e:90:90:ca:11:6b:9a:e3:22:76:5e:42:d5:63:2b:
3f:1c:77:f2:89:a9:d4:6a:a8:34:ec:6a:52:74:1e:
0e:a8:c7:bd:98:b0:a5:f7:11:90:7f:37:aa:05:d8:
07:6d:55:ff:1f:3a:71:54:a2:76:1d:a5:59:93:92:
b8:77:b9:57:33:fc:61:35:64:76:96:b5:3f:44:88:
b2:05:d6:b9:e7:71:66:b6:a5:ce:05:0a:df:9d:b1:
b2:8a:24:ac:7a:62:8c:39:18:e7:ec:6d:e8:62:37:
4e:2a:78:83:3c:6f:25:48:ef:e5:13:7e:b8:89:9f:
39:57:95:67:99:c6:96:db:52:24:a6:15:18:11:b4:
01:a0:e0:35:8d:d7:95:3b:ba:35:af:33:6d:bd:61:
6c:3e:5d:cb:f6:39:e4:f6:35:28:bb:7a:07:70:25:
14:28:65:0b:10:59:d8:c4:5e:66:df:b0:fb:fe:87:
d2:7e:e7:cf:05:06:a5:da:c1:07:37:2a:ce:64:4b:
af:2d:e5:2c:3f:41:1d:08:bd:49:b7:3b:22:67:00:
a1:45:51:92:70:6e:b6:76:5f:4b:b8:40:bd:e9:4a:
75:33:59:36:3f:9e:3a:69:66:2f:9c:3e:37:8c:62:
f4:41:bc:6f:c7:74:80:52:a1:a9:c5:63:3d:50:2c:
27:54:e9:57:f1:58:41:91:45:26:95:3f:df:3a:92:
12:db:21:e4:08:61:b2:f6:d1:b1:1a:e5:b1:cf:7c:
e9:7d:c7:27:a2:0f:c6:11:a4:8f:c2:6f:6a:51:fe:
76:be:75:29:69:f2:dc:a5:28:d6:e7:b3:ca:ed:f2:
7f:59:cb:5f:75:d0:f6:6a:71:50:81:e2:9f:5b:e7:
88:98:cd:15:e0:d1:61:53:65:04:08:e9:ab:cb:14:
5c:6f:34:9b:ca:30:99:e0:bf:0e:de:4d:9a:bf:8f:
c7:dd:e1:6c:7d:24:b8:89:53:70:4a:16:d0:15:bc:
6d:40:63:3e:df:90:1b:a2:2b:bb:78:e6:47:9e:08:
e3:ea:79
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
Authority Information Access:
OCSP - URI:http://127.0.0.1:8888
TLS Feature:
status_request
X509v3 Subject Alternative Name:
DNS:localhost, DNS:server.localhost
Signature Algorithm: sha256WithRSAEncryption
3b:ea:73:11:c9:25:93:5e:00:56:bd:a6:ea:0d:9f:b6:77:10:
a6:8a:2a:f0:b9:bc:60:99:d9:aa:7d:c2:a0:cc:37:9e:8a:65:
6c:b1:24:9e:75:e1:53:e3:65:12:fa:ca:d4:ad:c6:07:60:a3:
9b:f4:c1:64:0c:04:33:28:67:1f:55:3d:1b:eb:05:b5:90:b0:
5d:36:ea:47:7c:ab:8c:75:36:f4:76:89:c6:8f:e3:3e:f0:d3:
82:d0:55:f5:76:4e:88:74:bc:29:d9:c8:ee:8e:42:56:f1:72:
39:1d:5f:b1:ef:00:d4:86:a0:0f:82:39:98:6a:fb:fc:44:e2:
e3:e5:0a:5a:ed:36:f9:32:d6:e8:93:a8:a0:e1:7a:ff:4f:23:
e6:d1:70:af:6a:95:0f:98:94:d6:f4:bd:e1:aa:89:6c:68:04:
c9:ea:eb:39:64:d7:4a:8d:d1:3f:68:36:f6:45:55:d6:0f:6e:
3c:f0:19:55:fb:73:01:64:7d:f3:92:8f:4a:29:08:70:f3:f7:
75:c8:b8:75:1d:55:7c:e8:36:2d:47:96:52:f3:23:c2:03:10:
9b:4f:eb:df:53:11:15:d7:5c:a9:32:59:45:a8:44:23:a8:63:
db:a4:4a:14:e1:ea:56:00:b6:e7:9b:7f:29:5b:54:6b:84:79:
80:05:1a:e3:1a:b5:a0:0d:bc:e4:15:aa:f9:98:05:05:ac:10:
a0:37:13:a4:0f:46:7e:2f:d1:b4:12:a8:e5:18:5f:13:8f:09:
06:2d:20:1b:f1:74:5e:2f:c0:a0:80:97:da:7b:94:29:b9:72:
be:13:47:91:d6:c2:3a:51:25:e1:e1:a1:da:83:6c:8b:0a:87:
b8:fb:a4:91:e2:80:62:cf:c7:b7:ad:69:40:bd:de:df:b0:73:
de:eb:82:69:a9:16:04:75:2f:76:8c:49:ea:d1:e0:61:e8:91:
8e:a4:fd:5e:90:7c:2f:46:99:ed:bc:2e:61:a8:ba:9f:ff:52:
c0:3d:11:2c:cd:96:e3:f7:d1:9a:52:b7:8f:56:cd:23:6e:a5:
31:f4:32:f3:dd:26:41:1f:bb:94:93:54:c0:7d:c1:4b:fc:0a:
5b:5a:53:83:cc:da:9a:92:48:3b:ab:ff:bb:ad:9b:af:8b:cd:
84:20:b9:57:75:a0:19:0a:07:7b:8c:7b:39:34:db:d8:dc:15:
22:08:69:20:2b:a8:29:e2:9f:f3:f8:e1:c9:87:a8:02:d2:20:
a4:65:26:91:6f:e9:e5:37:9c:c8:f8:61:47:12:52:8b:0d:0d:
c1:67:57:ba:eb:f3:d1:09:58:af:38:c5:69:fe:0d:06:2b:3c:
73:31:35:a7:05:2e:c2:7e
-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIBATANBgkqhkiG9w0BAQsFADBxMQswCQYDVQQGEwJVUzET
MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzETMBEG
A1UECgwKRXhhbXBsZSBDQTELMAkGA1UECwwCSVQxEzARBgNVBAMMCmNhLmV4YW1w
bGUwHhcNMjEwNDI5MjIyNzA2WhcNMjEwNzI4MjIyNzA2WjB7MQswCQYDVQQGEwJV
UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEc
MBoGA1UECgwTRXhhbXBsZSBXaWRnZXRzIEx0ZDELMAkGA1UECwwCSVQxFDASBgNV
BAMMC05BVFMgU2VydmVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
spUlWRFqQkLwMedJ96CP/C8ztkNNO5jWwwapsNypwO534ieMis2+DU1FZNS+F56p
wF+yIUJcLKAJMioBdR3nBWIzMZ2W/vpEwwoIUIDcDibdxcZaxmbH9kzbLhI2p6Ae
Y7pmkuXWr8D72iwB/muRHh0t5xknVmk+kJDKEWua4yJ2XkLVYys/HHfyianUaqg0
7GpSdB4OqMe9mLCl9xGQfzeqBdgHbVX/HzpxVKJ2HaVZk5K4d7lXM/xhNWR2lrU/
RIiyBda553FmtqXOBQrfnbGyiiSsemKMORjn7G3oYjdOKniDPG8lSO/lE364iZ85
V5VnmcaW21IkphUYEbQBoOA1jdeVO7o1rzNtvWFsPl3L9jnk9jUou3oHcCUUKGUL
EFnYxF5m37D7/ofSfufPBQal2sEHNyrOZEuvLeUsP0EdCL1JtzsiZwChRVGScG62
dl9LuEC96Up1M1k2P546aWYvnD43jGL0Qbxvx3SAUqGpxWM9UCwnVOlX8VhBkUUm
lT/fOpIS2yHkCGGy9tGxGuWxz3zpfccnog/GEaSPwm9qUf52vnUpafLcpSjW57PK
7fJ/WctfddD2anFQgeKfW+eImM0V4NFhU2UECOmryxRcbzSbyjCZ4L8O3k2av4/H
3eFsfSS4iVNwShbQFbxtQGM+35Aboiu7eOZHngjj6nkCAwEAAaOBiTCBhjAJBgNV
HRMEAjAAMAsGA1UdDwQEAwIF4DAxBggrBgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGG
FWh0dHA6Ly8xMjcuMC4wLjE6ODg4ODARBggrBgEFBQcBGAQFMAMCAQUwJgYDVR0R
BB8wHYIJbG9jYWxob3N0ghBzZXJ2ZXIubG9jYWxob3N0MA0GCSqGSIb3DQEBCwUA
A4ICAQA76nMRySWTXgBWvabqDZ+2dxCmiirwubxgmdmqfcKgzDeeimVssSSedeFT
42US+srUrcYHYKOb9MFkDAQzKGcfVT0b6wW1kLBdNupHfKuMdTb0donGj+M+8NOC
0FX1dk6IdLwp2cjujkJW8XI5HV+x7wDUhqAPgjmYavv8ROLj5Qpa7Tb5Mtbok6ig
4Xr/TyPm0XCvapUPmJTW9L3hqolsaATJ6us5ZNdKjdE/aDb2RVXWD2488BlV+3MB
ZH3zko9KKQhw8/d1yLh1HVV86DYtR5ZS8yPCAxCbT+vfUxEV11ypMllFqEQjqGPb
pEoU4epWALbnm38pW1RrhHmABRrjGrWgDbzkFar5mAUFrBCgNxOkD0Z+L9G0Eqjl
GF8TjwkGLSAb8XReL8CggJfae5QpuXK+E0eR1sI6USXh4aHag2yLCoe4+6SR4oBi
z8e3rWlAvd7fsHPe64JpqRYEdS92jEnq0eBh6JGOpP1ekHwvRpntvC5hqLqf/1LA
PREszZbj99GaUrePVs0jbqUx9DLz3SZBH7uUk1TAfcFL/ApbWlODzNqakkg7q/+7
rZuvi82EILlXdaAZCgd7jHs5NNvY3BUiCGkgK6gp4p/z+OHJh6gC0iCkZSaRb+nl
N5zI+GFHElKLDQ3BZ1e66/PRCVivOMVp/g0GKzxzMTWnBS7Cfg==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

245
test/ocspresponder_test.go Normal file
View File

@@ -0,0 +1,245 @@
package test
import (
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"sync"
"testing"
"time"
"golang.org/x/crypto/ocsp"
)
func TestOCSPResponder(t *testing.T) {
const (
caCert = "../test/configs/certs/ocsp/ca-cert.pem"
caKey = "../test/configs/certs/ocsp/ca-key.pem"
userCert = "../test/configs/certs/ocsp/server.crt"
)
s := newOCSPResponder(t, caCert, caKey)
defer s.Close()
// setOCSPDatabase(t, s.URL, userCert, ocsp.Good) // FAIL
setOCSPDatabase(t, s.Addr, userCert, ocsp.Revoked) // PASS
resp := postOCSPRequest(t, s.Addr, userCert, caCert)
if got, want := resp.Status, ocsp.Revoked; got != want {
t.Fatalf("unexpected cert status, got %d, want %d", got, want)
}
}
func postOCSPRequest(t *testing.T, ocspURL, certPEM, issuerPEM string) *ocsp.Response {
t.Helper()
cert := parseCertPEM(t, certPEM)
issuer := parseCertPEM(t, issuerPEM)
reqData, err := ocsp.CreateRequest(cert, issuer, nil)
if err != nil {
t.Fatalf("failed to create OCSP request: %s", err)
}
r := bytes.NewReader(reqData)
hc := &http.Client{Timeout: 3 * time.Second}
httpResp, err := hc.Post(ocspURL, "application/ocsp-request", r)
if err != nil {
t.Fatalf("failed POST request: %s", err)
}
defer httpResp.Body.Close()
respData, err := io.ReadAll(httpResp.Body)
if err != nil {
t.Fatalf("failed to read OCSP HTTP response body: %s", err)
}
if got, want := httpResp.Status, "200 OK"; got != want {
t.Error(strings.TrimSpace(string(respData)))
t.Fatalf("unexpected OCSP HTTP status, got %q, want %q", got, want)
}
resp, err := ocsp.ParseResponse(respData, issuer)
if err != nil {
t.Fatalf("failed to parse OCSP response: %s", err)
}
return resp
}
func setOCSPDatabase(t *testing.T, ocspURL, certPEM string, status int) {
t.Helper()
cert := parseCertPEM(t, certPEM)
hc := &http.Client{Timeout: 3 * time.Second}
resp, err := hc.Post(
fmt.Sprintf("%s/statuses/%s", ocspURL, cert.SerialNumber),
"",
strings.NewReader(fmt.Sprint(status)),
)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("failed to read OCSP HTTP response body: %s", err)
}
if got, want := resp.Status, "200 OK"; got != want {
t.Error(strings.TrimSpace(string(data)))
t.Fatalf("unexpected OCSP HTTP set status, got %q, want %q", got, want)
}
}
func newOCSPResponder(t *testing.T, issuerCertPEM, issuerKeyPEM string) *http.Server {
t.Helper()
var mu sync.Mutex
status := make(map[string]int)
issuerCert := parseCertPEM(t, issuerCertPEM)
issuerKey := parseKeyPEM(t, issuerKeyPEM)
mux := http.NewServeMux()
// The "/statuses/" endpoint is for directly setting a key-value pair in
// the CA's status database.
mux.HandleFunc("/statuses/", func(rw http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
key := r.URL.Path[len("/statuses/"):]
switch r.Method {
case "GET":
mu.Lock()
n, ok := status[key]
if !ok {
n = ocsp.Unknown
}
mu.Unlock()
fmt.Fprintf(rw, "%s %d", key, n)
case "POST":
data, err := io.ReadAll(r.Body)
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
n, err := strconv.Atoi(string(data))
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
mu.Lock()
status[key] = n
mu.Unlock()
fmt.Fprintf(rw, "%s %d", key, n)
default:
http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
})
// The "/" endpoint is for normal OCSP requests. This actually parses an
// OCSP status request and signs a response with a CA. Lightly based off:
// https://www.ietf.org/rfc/rfc2560.txt
mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if r.Method != "POST" {
http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
if r.Header.Get("Content-Type") != "application/ocsp-request" {
http.Error(rw, "Unsupported Media Type", http.StatusUnsupportedMediaType)
return
}
reqData, err := io.ReadAll(r.Body)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
ocspReq, err := ocsp.ParseRequest(reqData)
if err != nil {
http.Error(rw, err.Error(), http.StatusBadRequest)
return
}
mu.Lock()
n, ok := status[ocspReq.SerialNumber.String()]
if !ok {
n = ocsp.Unknown
}
mu.Unlock()
tmpl := ocsp.Response{
Status: n,
SerialNumber: ocspReq.SerialNumber,
}
respData, err := ocsp.CreateResponse(issuerCert, issuerCert, tmpl, issuerKey)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
rw.Header().Set("Content-Type", "application/ocsp-response")
rw.Header().Set("Content-Length", fmt.Sprint(len(respData)))
fmt.Fprint(rw, string(respData))
})
srv := &http.Server{
Addr: "127.0.0.1:8888",
Handler: mux,
}
go srv.ListenAndServe()
return srv
}
func parseCertPEM(t *testing.T, certPEM string) *x509.Certificate {
t.Helper()
block := parsePEM(t, certPEM)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
t.Fatalf("failed to parse cert %s: %s", certPEM, err)
}
return cert
}
func parseKeyPEM(t *testing.T, keyPEM string) *rsa.PrivateKey {
t.Helper()
block := parsePEM(t, keyPEM)
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
t.Fatalf("failed to parse ikey %s: %s", keyPEM, err)
}
return key
}
func parsePEM(t *testing.T, pemPath string) *pem.Block {
t.Helper()
data, err := os.ReadFile(pemPath)
if err != nil {
t.Fatal(err)
}
block, _ := pem.Decode(data)
if block == nil {
t.Fatalf("failed to decode PEM %s", pemPath)
}
return block
}

View File

@@ -15,6 +15,7 @@ package test
import ( import (
"bufio" "bufio"
"context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"errors" "errors"
@@ -29,6 +30,7 @@ import (
"github.com/nats-io/nats-server/v2/server" "github.com/nats-io/nats-server/v2/server"
"github.com/nats-io/nats.go" "github.com/nats-io/nats.go"
"golang.org/x/crypto/ocsp"
) )
var noOpErrHandler = func(_ *nats.Conn, _ *nats.Subscription, _ error) {} var noOpErrHandler = func(_ *nats.Conn, _ *nats.Subscription, _ error) {}
@@ -1935,3 +1937,106 @@ func TestTLSClientSVIDAuth(t *testing.T) {
}) })
} }
} }
func TestTLSClientOCSP(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Start mock HTTP responder service.
s := newOCSPResponder(t, "configs/certs/ocsp/ca-cert.pem", "configs/certs/ocsp/ca-key.pem")
defer s.Shutdown(ctx)
// Give it some time for the HTTP server to start.
time.Sleep(2 * time.Second)
setCertStatus := func(t *testing.T, cert string, ocspStatus int) {
setOCSPDatabase(t,
fmt.Sprintf("http://%s", s.Addr),
cert,
ocspStatus,
)
}
for _, test := range []struct {
name string
config string
certs nats.Option
err error
rerr error
configure func()
}{
{
"connect with tls and get a staple from valid certificate",
`
port: -1
%s
`,
nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
nil,
nil,
func() { setCertStatus(t, "configs/certs/ocsp/server-cert.pem", ocsp.Good) },
},
// FIXME: Test that the server will not start with an invalid cert.
// {
// "connect with tls and get a staple from invalid certificate",
// `
// port: -1
// %s
// `,
// nats.ClientCert("./configs/certs/ocsp/client-cert.pem", "./configs/certs/ocsp/client-key.pem"),
// nil,
// nil,
// func() { setCertStatus(t, "configs/certs/ocsp/server-cert.pem", ocsp.Revoked) },
// },
} {
t.Run(test.name, func(t *testing.T) {
test.configure()
content := fmt.Sprintf(test.config, `
tls {
cert_file: "configs/certs/ocsp/server-cert.pem"
key_file: "configs/certs/ocsp/server-key.pem"
ca_file: "configs/certs/ocsp/ca-cert.pem"
timeout: 5
# Makes the server honor must-staple flag if present in server cert.
ocsp: true
}
`)
conf := createConfFile(t, []byte(content))
defer removeFile(t, conf)
s, opts := RunServerWithConfig(conf)
defer s.Shutdown()
nc, err := nats.Connect(fmt.Sprintf("tls://localhost:%d", opts.Port),
test.certs,
nats.RootCAs("configs/certs/ocsp/ca-cert.pem"),
nats.ErrorHandler(noOpErrHandler),
)
if test.err == nil && err != nil {
t.Errorf("Expected to connect, got %v", err)
} else if test.err != nil && err == nil {
t.Errorf("Expected error on connect")
} else if test.err != nil && err != nil {
// Error on connect was expected
if test.err.Error() != err.Error() {
t.Errorf("Expected error %s, got: %s", test.err, err)
}
return
}
defer nc.Close()
nc.Subscribe("ping", func(m *nats.Msg) {
m.Respond([]byte("pong"))
})
nc.Flush()
_, err = nc.Request("ping", []byte("ping"), 250*time.Millisecond)
if test.rerr != nil && err == nil {
t.Errorf("Expected error getting response")
} else if test.rerr == nil && err != nil {
t.Errorf("Expected response")
}
})
}
}

789
vendor/golang.org/x/crypto/ocsp/ocsp.go generated vendored Normal file
View File

@@ -0,0 +1,789 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses
// are signed messages attesting to the validity of a certificate for a small
// period of time. This is used to manage revocation for X.509 certificates.
package ocsp // import "golang.org/x/crypto/ocsp"
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"math/big"
"strconv"
"time"
)
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
// ResponseStatus contains the result of an OCSP request. See
// https://tools.ietf.org/html/rfc6960#section-2.3
type ResponseStatus int
const (
Success ResponseStatus = 0
Malformed ResponseStatus = 1
InternalError ResponseStatus = 2
TryLater ResponseStatus = 3
// Status code four is unused in OCSP. See
// https://tools.ietf.org/html/rfc6960#section-4.2.1
SignatureRequired ResponseStatus = 5
Unauthorized ResponseStatus = 6
)
func (r ResponseStatus) String() string {
switch r {
case Success:
return "success"
case Malformed:
return "malformed"
case InternalError:
return "internal error"
case TryLater:
return "try later"
case SignatureRequired:
return "signature required"
case Unauthorized:
return "unauthorized"
default:
return "unknown OCSP status: " + strconv.Itoa(int(r))
}
}
// ResponseError is an error that may be returned by ParseResponse to indicate
// that the response itself is an error, not just that it's indicating that a
// certificate is revoked, unknown, etc.
type ResponseError struct {
Status ResponseStatus
}
func (r ResponseError) Error() string {
return "ocsp: error from server: " + r.Status.String()
}
// These are internal structures that reflect the ASN.1 structure of an OCSP
// response. See RFC 2560, section 4.2.
type certID struct {
HashAlgorithm pkix.AlgorithmIdentifier
NameHash []byte
IssuerKeyHash []byte
SerialNumber *big.Int
}
// https://tools.ietf.org/html/rfc2560#section-4.1.1
type ocspRequest struct {
TBSRequest tbsRequest
}
type tbsRequest struct {
Version int `asn1:"explicit,tag:0,default:0,optional"`
RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"`
RequestList []request
}
type request struct {
Cert certID
}
type responseASN1 struct {
Status asn1.Enumerated
Response responseBytes `asn1:"explicit,tag:0,optional"`
}
type responseBytes struct {
ResponseType asn1.ObjectIdentifier
Response []byte
}
type basicResponse struct {
TBSResponseData responseData
SignatureAlgorithm pkix.AlgorithmIdentifier
Signature asn1.BitString
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
}
type responseData struct {
Raw asn1.RawContent
Version int `asn1:"optional,default:0,explicit,tag:0"`
RawResponderID asn1.RawValue
ProducedAt time.Time `asn1:"generalized"`
Responses []singleResponse
}
type singleResponse struct {
CertID certID
Good asn1.Flag `asn1:"tag:0,optional"`
Revoked revokedInfo `asn1:"tag:1,optional"`
Unknown asn1.Flag `asn1:"tag:2,optional"`
ThisUpdate time.Time `asn1:"generalized"`
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
SingleExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"`
}
type revokedInfo struct {
RevocationTime time.Time `asn1:"generalized"`
Reason asn1.Enumerated `asn1:"explicit,tag:0,optional"`
}
var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
)
var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{
crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}),
crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}),
crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}),
crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}),
}
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
var signatureAlgorithmDetails = []struct {
algo x509.SignatureAlgorithm
oid asn1.ObjectIdentifier
pubKeyAlgo x509.PublicKeyAlgorithm
hash crypto.Hash
}{
{x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
{x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
{x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
{x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
{x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
{x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
{x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
{x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
{x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
{x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
{x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
{x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
}
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
var pubType x509.PublicKeyAlgorithm
switch pub := pub.(type) {
case *rsa.PublicKey:
pubType = x509.RSA
hashFunc = crypto.SHA256
sigAlgo.Algorithm = oidSignatureSHA256WithRSA
sigAlgo.Parameters = asn1.RawValue{
Tag: 5,
}
case *ecdsa.PublicKey:
pubType = x509.ECDSA
switch pub.Curve {
case elliptic.P224(), elliptic.P256():
hashFunc = crypto.SHA256
sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
case elliptic.P384():
hashFunc = crypto.SHA384
sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
case elliptic.P521():
hashFunc = crypto.SHA512
sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
default:
err = errors.New("x509: unknown elliptic curve")
}
default:
err = errors.New("x509: only RSA and ECDSA keys supported")
}
if err != nil {
return
}
if requestedSigAlgo == 0 {
return
}
found := false
for _, details := range signatureAlgorithmDetails {
if details.algo == requestedSigAlgo {
if details.pubKeyAlgo != pubType {
err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
return
}
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
if hashFunc == 0 {
err = errors.New("x509: cannot sign with hash function requested")
return
}
found = true
break
}
}
if !found {
err = errors.New("x509: unknown SignatureAlgorithm")
}
return
}
// TODO(agl): this is taken from crypto/x509 and so should probably be exported
// from crypto/x509 or crypto/x509/pkix.
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm {
for _, details := range signatureAlgorithmDetails {
if oid.Equal(details.oid) {
return details.algo
}
}
return x509.UnknownSignatureAlgorithm
}
// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form.
func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
for hash, oid := range hashOIDs {
if oid.Equal(target) {
return hash
}
}
return crypto.Hash(0)
}
func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier {
for hash, oid := range hashOIDs {
if hash == target {
return oid
}
}
return nil
}
// This is the exposed reflection of the internal OCSP structures.
// The status values that can be expressed in OCSP. See RFC 6960.
const (
// Good means that the certificate is valid.
Good = iota
// Revoked means that the certificate has been deliberately revoked.
Revoked
// Unknown means that the OCSP responder doesn't know about the certificate.
Unknown
// ServerFailed is unused and was never used (see
// https://go-review.googlesource.com/#/c/18944). ParseResponse will
// return a ResponseError when an error response is parsed.
ServerFailed
)
// The enumerated reasons for revoking a certificate. See RFC 5280.
const (
Unspecified = 0
KeyCompromise = 1
CACompromise = 2
AffiliationChanged = 3
Superseded = 4
CessationOfOperation = 5
CertificateHold = 6
RemoveFromCRL = 8
PrivilegeWithdrawn = 9
AACompromise = 10
)
// Request represents an OCSP request. See RFC 6960.
type Request struct {
HashAlgorithm crypto.Hash
IssuerNameHash []byte
IssuerKeyHash []byte
SerialNumber *big.Int
}
// Marshal marshals the OCSP request to ASN.1 DER encoded form.
func (req *Request) Marshal() ([]byte, error) {
hashAlg := getOIDFromHashAlgorithm(req.HashAlgorithm)
if hashAlg == nil {
return nil, errors.New("Unknown hash algorithm")
}
return asn1.Marshal(ocspRequest{
tbsRequest{
Version: 0,
RequestList: []request{
{
Cert: certID{
pkix.AlgorithmIdentifier{
Algorithm: hashAlg,
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
},
req.IssuerNameHash,
req.IssuerKeyHash,
req.SerialNumber,
},
},
},
},
})
}
// Response represents an OCSP response containing a single SingleResponse. See
// RFC 6960.
type Response struct {
// Status is one of {Good, Revoked, Unknown}
Status int
SerialNumber *big.Int
ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
RevocationReason int
Certificate *x509.Certificate
// TBSResponseData contains the raw bytes of the signed response. If
// Certificate is nil then this can be used to verify Signature.
TBSResponseData []byte
Signature []byte
SignatureAlgorithm x509.SignatureAlgorithm
// IssuerHash is the hash used to compute the IssuerNameHash and IssuerKeyHash.
// Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384, and crypto.SHA512.
// If zero, the default is crypto.SHA1.
IssuerHash crypto.Hash
// RawResponderName optionally contains the DER-encoded subject of the
// responder certificate. Exactly one of RawResponderName and
// ResponderKeyHash is set.
RawResponderName []byte
// ResponderKeyHash optionally contains the SHA-1 hash of the
// responder's public key. Exactly one of RawResponderName and
// ResponderKeyHash is set.
ResponderKeyHash []byte
// Extensions contains raw X.509 extensions from the singleExtensions field
// of the OCSP response. When parsing certificates, this can be used to
// extract non-critical extensions that are not parsed by this package. When
// marshaling OCSP responses, the Extensions field is ignored, see
// ExtraExtensions.
Extensions []pkix.Extension
// ExtraExtensions contains extensions to be copied, raw, into any marshaled
// OCSP response (in the singleExtensions field). Values override any
// extensions that would otherwise be produced based on the other fields. The
// ExtraExtensions field is not populated when parsing certificates, see
// Extensions.
ExtraExtensions []pkix.Extension
}
// These are pre-serialized error responses for the various non-success codes
// defined by OCSP. The Unauthorized code in particular can be used by an OCSP
// responder that supports only pre-signed responses as a response to requests
// for certificates with unknown status. See RFC 5019.
var (
MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
)
// CheckSignatureFrom checks that the signature in resp is a valid signature
// from issuer. This should only be used if resp.Certificate is nil. Otherwise,
// the OCSP response contained an intermediate certificate that created the
// signature. That signature is checked by ParseResponse and only
// resp.Certificate remains to be validated.
func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
}
// ParseError results from an invalid OCSP response.
type ParseError string
func (p ParseError) Error() string {
return string(p)
}
// ParseRequest parses an OCSP request in DER form. It only supports
// requests for a single certificate. Signed requests are not supported.
// If a request includes a signature, it will result in a ParseError.
func ParseRequest(bytes []byte) (*Request, error) {
var req ocspRequest
rest, err := asn1.Unmarshal(bytes, &req)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP request")
}
if len(req.TBSRequest.RequestList) == 0 {
return nil, ParseError("OCSP request contains no request body")
}
innerRequest := req.TBSRequest.RequestList[0]
hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
if hashFunc == crypto.Hash(0) {
return nil, ParseError("OCSP request uses unknown hash function")
}
return &Request{
HashAlgorithm: hashFunc,
IssuerNameHash: innerRequest.Cert.NameHash,
IssuerKeyHash: innerRequest.Cert.IssuerKeyHash,
SerialNumber: innerRequest.Cert.SerialNumber,
}, nil
}
// ParseResponse parses an OCSP response in DER form. The response must contain
// only one certificate status. To parse the status of a specific certificate
// from a response which may contain multiple statuses, use ParseResponseForCert
// instead.
//
// If the response contains an embedded certificate, then that certificate will
// be used to verify the response signature. If the response contains an
// embedded certificate and issuer is not nil, then issuer will be used to verify
// the signature on the embedded certificate.
//
// If the response does not contain an embedded certificate and issuer is not
// nil, then issuer will be used to verify the response signature.
//
// Invalid responses and parse failures will result in a ParseError.
// Error responses will result in a ResponseError.
func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
return ParseResponseForCert(bytes, nil, issuer)
}
// ParseResponseForCert acts identically to ParseResponse, except it supports
// parsing responses that contain multiple statuses. If the response contains
// multiple statuses and cert is not nil, then ParseResponseForCert will return
// the first status which contains a matching serial, otherwise it will return an
// error. If cert is nil, then the first status in the response will be returned.
func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) {
var resp responseASN1
rest, err := asn1.Unmarshal(bytes, &resp)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP response")
}
if status := ResponseStatus(resp.Status); status != Success {
return nil, ResponseError{status}
}
if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
return nil, ParseError("bad OCSP response type")
}
var basicResp basicResponse
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
if err != nil {
return nil, err
}
if len(rest) > 0 {
return nil, ParseError("trailing data in OCSP response")
}
if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 {
return nil, ParseError("OCSP response contains bad number of responses")
}
var singleResp singleResponse
if cert == nil {
singleResp = basicResp.TBSResponseData.Responses[0]
} else {
match := false
for _, resp := range basicResp.TBSResponseData.Responses {
if cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 {
singleResp = resp
match = true
break
}
}
if !match {
return nil, ParseError("no response matching the supplied certificate")
}
}
ret := &Response{
TBSResponseData: basicResp.TBSResponseData.Raw,
Signature: basicResp.Signature.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm),
Extensions: singleResp.SingleExtensions,
SerialNumber: singleResp.CertID.SerialNumber,
ProducedAt: basicResp.TBSResponseData.ProducedAt,
ThisUpdate: singleResp.ThisUpdate,
NextUpdate: singleResp.NextUpdate,
}
// Handle the ResponderID CHOICE tag. ResponderID can be flattened into
// TBSResponseData once https://go-review.googlesource.com/34503 has been
// released.
rawResponderID := basicResp.TBSResponseData.RawResponderID
switch rawResponderID.Tag {
case 1: // Name
var rdn pkix.RDNSequence
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &rdn); err != nil || len(rest) != 0 {
return nil, ParseError("invalid responder name")
}
ret.RawResponderName = rawResponderID.Bytes
case 2: // KeyHash
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &ret.ResponderKeyHash); err != nil || len(rest) != 0 {
return nil, ParseError("invalid responder key hash")
}
default:
return nil, ParseError("invalid responder id tag")
}
if len(basicResp.Certificates) > 0 {
// Responders should only send a single certificate (if they
// send any) that connects the responder's certificate to the
// original issuer. We accept responses with multiple
// certificates due to a number responders sending them[1], but
// ignore all but the first.
//
// [1] https://github.com/golang/go/issues/21527
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
if err != nil {
return nil, err
}
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
return nil, ParseError("bad signature on embedded certificate: " + err.Error())
}
if issuer != nil {
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
return nil, ParseError("bad OCSP signature: " + err.Error())
}
}
} else if issuer != nil {
if err := ret.CheckSignatureFrom(issuer); err != nil {
return nil, ParseError("bad OCSP signature: " + err.Error())
}
}
for _, ext := range singleResp.SingleExtensions {
if ext.Critical {
return nil, ParseError("unsupported critical extension")
}
}
for h, oid := range hashOIDs {
if singleResp.CertID.HashAlgorithm.Algorithm.Equal(oid) {
ret.IssuerHash = h
break
}
}
if ret.IssuerHash == 0 {
return nil, ParseError("unsupported issuer hash algorithm")
}
switch {
case bool(singleResp.Good):
ret.Status = Good
case bool(singleResp.Unknown):
ret.Status = Unknown
default:
ret.Status = Revoked
ret.RevokedAt = singleResp.Revoked.RevocationTime
ret.RevocationReason = int(singleResp.Revoked.Reason)
}
return ret, nil
}
// RequestOptions contains options for constructing OCSP requests.
type RequestOptions struct {
// Hash contains the hash function that should be used when
// constructing the OCSP request. If zero, SHA-1 will be used.
Hash crypto.Hash
}
func (opts *RequestOptions) hash() crypto.Hash {
if opts == nil || opts.Hash == 0 {
// SHA-1 is nearly universally used in OCSP.
return crypto.SHA1
}
return opts.Hash
}
// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If
// opts is nil then sensible defaults are used.
func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) {
hashFunc := opts.hash()
// OCSP seems to be the only place where these raw hash identifiers are
// used. I took the following from
// http://msdn.microsoft.com/en-us/library/ff635603.aspx
_, ok := hashOIDs[hashFunc]
if !ok {
return nil, x509.ErrUnsupportedAlgorithm
}
if !hashFunc.Available() {
return nil, x509.ErrUnsupportedAlgorithm
}
h := opts.hash().New()
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
return nil, err
}
h.Write(publicKeyInfo.PublicKey.RightAlign())
issuerKeyHash := h.Sum(nil)
h.Reset()
h.Write(issuer.RawSubject)
issuerNameHash := h.Sum(nil)
req := &Request{
HashAlgorithm: hashFunc,
IssuerNameHash: issuerNameHash,
IssuerKeyHash: issuerKeyHash,
SerialNumber: cert.SerialNumber,
}
return req.Marshal()
}
// CreateResponse returns a DER-encoded OCSP response with the specified contents.
// The fields in the response are populated as follows:
//
// The responder cert is used to populate the responder's name field, and the
// certificate itself is provided alongside the OCSP response signature.
//
// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
//
// The template is used to populate the SerialNumber, Status, RevokedAt,
// RevocationReason, ThisUpdate, and NextUpdate fields.
//
// If template.IssuerHash is not set, SHA1 will be used.
//
// The ProducedAt date is automatically set to the current date, to the nearest minute.
func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) {
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
return nil, err
}
if template.IssuerHash == 0 {
template.IssuerHash = crypto.SHA1
}
hashOID := getOIDFromHashAlgorithm(template.IssuerHash)
if hashOID == nil {
return nil, errors.New("unsupported issuer hash algorithm")
}
if !template.IssuerHash.Available() {
return nil, fmt.Errorf("issuer hash algorithm %v not linked into binary", template.IssuerHash)
}
h := template.IssuerHash.New()
h.Write(publicKeyInfo.PublicKey.RightAlign())
issuerKeyHash := h.Sum(nil)
h.Reset()
h.Write(issuer.RawSubject)
issuerNameHash := h.Sum(nil)
innerResponse := singleResponse{
CertID: certID{
HashAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: hashOID,
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
},
NameHash: issuerNameHash,
IssuerKeyHash: issuerKeyHash,
SerialNumber: template.SerialNumber,
},
ThisUpdate: template.ThisUpdate.UTC(),
NextUpdate: template.NextUpdate.UTC(),
SingleExtensions: template.ExtraExtensions,
}
switch template.Status {
case Good:
innerResponse.Good = true
case Unknown:
innerResponse.Unknown = true
case Revoked:
innerResponse.Revoked = revokedInfo{
RevocationTime: template.RevokedAt.UTC(),
Reason: asn1.Enumerated(template.RevocationReason),
}
}
rawResponderID := asn1.RawValue{
Class: 2, // context-specific
Tag: 1, // Name (explicit tag)
IsCompound: true,
Bytes: responderCert.RawSubject,
}
tbsResponseData := responseData{
Version: 0,
RawResponderID: rawResponderID,
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
Responses: []singleResponse{innerResponse},
}
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
if err != nil {
return nil, err
}
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
if err != nil {
return nil, err
}
responseHash := hashFunc.New()
responseHash.Write(tbsResponseDataDER)
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
if err != nil {
return nil, err
}
response := basicResponse{
TBSResponseData: tbsResponseData,
SignatureAlgorithm: signatureAlgorithm,
Signature: asn1.BitString{
Bytes: signature,
BitLength: 8 * len(signature),
},
}
if template.Certificate != nil {
response.Certificates = []asn1.RawValue{
{FullBytes: template.Certificate.Raw},
}
}
responseDER, err := asn1.Marshal(response)
if err != nil {
return nil, err
}
return asn1.Marshal(responseASN1{
Status: asn1.Enumerated(Success),
Response: responseBytes{
ResponseType: idPKIXOCSPBasic,
Response: responseDER,
},
})
}

3
vendor/modules.txt vendored
View File

@@ -20,12 +20,13 @@ github.com/nats-io/nkeys
# github.com/nats-io/nuid v1.0.1 # github.com/nats-io/nuid v1.0.1
## explicit ## explicit
github.com/nats-io/nuid github.com/nats-io/nuid
# golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b # golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
## explicit ## explicit
golang.org/x/crypto/bcrypt golang.org/x/crypto/bcrypt
golang.org/x/crypto/blowfish golang.org/x/crypto/blowfish
golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519
golang.org/x/crypto/ed25519/internal/edwards25519 golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/ocsp
# golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 # golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
## explicit ## explicit
golang.org/x/sys/cpu golang.org/x/sys/cpu