vendor: add cfssl dependency

This commit is contained in:
George Tankersley
2016-06-29 15:56:16 -07:00
parent 9e45f62fc3
commit 902b9faa6f
64 changed files with 16192 additions and 3 deletions

View File

@@ -0,0 +1,447 @@
// Package local implements certificate signature functionality for CFSSL.
package local
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"encoding/pem"
"errors"
"io"
"io/ioutil"
"math/big"
"net"
"net/mail"
"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"
)
// 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 := ioutil.ReadFile(caFile)
if err != nil {
return nil, err
}
log.Debug("Loading CA key: ", caKeyFile)
cakey, err := ioutil.ReadFile(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, profile *config.SigningProfile) (cert []byte, err error) {
err = signer.FillTemplate(template, s.policy.Default, profile)
if err != nil {
return
}
var initRoot bool
if s.ca == nil {
if !template.IsCA {
err = cferr.New(cferr.PolicyError, cferr.InvalidRequest)
return
}
template.DNSNames = nil
template.EmailAddresses = nil
s.ca = template
initRoot = true
template.MaxPathLen = signer.MaxPathLen
} else if template.IsCA {
template.MaxPathLen = 1
template.DNSNames = nil
template.EmailAddresses = nil
}
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, and DNSNames 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{}
}
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 {
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 != "CERTIFICATE REQUEST" {
return nil, cferr.Wrap(cferr.CSRError,
cferr.BadRequest, errors.New("not a certificate or 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
}
}
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.InvalidPolicy)
}
}
for _, name := range safeTemplate.DNSNames {
if profile.NameWhitelist.Find([]byte(name)) == nil {
return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
}
}
for _, name := range safeTemplate.EmailAddresses {
if profile.NameWhitelist.Find([]byte(name)) == nil {
return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
}
}
}
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 certTBS = safeTemplate
if len(profile.CTLogServers) > 0 {
// 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, profile)
if err != nil {
return
}
derCert, _ := pem.Decode(cert)
prechain := []ct.ASN1Cert{derCert.Bytes, s.ca.Raw}
var sctList []ct.SignedCertificateTimestamp
for _, server := range profile.CTLogServers {
log.Infof("submitting poisoned precertificate to %s", server)
var ctclient = client.New(server)
var resp *ct.SignedCertificateTimestamp
resp, err = ctclient.AddPreChain(prechain)
if err != nil {
return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err)
}
sctList = append(sctList, *resp)
}
var serializedSCTList []byte
serializedSCTList, err = 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, profile)
if err != nil {
return nil, err
}
if s.dbAccessor != nil {
var certRecord = certdb.CertificateRecord{
Serial: certTBS.SerialNumber.String(),
// this relies on the specific behavior of x509.CreateCertificate
// which updates certTBS AuthorityKeyId from the signer's SubjectKeyId
AKI: hex.EncodeToString(certTBS.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
}
func serializeSCTList(sctList []ct.SignedCertificateTimestamp) ([]byte, error) {
var buf bytes.Buffer
for _, sct := range sctList {
sct, err := ct.SerializeSCT(sct)
if err != nil {
return nil, err
}
binary.Write(&buf, binary.BigEndian, uint16(len(sct)))
buf.Write(sct)
}
var sctListLengthField = make([]byte, 2)
binary.BigEndian.PutUint16(sctListLengthField, uint16(buf.Len()))
return bytes.Join([][]byte{sctListLengthField, buf.Bytes()}, nil), nil
}
// 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
}
// Policy returns the signer's policy.
func (s *Signer) Policy() *config.Signing {
return s.policy
}

385
vendor/github.com/cloudflare/cfssl/signer/signer.go generated vendored Normal file
View File

@@ -0,0 +1,385 @@
// Package signer implements certificate signature functionality for CFSSL.
package signer
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"math/big"
"strings"
"time"
"github.com/cloudflare/cfssl/certdb"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/info"
)
// MaxPathLen is the default path length for a new CA certificate.
var MaxPathLen = 2
// Subject contains the information that should be used to override the
// subject information when signing a certificate.
type Subject struct {
CN string
Names []csr.Name `json:"names"`
SerialNumber string
}
// Extension represents a raw extension to be included in the certificate. The
// "value" field must be hex encoded.
type Extension struct {
ID config.OID `json:"id"`
Critical bool `json:"critical"`
Value string `json:"value"`
}
// SignRequest stores a signature request, which contains the hostname,
// the CSR, optional subject information, and the signature profile.
//
// Extensions provided in the signRequest are copied into the certificate, as
// long as they are in the ExtensionWhitelist for the signer's policy.
// Extensions requested in the CSR are ignored, except for those processed by
// ParseCertificateRequest (mainly subjectAltName).
type SignRequest struct {
Hosts []string `json:"hosts"`
Request string `json:"certificate_request"`
Subject *Subject `json:"subject,omitempty"`
Profile string `json:"profile"`
Label string `json:"label"`
Serial *big.Int `json:"serial,omitempty"`
Extensions []Extension `json:"extensions,omitempty"`
}
// appendIf appends to a if s is not an empty string.
func appendIf(s string, a *[]string) {
if s != "" {
*a = append(*a, s)
}
}
// Name returns the PKIX name for the subject.
func (s *Subject) Name() pkix.Name {
var name pkix.Name
name.CommonName = s.CN
for _, n := range s.Names {
appendIf(n.C, &name.Country)
appendIf(n.ST, &name.Province)
appendIf(n.L, &name.Locality)
appendIf(n.O, &name.Organization)
appendIf(n.OU, &name.OrganizationalUnit)
}
name.SerialNumber = s.SerialNumber
return name
}
// SplitHosts takes a comma-spearated list of hosts and returns a slice
// with the hosts split
func SplitHosts(hostList string) []string {
if hostList == "" {
return nil
}
return strings.Split(hostList, ",")
}
// A Signer contains a CA's certificate and private key for signing
// certificates, a Signing policy to refer to and a SignatureAlgorithm.
type Signer interface {
Info(info.Req) (*info.Resp, error)
Policy() *config.Signing
SetDBAccessor(certdb.Accessor)
SetPolicy(*config.Signing)
SigAlgo() x509.SignatureAlgorithm
Sign(req SignRequest) (cert []byte, err error)
}
// Profile gets the specific profile from the signer
func Profile(s Signer, profile string) (*config.SigningProfile, error) {
var p *config.SigningProfile
policy := s.Policy()
if policy != nil && policy.Profiles != nil && profile != "" {
p = policy.Profiles[profile]
}
if p == nil && policy != nil {
p = policy.Default
}
if p == nil {
return nil, cferr.Wrap(cferr.APIClientError, cferr.ClientHTTPError, errors.New("profile must not be nil"))
}
return p, nil
}
// DefaultSigAlgo returns an appropriate X.509 signature algorithm given
// the CA's private key.
func DefaultSigAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
pub := priv.Public()
switch pub := pub.(type) {
case *rsa.PublicKey:
keySize := pub.N.BitLen()
switch {
case keySize >= 4096:
return x509.SHA512WithRSA
case keySize >= 3072:
return x509.SHA384WithRSA
case keySize >= 2048:
return x509.SHA256WithRSA
default:
return x509.SHA1WithRSA
}
case *ecdsa.PublicKey:
switch pub.Curve {
case elliptic.P256():
return x509.ECDSAWithSHA256
case elliptic.P384():
return x509.ECDSAWithSHA384
case elliptic.P521():
return x509.ECDSAWithSHA512
default:
return x509.ECDSAWithSHA1
}
default:
return x509.UnknownSignatureAlgorithm
}
}
// ParseCertificateRequest takes an incoming certificate request and
// builds a certificate template from it.
func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certificate, err error) {
csr, err := x509.ParseCertificateRequest(csrBytes)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
return
}
err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
return
}
template = &x509.Certificate{
Subject: csr.Subject,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SignatureAlgorithm: s.SigAlgo(),
DNSNames: csr.DNSNames,
IPAddresses: csr.IPAddresses,
EmailAddresses: csr.EmailAddresses,
}
return
}
type subjectPublicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
SubjectPublicKey asn1.BitString
}
// ComputeSKI derives an SKI from the certificate's public key in a
// standard manner. This is done by computing the SHA-1 digest of the
// SubjectPublicKeyInfo component of the certificate.
func ComputeSKI(template *x509.Certificate) ([]byte, error) {
pub := template.PublicKey
encodedPub, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, err
}
var subPKI subjectPublicKeyInfo
_, err = asn1.Unmarshal(encodedPub, &subPKI)
if err != nil {
return nil, err
}
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes)
return pubHash[:], nil
}
// FillTemplate is a utility function that tries to load as much of
// the certificate template as possible from the profiles and current
// template. It fills in the key uses, expiration, revocation URLs
// and SKI.
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error {
ski, err := ComputeSKI(template)
var (
eku []x509.ExtKeyUsage
ku x509.KeyUsage
backdate time.Duration
expiry time.Duration
notBefore time.Time
notAfter time.Time
crlURL, ocspURL string
)
// The third value returned from Usages is a list of unknown key usages.
// This should be used when validating the profile at load, and isn't used
// here.
ku, eku, _ = profile.Usages()
if profile.IssuerURL == nil {
profile.IssuerURL = defaultProfile.IssuerURL
}
if ku == 0 && len(eku) == 0 {
return cferr.New(cferr.PolicyError, cferr.NoKeyUsages)
}
if expiry = profile.Expiry; expiry == 0 {
expiry = defaultProfile.Expiry
}
if crlURL = profile.CRL; crlURL == "" {
crlURL = defaultProfile.CRL
}
if ocspURL = profile.OCSP; ocspURL == "" {
ocspURL = defaultProfile.OCSP
}
if backdate = profile.Backdate; backdate == 0 {
backdate = -5 * time.Minute
} else {
backdate = -1 * profile.Backdate
}
if !profile.NotBefore.IsZero() {
notBefore = profile.NotBefore.UTC()
} else {
notBefore = time.Now().Round(time.Minute).Add(backdate).UTC()
}
if !profile.NotAfter.IsZero() {
notAfter = profile.NotAfter.UTC()
} else {
notAfter = notBefore.Add(expiry).UTC()
}
template.NotBefore = notBefore
template.NotAfter = notAfter
template.KeyUsage = ku
template.ExtKeyUsage = eku
template.BasicConstraintsValid = true
template.IsCA = profile.CA
template.SubjectKeyId = ski
if ocspURL != "" {
template.OCSPServer = []string{ocspURL}
}
if crlURL != "" {
template.CRLDistributionPoints = []string{crlURL}
}
if len(profile.IssuerURL) != 0 {
template.IssuingCertificateURL = profile.IssuerURL
}
if len(profile.Policies) != 0 {
err = addPolicies(template, profile.Policies)
if err != nil {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
}
}
if profile.OCSPNoCheck {
ocspNoCheckExtension := pkix.Extension{
Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5},
Critical: false,
Value: []byte{0x05, 0x00},
}
template.ExtraExtensions = append(template.ExtraExtensions, ocspNoCheckExtension)
}
return nil
}
type policyInformation struct {
PolicyIdentifier asn1.ObjectIdentifier
Qualifiers []interface{} `asn1:"tag:optional,omitempty"`
}
type cpsPolicyQualifier struct {
PolicyQualifierID asn1.ObjectIdentifier
Qualifier string `asn1:"tag:optional,ia5"`
}
type userNotice struct {
ExplicitText string `asn1:"tag:optional,utf8"`
}
type userNoticePolicyQualifier struct {
PolicyQualifierID asn1.ObjectIdentifier
Qualifier userNotice
}
var (
// Per https://tools.ietf.org/html/rfc3280.html#page-106, this represents:
// iso(1) identified-organization(3) dod(6) internet(1) security(5)
// mechanisms(5) pkix(7) id-qt(2) id-qt-cps(1)
iDQTCertificationPracticeStatement = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 1}
// iso(1) identified-organization(3) dod(6) internet(1) security(5)
// mechanisms(5) pkix(7) id-qt(2) id-qt-unotice(2)
iDQTUserNotice = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 2}
// CTPoisonOID is the object ID of the critical poison extension for precertificates
// https://tools.ietf.org/html/rfc6962#page-9
CTPoisonOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
// SCTListOID is the object ID for the Signed Certificate Timestamp certificate extension
// https://tools.ietf.org/html/rfc6962#page-14
SCTListOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
)
// addPolicies adds Certificate Policies and optional Policy Qualifiers to a
// certificate, based on the input config. Go's x509 library allows setting
// Certificate Policies easily, but does not support nested Policy Qualifiers
// under those policies. So we need to construct the ASN.1 structure ourselves.
func addPolicies(template *x509.Certificate, policies []config.CertificatePolicy) error {
asn1PolicyList := []policyInformation{}
for _, policy := range policies {
pi := policyInformation{
// The PolicyIdentifier is an OID assigned to a given issuer.
PolicyIdentifier: asn1.ObjectIdentifier(policy.ID),
}
for _, qualifier := range policy.Qualifiers {
switch qualifier.Type {
case "id-qt-unotice":
pi.Qualifiers = append(pi.Qualifiers,
userNoticePolicyQualifier{
PolicyQualifierID: iDQTUserNotice,
Qualifier: userNotice{
ExplicitText: qualifier.Value,
},
})
case "id-qt-cps":
pi.Qualifiers = append(pi.Qualifiers,
cpsPolicyQualifier{
PolicyQualifierID: iDQTCertificationPracticeStatement,
Qualifier: qualifier.Value,
})
default:
return errors.New("Invalid qualifier type in Policies " + qualifier.Type)
}
}
asn1PolicyList = append(asn1PolicyList, pi)
}
asn1Bytes, err := asn1.Marshal(asn1PolicyList)
if err != nil {
return err
}
template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
Id: asn1.ObjectIdentifier{2, 5, 29, 32},
Critical: false,
Value: asn1Bytes,
})
return nil
}