func prepDB() (db *sql.DB, err error) { db = testdb.SQLiteDB("../../certdb/testdb/certstore_development.db") expirationTime := time.Now().AddDate(1, 0, 0) var cert = &certdb.CertificateRecord{ Serial: "1", Expiry: expirationTime, PEM: "unexpired cert", } err = certdb.InsertCertificate(db, cert) if err != nil { return nil, err } return }
// 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 TestOCSPRefreshMain(t *testing.T) { db := testdb.SQLiteDB("../../certdb/testdb/certstore_development.db") certPEM, err := ioutil.ReadFile("../../ocsp/testdata/cert.pem") if err != nil { t.Fatal(err) } expirationTime := time.Now().AddDate(1, 0, 0) var cert = &certdb.CertificateRecord{ Serial: "1333308112180215502", // from cert.pem Expiry: expirationTime, PEM: string(certPEM), Status: "good", } err = certdb.InsertCertificate(db, cert) if err != nil { t.Fatal(err) } err = ocsprefreshMain([]string{}, cli.Config{ CAFile: "../../ocsp/testdata/ca.pem", ResponderFile: "../../ocsp/testdata/server.crt", ResponderKeyFile: "../../ocsp/testdata/server.key", DBConfigFile: "../testdata/db-config.json", Interval: helpers.OneDay, }) if err != nil { t.Fatal(err) } var records []*certdb.OCSPRecord records, err = certdb.GetUnexpiredOCSPs(db) if err != nil { t.Fatal("Failed to get OCSP responses") } if len(records) != 1 { t.Fatal("Expected one OCSP response") } var resp *ocsp.Response resp, err = ocsp.ParseResponse([]byte(records[0].Body), nil) if err != nil { t.Fatal("Failed to parse OCSP response") } if resp.Status != ocsp.Good { t.Fatal("Expected cert status 'good'") } err = certdb.RevokeCertificate(db, cert.Serial, ocsp.KeyCompromise) if err != nil { t.Fatal("Failed to revoke certificate") } err = ocsprefreshMain([]string{}, cli.Config{ CAFile: "../../ocsp/testdata/ca.pem", ResponderFile: "../../ocsp/testdata/server.crt", ResponderKeyFile: "../../ocsp/testdata/server.key", DBConfigFile: "../testdata/db-config.json", Interval: helpers.OneDay, }) if err != nil { t.Fatal(err) } records, err = certdb.GetUnexpiredOCSPs(db) if err != nil { t.Fatal("Failed to get OCSP responses") } if len(records) != 1 { t.Fatal("Expected one OCSP response") } resp, err = ocsp.ParseResponse([]byte(records[0].Body), nil) if err != nil { t.Fatal("Failed to parse OCSP response") } if resp.Status != ocsp.Revoked { t.Fatal("Expected cert status 'revoked'") } }