func TestNewRegistration(t *testing.T) { _, sa, ra, _, cleanUp := initAuthorities(t) defer cleanUp() mailto, _ := core.ParseAcmeURL("mailto:[email protected]") input := core.Registration{ Contact: []*core.AcmeURL{mailto}, Key: AccountKeyB, InitialIP: net.ParseIP("7.6.6.5"), } result, err := ra.NewRegistration(input) if err != nil { t.Fatalf("could not create new registration: %s", err) } test.Assert(t, core.KeyDigestEquals(result.Key, AccountKeyB), "Key didn't match") test.Assert(t, len(result.Contact) == 1, "Wrong number of contacts") test.Assert(t, mailto.String() == result.Contact[0].String(), "Contact didn't match") test.Assert(t, result.Agreement == "", "Agreement didn't default empty") reg, err := sa.GetRegistration(result.ID) test.AssertNotError(t, err, "Failed to retrieve registration") test.Assert(t, core.KeyDigestEquals(reg.Key, AccountKeyB), "Retrieved registration differed.") }
func TestNewRegistrationNoFieldOverwrite(t *testing.T) { _, _, _, ra := initAuthorities(t) mailto, _ := url.Parse("mailto:[email protected]") input := core.Registration{ ID: 23, Key: AccountKeyC, RecoveryToken: "RecoverMe", Contact: []core.AcmeURL{core.AcmeURL(*mailto)}, Agreement: "I agreed", } result, err := ra.NewRegistration(input) test.AssertNotError(t, err, "Could not create new registration") test.Assert(t, result.ID != 23, "ID shouldn't be set by user") // TODO: Enable this test case once we validate terms agreement. //test.Assert(t, result.Agreement != "I agreed", "Agreement shouldn't be set with invalid URL") test.Assert(t, result.RecoveryToken != "RecoverMe", "Recovery token shouldn't be set by user") result2, err := ra.UpdateRegistration(result, core.Registration{ ID: 33, Key: ShortKey, RecoveryToken: "RecoverMe2", }) test.AssertNotError(t, err, "Could not update registration") test.Assert(t, result2.ID != 33, "ID shouldn't be overwritten.") test.Assert(t, !core.KeyDigestEquals(result2.Key, ShortKey), "Key shouldn't be overwritten") test.Assert(t, result2.RecoveryToken != "RecoverMe2", "Recovery token shouldn't be overwritten by user") }
func TestNewRegistrationNoFieldOverwrite(t *testing.T) { _, _, _, ra, cleanUp := initAuthorities(t) defer cleanUp() mailto, _ := core.ParseAcmeURL("mailto:[email protected]") input := core.Registration{ ID: 23, Key: AccountKeyC, Contact: []*core.AcmeURL{mailto}, Agreement: "I agreed", } result, err := ra.NewRegistration(input) test.AssertNotError(t, err, "Could not create new registration") test.Assert(t, result.ID != 23, "ID shouldn't be set by user") // TODO: Enable this test case once we validate terms agreement. //test.Assert(t, result.Agreement != "I agreed", "Agreement shouldn't be set with invalid URL") id := result.ID result2, err := ra.UpdateRegistration(result, core.Registration{ ID: 33, Key: ShortKey, }) test.AssertNotError(t, err, "Could not update registration") test.Assert(t, result2.ID != 33, fmt.Sprintf("ID shouldn't be overwritten. expected %d, got %d", id, result2.ID)) test.Assert(t, !core.KeyDigestEquals(result2.Key, ShortKey), "Key shouldn't be overwritten") }
func TestAddRegistration(t *testing.T) { sa, clk, cleanUp := initSA(t) defer cleanUp() jwk := satest.GoodJWK() contact, err := core.ParseAcmeURL("mailto:[email protected]") if err != nil { t.Fatalf("unable to parse contact link: %s", err) } contacts := []*core.AcmeURL{contact} reg, err := sa.NewRegistration(core.Registration{ Key: jwk, Contact: contacts, InitialIP: net.ParseIP("43.34.43.34"), }) if err != nil { t.Fatalf("Couldn't create new registration: %s", err) } test.Assert(t, reg.ID != 0, "ID shouldn't be 0") test.AssertDeepEquals(t, reg.Contact, contacts) _, err = sa.GetRegistration(0) test.AssertError(t, err, "Registration object for ID 0 was returned") dbReg, err := sa.GetRegistration(reg.ID) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) expectedReg := core.Registration{ ID: reg.ID, Key: jwk, InitialIP: net.ParseIP("43.34.43.34"), CreatedAt: clk.Now(), } test.AssertEquals(t, dbReg.ID, expectedReg.ID) test.Assert(t, core.KeyDigestEquals(dbReg.Key, expectedReg.Key), "Stored key != expected") u, _ := core.ParseAcmeURL("test.com") newReg := core.Registration{ ID: reg.ID, Key: jwk, Contact: []*core.AcmeURL{u}, InitialIP: net.ParseIP("72.72.72.72"), Agreement: "yes", } err = sa.UpdateRegistration(newReg) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) dbReg, err = sa.GetRegistrationByKey(jwk) test.AssertNotError(t, err, "Couldn't get registration by key") test.AssertEquals(t, dbReg.ID, newReg.ID) test.AssertEquals(t, dbReg.Agreement, newReg.Agreement) var anotherJWK jose.JsonWebKey err = json.Unmarshal([]byte(anotherKey), &anotherJWK) test.AssertNotError(t, err, "couldn't unmarshal anotherJWK") _, err = sa.GetRegistrationByKey(anotherJWK) test.AssertError(t, err, "Registration object for invalid key was returned") }
func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) { var test1KeyPublic jose.JsonWebKey var test2KeyPublic jose.JsonWebKey test1KeyPublic.UnmarshalJSON([]byte(test1KeyPublicJSON)) test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON)) if core.KeyDigestEquals(jwk, test1KeyPublic) { return core.Registration{ID: 1, Key: jwk}, nil } if core.KeyDigestEquals(jwk, test2KeyPublic) { // No key found return core.Registration{ID: 2}, sql.ErrNoRows } // Return a fake registration return core.Registration{ID: 1, Agreement: "yup"}, nil }
// GetRegistrationByKey is a mock func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) { var test1KeyPublic jose.JsonWebKey var test2KeyPublic jose.JsonWebKey test1KeyPublic.UnmarshalJSON([]byte(test1KeyPublicJSON)) test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON)) if core.KeyDigestEquals(jwk, test1KeyPublic) { return core.Registration{ID: 1, Key: jwk, Agreement: agreementURL}, nil } if core.KeyDigestEquals(jwk, test2KeyPublic) { // No key found return core.Registration{ID: 2}, sql.ErrNoRows } // Return a fake registration. Make sure to fill the key field to avoid marshaling errors. return core.Registration{ID: 1, Key: test1KeyPublic, Agreement: agreementURL}, nil }
func main() { issuerFile := flag.String("issuer", "", "Issuer certificate (PEM)") responderFile := flag.String("responder", "", "OCSP responder certificate (DER)") targetFile := flag.String("target", "", "Certificate whose status is being reported (PEM)") pkcs11File := flag.String("pkcs11", "", pkcs11Usage) outFile := flag.String("out", "", "File to which the OCSP response will be written") thisUpdateString := flag.String("thisUpdate", "", "Time for ThisUpdate field, RFC3339 format (e.g. 2016-09-02T00:00:00Z)") nextUpdateString := flag.String("nextUpdate", "", "Time for NextUpdate field, RFC3339 format") status := flag.Int("status", 0, "Status for response (0 = good, 1 = revoked)") flag.Usage = func() { fmt.Fprint(os.Stderr, usage) flag.PrintDefaults() } flag.Parse() if len(*outFile) == 0 { cmd.FailOnError(fmt.Errorf("No output file provided"), "") } thisUpdate, err := time.Parse(time.RFC3339, *thisUpdateString) cmd.FailOnError(err, "Parsing thisUpdate flag") nextUpdate, err := time.Parse(time.RFC3339, *nextUpdateString) cmd.FailOnError(err, "Parsing nextUpdate flag") issuer, responder, target, pkcs11, err := readFiles(*issuerFile, *responderFile, *targetFile, *pkcs11File) cmd.FailOnError(err, "Failed to read files") // Instantiate the private key from PKCS11 priv, err := pkcs11key.New(pkcs11.Module, pkcs11.TokenLabel, pkcs11.PIN, pkcs11.PrivateKeyLabel) cmd.FailOnError(err, "Failed to load PKCS#11 key") // Populate the remaining fields in the template template := ocsp.Response{ SerialNumber: target.SerialNumber, Certificate: responder, Status: *status, ThisUpdate: thisUpdate, NextUpdate: nextUpdate, } if !core.KeyDigestEquals(responder.PublicKey, priv.Public()) { cmd.FailOnError(fmt.Errorf("PKCS#11 pubkey does not match pubkey "+ "in responder certificate"), "loading keys") } // Sign the OCSP response responseBytes, err := ocsp.CreateResponse(issuer, responder, template, priv) cmd.FailOnError(err, "Failed to sign OCSP response") _, err = ocsp.ParseResponse(responseBytes, nil) cmd.FailOnError(err, "Failed to parse signed response") responseBytesBase64 := base64.StdEncoding.EncodeToString(responseBytes) + "\n" // Write the OCSP response to stdout err = ioutil.WriteFile(*outFile, []byte(responseBytesBase64), 0666) cmd.FailOnError(err, "Failed to write output file") }
func TestNewRegistration(t *testing.T) { _, _, sa, ra := initAuthorities(t) mailto, _ := url.Parse("mailto:[email protected]") input := core.Registration{ Contact: []core.AcmeURL{core.AcmeURL(*mailto)}, Key: AccountKeyB, } result, err := ra.NewRegistration(input) test.AssertNotError(t, err, "Could not create new registration") test.Assert(t, core.KeyDigestEquals(result.Key, AccountKeyB), "Key didn't match") test.Assert(t, len(result.Contact) == 1, "Wrong number of contacts") test.Assert(t, mailto.String() == result.Contact[0].String(), "Contact didn't match") test.Assert(t, result.Agreement == "", "Agreement didn't default empty") test.Assert(t, result.RecoveryToken != "", "Recovery token not filled") reg, err := sa.GetRegistration(result.ID) test.AssertNotError(t, err, "Failed to retrieve registration") test.Assert(t, core.KeyDigestEquals(reg.Key, AccountKeyB), "Retrieved registration differed.") }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Refuse to update expired authorizations if base.Expires == nil || base.Expires.Before(ra.clk.Now()) { err = core.NotFoundError("Expired authorization") return } // Copy information over that the client is allowed to supply authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } authz.Challenges[challengeIndex].KeyAuthorization = response.KeyAuthorization // At this point, the challenge should be sane as a complete challenge if !authz.Challenges[challengeIndex].IsSane(true) { err = core.MalformedRequestError("Response does not complete challenge") return } // Store the updated version if err = ra.SA.UpdatePendingAuthorization(authz); err != nil { // This can pretty much only happen when the client corrupts the Challenge // data. err = core.MalformedRequestError("Challenge data was corrupted") return } ra.stats.Inc("RA.NewPendingAuthorizations", 1, 1.0) // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Reject the update if the challenge in question was created // with a different account key if !core.KeyDigestEquals(reg.Key, authz.Challenges[challengeIndex].AccountKey) { err = core.UnauthorizedError("Challenge cannot be updated with a different key") return } // Dispatch to the VA for service ra.VA.UpdateValidations(authz, challengeIndex) ra.stats.Inc("RA.UpdatedPendingAuthorizations", 1, 1.0) return }
func loadIssuer(issuerConfig cmd.IssuerConfig) (crypto.Signer, *x509.Certificate, error) { cert, err := core.LoadCert(issuerConfig.CertFile) if err != nil { return nil, nil, err } signer, err := loadSigner(issuerConfig) if err != nil { return nil, nil, err } if !core.KeyDigestEquals(signer.Public(), cert.PublicKey) { return nil, nil, fmt.Errorf("Issuer key did not match issuer cert %s", issuerConfig.CertFile) } return signer, cert, err }
func TestAddRegistration(t *testing.T) { sa := initSA(t) var jwk jose.JsonWebKey err := json.Unmarshal([]byte(theKey), &jwk) if err != nil { t.Errorf("JSON unmarshal error: %+v", err) return } reg, err := sa.NewRegistration(core.Registration{ Key: jwk, }) test.AssertNotError(t, err, "Couldn't create new registration") test.Assert(t, reg.ID != 0, "ID shouldn't be 0") _, err = sa.GetRegistration(0) test.AssertError(t, err, "Registration object for ID 0 was returned") dbReg, err := sa.GetRegistration(reg.ID) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) expectedReg := core.Registration{ ID: reg.ID, Key: jwk, } test.AssertEquals(t, dbReg.ID, expectedReg.ID) test.Assert(t, core.KeyDigestEquals(dbReg.Key, expectedReg.Key), "Stored key != expected") uu, err := url.Parse("test.com") u := core.AcmeURL(*uu) newReg := core.Registration{ID: reg.ID, Key: jwk, RecoveryToken: "RBNvo1WzZ4oRRq0W9", Contact: []core.AcmeURL{u}, Agreement: "yes"} err = sa.UpdateRegistration(newReg) test.AssertNotError(t, err, fmt.Sprintf("Couldn't get registration with ID %v", reg.ID)) dbReg, err = sa.GetRegistrationByKey(jwk) test.AssertNotError(t, err, "Couldn't get registration by key") test.AssertEquals(t, dbReg.ID, newReg.ID) test.AssertEquals(t, dbReg.RecoveryToken, newReg.RecoveryToken) test.AssertEquals(t, dbReg.Agreement, newReg.Agreement) jwk.KeyID = "bad" _, err = sa.GetRegistrationByKey(jwk) test.AssertError(t, err, "Registration object for invalid key was returned") }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Copy information over that the client is allowed to supply authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } authz.Challenges[challengeIndex] = authz.Challenges[challengeIndex].MergeResponse(response) // Store the updated version if err = ra.SA.UpdatePendingAuthorization(authz); err != nil { // This can pretty much only happen when the client corrupts the Challenge // data. err = core.MalformedRequestError("Challenge data was corrupted") return } // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Reject the update if the challenge in question was created // with a different account key if !core.KeyDigestEquals(reg.Key, authz.Challenges[challengeIndex].AccountKey) { err = core.UnauthorizedError("Challenge cannot be updated with a different key") return } // Dispatch to the VA for service ra.VA.UpdateValidations(authz, challengeIndex) return }
// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(ctx context.Context, req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { emptyCert := core.Certificate{} var logEventResult string // Assume the worst logEventResult = "error" // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: regID, RequestMethod: "online", RequestTime: ra.clk.Now(), } // No matter what, log the request defer func() { ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent) }() if regID <= 0 { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return emptyCert, err } registration, err := ra.SA.GetRegistration(ctx, regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Verify the CSR csr := req.CSR if err := csrlib.VerifyCSR(csr, ra.maxNames, &ra.keyPolicy, ra.PA, ra.forceCNFromSAN, regID); err != nil { err = core.MalformedRequestError(err.Error()) return emptyCert, err } logEvent.CommonName = csr.Subject.CommonName logEvent.Names = csr.DNSNames // Validate that authorization key is authorized for all domains names := make([]string, len(csr.DNSNames)) copy(names, csr.DNSNames) if len(names) == 0 { err = core.UnauthorizedError("CSR has no names in it") logEvent.Error = err.Error() return emptyCert, err } if core.KeyDigestEquals(csr.PublicKey, registration.Key) { err = core.MalformedRequestError("Certificate public key must be different than account key") return emptyCert, err } // Check rate limits before checking authorizations. If someone is unable to // issue a cert due to rate limiting, we don't want to tell them to go get the // necessary authorizations, only to later fail the rate limit check. err = ra.checkLimits(ctx, names, registration.ID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } err = ra.checkAuthorizations(ctx, names, ®istration) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Mark that we verified the CN and SANs logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"} // Create the certificate and log the result if cert, err = ra.CA.IssueCertificate(ctx, *csr, regID); err != nil { logEvent.Error = err.Error() return emptyCert, err } if ra.publisher != nil { go func() { // Since we don't want this method to be canceled if the parent context // expires, pass a background context to it and run it in a goroutine. _ = ra.publisher.SubmitToCT(context.Background(), cert.DER) }() } err = ra.MatchesCSR(cert, csr) if err != nil { logEvent.Error = err.Error() return emptyCert, err } parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { // InternalServerError because the certificate from the CA should be // parseable. err = core.InternalServerError(err.Error()) logEvent.Error = err.Error() return emptyCert, err } now := ra.clk.Now() logEvent.SerialNumber = core.SerialToString(parsedCertificate.SerialNumber) logEvent.CommonName = parsedCertificate.Subject.CommonName logEvent.NotBefore = parsedCertificate.NotBefore logEvent.NotAfter = parsedCertificate.NotAfter logEvent.ResponseTime = now logEventResult = "successful" issuanceExpvar.Set(now.Unix()) ra.stats.Inc("NewCertificates", 1) return cert, nil }
// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { emptyCert := core.Certificate{} var logEventResult string // Assume the worst logEventResult = "error" // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: regID, RequestMethod: "online", RequestTime: time.Now(), } // No matter what, log the request defer func() { // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent) }() if regID <= 0 { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return emptyCert, err } registration, err := ra.SA.GetRegistration(regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Verify the CSR csr := req.CSR if err = core.VerifyCSR(csr); err != nil { logEvent.Error = err.Error() err = core.UnauthorizedError("Invalid signature on CSR") return emptyCert, err } logEvent.CommonName = csr.Subject.CommonName logEvent.Names = csr.DNSNames // Validate that authorization key is authorized for all domains names := make([]string, len(csr.DNSNames)) copy(names, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } if len(names) == 0 { err = core.UnauthorizedError("CSR has no names in it") logEvent.Error = err.Error() return emptyCert, err } csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(names) if err != nil { logEvent.Error = err.Error() return emptyCert, err } if csrPreviousDenied { err = core.UnauthorizedError("CSR has already been revoked/denied") logEvent.Error = err.Error() return emptyCert, err } if core.KeyDigestEquals(csr.PublicKey, registration.Key) { err = core.MalformedRequestError("Certificate public key must be different than account key") return emptyCert, err } // Check that each requested name has a valid authorization now := time.Now() earliestExpiry := time.Date(2100, 01, 01, 0, 0, 0, 0, time.UTC) for _, name := range names { authz, err := ra.SA.GetLatestValidAuthorization(registration.ID, core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name}) if err != nil || authz.Expires.Before(now) { // unable to find a valid authorization or authz is expired err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name)) logEvent.Error = err.Error() return emptyCert, err } if authz.Expires.Before(earliestExpiry) { earliestExpiry = *authz.Expires } } // Mark that we verified the CN and SANs logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"} // Create the certificate and log the result if cert, err = ra.CA.IssueCertificate(*csr, regID, earliestExpiry); err != nil { // While this could be InternalServerError for certain conditions, most // of the failure reasons (such as GoodKey failing) are caused by malformed // requests. logEvent.Error = err.Error() err = core.MalformedRequestError("Certificate request was invalid") return emptyCert, err } err = cert.MatchesCSR(csr, earliestExpiry) if err != nil { logEvent.Error = err.Error() return emptyCert, err } parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { // InternalServerError because the certificate from the CA should be // parseable. err = core.InternalServerError(err.Error()) logEvent.Error = err.Error() return emptyCert, err } logEvent.SerialNumber = core.SerialToString(parsedCertificate.SerialNumber) logEvent.CommonName = parsedCertificate.Subject.CommonName logEvent.NotBefore = parsedCertificate.NotBefore logEvent.NotAfter = parsedCertificate.NotAfter logEvent.ResponseTime = time.Now() logEventResult = "successful" return cert, nil }
// RevokeCertificate is used by clients to request the revocation of a cert. func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) { logEvent := wfe.populateRequestEvent(request) defer wfe.logRequestDetails(&logEvent) // We don't ask verifyPOST to verify there is a correponding registration, // because anyone with the right private key can revoke a certificate. body, requestKey, registration, err := wfe.verifyPOST(request, false, core.ResourceRevokeCert) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, malformedJWS, err, http.StatusBadRequest) return } logEvent.Requester = registration.ID logEvent.Contacts = registration.Contact type RevokeRequest struct { CertificateDER core.JSONBuffer `json:"certificate"` } var revokeRequest RevokeRequest if err = json.Unmarshal(body, &revokeRequest); err != nil { logEvent.Error = err.Error() wfe.log.Debug(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body))) wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER) if err != nil { logEvent.Error = err.Error() wfe.log.Debug("Couldn't parse cert in revoke request.") wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } serial := core.SerialToString(providedCert.SerialNumber) logEvent.Extra["ProvidedCertificateSerial"] = serial cert, err := wfe.SA.GetCertificate(serial) if err != nil || !bytes.Equal(cert.DER, revokeRequest.CertificateDER) { wfe.sendError(response, "No such certificate", err, http.StatusNotFound) return } parsedCertificate, err := x509.ParseCertificate(cert.DER) if err != nil { logEvent.Error = err.Error() // InternalServerError because this is a failure to decode from our DB. wfe.sendError(response, "Invalid certificate", err, http.StatusInternalServerError) return } logEvent.Extra["RetrievedCertificateSerial"] = core.SerialToString(parsedCertificate.SerialNumber) logEvent.Extra["RetrievedCertificateDNSNames"] = parsedCertificate.DNSNames logEvent.Extra["RetrievedCertificateEmailAddresses"] = parsedCertificate.EmailAddresses logEvent.Extra["RetrievedCertificateIPAddresses"] = parsedCertificate.IPAddresses certStatus, err := wfe.SA.GetCertificateStatus(serial) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Certificate status not yet available", err, http.StatusNotFound) return } logEvent.Extra["CertificateStatus"] = certStatus.Status if certStatus.Status == core.OCSPStatusRevoked { logEvent.Error = "Certificate already revoked" wfe.sendError(response, logEvent.Error, "", http.StatusConflict) return } // TODO: Implement method of revocation by authorizations on account. if !(core.KeyDigestEquals(requestKey, parsedCertificate.PublicKey) || registration.ID == cert.RegistrationID) { logEvent.Error = "Revocation request must be signed by private key of cert to be revoked" wfe.log.Debug("Key mismatch for revoke") wfe.sendError(response, logEvent.Error, requestKey, http.StatusForbidden) return } err = wfe.RA.RevokeCertificate(*parsedCertificate) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Failed to revoke certificate", err, statusCodeFromError(err)) } else { wfe.log.Debug(fmt.Sprintf("Revoked %v", serial)) // incr revoked cert stat wfe.Stats.Inc("RevokedCertificates", 1, 1.0) response.WriteHeader(http.StatusOK) } }
// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { emptyCert := core.Certificate{} var logEventResult string // Assume the worst logEventResult = "error" // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: regID, RequestMethod: "online", RequestTime: ra.clk.Now(), } // No matter what, log the request defer func() { // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent) }() if regID <= 0 { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) return emptyCert, err } registration, err := ra.SA.GetRegistration(regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Verify the CSR csr := req.CSR if err = core.VerifyCSR(csr); err != nil { logEvent.Error = err.Error() err = core.UnauthorizedError("Invalid signature on CSR") return emptyCert, err } logEvent.CommonName = csr.Subject.CommonName logEvent.Names = csr.DNSNames // Validate that authorization key is authorized for all domains names := make([]string, len(csr.DNSNames)) copy(names, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } if len(names) == 0 { err = core.UnauthorizedError("CSR has no names in it") logEvent.Error = err.Error() return emptyCert, err } csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(names) if err != nil { logEvent.Error = err.Error() return emptyCert, err } if csrPreviousDenied { err = core.UnauthorizedError("CSR has already been revoked/denied") logEvent.Error = err.Error() return emptyCert, err } if core.KeyDigestEquals(csr.PublicKey, registration.Key) { err = core.MalformedRequestError("Certificate public key must be different than account key") return emptyCert, err } // Check rate limits before checking authorizations. If someone is unable to // issue a cert due to rate limiting, we don't want to tell them to go get the // necessary authorizations, only to later fail the rate limit check. err = ra.checkLimits(names, registration.ID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } err = ra.checkAuthorizations(names, ®istration) if err != nil { logEvent.Error = err.Error() return emptyCert, err } // Mark that we verified the CN and SANs logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"} // Create the certificate and log the result if cert, err = ra.CA.IssueCertificate(*csr, regID); err != nil { logEvent.Error = err.Error() return emptyCert, err } err = ra.MatchesCSR(cert, csr) if err != nil { logEvent.Error = err.Error() return emptyCert, err } parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { // InternalServerError because the certificate from the CA should be // parseable. err = core.InternalServerError(err.Error()) logEvent.Error = err.Error() return emptyCert, err } logEvent.SerialNumber = core.SerialToString(parsedCertificate.SerialNumber) logEvent.CommonName = parsedCertificate.Subject.CommonName logEvent.NotBefore = parsedCertificate.NotBefore logEvent.NotAfter = parsedCertificate.NotAfter logEvent.ResponseTime = ra.clk.Now() logEventResult = "successful" ra.stats.Inc("RA.NewCertificates", 1, 1.0) return cert, nil }
// MatchesCSR tests the contents of a generated certificate to make sure // that the PublicKey, CommonName, and DNSNames match those provided in // the CSR that was used to generate the certificate. It also checks the // following fields for: // * notBefore is not more than 24 hours ago // * BasicConstraintsValid is true // * IsCA is false // * ExtKeyUsage only contains ExtKeyUsageServerAuth & ExtKeyUsageClientAuth // * Subject only contains CommonName & Names func (ra *RegistrationAuthorityImpl) MatchesCSR(cert core.Certificate, csr *x509.CertificateRequest) (err error) { parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { return } // Check issued certificate matches what was expected from the CSR hostNames := make([]string, len(csr.DNSNames)) copy(hostNames, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { hostNames = append(hostNames, csr.Subject.CommonName) } hostNames = core.UniqueLowerNames(hostNames) if !core.KeyDigestEquals(parsedCertificate.PublicKey, csr.PublicKey) { err = core.InternalServerError("Generated certificate public key doesn't match CSR public key") return } if len(csr.Subject.CommonName) > 0 && parsedCertificate.Subject.CommonName != strings.ToLower(csr.Subject.CommonName) { err = core.InternalServerError("Generated certificate CommonName doesn't match CSR CommonName") return } // Sort both slices of names before comparison. parsedNames := parsedCertificate.DNSNames sort.Strings(parsedNames) sort.Strings(hostNames) if !reflect.DeepEqual(parsedNames, hostNames) { err = core.InternalServerError("Generated certificate DNSNames don't match CSR DNSNames") return } if !reflect.DeepEqual(parsedCertificate.IPAddresses, csr.IPAddresses) { err = core.InternalServerError("Generated certificate IPAddresses don't match CSR IPAddresses") return } if !reflect.DeepEqual(parsedCertificate.EmailAddresses, csr.EmailAddresses) { err = core.InternalServerError("Generated certificate EmailAddresses don't match CSR EmailAddresses") return } if len(parsedCertificate.Subject.Country) > 0 || len(parsedCertificate.Subject.Organization) > 0 || len(parsedCertificate.Subject.OrganizationalUnit) > 0 || len(parsedCertificate.Subject.Locality) > 0 || len(parsedCertificate.Subject.Province) > 0 || len(parsedCertificate.Subject.StreetAddress) > 0 || len(parsedCertificate.Subject.PostalCode) > 0 || len(parsedCertificate.Subject.SerialNumber) > 0 { err = core.InternalServerError("Generated certificate Subject contains fields other than CommonName or Names") return } now := ra.clk.Now() if now.Sub(parsedCertificate.NotBefore) > time.Hour*24 { err = core.InternalServerError(fmt.Sprintf("Generated certificate is back dated %s", now.Sub(parsedCertificate.NotBefore))) return } if !parsedCertificate.BasicConstraintsValid { err = core.InternalServerError("Generated certificate doesn't have basic constraints set") return } if parsedCertificate.IsCA { err = core.InternalServerError("Generated certificate can sign other certificates") return } if !reflect.DeepEqual(parsedCertificate.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}) { err = core.InternalServerError("Generated certificate doesn't have correct key usage extensions") return } return }
// RevokeCertificate is used by clients to request the revocation of a cert. func (wfe *WebFrontEndImpl) RevokeCertificate(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) { // We don't ask verifyPOST to verify there is a correponding registration, // because anyone with the right private key can revoke a certificate. body, requestKey, registration, err := wfe.verifyPOST(logEvent, request, false, core.ResourceRevokeCert) if err != nil { // verifyPOST handles its own setting of logEvent.Errors wfe.sendError(response, logEvent, malformedJWS, err, statusCodeFromError(err)) return } type RevokeRequest struct { CertificateDER core.JSONBuffer `json:"certificate"` } var revokeRequest RevokeRequest if err = json.Unmarshal(body, &revokeRequest); err != nil { logEvent.AddError(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body))) wfe.sendError(response, logEvent, "Unable to read/verify body", err, http.StatusBadRequest) return } providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER) if err != nil { logEvent.AddError("unable to parse revoke certificate DER: %s", err) wfe.sendError(response, logEvent, "Unable to read/verify body", err, http.StatusBadRequest) return } serial := core.SerialToString(providedCert.SerialNumber) logEvent.Extra["ProvidedCertificateSerial"] = serial cert, err := wfe.SA.GetCertificate(serial) if err != nil || !bytes.Equal(cert.DER, revokeRequest.CertificateDER) { wfe.sendError(response, logEvent, "No such certificate", err, http.StatusNotFound) return } parsedCertificate, err := x509.ParseCertificate(cert.DER) if err != nil { // InternalServerError because this is a failure to decode from our DB. wfe.sendError(response, logEvent, "Invalid certificate", err, http.StatusInternalServerError) return } logEvent.Extra["RetrievedCertificateSerial"] = core.SerialToString(parsedCertificate.SerialNumber) logEvent.Extra["RetrievedCertificateDNSNames"] = parsedCertificate.DNSNames logEvent.Extra["RetrievedCertificateEmailAddresses"] = parsedCertificate.EmailAddresses logEvent.Extra["RetrievedCertificateIPAddresses"] = parsedCertificate.IPAddresses certStatus, err := wfe.SA.GetCertificateStatus(serial) if err != nil { logEvent.AddError("unable to get certificate status: %s", err) wfe.sendError(response, logEvent, "Certificate status not yet available", err, http.StatusNotFound) return } logEvent.Extra["CertificateStatus"] = certStatus.Status if certStatus.Status == core.OCSPStatusRevoked { logEvent.AddError("Certificate already revoked: %#v", serial) wfe.sendError(response, logEvent, "Certificate already revoked", "", http.StatusConflict) return } // TODO: Implement method of revocation by authorizations on account. if !(core.KeyDigestEquals(requestKey, parsedCertificate.PublicKey) || registration.ID == cert.RegistrationID) { wfe.sendError(response, logEvent, "Revocation request must be signed by private key of cert to be revoked, or by the account key of the account that issued it.", requestKey, http.StatusForbidden) return } // Use revocation code 0, meaning "unspecified" err = wfe.RA.RevokeCertificateWithReg(*parsedCertificate, 0, registration.ID) if err != nil { logEvent.AddError("failed to revoke certificate: %s", err) wfe.sendError(response, logEvent, "Failed to revoke certificate", err, statusCodeFromError(err)) } else { wfe.log.Debug(fmt.Sprintf("Revoked %v", serial)) response.WriteHeader(http.StatusOK) } }
func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (core.Certificate, error) { emptyCert := core.Certificate{} var err error var logEventResult string // Assume the worst logEventResult = "error" // Construct the log event logEvent := certificateRequestEvent{ ID: core.NewToken(), Requester: regID, RequestMethod: "online", RequestTime: time.Now(), } // No matter what, log the request defer func() { // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ra.log.AuditObject(fmt.Sprintf("Certificate request - %s", logEventResult), logEvent) }() if regID <= 0 { err = fmt.Errorf("Invalid registration ID") return emptyCert, err } // Verify the CSR // TODO: Verify that other aspects of the CSR are appropriate csr := req.CSR if err = core.VerifyCSR(csr); err != nil { logEvent.Error = err.Error() err = core.UnauthorizedError("Invalid signature on CSR") return emptyCert, err } logEvent.CommonName = csr.Subject.CommonName logEvent.Names = csr.DNSNames csrPreviousDenied, err := ra.SA.AlreadyDeniedCSR(append(csr.DNSNames, csr.Subject.CommonName)) if err != nil { logEvent.Error = err.Error() return emptyCert, err } if csrPreviousDenied { err = core.UnauthorizedError("CSR has already been revoked/denied") logEvent.Error = err.Error() return emptyCert, err } registration, err := ra.SA.GetRegistration(regID) if err != nil { logEvent.Error = err.Error() return emptyCert, err } if core.KeyDigestEquals(csr.PublicKey, registration.Key) { err = core.MalformedRequestError("Certificate public key must be different than account key") return emptyCert, err } // Gather authorized domains from the referenced authorizations authorizedDomains := map[string]bool{} verificationMethodSet := map[string]bool{} now := time.Now() for _, url := range req.Authorizations { id := lastPathSegment(url) authz, err := ra.SA.GetAuthorization(id) if err != nil || // Couldn't find authorization authz.RegistrationID != registration.ID || authz.Status != core.StatusValid || // Not finalized or not successful authz.Expires.Before(now) || // Expired authz.Identifier.Type != core.IdentifierDNS { // XXX: It may be good to fail here instead of ignoring invalid authorizations. // However, it seems like this treatment is more in the spirit of Postel's // law, and it hides information from attackers. continue } for _, challenge := range authz.Challenges { if challenge.Status == core.StatusValid { verificationMethodSet[challenge.Type] = true } } authorizedDomains[authz.Identifier.Value] = true } verificationMethods := []string{} for method, _ := range verificationMethodSet { verificationMethods = append(verificationMethods, method) } logEvent.VerificationMethods = verificationMethods // Validate that authorization key is authorized for all domains names := csr.DNSNames if len(csr.Subject.CommonName) > 0 { names = append(names, csr.Subject.CommonName) } // Validate all domains for _, name := range names { if !authorizedDomains[name] { err = core.UnauthorizedError(fmt.Sprintf("Key not authorized for name %s", name)) logEvent.Error = err.Error() return emptyCert, err } } // Mark that we verified the CN and SANs logEvent.VerifiedFields = []string{"subject.commonName", "subjectAltName"} // Create the certificate and log the result var cert core.Certificate if cert, err = ra.CA.IssueCertificate(*csr, regID); err != nil { logEvent.Error = err.Error() return emptyCert, nil } cert.ParsedCertificate, err = x509.ParseCertificate([]byte(cert.DER)) logEvent.SerialNumber = cert.ParsedCertificate.SerialNumber logEvent.CommonName = cert.ParsedCertificate.Subject.CommonName logEvent.NotBefore = cert.ParsedCertificate.NotBefore logEvent.NotAfter = cert.ParsedCertificate.NotAfter logEvent.ResponseTime = time.Now() logEventResult = "successful" return cert, nil }
// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(ctx context.Context, base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Refuse to update expired authorizations if base.Expires == nil || base.Expires.Before(ra.clk.Now()) { err = core.NotFoundError("Expired authorization") return } authz = base if challengeIndex >= len(authz.Challenges) { err = core.MalformedRequestError(fmt.Sprintf("Invalid challenge index: %d", challengeIndex)) return } ch := &authz.Challenges[challengeIndex] // Copy information over that the client is allowed to supply ch.ProvidedKeyAuthorization = response.ProvidedKeyAuthorization if response.Type != "" && ch.Type != response.Type { // TODO(riking): Check the rate on this, uncomment error return if negligible ra.stats.Inc("RA.StartChallengeWrongType", 1, 1.0) // err = core.MalformedRequestError(fmt.Sprintf("Invalid update to challenge - provided type was %s but actual type is %s", response.Type, ch.Type)) // return } // Recompute the key authorization field provided by the client and // check it against the value provided expectedKeyAuthorization, err := ch.ExpectedKeyAuthorization() if err != nil { err = core.InternalServerError("Could not compute expected key authorization value") return } if expectedKeyAuthorization != ch.ProvidedKeyAuthorization { err = core.MalformedRequestError("Response does not complete challenge") return } // Double check before sending to VA if !ch.IsSaneForValidation() { err = core.MalformedRequestError("Response does not complete challenge") return } // Store the updated version if err = ra.SA.UpdatePendingAuthorization(ctx, authz); err != nil { // This can pretty much only happen when the client corrupts the Challenge // data. err = core.MalformedRequestError("Challenge data was corrupted") return } ra.stats.Inc("RA.NewPendingAuthorizations", 1, 1.0) // Look up the account key for this authorization reg, err := ra.SA.GetRegistration(ctx, authz.RegistrationID) if err != nil { err = core.InternalServerError(err.Error()) return } // Reject the update if the challenge in question was created // with a different account key if !core.KeyDigestEquals(reg.Key, ch.AccountKey) { err = core.UnauthorizedError("Challenge cannot be updated with a different key") return } // Dispatch to the VA for service vaCtx := context.Background() if !ra.useNewVARPC { // TODO(#1167): remove _ = ra.VA.UpdateValidations(vaCtx, authz, challengeIndex) ra.stats.Inc("RA.UpdatedPendingAuthorizations", 1, 1.0) } else { go func() { records, err := ra.VA.PerformValidation(vaCtx, authz.Identifier.Value, authz.Challenges[challengeIndex], authz) var prob *probs.ProblemDetails if p, ok := err.(*probs.ProblemDetails); ok { prob = p } else if err != nil { prob = probs.ServerInternal("Could not communicate with VA") ra.log.Err(fmt.Sprintf("Could not communicate with VA: %s", err)) } // Save the updated records challenge := &authz.Challenges[challengeIndex] challenge.ValidationRecord = records if !challenge.RecordsSane() && prob == nil { prob = probs.ServerInternal("Records for validation failed sanity check") } if prob != nil { challenge.Status = core.StatusInvalid challenge.Error = prob } else { challenge.Status = core.StatusValid } authz.Challenges[challengeIndex] = *challenge err = ra.OnValidationUpdate(vaCtx, authz) if err != nil { ra.log.Err(fmt.Sprintf("Could not record updated validation: err=[%s] regID=[%d]", err, authz.RegistrationID)) } }() ra.stats.Inc("RA.UpdatedPendingAuthorizations", 1, 1.0) } return }
func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed) return } body, requestKey, reg, err := wfe.verifyPOST(request, false) if err != nil { wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } if reg.Agreement == "" { wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden) return } type RevokeRequest struct { CertificateDER core.JsonBuffer `json:"certificate"` } var revokeRequest RevokeRequest if err = json.Unmarshal(body, &revokeRequest); err != nil { wfe.log.Debug(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body))) wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER) if err != nil { wfe.log.Debug("Couldn't parse cert in revoke request.") wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest) return } serial := core.SerialToString(providedCert.SerialNumber) certDER, err := wfe.SA.GetCertificate(serial) if err != nil || !bytes.Equal(certDER, revokeRequest.CertificateDER) { wfe.sendError(response, "No such certificate", err, http.StatusNotFound) return } parsedCertificate, err := x509.ParseCertificate(certDER) if err != nil { wfe.sendError(response, "Invalid certificate", err, http.StatusInternalServerError) return } certStatus, err := wfe.SA.GetCertificateStatus(serial) if err != nil { wfe.sendError(response, "No such certificate", err, http.StatusNotFound) return } if certStatus.Status == core.OCSPStatusRevoked { wfe.sendError(response, "Certificate already revoked", "", http.StatusConflict) return } // TODO: Implement other methods of validating revocation, e.g. through // authorizations on account. if !core.KeyDigestEquals(requestKey, parsedCertificate.PublicKey) { wfe.log.Debug("Key mismatch for revoke") wfe.sendError(response, "Revocation request must be signed by private key of cert to be revoked", requestKey, http.StatusForbidden) return } err = wfe.RA.RevokeCertificate(*parsedCertificate) if err != nil { wfe.sendError(response, "Failed to revoke certificate", err, http.StatusInternalServerError) } else { wfe.log.Debug(fmt.Sprintf("Revoked %v", serial)) // incr revoked cert stat wfe.Stats.Inc("RevokedCertificates", 1, 1.0) response.WriteHeader(http.StatusOK) } }