func TestStoreResponseGuard(t *testing.T) { updater, sa, _, _, cleanUp := setup(t) defer cleanUp() reg := satest.CreateWorkingRegistration(t, sa) parsedCert, err := core.LoadCert("test-cert.pem") test.AssertNotError(t, err, "Couldn't read test certificate") _, err = sa.AddCertificate(parsedCert.Raw, reg.ID) test.AssertNotError(t, err, "Couldn't add www.eff.org.der") status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") status.OCSPResponse = []byte{0} err = updater.storeResponse(&status, core.OCSPStatusRevoked) test.AssertNotError(t, err, "Failed to update certificate status") unchangedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") test.AssertEquals(t, len(unchangedStatus.OCSPResponse), 0) err = updater.storeResponse(&status, core.OCSPStatusGood) test.AssertNotError(t, err, "Failed to updated certificate status") changedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") test.AssertEquals(t, len(changedStatus.OCSPResponse), 1) }
func checkDER(sai core.StorageAuthority, der []byte) error { cert, err := x509.ParseCertificate(der) if err != nil { return fmt.Errorf("Failed to parse DER: %s", err) } _, err = sai.GetCertificate(core.SerialToString(cert.SerialNumber)) if err == nil { return fmt.Errorf("Existing certificate found with serial %s", core.SerialToString(cert.SerialNumber)) } if _, ok := err.(core.NotFoundError); ok { return nil } return fmt.Errorf("Existing certificate lookup failed: %s", err) }
// Response is called by the HTTP server to handle a new OCSP request. func (src *DBSource) Response(req *ocsp.Request) (response []byte, present bool) { log := blog.GetAuditLogger() // Check that this request is for the proper CA if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 { log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash))) present = false return } serialString := core.SerialToString(req.SerialNumber) log.Debug(fmt.Sprintf("Searching for OCSP issued by us for serial %s", serialString)) var ocspResponse core.OCSPResponse err := src.dbMap.SelectOne(&ocspResponse, "SELECT * from ocspResponses WHERE serial = :serial ORDER BY createdAt DESC LIMIT 1;", map[string]interface{}{"serial": serialString}) if err != nil { present = false return } log.Info(fmt.Sprintf("OCSP Response sent for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString)) response = ocspResponse.Response present = true return }
// RevokeCertificateWithReg terminates trust in the certificate provided. func (ra *RegistrationAuthorityImpl) RevokeCertificateWithReg(cert x509.Certificate, revocationCode core.RevocationCode, regID int64) (err error) { serialString := core.SerialToString(cert.SerialNumber) err = ra.SA.MarkCertificateRevoked(serialString, revocationCode) state := "Failure" defer func() { // AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134 // Needed: // Serial // CN // DNS names // Revocation reason // Registration ID of requester // Error (if there was one) ra.log.Audit(fmt.Sprintf( "%s, Request by registration ID: %d", revokeEvent(state, serialString, cert.Subject.CommonName, cert.DNSNames, revocationCode), regID, )) }() if err != nil { state = fmt.Sprintf("Failure -- %s", err) return err } state = "Success" return nil }
// Response is called by the HTTP server to handle a new OCSP request. func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) { // Check that this request is for the proper CA if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 { src.log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash))) return nil, false } serialString := core.SerialToString(req.SerialNumber) src.log.Debug(fmt.Sprintf("Searching for OCSP issued by us for serial %s", serialString)) var response []byte defer func() { if len(response) != 0 { src.log.Info(fmt.Sprintf("OCSP Response sent for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString)) } }() err := src.dbMap.SelectOne( &response, "SELECT ocspResponse FROM certificateStatus WHERE serial = :serial", map[string]interface{}{"serial": serialString}, ) if err != nil && err != sql.ErrNoRows { src.log.Err(fmt.Sprintf("Failed to retrieve response from certificateStatus table: %s", err)) } if err != nil { return nil, false } return response, true }
// GenerateOCSP produces a new OCSP response and returns it func (ca *CertificateAuthorityImpl) GenerateOCSP(ctx context.Context, xferObj core.OCSPSigningRequest) ([]byte, error) { cert, err := x509.ParseCertificate(xferObj.CertDER) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.AuditErr(err) return nil, err } signRequest := ocsp.SignRequest{ Certificate: cert, Status: xferObj.Status, Reason: int(xferObj.Reason), RevokedAt: xferObj.RevokedAt, } cn := cert.Issuer.CommonName issuer := ca.issuers[cn] if issuer == nil { return nil, fmt.Errorf("This CA doesn't have an issuer cert with CommonName %q", cn) } err = cert.CheckSignatureFrom(issuer.cert) if err != nil { return nil, fmt.Errorf("GenerateOCSP was asked to sign OCSP for cert "+ "%s from %q, but the cert's signature was not valid: %s.", core.SerialToString(cert.SerialNumber), cn, err) } ocspResponse, err := issuer.ocspSigner.Sign(signRequest) ca.noteSignError(err) return ocspResponse, err }
// SubmitToCT will submit the certificate represented by certDER to any CT // logs configured in pub.CT.Logs func (pub *PublisherImpl) SubmitToCT(der []byte) error { cert, err := x509.ParseCertificate(der) if err != nil { pub.log.Err(fmt.Sprintf("Unable to parse certificate, %s", err)) return err } submission := ctSubmissionRequest{Chain: []string{base64.StdEncoding.EncodeToString(cert.Raw)}} // Add all intermediate certificates needed for submission submission.Chain = append(submission.Chain, pub.issuerBundle...) jsonSubmission, err := json.Marshal(submission) if err != nil { pub.log.Err(fmt.Sprintf("Unable to marshal CT submission, %s", err)) return err } for _, ctLog := range pub.ctLogs { err = pub.submitToCTLog(core.SerialToString(cert.SerialNumber), jsonSubmission, ctLog) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(err) continue } } return nil }
// Response is called by the HTTP server to handle a new OCSP request. func (src *DBSource) Response(req *ocsp.Request) (response []byte, present bool) { log := blog.GetAuditLogger() // Check that this request is for the proper CA if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 { log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash))) present = false return } serialString := core.SerialToString(req.SerialNumber) log.Debug(fmt.Sprintf("Searching for OCSP issued by us for serial %s", serialString)) var ocspResponse core.OCSPResponse // Note: we order by id rather than createdAt, because otherwise we sometimes // get the wrong result if a certificate is revoked in the same second as its // last update (e.g. client issues and instant revokes). err := src.dbMap.SelectOne(&ocspResponse, "SELECT * from ocspResponses WHERE serial = :serial ORDER BY id DESC LIMIT 1;", map[string]interface{}{"serial": serialString}) if err != nil { present = false return } log.Info(fmt.Sprintf("OCSP Response sent for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString)) response = ocspResponse.Response present = true return }
// AdministrativelyRevokeCertificate terminates trust in the certificate provided and // does not require the registration ID of the requester since this method is only // called from the admin-revoker tool. func (ra *RegistrationAuthorityImpl) AdministrativelyRevokeCertificate(cert x509.Certificate, revocationCode core.RevocationCode, user string) error { serialString := core.SerialToString(cert.SerialNumber) err := ra.SA.MarkCertificateRevoked(serialString, revocationCode) state := "Failure" defer func() { // AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134 // Needed: // Serial // CN // DNS names // Revocation reason // Name of admin-revoker user // Error (if there was one) ra.log.Audit(fmt.Sprintf( "%s, admin-revoker user: %s", revokeEvent(state, serialString, cert.Subject.CommonName, cert.DNSNames, revocationCode), user, )) }() if err != nil { state = fmt.Sprintf("Failure -- %s", err) return err } state = "Success" ra.stats.Inc("RA.RevokedCertificates", 1, 1.0) return nil }
// AdministrativelyRevokeCertificate terminates trust in the certificate provided and // does not require the registration ID of the requester since this method is only // called from the admin-revoker tool. func (ra *RegistrationAuthorityImpl) AdministrativelyRevokeCertificate(ctx context.Context, cert x509.Certificate, revocationCode revocation.Reason, user string) error { serialString := core.SerialToString(cert.SerialNumber) err := ra.SA.MarkCertificateRevoked(ctx, serialString, revocationCode) state := "Failure" defer func() { // Needed: // Serial // CN // DNS names // Revocation reason // Name of admin-revoker user // Error (if there was one) ra.log.AuditInfo(fmt.Sprintf( "%s, admin-revoker user: %s", revokeEvent(state, serialString, cert.Subject.CommonName, cert.DNSNames, revocationCode), user, )) }() if err != nil { state = fmt.Sprintf("Failure -- %s", err) return err } state = "Success" ra.stats.Inc("RevokedCertificates", 1) return nil }
func BenchmarkCheckCert(b *testing.B) { dbMap, err := sa.NewDbMap(dbConnStr) if err != nil { fmt.Println("Couldn't connect to database") return } checker := newChecker(dbMap) testKey, _ := rsa.GenerateKey(rand.Reader, 1024) expiry := time.Now().AddDate(0, 0, 1) serial := big.NewInt(1337) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotAfter: expiry, DNSNames: []string{"example-a.com"}, SerialNumber: serial, } certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) cert := core.Certificate{ Status: core.StatusValid, Serial: core.SerialToString(serial), Digest: core.Fingerprint256(certDer), DER: certDer, Issued: time.Now(), Expires: expiry, } b.ResetTimer() for i := 0; i < b.N; i++ { checker.checkCert(cert) } }
func TestRevoke(t *testing.T) { cadb, storageAuthority, caConfig := setup(t) ca, err := NewCertificateAuthorityImpl(cadb, caConfig, caCertFile) test.AssertNotError(t, err, "Failed to create CA") if err != nil { return } ca.SA = storageAuthority ca.MaxKeySize = 4096 csrDER, _ := hex.DecodeString(CNandSANCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) certObj, err := ca.IssueCertificate(*csr, 1, FarFuture) test.AssertNotError(t, err, "Failed to sign certificate") if err != nil { return } cert, err := x509.ParseCertificate(certObj.DER) test.AssertNotError(t, err, "Certificate failed to parse") serialString := core.SerialToString(cert.SerialNumber) err = ca.RevokeCertificate(serialString, 0) test.AssertNotError(t, err, "Revocation failed") status, err := storageAuthority.GetCertificateStatus(serialString) test.AssertNotError(t, err, "Failed to get cert status") test.AssertEquals(t, status.Status, core.OCSPStatusRevoked) test.Assert(t, time.Now().Sub(status.OCSPLastUpdated) > time.Second, fmt.Sprintf("OCSP LastUpdated was wrong: %v", status.OCSPLastUpdated)) }
func TestRevoke(t *testing.T) { ctx := setup(t) defer ctx.cleanUp() ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, caCertFile) test.AssertNotError(t, err, "Failed to create CA") if err != nil { return } ca.SA = ctx.sa ca.MaxKeySize = 4096 csrDER, _ := hex.DecodeString(CNandSANCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) certObj, err := ca.IssueCertificate(*csr, ctx.reg.ID, FarFuture) test.AssertNotError(t, err, "Failed to sign certificate") if err != nil { return } cert, err := x509.ParseCertificate(certObj.DER) test.AssertNotError(t, err, "Certificate failed to parse") serialString := core.SerialToString(cert.SerialNumber) err = ca.RevokeCertificate(serialString, 0) test.AssertNotError(t, err, "Revocation failed") status, err := ctx.sa.GetCertificateStatus(serialString) test.AssertNotError(t, err, "Failed to get cert status") test.AssertEquals(t, status.Status, core.OCSPStatusRevoked) secondAgo := time.Now().Add(-time.Second) test.Assert(t, status.OCSPLastUpdated.After(secondAgo), fmt.Sprintf("OCSP LastUpdated was more than a second old: %v", status.OCSPLastUpdated)) }
func TestGenerateAndStoreOCSPResponse(t *testing.T) { updater, sa, _, _, cleanUp := setup(t) defer cleanUp() reg := satest.CreateWorkingRegistration(t, sa) parsedCert, err := core.LoadCert("test-cert.pem") test.AssertNotError(t, err, "Couldn't read test certificate") _, err = sa.AddCertificate(parsedCert.Raw, reg.ID) test.AssertNotError(t, err, "Couldn't add www.eff.org.der") status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Couldn't get the core.CertificateStatus from the database") meta, err := updater.generateResponse(status) test.AssertNotError(t, err, "Couldn't generate OCSP response") err = updater.storeResponse(meta) test.AssertNotError(t, err, "Couldn't store certificate status") secondMeta, err := updater.generateRevokedResponse(status) test.AssertNotError(t, err, "Couldn't generate revoked OCSP response") err = updater.storeResponse(secondMeta) test.AssertNotError(t, err, "Couldn't store certificate status") newStatus, err := sa.GetCertificateStatus(status.Serial) test.AssertNotError(t, err, "Couldn't retrieve certificate status") test.AssertByteEquals(t, meta.OCSPResponse, newStatus.OCSPResponse) }
func TestFindStaleOCSPResponses(t *testing.T) { updater, sa, _, fc, cleanUp := setup(t) defer cleanUp() reg := satest.CreateWorkingRegistration(t, sa) parsedCert, err := core.LoadCert("test-cert.pem") test.AssertNotError(t, err, "Couldn't read test certificate") _, err = sa.AddCertificate(parsedCert.Raw, reg.ID) test.AssertNotError(t, err, "Couldn't add www.eff.org.der") earliest := fc.Now().Add(-time.Hour) certs, err := updater.findStaleOCSPResponses(earliest, 10) test.AssertNotError(t, err, "Couldn't find certificate") test.AssertEquals(t, len(certs), 1) status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Couldn't get the core.Certificate from the database") meta, err := updater.generateResponse(status) test.AssertNotError(t, err, "Couldn't generate OCSP response") err = updater.storeResponse(meta) test.AssertNotError(t, err, "Couldn't store OCSP response") certs, err = updater.findStaleOCSPResponses(earliest, 10) test.AssertNotError(t, err, "Failed to find stale responses") test.AssertEquals(t, len(certs), 0) }
func TestCheckCert(t *testing.T) { testKey, _ := rsa.GenerateKey(rand.Reader, 1024) checker := newChecker(nil) fc := clock.NewFake() fc.Add(time.Hour * 24 * 90) checker.clock = fc issued := checker.clock.Now().Add(-time.Hour * 24 * 45) goodExpiry := issued.Add(checkPeriod) serial := big.NewInt(1337) // Problems // Blacklsited common name // Expiry period is too long // Basic Constraints aren't set // Wrong key usage (none) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotAfter: goodExpiry.AddDate(0, 0, 1), // Period too long DNSNames: []string{"example-a.com"}, SerialNumber: serial, BasicConstraintsValid: false, } brokenCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") // Problems // Digest doesn't match // Serial doesn't match // Expiry doesn't match cert := core.Certificate{ Status: core.StatusValid, DER: brokenCertDer, Issued: issued, Expires: goodExpiry.AddDate(0, 0, 2), // Expiration doesn't match } problems := checker.checkCert(cert) test.AssertEquals(t, len(problems), 7) // Fix the problems rawCert.Subject.CommonName = "example-a.com" rawCert.NotAfter = goodExpiry rawCert.BasicConstraintsValid = true rawCert.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} goodCertDer, err := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) test.AssertNotError(t, err, "Couldn't create certificate") parsed, err := x509.ParseCertificate(goodCertDer) test.AssertNotError(t, err, "Couldn't parse created certificate") cert.Serial = core.SerialToString(serial) cert.Digest = core.Fingerprint256(goodCertDer) cert.DER = goodCertDer cert.Expires = parsed.NotAfter problems = checker.checkCert(cert) test.AssertEquals(t, len(problems), 0) }
func TestNewCertificate(t *testing.T) { _, sa, ra, _, cleanUp := initAuthorities(t) defer cleanUp() AuthzFinal.RegistrationID = Registration.ID AuthzFinal, _ = sa.NewPendingAuthorization(AuthzFinal) sa.UpdatePendingAuthorization(AuthzFinal) sa.FinalizeAuthorization(AuthzFinal) // Inject another final authorization to cover www.example.com authzFinalWWW := AuthzFinal authzFinalWWW.Identifier.Value = "www.not-example.com" authzFinalWWW, _ = sa.NewPendingAuthorization(authzFinalWWW) sa.FinalizeAuthorization(authzFinalWWW) // Check that we fail if the CSR signature is invalid ExampleCSR.Signature[0] += 1 certRequest := core.CertificateRequest{ CSR: ExampleCSR, } _, err := ra.NewCertificate(certRequest, Registration.ID) ExampleCSR.Signature[0] -= 1 test.AssertError(t, err, "Failed to check CSR signature") // Check that we don't fail on case mismatches ExampleCSR.Subject.CommonName = "www.NOT-example.com" certRequest = core.CertificateRequest{ CSR: ExampleCSR, } cert, err := ra.NewCertificate(certRequest, Registration.ID) test.AssertNotError(t, err, "Failed to issue certificate") if err != nil { return } parsedCert, err := x509.ParseCertificate(cert.DER) test.AssertNotError(t, err, "Failed to parse certificate") if err != nil { return } // Verify that cert shows up and is as expected dbCert, err := sa.GetCertificate(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, fmt.Sprintf("Could not fetch certificate %032x from database", parsedCert.SerialNumber)) if err != nil { return } test.Assert(t, bytes.Compare(cert.DER, dbCert.DER) == 0, "Certificates differ") t.Log("DONE TestOnValidationUpdate") }
func (c *certChecker) checkCert(cert core.Certificate) (problems []string) { // Check digests match if cert.Digest != core.Fingerprint256(cert.DER) { problems = append(problems, "Stored digest doesn't match certificate digest") } // Parse certificate parsedCert, err := x509.ParseCertificate(cert.DER) if err != nil { problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err)) } else { // Check stored serial is correct if core.SerialToString(parsedCert.SerialNumber) != cert.Serial { problems = append(problems, "Stored serial doesn't match certificate serial") } // Check we have the right expiration time if !parsedCert.NotAfter.Equal(cert.Expires) { problems = append(problems, "Stored expiration doesn't match certificate NotAfter") } // Check basic constraints are set if !parsedCert.BasicConstraintsValid { problems = append(problems, "Certificate doesn't have basic constraints set") } // Check the cert isn't able to sign other certificates if parsedCert.IsCA { problems = append(problems, "Certificate can sign other certificates") } // Check the cert has the correct validity period validityPeriod := parsedCert.NotAfter.Sub(parsedCert.NotBefore) if validityPeriod > checkPeriod { problems = append(problems, fmt.Sprintf("Certificate has a validity period longer than %s", checkPeriod)) } else if validityPeriod < checkPeriod { problems = append(problems, fmt.Sprintf("Certificate has a validity period shorter than %s", checkPeriod)) } if parsedCert.NotBefore.Before(cert.Issued.Add(-6*time.Hour)) || parsedCert.NotBefore.After(cert.Issued.Add(6*time.Hour)) { problems = append(problems, "Stored issuance date is outside of 6 hour window of certificate NotBefore") } // Check that the PA is still willing to issue for each name in DNSNames + CommonName for _, name := range append(parsedCert.DNSNames, parsedCert.Subject.CommonName) { if err = c.pa.WillingToIssue(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name}); err != nil { problems = append(problems, fmt.Sprintf("Policy Authority isn't willing to issue for %s: %s", name, err)) } } // Check the cert has the correct key usage extensions if !reflect.DeepEqual(parsedCert.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}) { problems = append(problems, "Certificate has incorrect key usage extensions") } } return problems }
// RevokeCertificate terminates trust in the certificate provided. func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (err error) { serialString := core.SerialToString(cert.SerialNumber) err = ra.CA.RevokeCertificate(serialString, 0) // AUDIT[ Revocation Requests ] 4e85d791-09c0-4ab3-a837-d3d67e945134 if err != nil { ra.log.Audit(fmt.Sprintf("Revocation error - %s - %s", serialString, err)) return err } ra.log.Audit(fmt.Sprintf("Revocation - %s", serialString)) return err }
// SubmitToCT will submit the certificate represented by certDER to any CT // logs configured in pub.CT.Logs (AMQP RPC method). func (pub *Impl) SubmitToCT(ctx context.Context, der []byte) error { cert, err := x509.ParseCertificate(der) if err != nil { pub.log.AuditErr(fmt.Sprintf("Failed to parse certificate: %s", err)) return err } localCtx, cancel := context.WithTimeout(ctx, pub.submissionTimeout) defer cancel() chain := append([]ct.ASN1Cert{der}, pub.issuerBundle...) for _, ctLog := range pub.ctLogs { sct, err := ctLog.client.AddChainWithContext(localCtx, chain) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to submit certificate to CT log at %s: %s", ctLog.uri, err)) continue } err = ctLog.verifier.VerifySCTSignature(*sct, ct.LogEntry{ Leaf: ct.MerkleTreeLeaf{ LeafType: ct.TimestampedEntryLeafType, TimestampedEntry: ct.TimestampedEntry{ X509Entry: ct.ASN1Cert(der), EntryType: ct.X509LogEntryType, }, }, }) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to verify SCT receipt: %s", err)) continue } internalSCT, err := sctToInternal(sct, core.SerialToString(cert.SerialNumber)) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to convert SCT receipt: %s", err)) continue } err = pub.SA.AddSCTReceipt(localCtx, internalSCT) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 pub.log.AuditErr(fmt.Sprintf("Failed to store SCT receipt in database: %s", err)) continue } } return nil }
func addIssuedNames(tx execable, cert *x509.Certificate) error { var qmarks []string var values []interface{} for _, name := range cert.DNSNames { values = append(values, core.ReverseName(name), core.SerialToString(cert.SerialNumber), cert.NotBefore) qmarks = append(qmarks, "(?, ?, ?)") } query := `INSERT INTO issuedNames (reversedName, serial, notBefore) VALUES ` + strings.Join(qmarks, ", ") + `;` _, err := tx.Exec(query, values...) return err }
func TestStoreResponseGuard(t *testing.T) { updater, sa, _, _, cleanUp := setup(t) defer cleanUp() reg := satest.CreateWorkingRegistration(t, sa) parsedCert, err := core.LoadCert("test-cert.pem") test.AssertNotError(t, err, "Couldn't read test certificate") _, err = sa.AddCertificate(parsedCert.Raw, reg.ID) test.AssertNotError(t, err, "Couldn't add www.eff.org.der") status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") err = sa.MarkCertificateRevoked(core.SerialToString(parsedCert.SerialNumber), 0) test.AssertNotError(t, err, "Failed to revoked certificate") // Attempt to update OCSP response where status.Status is good but stored status // is revoked, this should fail silently status.OCSPResponse = []byte{0, 1, 1} err = updater.storeResponse(&status) test.AssertNotError(t, err, "Failed to update certificate status") // Make sure the OCSP response hasn't actually changed unchangedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") test.AssertEquals(t, len(unchangedStatus.OCSPResponse), 0) // Changing the status to the stored status should allow the update to occur status.Status = core.OCSPStatusRevoked err = updater.storeResponse(&status) test.AssertNotError(t, err, "Failed to updated certificate status") // Make sure the OCSP response has been updated changedStatus, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") test.AssertEquals(t, len(changedStatus.OCSPResponse), 3) }
func TestRevokedCertificatesTick(t *testing.T) { updater, sa, _, _, cleanUp := setup(t) defer cleanUp() reg := satest.CreateWorkingRegistration(t, sa) parsedCert, err := core.LoadCert("test-cert.pem") test.AssertNotError(t, err, "Couldn't read test certificate") _, err = sa.AddCertificate(parsedCert.Raw, reg.ID) test.AssertNotError(t, err, "Couldn't add www.eff.org.der") err = sa.MarkCertificateRevoked(core.SerialToString(parsedCert.SerialNumber), core.RevocationCode(1)) test.AssertNotError(t, err, "Failed to revoke certificate") statuses, err := updater.findRevokedCertificatesToUpdate(10) test.AssertNotError(t, err, "Failed to find revoked certificates") test.AssertEquals(t, len(statuses), 1) updater.revokedCertificatesTick(10) status, err := sa.GetCertificateStatus(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, "Failed to get certificate status") test.AssertEquals(t, status.Status, core.OCSPStatusRevoked) test.Assert(t, len(status.OCSPResponse) != 0, "Certificate status doesn't contain OCSP response") }
func checkDER(sai certificateStorage, der []byte) error { ctx := context.Background() cert, err := x509.ParseCertificate(der) if err != nil { return fmt.Errorf("Failed to parse DER: %s", err) } _, err = sai.GetCertificate(ctx, core.SerialToString(cert.SerialNumber)) if err == nil { return errAlreadyExists } if _, ok := err.(core.NotFoundError); ok { return nil } return fmt.Errorf("Existing certificate lookup failed: %s", err) }
// AddCertificate stores an issued certificate. func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (digest string, err error) { var parsedCertificate *x509.Certificate parsedCertificate, err = x509.ParseCertificate(certDER) if err != nil { return } digest = core.Fingerprint256(certDER) serial := core.SerialToString(parsedCertificate.SerialNumber) cert := &core.Certificate{ RegistrationID: regID, Serial: serial, Digest: digest, DER: certDER, Issued: ssa.clk.Now(), Expires: parsedCertificate.NotAfter, } certStatus := &core.CertificateStatus{ SubscriberApproved: false, Status: core.OCSPStatus("good"), OCSPLastUpdated: time.Time{}, Serial: serial, RevokedDate: time.Time{}, RevokedReason: 0, LockCol: 0, } tx, err := ssa.dbMap.Begin() if err != nil { return } // TODO Verify that the serial number doesn't yet exist err = tx.Insert(cert) if err != nil { tx.Rollback() return } err = tx.Insert(certStatus) if err != nil { tx.Rollback() return } err = tx.Commit() return }
// Response is called by the HTTP server to handle a new OCSP request. func (src *DBSource) Response(req *ocsp.Request) ([]byte, bool) { // Check that this request is for the proper CA if bytes.Compare(req.IssuerKeyHash, src.caKeyHash) != 0 { src.log.Debug(fmt.Sprintf("Request intended for CA Cert ID: %s", hex.EncodeToString(req.IssuerKeyHash))) return nil, false } serialString := core.SerialToString(req.SerialNumber) src.log.Debug(fmt.Sprintf("Searching for OCSP issued by us for serial %s", serialString)) var response []byte defer func() { if len(response) != 0 { src.log.Info(fmt.Sprintf("OCSP Response sent for CA=%s, Serial=%s", hex.EncodeToString(src.caKeyHash), serialString)) } }() // Note: we first check for an OCSP response in the certificateStatus table ( // the new method) if we don't find a response there we instead look in the // ocspResponses table (the old method) while transitioning between the two // tables. err := src.dbMap.SelectOne( &response, "SELECT ocspResponse FROM certificateStatus WHERE serial = :serial", map[string]interface{}{"serial": serialString}, ) if err != nil && err != sql.ErrNoRows { src.log.Err(fmt.Sprintf("Failed to retrieve response from certificateStatus table: %s", err)) } // TODO(#970): Delete this ocspResponses check once the table has been removed if len(response) == 0 { // Ignoring possible error, if response hasn't been filled, attempt to find // response in old table err = src.dbMap.SelectOne( &response, "SELECT response from ocspResponses WHERE serial = :serial ORDER BY id DESC LIMIT 1;", map[string]interface{}{"serial": serialString}, ) if err != nil && err != sql.ErrNoRows { src.log.Err(fmt.Sprintf("Failed to retrieve response from ocspResponses table: %s", err)) } } if err != nil { return nil, false } return response, true }
func TestIssueCertificate(t *testing.T) { testCtx := setup(t) ca, err := NewCertificateAuthorityImpl( testCtx.caConfig, testCtx.fc, testCtx.stats, testCtx.issuers, testCtx.keyPolicy, testCtx.logger) test.AssertNotError(t, err, "Failed to create CA") ca.forceCNFromSAN = false ca.Publisher = &mocks.Publisher{} ca.PA = testCtx.pa sa := &mockSA{} ca.SA = sa csr, _ := x509.ParseCertificateRequest(CNandSANCSR) // Sign CSR issuedCert, err := ca.IssueCertificate(ctx, *csr, 1001) test.AssertNotError(t, err, "Failed to sign certificate") // Verify cert contents cert, err := x509.ParseCertificate(issuedCert.DER) test.AssertNotError(t, err, "Certificate failed to parse") test.AssertEquals(t, cert.Subject.CommonName, "not-example.com") if len(cert.DNSNames) == 1 { if cert.DNSNames[0] != "not-example.com" { t.Errorf("Improper list of domain names %v", cert.DNSNames) } else { } t.Errorf("Improper list of domain names %v", cert.DNSNames) } if len(cert.Subject.Country) > 0 { t.Errorf("Subject contained unauthorized values: %v", cert.Subject) } // Verify that the cert got stored in the DB serialString := core.SerialToString(cert.SerialNumber) if cert.Subject.SerialNumber != serialString { t.Errorf("SerialNumber: want %#v, got %#v", serialString, cert.Subject.SerialNumber) } test.Assert(t, bytes.Equal(issuedCert.DER, sa.certificate.DER), "Retrieved cert not equal to issued cert.") }
func TestNewCertificate(t *testing.T) { _, _, sa, ra := initAuthorities(t) AuthzFinal.RegistrationID = 1 AuthzFinal, _ = sa.NewPendingAuthorization(AuthzFinal) sa.UpdatePendingAuthorization(AuthzFinal) sa.FinalizeAuthorization(AuthzFinal) // Inject another final authorization to cover www.example.com authzFinalWWW := AuthzFinal authzFinalWWW.Identifier.Value = "www.not-example.com" authzFinalWWW, _ = sa.NewPendingAuthorization(authzFinalWWW) sa.FinalizeAuthorization(authzFinalWWW) // Construct a cert request referencing the two authorizations url1, _ := url.Parse("http://doesnt.matter/" + AuthzFinal.ID) url2, _ := url.Parse("http://doesnt.matter/" + authzFinalWWW.ID) certRequest := core.CertificateRequest{ CSR: ExampleCSR, Authorizations: []core.AcmeURL{core.AcmeURL(*url1), core.AcmeURL(*url2)}, } cert, err := ra.NewCertificate(certRequest, 1) test.AssertNotError(t, err, "Failed to issue certificate") if err != nil { return } parsedCert, err := x509.ParseCertificate(cert.DER) test.AssertNotError(t, err, "Failed to parse certificate") if err != nil { return } // Verify that cert shows up and is as expected dbCert, err := sa.GetCertificate(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, fmt.Sprintf("Could not fetch certificate %032x from database", parsedCert.SerialNumber)) if err != nil { return } test.Assert(t, bytes.Compare(cert.DER, dbCert.DER) == 0, "Certificates differ") t.Log("DONE TestOnValidationUpdate") }
func BenchmarkCheckCert(b *testing.B) { saDbMap, err := sa.NewDbMap(vars.DBConnSA) if err != nil { fmt.Println("Couldn't connect to database") return } paDbMap, err := sa.NewDbMap(vars.DBConnPolicy) if err != nil { fmt.Println("Couldn't connect to database") return } defer func() { err = saDbMap.TruncateTables() fmt.Printf("Failed to truncate tables: %s\n", err) err = paDbMap.TruncateTables() fmt.Printf("Failed to truncate tables: %s\n", err) }() checker := newChecker(saDbMap, paDbMap, clock.Default(), false, nil) testKey, _ := rsa.GenerateKey(rand.Reader, 1024) expiry := time.Now().AddDate(0, 0, 1) serial := big.NewInt(1337) rawCert := x509.Certificate{ Subject: pkix.Name{ CommonName: "example.com", }, NotAfter: expiry, DNSNames: []string{"example-a.com"}, SerialNumber: serial, } certDer, _ := x509.CreateCertificate(rand.Reader, &rawCert, &rawCert, &testKey.PublicKey, testKey) cert := core.Certificate{ Serial: core.SerialToString(serial), Digest: core.Fingerprint256(certDer), DER: certDer, Issued: time.Now(), Expires: expiry, } b.ResetTimer() for i := 0; i < b.N; i++ { checker.checkCert(cert) } }
func TestNewCertificate(t *testing.T) { _, _, sa, ra, cleanUp := initAuthorities(t) defer cleanUp() AuthzFinal.RegistrationID = 1 AuthzFinal, _ = sa.NewPendingAuthorization(AuthzFinal) sa.UpdatePendingAuthorization(AuthzFinal) sa.FinalizeAuthorization(AuthzFinal) // Inject another final authorization to cover www.example.com authzFinalWWW := AuthzFinal authzFinalWWW.Identifier.Value = "www.not-example.com" authzFinalWWW, _ = sa.NewPendingAuthorization(authzFinalWWW) sa.FinalizeAuthorization(authzFinalWWW) certRequest := core.CertificateRequest{ CSR: ExampleCSR, } cert, err := ra.NewCertificate(certRequest, 1) test.AssertNotError(t, err, "Failed to issue certificate") if err != nil { return } parsedCert, err := x509.ParseCertificate(cert.DER) test.AssertNotError(t, err, "Failed to parse certificate") if err != nil { return } // Verify that cert shows up and is as expected dbCert, err := sa.GetCertificate(core.SerialToString(parsedCert.SerialNumber)) test.AssertNotError(t, err, fmt.Sprintf("Could not fetch certificate %032x from database", parsedCert.SerialNumber)) if err != nil { return } test.Assert(t, bytes.Compare(cert.DER, dbCert.DER) == 0, "Certificates differ") t.Log("DONE TestOnValidationUpdate") }