mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
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:
2
go.mod
2
go.mod
@@ -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
2
go.sum
@@ -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=
|
||||||
|
|||||||
106
server/client.go
106
server/client.go
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
33
test/configs/certs/ocsp/ca-cert.pem
Normal file
33
test/configs/certs/ocsp/ca-cert.pem
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
51
test/configs/certs/ocsp/ca-key.pem
Normal file
51
test/configs/certs/ocsp/ca-key.pem
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
126
test/configs/certs/ocsp/client-cert.pem
Normal file
126
test/configs/certs/ocsp/client-cert.pem
Normal 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-----
|
||||||
51
test/configs/certs/ocsp/client-key.pem
Normal file
51
test/configs/certs/ocsp/client-key.pem
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
126
test/configs/certs/ocsp/server-cert.pem
Normal file
126
test/configs/certs/ocsp/server-cert.pem
Normal 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-----
|
||||||
51
test/configs/certs/ocsp/server-key.pem
Normal file
51
test/configs/certs/ocsp/server-key.pem
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
245
test/ocspresponder_test.go
Normal file
245
test/ocspresponder_test.go
Normal 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
|
||||||
|
}
|
||||||
105
test/tls_test.go
105
test/tls_test.go
@@ -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
789
vendor/golang.org/x/crypto/ocsp/ocsp.go
generated
vendored
Normal 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
3
vendor/modules.txt
vendored
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user