func createCertificate(commonName string, final bool, parent *x509.Certificate, parentPKey *rsa.PrivateKey) ([]byte, *rsa.PrivateKey) { var keyLength = 2048 var sAlg = SHA256WithRSA pkey, err := rsa.GenerateKey(rand.Reader, keyLength) mustNoErr(err) template := x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), IsCA: !final, NotBefore: earlyNotBefore, NotAfter: earlyNotAfter, Subject: pkix.Name{ CommonName: commonName, }, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, } if final { if ip := net.ParseIP(commonName); ip != nil { template.IPAddresses = []net.IP{ip} } } template.KeyUsage = x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature if !final { template.KeyUsage = template.KeyUsage | x509.KeyUsageCertSign } if parent != nil { template.SignatureAlgorithm = parent.SignatureAlgorithm } else { template.SignatureAlgorithm = sAlg } signer := &template publicKey := &pkey.PublicKey privateKey := pkey if parent != nil { if parentPKey == nil { panic("parentPKey should be supplied") } signer = parent privateKey = parentPKey } certDer, err := x509.CreateCertificate(rand.Reader, &template, signer, publicKey, privateKey) mustNoErr(err) return certDer, pkey }
// 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 } serialSeq := "" if profile.UseSerialSeq { serialSeq = req.SerialSeq } 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 } } OverrideHosts(&safeTemplate, req.Hosts) safeTemplate.Subject = PopulateSubjectFromCSR(req.Subject, safeTemplate.Subject) return s.sign(&safeTemplate, profile, serialSeq) }
// 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 } } 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) } } } if profile.ClientProvidesSerialNumbers { if req.Serial == nil { fmt.Printf("xx %#v\n", profile) return nil, cferr.New(cferr.CertificateError, cferr.MissingSerial) } safeTemplate.SerialNumber = req.Serial } else { serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) } safeTemplate.SerialNumber = 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) } return s.sign(&certTBS, profile) }
// 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 } } 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) } } } if profile.ClientProvidesSerialNumbers { if req.Serial == nil { fmt.Printf("xx %#v\n", profile) 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.db != nil { var certRecord = &certdb.CertificateRecord{ Serial: certTBS.SerialNumber.String(), CALabel: req.Label, Status: "good", Expiry: certTBS.NotAfter, PEM: string(signedCert), } err = certdb.InsertCertificate(s.db, certRecord) if err != nil { return nil, err } log.Debug("saved certificate with serial number ", certTBS.SerialNumber) } return signedCert, nil }
func main() { var genereateLeaf bool var commonName string var useSha1 bool flag.StringVar(&commonName, "common-name", "*", "common name field of certificate (hostname)") flag.BoolVar(&genereateLeaf, "generate-leaf", false, "whether to generate leaf certificat (passing ca cert and pkey via environment variables)") flag.BoolVar(&useSha1, "use-sha1", false, "whether to use sha1 instead of default sha256 signature algorithm") flag.Parse() if genereateLeaf { cacertPEM := os.Getenv("CACERT") certBlock, rest := pem.Decode(([]byte)(cacertPEM)) if (string)(rest) != "" || certBlock == nil || certBlock.Type != "CERTIFICATE" { log.Fatal("garbage CACERT environment variable") } capkeyPEM := os.Getenv("CAPKEY") pkeyBlock, rest := pem.Decode(([]byte)(capkeyPEM)) // TODO: support EC keys too, which might be useful sometimes if (string)(rest) != "" || pkeyBlock == nil || pkeyBlock.Type != "RSA PRIVATE KEY" { log.Fatal("garbage CAPKEY environment variable") } caCert, err := x509.ParseCertificate(certBlock.Bytes) mustNoErr(err) pkey := derToPKey(pkeyBlock.Bytes) leafPKey, err := rsa.GenerateKey(rand.Reader, keyLength) mustNoErr(err) leafTemplate := x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), NotBefore: earlyNotBefore, NotAfter: earlyNotAfter, Subject: pkix.Name{ CommonName: commonName, }, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, SignatureAlgorithm: caCert.SignatureAlgorithm, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, } if ip := net.ParseIP(commonName); ip != nil { leafTemplate.IPAddresses = []net.IP{ip} } certDer, err := x509.CreateCertificate(rand.Reader, &leafTemplate, caCert, &leafPKey.PublicKey, pkey) mustNoErr(err) pemIfy(certDer, "CERTIFICATE", os.Stdout) pemIfy(x509.MarshalPKCS1PrivateKey(leafPKey), "RSA PRIVATE KEY", os.Stdout) } else { pkey, err := rsa.GenerateKey(rand.Reader, keyLength) mustNoErr(err) commonName = fmt.Sprintf("Couchbase Server %08x", crc32.ChecksumIEEE(pkey.N.Bytes())) template := x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), IsCA: true, NotBefore: earlyNotBefore, NotAfter: earlyNotAfter, Subject: pkix.Name{ CommonName: commonName, }, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, } if useSha1 { template.SignatureAlgorithm = x509.SHA1WithRSA } certDer, err := x509.CreateCertificate(rand.Reader, &template, &template, &pkey.PublicKey, pkey) mustNoErr(err) pemIfy(certDer, "CERTIFICATE", os.Stdout) pemIfy(x509.MarshalPKCS1PrivateKey(pkey), "RSA PRIVATE KEY", os.Stdout) } }
func GenerateCertifcate(pkiroot, name string, template *x509.Certificate) error { // TODO(jclerc): check that pki has been init var crtPath string privateKeyPath := filepath.Join(pkiroot, "private", name+".key") if name == "ca" { crtPath = filepath.Join(pkiroot, name+".crt") } else { crtPath = filepath.Join(pkiroot, "issued", name+".crt") } var caCrt *x509.Certificate var caKey *rsa.PrivateKey if _, err := os.Stat(privateKeyPath); err == nil { return fmt.Errorf("a key pair for %v already exists", name) } privateKey, err := GeneratePrivateKey(privateKeyPath) if err != nil { return fmt.Errorf("generate private key: %v", err) } publicKeyBytes, err := asn1.Marshal(*privateKey.Public().(*rsa.PublicKey)) if err != nil { return fmt.Errorf("marshal public key: %v", err) } subjectKeyId := sha1.Sum(publicKeyBytes) template.SubjectKeyId = subjectKeyId[:] template.NotBefore = time.Now() template.SignatureAlgorithm = x509.SHA256WithRSA if template.IsCA { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return fmt.Errorf("failed to generate ca serial number: %s", err) } template.SerialNumber = serialNumber template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageCRLSign template.BasicConstraintsValid = true template.Issuer = template.Subject template.AuthorityKeyId = template.SubjectKeyId caCrt = template caKey = privateKey } else { template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment serialNumber, err := NextNumber(pkiroot, "serial") if err != nil { return fmt.Errorf("get next serial: %v", err) } template.SerialNumber = serialNumber caCrt, caKey, err = GetCA(pkiroot) if err != nil { return fmt.Errorf("get ca: %v", err) } } crt, err := x509.CreateCertificate(rand.Reader, template, caCrt, privateKey.Public(), caKey) if err != nil { return fmt.Errorf("create certificate: %v", err) } crtFile, err := os.Create(crtPath) if err != nil { return fmt.Errorf("create %v: %v", crtPath, err) } defer crtFile.Close() err = pem.Encode(crtFile, &pem.Block{ Type: "CERTIFICATE", Bytes: crt, }) if err != nil { return fmt.Errorf("pem encode crt: %v", err) } // I do not think we have to write the ca.crt in the index if !template.IsCA { WriteIndex(pkiroot, name, template) if err != nil { return fmt.Errorf("write index: %v", err) } } return nil }
// 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 } } 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) } } } if profile.ClientProvidesSerialNumbers { if req.Serial == nil { fmt.Printf("xx %#v\n", profile) return nil, cferr.New(cferr.CertificateError, cferr.MissingSerial) } safeTemplate.SerialNumber = req.Serial } else { serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64)) if err != nil { return nil, cferr.Wrap(cferr.CertificateError, cferr.Unknown, err) } safeTemplate.SerialNumber = serialNumber } return s.sign(&safeTemplate, profile) }