// Package local implements certificate signature functionality for CFSSL. package local import ( "bytes" "crypto" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/hex" "encoding/pem" "errors" "io" "math/big" "net" "net/http" "net/mail" "net/url" "os" "github.com/cloudflare/cfssl/certdb" "github.com/cloudflare/cfssl/config" cferr "github.com/cloudflare/cfssl/errors" "github.com/cloudflare/cfssl/helpers" "github.com/cloudflare/cfssl/info" "github.com/cloudflare/cfssl/log" "github.com/cloudflare/cfssl/signer" "github.com/google/certificate-transparency-go" "github.com/google/certificate-transparency-go/client" "github.com/google/certificate-transparency-go/jsonclient" "golang.org/x/net/context" ) // Signer contains a signer that uses the standard library to // support both ECDSA and RSA CA keys. type Signer struct { ca *x509.Certificate priv crypto.Signer policy *config.Signing sigAlgo x509.SignatureAlgorithm dbAccessor certdb.Accessor } // NewSigner creates a new Signer directly from a // private key and certificate, with optional policy. func NewSigner(priv crypto.Signer, cert *x509.Certificate, sigAlgo x509.SignatureAlgorithm, policy *config.Signing) (*Signer, error) { if policy == nil { policy = &config.Signing{ Profiles: map[string]*config.SigningProfile{}, Default: config.DefaultConfig()} } if !policy.Valid() { return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy) } return &Signer{ ca: cert, priv: priv, sigAlgo: sigAlgo, policy: policy, }, nil } // NewSignerFromFile generates a new local signer from a caFile // and a caKey file, both PEM encoded. func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signer, error) { log.Debug("Loading CA: ", caFile) ca, err := helpers.ReadBytes(caFile) if err != nil { return nil, err } log.Debug("Loading CA key: ", caKeyFile) cakey, err := helpers.ReadBytes(caKeyFile) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err) } parsedCa, err := helpers.ParseCertificatePEM(ca) if err != nil { return nil, err } strPassword := os.Getenv("CFSSL_CA_PK_PASSWORD") password := []byte(strPassword) if strPassword == "" { password = nil } priv, err := helpers.ParsePrivateKeyPEMWithPassword(cakey, password) if err != nil { log.Debug("Malformed private key %v", err) return nil, err } return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy) } func (s *Signer) sign(template *x509.Certificate) (cert []byte, err error) { var initRoot bool if s.ca == nil { if !template.IsCA { err = cferr.New(cferr.PolicyError, cferr.InvalidRequest) return } template.DNSNames = nil template.EmailAddresses = nil template.URIs = nil s.ca = template initRoot = true } derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) } if initRoot { s.ca, err = x509.ParseCertificate(derBytes) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) } } cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) log.Infof("signed certificate with serial number %d", template.SerialNumber) return } // replaceSliceIfEmpty replaces the contents of replaced with newContents if // the slice referenced by replaced is empty func replaceSliceIfEmpty(replaced, newContents *[]string) { if len(*replaced) == 0 { *replaced = *newContents } } // PopulateSubjectFromCSR has functionality similar to Name, except // it fills the fields of the resulting pkix.Name with req's if the // subject's corresponding fields are empty func PopulateSubjectFromCSR(s *signer.Subject, req pkix.Name) pkix.Name { // if no subject, use req if s == nil { return req } name := s.Name() if name.CommonName == "" { name.CommonName = req.CommonName } replaceSliceIfEmpty(&name.Country, &req.Country) replaceSliceIfEmpty(&name.Province, &req.Province) replaceSliceIfEmpty(&name.Locality, &req.Locality) replaceSliceIfEmpty(&name.Organization, &req.Organization) replaceSliceIfEmpty(&name.OrganizationalUnit, &req.OrganizationalUnit) if name.SerialNumber == "" { name.SerialNumber = req.SerialNumber } return name } // OverrideHosts fills template's IPAddresses, EmailAddresses, DNSNames, and URIs with the // content of hosts, if it is not nil. func OverrideHosts(template *x509.Certificate, hosts []string) { if hosts != nil { template.IPAddresses = []net.IP{} template.EmailAddresses = []string{} template.DNSNames = []string{} template.URIs = []*url.URL{} } for i := range hosts { if ip := net.ParseIP(hosts[i]); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) } else if email, err := mail.ParseAddress(hosts[i]); err == nil && email != nil { template.EmailAddresses = append(template.EmailAddresses, email.Address) } else if uri, err := url.ParseRequestURI(hosts[i]); err == nil && uri != nil { template.URIs = append(template.URIs, uri) } else { template.DNSNames = append(template.DNSNames, hosts[i]) } } } // Sign signs a new certificate based on the PEM-encoded client // certificate or certificate request with the signing profile, // specified by profileName. func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) { profile, err := signer.Profile(s, req.Profile) if err != nil { return } block, _ := pem.Decode([]byte(req.Request)) if block == nil { return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed) } if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" { return nil, cferr.Wrap(cferr.CSRError, cferr.BadRequest, errors.New("not a csr")) } csrTemplate, err := signer.ParseCertificateRequest(s, block.Bytes) if err != nil { return nil, err } // Copy out only the fields from the CSR authorized by policy. safeTemplate := x509.Certificate{} // If the profile contains no explicit whitelist, assume that all fields // should be copied from the CSR. if profile.CSRWhitelist == nil { safeTemplate = *csrTemplate } else { if profile.CSRWhitelist.Subject { safeTemplate.Subject = csrTemplate.Subject } if profile.CSRWhitelist.PublicKeyAlgorithm { safeTemplate.PublicKeyAlgorithm = csrTemplate.PublicKeyAlgorithm } if profile.CSRWhitelist.PublicKey { safeTemplate.PublicKey = csrTemplate.PublicKey } if profile.CSRWhitelist.SignatureAlgorithm { safeTemplate.SignatureAlgorithm = csrTemplate.SignatureAlgorithm } if profile.CSRWhitelist.DNSNames { safeTemplate.DNSNames = csrTemplate.DNSNames } if profile.CSRWhitelist.IPAddresses { safeTemplate.IPAddresses = csrTemplate.IPAddresses } if profile.CSRWhitelist.EmailAddresses { safeTemplate.EmailAddresses = csrTemplate.EmailAddresses } if profile.CSRWhitelist.URIs { safeTemplate.URIs = csrTemplate.URIs } } if req.CRLOverride != "" { safeTemplate.CRLDistributionPoints = []string{req.CRLOverride} } if safeTemplate.IsCA { if !profile.CAConstraint.IsCA { log.Error("local signer policy disallows issuing CA certificate") return nil, cferr.New(cferr.PolicyError, cferr.InvalidRequest) } if s.ca != nil && s.ca.MaxPathLen > 0 { if safeTemplate.MaxPathLen >= s.ca.MaxPathLen { log.Error("local signer certificate disallows CA MaxPathLen extending") // do not sign a cert with pathlen > current return nil, cferr.New(cferr.PolicyError, cferr.InvalidRequest) } } else if s.ca != nil && s.ca.MaxPathLen == 0 && s.ca.MaxPathLenZero { log.Error("local signer certificate disallows issuing CA certificate") // signer has pathlen of 0, do not sign more intermediate CAs return nil, cferr.New(cferr.PolicyError, cferr.InvalidRequest) } } OverrideHosts(&safeTemplate, req.Hosts) safeTemplate.Subject = PopulateSubjectFromCSR(req.Subject, safeTemplate.Subject) // If there is a whitelist, ensure that both the Common Name and SAN DNSNames match if profile.NameWhitelist != nil { if safeTemplate.Subject.CommonName != "" { if profile.NameWhitelist.Find([]byte(safeTemplate.Subject.CommonName)) == nil { return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist) } } for _, name := range safeTemplate.DNSNames { if profile.NameWhitelist.Find([]byte(name)) == nil { return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist) } } for _, name := range safeTemplate.EmailAddresses { if profile.NameWhitelist.Find([]byte(name)) == nil { return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist) } } for _, name := range safeTemplate.URIs { if profile.NameWhitelist.Find([]byte(name.String())) == nil { return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist) } } } if profile.ClientProvidesSerialNumbers { if req.Serial == nil { return nil, cferr.New(cferr.CertificateError, cferr.MissingSerial) } safeTemplate.SerialNumber = req.Serial } else { // RFC 5280 4.1.2.2: // Certificate users MUST be able to handle serialNumber // values up to 20 octets. Conforming CAs MUST NOT use // serialNumber values longer than 20 octets. // // If CFSSL is providing the serial numbers, it makes // sense to use the max supported size. serialNumber := make([]byte, 20) _, err = io.ReadFull(rand.Reader, serialNumber) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) } // SetBytes interprets buf as the bytes of a big-endian // unsigned integer. The leading byte should be masked // off to ensure it isn't negative. serialNumber[0] &= 0x7F safeTemplate.SerialNumber = new(big.Int).SetBytes(serialNumber) } if len(req.Extensions) > 0 { for _, ext := range req.Extensions { oid := asn1.ObjectIdentifier(ext.ID) if !profile.ExtensionWhitelist[oid.String()] { return nil, cferr.New(cferr.CertificateError, cferr.InvalidRequest) } rawValue, err := hex.DecodeString(ext.Value) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.InvalidRequest, err) } safeTemplate.ExtraExtensions = append(safeTemplate.ExtraExtensions, pkix.Extension{ Id: oid, Critical: ext.Critical, Value: rawValue, }) } } var distPoints = safeTemplate.CRLDistributionPoints err = signer.FillTemplate(&safeTemplate, s.policy.Default, profile, req.NotBefore, req.NotAfter) if err != nil { return nil, err } if distPoints != nil && len(distPoints) > 0 { safeTemplate.CRLDistributionPoints = distPoints } var certTBS = safeTemplate if len(profile.CTLogServers) > 0 || req.ReturnPrecert { // Add a poison extension which prevents validation var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}} var poisonedPreCert = certTBS poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension) cert, err = s.sign(&poisonedPreCert) if err != nil { return } if req.ReturnPrecert { return cert, nil } derCert, _ := pem.Decode(cert) prechain := []ct.ASN1Cert{{Data: derCert.Bytes}, {Data: s.ca.Raw}} var sctList []ct.SignedCertificateTimestamp for _, server := range profile.CTLogServers { log.Infof("submitting poisoned precertificate to %s", server) ctclient, err := client.New(server, nil, jsonclient.Options{}) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err) } var resp *ct.SignedCertificateTimestamp ctx := context.Background() resp, err = ctclient.AddPreChain(ctx, prechain) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err) } sctList = append(sctList, *resp) } var serializedSCTList []byte serializedSCTList, err = helpers.SerializeSCTList(sctList) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err) } // Serialize again as an octet string before embedding serializedSCTList, err = asn1.Marshal(serializedSCTList) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err) } var SCTListExtension = pkix.Extension{Id: signer.SCTListOID, Critical: false, Value: serializedSCTList} certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension) } var signedCert []byte signedCert, err = s.sign(&certTBS) if err != nil { 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 { var certRecord = certdb.CertificateRecord{ Serial: certTBS.SerialNumber.String(), // this relies on the specific behavior of x509.CreateCertificate // which sets the AuthorityKeyId from the signer's SubjectKeyId AKI: hex.EncodeToString(parsedCert.AuthorityKeyId), CALabel: req.Label, Status: "good", Expiry: certTBS.NotAfter, PEM: string(signedCert), } err = s.dbAccessor.InsertCertificate(certRecord) if err != nil { return nil, err } log.Debug("saved certificate with serial number ", certTBS.SerialNumber) } return signedCert, nil } // SignFromPrecert creates and signs a certificate from an existing precertificate // that was previously signed by Signer.ca and inserts the provided SCTs into the // new certificate. The resulting certificate will be a exact copy of the precert // except for the removal of the poison extension and the addition of the SCT list // extension. SignFromPrecert does not verify that the contents of the certificate // still match the signing profile of the signer, it only requires that the precert // was previously signed by the Signers CA. func (s *Signer) SignFromPrecert(precert *x509.Certificate, scts []ct.SignedCertificateTimestamp) ([]byte, error) { // Verify certificate was signed by s.ca if err := precert.CheckSignatureFrom(s.ca); err != nil { return nil, err } // Verify certificate is a precert isPrecert := false poisonIndex := 0 for i, ext := range precert.Extensions { if ext.Id.Equal(signer.CTPoisonOID) { if !ext.Critical { return nil, cferr.New(cferr.CTError, cferr.PrecertInvalidPoison) } // Check extension contains ASN.1 NULL if bytes.Compare(ext.Value, []byte{0x05, 0x00}) != 0 { return nil, cferr.New(cferr.CTError, cferr.PrecertInvalidPoison) } isPrecert = true poisonIndex = i break } } if !isPrecert { return nil, cferr.New(cferr.CTError, cferr.PrecertMissingPoison) } // Serialize SCTs into list format and create extension serializedList, err := helpers.SerializeSCTList(scts) if err != nil { return nil, err } // Serialize again as an octet string before embedding serializedList, err = asn1.Marshal(serializedList) if err != nil { return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err) } sctExt := pkix.Extension{Id: signer.SCTListOID, Critical: false, Value: serializedList} // Create the new tbsCert from precert. Do explicit copies of any slices so that we don't // use memory that may be altered by us or the caller at a later stage. tbsCert := x509.Certificate{ SignatureAlgorithm: precert.SignatureAlgorithm, PublicKeyAlgorithm: precert.PublicKeyAlgorithm, PublicKey: precert.PublicKey, Version: precert.Version, SerialNumber: precert.SerialNumber, Issuer: precert.Issuer, Subject: precert.Subject, NotBefore: precert.NotBefore, NotAfter: precert.NotAfter, KeyUsage: precert.KeyUsage, BasicConstraintsValid: precert.BasicConstraintsValid, IsCA: precert.IsCA, MaxPathLen: precert.MaxPathLen, MaxPathLenZero: precert.MaxPathLenZero, PermittedDNSDomainsCritical: precert.PermittedDNSDomainsCritical, } if len(precert.Extensions) > 0 { tbsCert.ExtraExtensions = make([]pkix.Extension, len(precert.Extensions)) copy(tbsCert.ExtraExtensions, precert.Extensions) } // Remove the poison extension from ExtraExtensions tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions[:poisonIndex], tbsCert.ExtraExtensions[poisonIndex+1:]...) // Insert the SCT list extension tbsCert.ExtraExtensions = append(tbsCert.ExtraExtensions, sctExt) // Sign the tbsCert return s.sign(&tbsCert) } // Info return a populated info.Resp struct or an error. func (s *Signer) Info(req info.Req) (resp *info.Resp, err error) { cert, err := s.Certificate(req.Label, req.Profile) if err != nil { return } profile, err := signer.Profile(s, req.Profile) if err != nil { return } resp = new(info.Resp) if cert.Raw != nil { resp.Certificate = string(bytes.TrimSpace(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))) } resp.Usage = profile.Usage resp.ExpiryString = profile.ExpiryString return } // SigAlgo returns the RSA signer's signature algorithm. func (s *Signer) SigAlgo() x509.SignatureAlgorithm { return s.sigAlgo } // Certificate returns the signer's certificate. func (s *Signer) Certificate(label, profile string) (*x509.Certificate, error) { cert := *s.ca return &cert, nil } // SetPolicy sets the signer's signature policy. func (s *Signer) SetPolicy(policy *config.Signing) { s.policy = policy } // SetDBAccessor sets the signers' cert db accessor func (s *Signer) SetDBAccessor(dba certdb.Accessor) { s.dbAccessor = dba } // GetDBAccessor returns the signers' cert db accessor func (s *Signer) GetDBAccessor() certdb.Accessor { return s.dbAccessor } // SetReqModifier does nothing for local func (s *Signer) SetReqModifier(func(*http.Request, []byte)) { // noop } // Policy returns the signer's policy. func (s *Signer) Policy() *config.Signing { return s.policy }