Update vendored github.com/cloudflare/cfssl (#3078)

This commit is contained in:
Roland Bracewell Shoemaker 2017-09-13 12:23:38 -07:00 committed by Daniel McCarney
parent 94e4947c58
commit c03d96212b
9 changed files with 159 additions and 131 deletions

56
Godeps/Godeps.json generated
View File

@ -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",

View File

@ -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) {

View File

@ -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)
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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