Update vendored github.com/cloudflare/cfssl (#3078)
This commit is contained in:
parent
94e4947c58
commit
c03d96212b
|
|
@ -12,73 +12,73 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/auth",
|
"ImportPath": "github.com/cloudflare/cfssl/auth",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/certdb",
|
"ImportPath": "github.com/cloudflare/cfssl/certdb",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/config",
|
"ImportPath": "github.com/cloudflare/cfssl/config",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
|
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/csr",
|
"ImportPath": "github.com/cloudflare/cfssl/csr",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/errors",
|
"ImportPath": "github.com/cloudflare/cfssl/errors",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/helpers",
|
"ImportPath": "github.com/cloudflare/cfssl/helpers",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/helpers/derhelpers",
|
"ImportPath": "github.com/cloudflare/cfssl/helpers/derhelpers",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/info",
|
"ImportPath": "github.com/cloudflare/cfssl/info",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/log",
|
"ImportPath": "github.com/cloudflare/cfssl/log",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
|
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/ocsp/config",
|
"ImportPath": "github.com/cloudflare/cfssl/ocsp/config",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/signer",
|
"ImportPath": "github.com/cloudflare/cfssl/signer",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/cloudflare/cfssl/signer/local",
|
"ImportPath": "github.com/cloudflare/cfssl/signer/local",
|
||||||
"Comment": "1.2.0-169-gc9a961e",
|
"Comment": "1.2.0-195-gb92b544",
|
||||||
"Rev": "c9a961ed337dbde794abb97a2a302320a99869da"
|
"Rev": "b92b544a91e70c91d9db888dfc9216197329df30"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||||
|
|
|
||||||
|
|
@ -69,11 +69,11 @@ type dbResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response is called by the HTTP server to handle a new OCSP request.
|
// Response is called by the HTTP server to handle a new OCSP request.
|
||||||
func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
func (src *DBSource) Response(req *ocsp.Request) ([]byte, http.Header, error) {
|
||||||
// Check that this request is for the proper CA
|
// Check that this request is for the proper CA
|
||||||
if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 {
|
if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 {
|
||||||
src.log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash)))
|
src.log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash)))
|
||||||
return nil, false
|
return nil, nil, cfocsp.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
serialString := core.SerialToString(req.SerialNumber)
|
serialString := core.SerialToString(req.SerialNumber)
|
||||||
|
|
@ -94,14 +94,14 @@ func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
||||||
src.log.AuditErr(fmt.Sprintf("Failed to retrieve response from certificateStatus table: %s", err))
|
src.log.AuditErr(fmt.Sprintf("Failed to retrieve response from certificateStatus table: %s", err))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false
|
return nil, nil, cfocsp.ErrNotFound
|
||||||
}
|
}
|
||||||
if response.OCSPLastUpdated.IsZero() {
|
if response.OCSPLastUpdated.IsZero() {
|
||||||
src.log.Debug(fmt.Sprintf("OCSP Response not sent (ocspLastUpdated is zero) for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString))
|
src.log.Debug(fmt.Sprintf("OCSP Response not sent (ocspLastUpdated is zero) for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString))
|
||||||
return nil, false
|
return nil, nil, cfocsp.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.OCSPResponse, true
|
return response.OCSPResponse, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDBSource(dbMap dbSelector, issuerCert string, log blog.Logger) (*DBSource, error) {
|
func makeDBSource(dbMap dbSelector, issuerCert string, log blog.Logger) (*DBSource, error) {
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,8 @@ func TestErrorLog(t *testing.T) {
|
||||||
ocspReq, err := ocsp.ParseRequest(req)
|
ocspReq, err := ocsp.ParseRequest(req)
|
||||||
test.AssertNotError(t, err, "Failed to parse OCSP request")
|
test.AssertNotError(t, err, "Failed to parse OCSP request")
|
||||||
|
|
||||||
_, found := src.Response(ocspReq)
|
_, _, err = src.Response(ocspReq)
|
||||||
test.Assert(t, !found, "Somehow found OCSP response")
|
test.AssertEquals(t, err, cfocsp.ErrNotFound)
|
||||||
|
|
||||||
test.AssertEquals(t, len(mockLog.GetAllMatching("Failed to retrieve response from certificateStatus table")), 1)
|
test.AssertEquals(t, len(mockLog.GetAllMatching("Failed to retrieve response from certificateStatus table")), 1)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ type Accessor interface {
|
||||||
GetCertificate(serial, aki string) ([]CertificateRecord, error)
|
GetCertificate(serial, aki string) ([]CertificateRecord, error)
|
||||||
GetUnexpiredCertificates() ([]CertificateRecord, error)
|
GetUnexpiredCertificates() ([]CertificateRecord, error)
|
||||||
GetRevokedAndUnexpiredCertificates() ([]CertificateRecord, error)
|
GetRevokedAndUnexpiredCertificates() ([]CertificateRecord, error)
|
||||||
|
GetRevokedAndUnexpiredCertificatesByLabel(label string) ([]CertificateRecord, error)
|
||||||
RevokeCertificate(serial, aki string, reasonCode int) error
|
RevokeCertificate(serial, aki string, reasonCode int) error
|
||||||
InsertOCSP(rr OCSPRecord) error
|
InsertOCSP(rr OCSPRecord) error
|
||||||
GetOCSP(serial, aki string) ([]OCSPRecord, error)
|
GetOCSP(serial, aki string) ([]OCSPRecord, error)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/google/certificate-transparency-go"
|
"github.com/google/certificate-transparency-go"
|
||||||
|
|
@ -383,51 +382,6 @@ func GetKeyDERFromPEM(in []byte, password []byte) ([]byte, error) {
|
||||||
return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed)
|
return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckSignature verifies a signature made by the key on a CSR, such
|
|
||||||
// as on the CSR itself.
|
|
||||||
func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error {
|
|
||||||
var hashType crypto.Hash
|
|
||||||
|
|
||||||
switch algo {
|
|
||||||
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
|
|
||||||
hashType = crypto.SHA1
|
|
||||||
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
|
|
||||||
hashType = crypto.SHA256
|
|
||||||
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
|
||||||
hashType = crypto.SHA384
|
|
||||||
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
|
||||||
hashType = crypto.SHA512
|
|
||||||
default:
|
|
||||||
return x509.ErrUnsupportedAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hashType.Available() {
|
|
||||||
return x509.ErrUnsupportedAlgorithm
|
|
||||||
}
|
|
||||||
h := hashType.New()
|
|
||||||
|
|
||||||
h.Write(signed)
|
|
||||||
digest := h.Sum(nil)
|
|
||||||
|
|
||||||
switch pub := csr.PublicKey.(type) {
|
|
||||||
case *rsa.PublicKey:
|
|
||||||
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
ecdsaSig := new(struct{ R, S *big.Int })
|
|
||||||
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
|
|
||||||
return errors.New("x509: ECDSA signature contained zero or negative values")
|
|
||||||
}
|
|
||||||
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
|
|
||||||
return errors.New("x509: ECDSA verification failure")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return x509.ErrUnsupportedAlgorithm
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request.
|
// ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request.
|
||||||
func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) {
|
func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) {
|
||||||
in = bytes.TrimSpace(in)
|
in = bytes.TrimSpace(in)
|
||||||
|
|
@ -446,7 +400,7 @@ func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error)
|
||||||
return nil, rest, err
|
return nil, rest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
|
err = csr.CheckSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, rest, err
|
return nil, rest, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,10 @@ type SignRequest struct {
|
||||||
// in the OCSP response. Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384,
|
// in the OCSP response. Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384,
|
||||||
// and crypto.SHA512. If zero, the default is crypto.SHA1.
|
// and crypto.SHA512. If zero, the default is crypto.SHA1.
|
||||||
IssuerHash crypto.Hash
|
IssuerHash crypto.Hash
|
||||||
|
// If provided ThisUpdate will override the default usage of time.Now().Truncate(time.Hour)
|
||||||
|
ThisUpdate *time.Time
|
||||||
|
// If provided NextUpdate will override the default usage of ThisUpdate.Add(signerInterval)
|
||||||
|
NextUpdate *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signer represents a general signer of OCSP responses. It is
|
// Signer represents a general signer of OCSP responses. It is
|
||||||
|
|
@ -164,9 +168,18 @@ func (s StandardSigner) Sign(req SignRequest) ([]byte, error) {
|
||||||
return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
|
return nil, cferr.New(cferr.OCSPError, cferr.IssuerMismatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var thisUpdate, nextUpdate time.Time
|
||||||
|
if req.ThisUpdate != nil {
|
||||||
|
thisUpdate = *req.ThisUpdate
|
||||||
|
} else {
|
||||||
// Round thisUpdate times down to the nearest hour
|
// Round thisUpdate times down to the nearest hour
|
||||||
thisUpdate := time.Now().Truncate(time.Hour)
|
thisUpdate = time.Now().Truncate(time.Hour)
|
||||||
nextUpdate := thisUpdate.Add(s.interval)
|
}
|
||||||
|
if req.NextUpdate != nil {
|
||||||
|
nextUpdate = *req.NextUpdate
|
||||||
|
} else {
|
||||||
|
nextUpdate = thisUpdate.Add(s.interval)
|
||||||
|
}
|
||||||
|
|
||||||
status, ok := StatusCode[req.Status]
|
status, ok := StatusCode[req.Status]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -30,14 +31,25 @@ var (
|
||||||
tryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
|
tryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
|
||||||
sigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
|
sigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
|
||||||
unauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
|
unauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
|
||||||
|
|
||||||
|
// ErrNotFound indicates the request OCSP response was not found. It is used to
|
||||||
|
// indicate that the responder should reply with unauthorizedErrorResponse.
|
||||||
|
ErrNotFound = errors.New("Request OCSP Response not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source represents the logical source of OCSP responses, i.e.,
|
// Source represents the logical source of OCSP responses, i.e.,
|
||||||
// the logic that actually chooses a response based on a request. In
|
// the logic that actually chooses a response based on a request. In
|
||||||
// order to create an actual responder, wrap one of these in a Responder
|
// order to create an actual responder, wrap one of these in a Responder
|
||||||
// object and pass it to http.Handle.
|
// object and pass it to http.Handle. By default the Responder will set
|
||||||
|
// the headers Cache-Control to "max-age=(response.NextUpdate-now), public, no-transform, must-revalidate",
|
||||||
|
// Last-Modified to response.ThisUpdate, Expires to response.NextUpdate,
|
||||||
|
// ETag to the SHA256 hash of the response, and Content-Type to
|
||||||
|
// application/ocsp-response. If you want to override these headers,
|
||||||
|
// or set extra headers, your source should return a http.Header
|
||||||
|
// with the headers you wish to set. If you don't want to set any
|
||||||
|
// extra headers you may return nil instead.
|
||||||
type Source interface {
|
type Source interface {
|
||||||
Response(*ocsp.Request) ([]byte, bool)
|
Response(*ocsp.Request) ([]byte, http.Header, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An InMemorySource is a map from serialNumber -> der(response)
|
// An InMemorySource is a map from serialNumber -> der(response)
|
||||||
|
|
@ -46,9 +58,12 @@ type InMemorySource map[string][]byte
|
||||||
// Response looks up an OCSP response to provide for a given request.
|
// Response looks up an OCSP response to provide for a given request.
|
||||||
// InMemorySource looks up a response purely based on serial number,
|
// InMemorySource looks up a response purely based on serial number,
|
||||||
// without regard to what issuer the request is asking for.
|
// without regard to what issuer the request is asking for.
|
||||||
func (src InMemorySource) Response(request *ocsp.Request) (response []byte, present bool) {
|
func (src InMemorySource) Response(request *ocsp.Request) ([]byte, http.Header, error) {
|
||||||
response, present = src[request.SerialNumber.String()]
|
response, present := src[request.SerialNumber.String()]
|
||||||
return
|
if !present {
|
||||||
|
return nil, nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return response, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DBSource represnts a source of OCSP responses backed by the certdb package.
|
// DBSource represnts a source of OCSP responses backed by the certdb package.
|
||||||
|
|
@ -65,26 +80,23 @@ func NewDBSource(dbAccessor certdb.Accessor) Source {
|
||||||
|
|
||||||
// Response implements cfssl.ocsp.responder.Source, which returns the
|
// Response implements cfssl.ocsp.responder.Source, which returns the
|
||||||
// OCSP response in the Database for the given request with the expiration
|
// OCSP response in the Database for the given request with the expiration
|
||||||
// date furthest in the future. Response also returns a bool that is false
|
// date furthest in the future.
|
||||||
// if there were any errors obtaining the OCSP response and/or no OCSP response
|
func (src DBSource) Response(req *ocsp.Request) ([]byte, http.Header, error) {
|
||||||
// is present in the DB for the given request. Response will return a true
|
|
||||||
// bool if the byte array returned is a valid OCSP response.
|
|
||||||
func (src DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
|
||||||
if req == nil {
|
if req == nil {
|
||||||
return nil, false
|
return nil, nil, errors.New("called with nil request")
|
||||||
}
|
}
|
||||||
|
|
||||||
aki := hex.EncodeToString(req.IssuerKeyHash)
|
aki := hex.EncodeToString(req.IssuerKeyHash)
|
||||||
sn := req.SerialNumber
|
sn := req.SerialNumber
|
||||||
|
|
||||||
if sn == nil {
|
if sn == nil {
|
||||||
return nil, false
|
return nil, nil, errors.New("request contains no serial")
|
||||||
}
|
}
|
||||||
strSN := sn.String()
|
strSN := sn.String()
|
||||||
|
|
||||||
if src.Accessor == nil {
|
if src.Accessor == nil {
|
||||||
log.Errorf("No DB Accessor")
|
log.Errorf("No DB Accessor")
|
||||||
return nil, false
|
return nil, nil, errors.New("called with nil DB accessor")
|
||||||
}
|
}
|
||||||
records, err := src.Accessor.GetOCSP(strSN, aki)
|
records, err := src.Accessor.GetOCSP(strSN, aki)
|
||||||
|
|
||||||
|
|
@ -92,11 +104,11 @@ func (src DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
||||||
// and returns nil, false.
|
// and returns nil, false.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error obtaining OCSP response: %s", err)
|
log.Errorf("Error obtaining OCSP response: %s", err)
|
||||||
return nil, false
|
return nil, nil, fmt.Errorf("failed to obtain OCSP response: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(records) == 0 {
|
if len(records) == 0 {
|
||||||
return nil, false
|
return nil, nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response() finds the OCSPRecord with the expiration date furthest in the future.
|
// Response() finds the OCSPRecord with the expiration date furthest in the future.
|
||||||
|
|
@ -106,7 +118,7 @@ func (src DBSource) Response(req *ocsp.Request) ([]byte, bool) {
|
||||||
cur = rec
|
cur = rec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []byte(cur.Body), true
|
return []byte(cur.Body), nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSourceFromFile reads the named file into an InMemorySource.
|
// NewSourceFromFile reads the named file into an InMemorySource.
|
||||||
|
|
@ -161,6 +173,19 @@ func NewResponder(source Source) *Responder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func overrideHeaders(response http.ResponseWriter, headers http.Header) {
|
||||||
|
for k, v := range headers {
|
||||||
|
if len(v) == 1 {
|
||||||
|
response.Header().Set(k, v[0])
|
||||||
|
} else if len(v) > 1 {
|
||||||
|
response.Header().Del(k)
|
||||||
|
for _, e := range v {
|
||||||
|
response.Header().Add(k, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A Responder can process both GET and POST requests. The mapping
|
// A Responder can process both GET and POST requests. The mapping
|
||||||
// from an OCSP request to an OCSP response is done by the Source;
|
// from an OCSP request to an OCSP response is done by the Source;
|
||||||
// the Responder simply decodes the request, and passes back whatever
|
// the Responder simply decodes the request, and passes back whatever
|
||||||
|
|
@ -243,13 +268,20 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up OCSP response from source
|
// Look up OCSP response from source
|
||||||
ocspResponse, found := rs.Source.Response(ocspRequest)
|
ocspResponse, headers, err := rs.Source.Response(ocspRequest)
|
||||||
if !found {
|
if err != nil {
|
||||||
|
if err == ErrNotFound {
|
||||||
log.Infof("No response found for request: serial %x, request body %s",
|
log.Infof("No response found for request: serial %x, request body %s",
|
||||||
ocspRequest.SerialNumber, b64Body)
|
ocspRequest.SerialNumber, b64Body)
|
||||||
response.Write(unauthorizedErrorResponse)
|
response.Write(unauthorizedErrorResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Infof("Error retrieving response for request: serial %x, request body %s, error: %s",
|
||||||
|
ocspRequest.SerialNumber, b64Body, err)
|
||||||
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
response.Write(internalErrorErrorResponse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
parsedResponse, err := ocsp.ParseResponse(ocspResponse, nil)
|
parsedResponse, err := ocsp.ParseResponse(ocspResponse, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -281,6 +313,10 @@ func (rs Responder) ServeHTTP(response http.ResponseWriter, request *http.Reques
|
||||||
responseHash := sha256.Sum256(ocspResponse)
|
responseHash := sha256.Sum256(ocspResponse)
|
||||||
response.Header().Add("ETag", fmt.Sprintf("\"%X\"", responseHash))
|
response.Header().Add("ETag", fmt.Sprintf("\"%X\"", responseHash))
|
||||||
|
|
||||||
|
if headers != nil {
|
||||||
|
overrideHeaders(response, headers)
|
||||||
|
}
|
||||||
|
|
||||||
// RFC 7232 says that a 304 response must contain the above
|
// RFC 7232 says that a 304 response must contain the above
|
||||||
// headers if they would also be sent for a 200 for the same
|
// headers if they would also be sent for a 200 for the same
|
||||||
// request, so we have to wait until here to do this
|
// request, so we have to wait until here to do this
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/google/certificate-transparency-go/client"
|
"github.com/google/certificate-transparency-go/client"
|
||||||
"github.com/google/certificate-transparency-go/jsonclient"
|
"github.com/google/certificate-transparency-go/jsonclient"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signer contains a signer that uses the standard library to
|
// Signer contains a signer that uses the standard library to
|
||||||
|
|
@ -96,14 +97,14 @@ func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signe
|
||||||
return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
|
return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) {
|
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile, notBefore time.Time, notAfter time.Time) (cert []byte, err error) {
|
||||||
var distPoints = template.CRLDistributionPoints
|
var distPoints = template.CRLDistributionPoints
|
||||||
err = signer.FillTemplate(template, s.policy.Default, profile)
|
|
||||||
if distPoints != nil && len(distPoints) > 0 {
|
if distPoints != nil && len(distPoints) > 0 {
|
||||||
template.CRLDistributionPoints = distPoints
|
template.CRLDistributionPoints = distPoints
|
||||||
}
|
}
|
||||||
|
err = signer.FillTemplate(template, s.policy.Default, profile, notBefore, notAfter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var initRoot bool
|
var initRoot bool
|
||||||
|
|
@ -342,7 +343,7 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
|
||||||
var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}}
|
var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}}
|
||||||
var poisonedPreCert = certTBS
|
var poisonedPreCert = certTBS
|
||||||
poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension)
|
poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension)
|
||||||
cert, err = s.sign(&poisonedPreCert, profile)
|
cert, err = s.sign(&poisonedPreCert, profile, req.NotBefore, req.NotAfter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -382,17 +383,22 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
|
||||||
certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension)
|
certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension)
|
||||||
}
|
}
|
||||||
var signedCert []byte
|
var signedCert []byte
|
||||||
signedCert, err = s.sign(&certTBS, profile)
|
signedCert, err = s.sign(&certTBS, profile, req.NotBefore, req.NotAfter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the AKI from signedCert. This is required to support Go 1.9+.
|
||||||
|
// In prior versions of Go, x509.CreateCertificate updated the
|
||||||
|
// AuthorityKeyId of certTBS.
|
||||||
|
parsedCert, _ := helpers.ParseCertificatePEM(signedCert)
|
||||||
|
|
||||||
if s.dbAccessor != nil {
|
if s.dbAccessor != nil {
|
||||||
var certRecord = certdb.CertificateRecord{
|
var certRecord = certdb.CertificateRecord{
|
||||||
Serial: certTBS.SerialNumber.String(),
|
Serial: certTBS.SerialNumber.String(),
|
||||||
// this relies on the specific behavior of x509.CreateCertificate
|
// this relies on the specific behavior of x509.CreateCertificate
|
||||||
// which updates certTBS AuthorityKeyId from the signer's SubjectKeyId
|
// which sets the AuthorityKeyId from the signer's SubjectKeyId
|
||||||
AKI: hex.EncodeToString(certTBS.AuthorityKeyId),
|
AKI: hex.EncodeToString(parsedCert.AuthorityKeyId),
|
||||||
CALabel: req.Label,
|
CALabel: req.Label,
|
||||||
Status: "good",
|
Status: "good",
|
||||||
Expiry: certTBS.NotAfter,
|
Expiry: certTBS.NotAfter,
|
||||||
|
|
@ -452,6 +458,11 @@ func (s *Signer) SetDBAccessor(dba certdb.Accessor) {
|
||||||
s.dbAccessor = dba
|
s.dbAccessor = dba
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDBAccessor returns the signers' cert db accessor
|
||||||
|
func (s *Signer) GetDBAccessor() certdb.Accessor {
|
||||||
|
return s.dbAccessor
|
||||||
|
}
|
||||||
|
|
||||||
// SetReqModifier does nothing for local
|
// SetReqModifier does nothing for local
|
||||||
func (s *Signer) SetReqModifier(func(*http.Request, []byte)) {
|
func (s *Signer) SetReqModifier(func(*http.Request, []byte)) {
|
||||||
// noop
|
// noop
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"github.com/cloudflare/cfssl/config"
|
"github.com/cloudflare/cfssl/config"
|
||||||
"github.com/cloudflare/cfssl/csr"
|
"github.com/cloudflare/cfssl/csr"
|
||||||
cferr "github.com/cloudflare/cfssl/errors"
|
cferr "github.com/cloudflare/cfssl/errors"
|
||||||
"github.com/cloudflare/cfssl/helpers"
|
|
||||||
"github.com/cloudflare/cfssl/info"
|
"github.com/cloudflare/cfssl/info"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -56,6 +55,15 @@ type SignRequest struct {
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Serial *big.Int `json:"serial,omitempty"`
|
Serial *big.Int `json:"serial,omitempty"`
|
||||||
Extensions []Extension `json:"extensions,omitempty"`
|
Extensions []Extension `json:"extensions,omitempty"`
|
||||||
|
// If provided, NotBefore will be used without modification (except
|
||||||
|
// for canonicalization) as the value of the notBefore field of the
|
||||||
|
// certificate. In particular no backdating adjustment will be made
|
||||||
|
// when NotBefore is provided.
|
||||||
|
NotBefore time.Time
|
||||||
|
// If provided, NotAfter will be used without modification (except
|
||||||
|
// for canonicalization) as the value of the notAfter field of the
|
||||||
|
// certificate.
|
||||||
|
NotAfter time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendIf appends to a if s is not an empty string.
|
// appendIf appends to a if s is not an empty string.
|
||||||
|
|
@ -97,6 +105,7 @@ type Signer interface {
|
||||||
Info(info.Req) (*info.Resp, error)
|
Info(info.Req) (*info.Resp, error)
|
||||||
Policy() *config.Signing
|
Policy() *config.Signing
|
||||||
SetDBAccessor(certdb.Accessor)
|
SetDBAccessor(certdb.Accessor)
|
||||||
|
GetDBAccessor() certdb.Accessor
|
||||||
SetPolicy(*config.Signing)
|
SetPolicy(*config.Signing)
|
||||||
SigAlgo() x509.SignatureAlgorithm
|
SigAlgo() x509.SignatureAlgorithm
|
||||||
Sign(req SignRequest) (cert []byte, err error)
|
Sign(req SignRequest) (cert []byte, err error)
|
||||||
|
|
@ -163,7 +172,7 @@ func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certific
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = helpers.CheckSignature(csrv, csrv.SignatureAlgorithm, csrv.RawTBSCertificateRequest, csrv.Signature)
|
err = csrv.CheckSignature()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
|
err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
|
||||||
return
|
return
|
||||||
|
|
@ -231,7 +240,7 @@ func ComputeSKI(template *x509.Certificate) ([]byte, error) {
|
||||||
// the certificate template as possible from the profiles and current
|
// the certificate template as possible from the profiles and current
|
||||||
// template. It fills in the key uses, expiration, revocation URLs
|
// template. It fills in the key uses, expiration, revocation URLs
|
||||||
// and SKI.
|
// and SKI.
|
||||||
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error {
|
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile, notBefore time.Time, notAfter time.Time) error {
|
||||||
ski, err := ComputeSKI(template)
|
ski, err := ComputeSKI(template)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -242,8 +251,6 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
|
||||||
ku x509.KeyUsage
|
ku x509.KeyUsage
|
||||||
backdate time.Duration
|
backdate time.Duration
|
||||||
expiry time.Duration
|
expiry time.Duration
|
||||||
notBefore time.Time
|
|
||||||
notAfter time.Time
|
|
||||||
crlURL, ocspURL string
|
crlURL, ocspURL string
|
||||||
issuerURL = profile.IssuerURL
|
issuerURL = profile.IssuerURL
|
||||||
)
|
)
|
||||||
|
|
@ -270,23 +277,29 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
|
||||||
if ocspURL = profile.OCSP; ocspURL == "" {
|
if ocspURL = profile.OCSP; ocspURL == "" {
|
||||||
ocspURL = defaultProfile.OCSP
|
ocspURL = defaultProfile.OCSP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if notBefore.IsZero() {
|
||||||
|
if !profile.NotBefore.IsZero() {
|
||||||
|
notBefore = profile.NotBefore
|
||||||
|
} else {
|
||||||
if backdate = profile.Backdate; backdate == 0 {
|
if backdate = profile.Backdate; backdate == 0 {
|
||||||
backdate = -5 * time.Minute
|
backdate = -5 * time.Minute
|
||||||
} else {
|
} else {
|
||||||
backdate = -1 * profile.Backdate
|
backdate = -1 * profile.Backdate
|
||||||
}
|
}
|
||||||
|
notBefore = time.Now().Round(time.Minute).Add(backdate)
|
||||||
if !profile.NotBefore.IsZero() {
|
|
||||||
notBefore = profile.NotBefore.UTC()
|
|
||||||
} else {
|
|
||||||
notBefore = time.Now().Round(time.Minute).Add(backdate).UTC()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
notBefore = notBefore.UTC()
|
||||||
|
|
||||||
|
if notAfter.IsZero() {
|
||||||
if !profile.NotAfter.IsZero() {
|
if !profile.NotAfter.IsZero() {
|
||||||
notAfter = profile.NotAfter.UTC()
|
notAfter = profile.NotAfter
|
||||||
} else {
|
} else {
|
||||||
notAfter = notBefore.Add(expiry).UTC()
|
notAfter = notBefore.Add(expiry)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
notAfter = notAfter.UTC()
|
||||||
|
|
||||||
template.NotBefore = notBefore
|
template.NotBefore = notBefore
|
||||||
template.NotAfter = notAfter
|
template.NotAfter = notAfter
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue