// NewRegistration constructs a new Registration from a request. func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { if err = core.GoodKey(init.Key.Key); err != nil { return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) } if err = ra.checkRegistrationLimit(init.InitialIP); err != nil { return core.Registration{}, err } reg = core.Registration{ Key: init.Key, } reg.MergeUpdate(init) // This field isn't updatable by the end user, so it isn't copied by // MergeUpdate. But we need to fill it in for new registrations. reg.InitialIP = init.InitialIP err = ra.validateContacts(reg.Contact) if err != nil { return } // Store the authorization object, then return it reg, err = ra.SA.NewRegistration(reg) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(err.Error()) } ra.stats.Inc("RA.NewRegistrations", 1, 1.0) return }
// NewRegistration constructs a new Registration from a request. func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { if err = core.GoodKey(init.Key.Key); err != nil { return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) } reg = core.Registration{ Key: init.Key, } reg.MergeUpdate(init) err = validateContacts(reg.Contact, ra.DNSResolver, ra.stats) if err != nil { return } // Store the authorization object, then return it reg, err = ra.SA.NewRegistration(reg) if err != nil { // InternalServerError since the user-data was validated before being // passed to the SA. err = core.InternalServerError(err.Error()) } ra.stats.Inc("RA.NewRegistrations", 1, 1.0) return }
// IssueCertificate attempts to convert a CSR into a signed Certificate, while // enforcing all policies. func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest, regID int64) (core.Certificate, error) { emptyCert := core.Certificate{} var err error key, ok := csr.PublicKey.(crypto.PublicKey) if !ok { err = fmt.Errorf("Invalid public key in CSR.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if err = core.GoodKey(key); err != nil { err = fmt.Errorf("Invalid public key in CSR: %s", err.Error()) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if badSignatureAlgorithms[csr.SignatureAlgorithm] { err = fmt.Errorf("Invalid signature algorithm in CSR") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Pull hostnames from CSR // Authorization is checked by the RA commonName := "" hostNames := make([]string, len(csr.DNSNames)) copy(hostNames, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { commonName = csr.Subject.CommonName hostNames = append(hostNames, csr.Subject.CommonName) } else if len(hostNames) > 0 { commonName = hostNames[0] } else { err = fmt.Errorf("Cannot issue a certificate without a hostname.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Collapse any duplicate names. Note that this operation may re-order the names hostNames = core.UniqueNames(hostNames) if ca.MaxNames > 0 && len(hostNames) > ca.MaxNames { err = fmt.Errorf("Certificate request has %d > %d names", len(hostNames), ca.MaxNames) ca.log.WarningErr(err) return emptyCert, err } // Verify that names are allowed by policy identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: commonName} if err = ca.PA.WillingToIssue(identifier); err != nil { err = fmt.Errorf("Policy forbids issuing for name %s", commonName) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } for _, name := range hostNames { identifier = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name} if err = ca.PA.WillingToIssue(identifier); err != nil { err = fmt.Errorf("Policy forbids issuing for name %s", name) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } } notAfter := ca.Clk.Now().Add(ca.ValidityPeriod) if ca.NotAfter.Before(notAfter) { // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c err = errors.New("Cannot issue a certificate that expires after the intermediate certificate.") ca.log.AuditErr(err) return emptyCert, err } // Convert the CSR to PEM csrPEM := string(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr.Raw, })) // Get the next serial number tx, err := ca.DB.Begin() if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.AuditErr(err) return emptyCert, err } serialDec, err := ca.DB.IncrementAndGetSerial(tx) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Serial increment failed, rolling back: err=[%v]", err)) tx.Rollback() return emptyCert, err } serialHex := fmt.Sprintf("%02X%014X", ca.Prefix, serialDec) // Send the cert off for signing req := signer.SignRequest{ Request: csrPEM, Profile: ca.profile, Hosts: hostNames, Subject: &signer.Subject{ CN: commonName, }, SerialSeq: serialHex, } certPEM, err := ca.Signer.Sign(req) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Signer failed, rolling back: serial=[%s] err=[%v]", serialHex, err)) tx.Rollback() return emptyCert, err } if len(certPEM) == 0 { err = fmt.Errorf("No certificate returned by server") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM empty from Signer, rolling back: serial=[%s] err=[%v]", serialHex, err)) tx.Rollback() return emptyCert, err } block, _ := pem.Decode(certPEM) if block == nil || block.Type != "CERTIFICATE" { err = fmt.Errorf("Invalid certificate value returned") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM decode error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) tx.Rollback() return emptyCert, err } certDER := block.Bytes cert := core.Certificate{ DER: certDER, } // This is one last check for uncaught errors if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Uncaught error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) tx.Rollback() return emptyCert, err } // Store the cert with the certificate authority, if provided _, err = ca.SA.AddCertificate(certDER, regID) if err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Failed RPC to store at SA, orphaning certificate: pem=[%s] err=[%v]", certPEM, err)) tx.Rollback() return emptyCert, err } if err = tx.Commit(); err != nil { // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Failed to commit, orphaning certificate: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } // Attempt to generate the OCSP Response now. If this raises an error, it is // logged but is not returned to the caller, as an error at this point does // not constitute an issuance failure. certObj, err := x509.ParseCertificate(certDER) if err != nil { ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed parsing Certificate: %s", err)) return cert, nil } serial := core.SerialToString(certObj.SerialNumber) signRequest := ocsp.SignRequest{ Certificate: certObj, Status: string(core.OCSPStatusGood), } ocspResponse, err := ca.OCSPSigner.Sign(signRequest) if err != nil { ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed signing: %s", err)) return cert, nil } err = ca.SA.UpdateOCSP(serial, ocspResponse) if err != nil { ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed storing: %s", err)) return cert, nil } // Do not return an err at this point; caller must know that the Certificate // was issued. (Also, it should be impossible for err to be non-nil here) return cert, nil }
// NewCertificate is used by clients to request the issuance of a cert for an // authorized identifier. func (wfe *WebFrontEndImpl) NewCertificate(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) { body, _, reg, err := wfe.verifyPOST(logEvent, request, true, core.ResourceNewCert) if err != nil { // verifyPOST handles its own setting of logEvent.Errors respMsg := malformedJWS respCode := statusCodeFromError(err) if _, ok := err.(core.NoSuchRegistrationError); ok { respMsg = unknownKey respCode = http.StatusForbidden } wfe.sendError(response, logEvent, respMsg, err, respCode) return } // Any version of the agreement is acceptable here. Version match is enforced in // wfe.Registration when agreeing the first time. Agreement updates happen // by mailing subscribers and don't require a registration update. if reg.Agreement == "" { wfe.sendError(response, logEvent, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden) return } var certificateRequest core.CertificateRequest if err = json.Unmarshal(body, &certificateRequest); err != nil { logEvent.AddError("unable to JSON unmarshal CertificateRequest: %s", err) wfe.sendError(response, logEvent, "Error unmarshaling certificate request", err, http.StatusBadRequest) return } wfe.logCsr(request, certificateRequest, reg) // Check that the key in the CSR is good. This will also be checked in the CA // component, but we want to discard CSRs with bad keys as early as possible // because (a) it's an easy check and we can save unnecessary requests and // bytes on the wire, and (b) the CA logs all rejections as audit events, but // a bad key from the client is just a malformed request and doesn't need to // be audited. if err = core.GoodKey(certificateRequest.CSR.PublicKey); err != nil { logEvent.AddError("CSR public key failed GoodKey: %s", err) wfe.sendError(response, logEvent, "Invalid key in certificate request", err, http.StatusBadRequest) return } logEvent.Extra["CSRDNSNames"] = certificateRequest.CSR.DNSNames logEvent.Extra["CSREmailAddresses"] = certificateRequest.CSR.EmailAddresses logEvent.Extra["CSRIPAddresses"] = certificateRequest.CSR.IPAddresses // Create new certificate and return // TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the // WFE is compromised, *and* the attacker knows the public key of an account // authorized for target site, they could cause issuance for that site by // lying to the RA. We should probably pass a copy of the whole rquest to the // RA for secondary validation. cert, err := wfe.RA.NewCertificate(certificateRequest, reg.ID) if err != nil { logEvent.AddError("unable to create new cert: %s", err) wfe.sendError(response, logEvent, "Error creating new cert", err, statusCodeFromError(err)) return } // Make a URL for this certificate. // We use only the sequential part of the serial number, because it should // uniquely identify the certificate, and this makes it easy for anybody to // enumerate and mirror our certificates. parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER)) if err != nil { logEvent.AddError("unable to parse certificate: %s", err) wfe.sendError(response, logEvent, "Error creating new cert", err, http.StatusBadRequest) return } serial := parsedCertificate.SerialNumber certURL := wfe.CertBase + core.SerialToString(serial) // TODO Content negotiation response.Header().Add("Location", certURL) response.Header().Add("Link", link(wfe.BaseURL+IssuerPath, "up")) response.Header().Set("Content-Type", "application/pkix-cert") response.WriteHeader(http.StatusCreated) if _, err = response.Write(cert.DER); err != nil { logEvent.AddError(err.Error()) wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err)) } }
// verifyPOST reads and parses the request body, looks up the Registration // corresponding to its JWK, verifies the JWS signature, checks that the // resource field is present and correct in the JWS protected header, and // returns the JWS payload bytes, the key used to verify, and the corresponding // Registration (or error). If regCheck is false, verifyPOST will still try to // look up a registration object, and will return it if found. However, if no // registration object is found, verifyPOST will attempt to verify the JWS using // the key in the JWS headers, and return the key plus a dummy registration if // successful. If a caller passes regCheck = false, it should plan on validating // the key itself. verifyPOST also appends its errors to requestEvent.Errors so // code calling it does not need to if they imediately return a response to the // user. func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) { var err error // TODO: We should return a pointer to a registration, which can be nil, // rather the a registration value with a sentinel value. // https://github.com/letsencrypt/boulder/issues/877 reg := core.Registration{ID: 0} if _, ok := request.Header["Content-Length"]; !ok { err = core.LengthRequiredError("Content-Length header is required for POST.") wfe.stats.Inc("WFE.HTTP.ClientErrors.LengthRequiredError", 1, 1.0) logEvent.AddError("missing Content-Length header on POST") return nil, nil, reg, err } // Read body if request.Body == nil { err = core.MalformedRequestError("No body on POST") wfe.stats.Inc("WFE.Errors.NoPOSTBody", 1, 1.0) logEvent.AddError("no body on POST") return nil, nil, reg, err } bodyBytes, err := ioutil.ReadAll(request.Body) if err != nil { err = core.InternalServerError("unable to read request body") wfe.stats.Inc("WFE.Errors.UnableToReadRequestBody", 1, 1.0) logEvent.AddError("unable to read request body") return nil, nil, reg, err } body := string(bodyBytes) // Parse as JWS parsedJws, err := jose.ParseSigned(body) if err != nil { puberr := core.SignatureValidationError("Parse error reading JWS") wfe.stats.Inc("WFE.Errors.UnableToParseJWS", 1, 1.0) logEvent.AddError("could not JSON parse body into JWS: %s", err) return nil, nil, reg, puberr } // Verify JWS // NOTE: It might seem insecure for the WFE to be trusted to verify // client requests, i.e., that the verification should be done at the // RA. However the WFE is the RA's only view of the outside world // *anyway*, so it could always lie about what key was used by faking // the signature itself. if len(parsedJws.Signatures) > 1 { err = core.SignatureValidationError("Too many signatures in POST body") wfe.stats.Inc("WFE.Errors.TooManyJWSSignaturesInPOST", 1, 1.0) logEvent.AddError("too many signatures in POST body: %d", len(parsedJws.Signatures)) return nil, nil, reg, err } if len(parsedJws.Signatures) == 0 { err = core.SignatureValidationError("POST JWS not signed") wfe.stats.Inc("WFE.Errors.JWSNotSignedInPOST", 1, 1.0) logEvent.AddError("no signatures in POST body") return nil, nil, reg, err } submittedKey := parsedJws.Signatures[0].Header.JsonWebKey if submittedKey == nil { err = core.SignatureValidationError("No JWK in JWS header") wfe.stats.Inc("WFE.Errors.NoJWKInJWSSignatureHeader", 1, 1.0) logEvent.AddError("no JWK in JWS signature header in POST body") return nil, nil, reg, err } var key *jose.JsonWebKey reg, err = wfe.SA.GetRegistrationByKey(*submittedKey) // Special case: If no registration was found, but regCheck is false, use an // empty registration and the submitted key. The caller is expected to do some // validation on the returned key. if _, ok := err.(core.NoSuchRegistrationError); ok && !regCheck { // When looking up keys from the registrations DB, we can be confident they // are "good". But when we are verifying against any submitted key, we want // to check its quality before doing the verify. if err = core.GoodKey(submittedKey.Key); err != nil { wfe.stats.Inc("WFE.Errors.JWKRejectedByGoodKey", 1, 1.0) logEvent.AddError("JWK in request was rejected by GoodKey: %s", err) return nil, nil, reg, err } key = submittedKey } else if err != nil { // For all other errors, or if regCheck is true, return error immediately. wfe.stats.Inc("WFE.Errors.UnableToGetRegistrationByKey", 1, 1.0) logEvent.AddError("unable to fetch registration by the given JWK: %s", err) return nil, nil, reg, err } else { // If the lookup was successful, use that key. key = ®.Key logEvent.Requester = reg.ID logEvent.Contacts = reg.Contact } payload, header, err := parsedJws.Verify(key) if err != nil { puberr := core.SignatureValidationError("JWS verification error") wfe.stats.Inc("WFE.Errors.JWSVerificationFailed", 1, 1.0) n := len(body) if n > 100 { n = 100 } logEvent.AddError("verification of JWS with the JWK failed: %v; body: %s", err, body[:n]) return nil, nil, reg, puberr } // Check that the request has a known anti-replay nonce // i.e., Nonce is in protected header and if err != nil || len(header.Nonce) == 0 { wfe.stats.Inc("WFE.Errors.JWSMissingNonce", 1, 1.0) logEvent.AddError("JWS is missing an anti-replay nonce") err = core.SignatureValidationError("JWS has no anti-replay nonce") return nil, nil, reg, err } else if !wfe.nonceService.Valid(header.Nonce) { wfe.stats.Inc("WFE.Errors.JWSInvalidNonce", 1, 1.0) logEvent.AddError("JWS has an invalid anti-replay nonce") err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce")) return nil, nil, reg, err } // Check that the "resource" field is present and has the correct value var parsedRequest struct { Resource string `json:"resource"` } err = json.Unmarshal([]byte(payload), &parsedRequest) if err != nil { wfe.stats.Inc("WFE.Errors.UnparsableJWSPayload", 1, 1.0) logEvent.AddError("unable to JSON parse resource from JWS payload: %s", err) puberr := core.SignatureValidationError("Request payload did not parse as JSON") return nil, nil, reg, puberr } if parsedRequest.Resource == "" { wfe.stats.Inc("WFE.Errors.NoResourceInJWSPayload", 1, 1.0) logEvent.AddError("JWS request payload does not specifiy a resource") err = core.MalformedRequestError("Request payload does not specify a resource") return nil, nil, reg, err } else if resource != core.AcmeResource(parsedRequest.Resource) { wfe.stats.Inc("WFE.Errors.MismatchedResourceInJWSPayload", 1, 1.0) logEvent.AddError("JWS request payload does not match resource") err = core.MalformedRequestError(fmt.Sprintf("JWS resource payload does not match the HTTP resource: %s != %s", parsedRequest.Resource, resource)) return nil, nil, reg, err } return []byte(payload), key, reg, nil }
// IssueCertificate attempts to convert a CSR into a signed Certificate, while // enforcing all policies. Names (domains) in the CertificateRequest will be // lowercased before storage. func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest, regID int64) (core.Certificate, error) { emptyCert := core.Certificate{} var err error if err := ca.checkHSMFault(); err != nil { return emptyCert, err } key, ok := csr.PublicKey.(crypto.PublicKey) if !ok { err = core.MalformedRequestError("Invalid public key in CSR.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if err = core.GoodKey(key); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Invalid public key in CSR: %s", err.Error())) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } if badSignatureAlgorithms[csr.SignatureAlgorithm] { err = core.MalformedRequestError("Invalid signature algorithm in CSR") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Pull hostnames from CSR // Authorization is checked by the RA commonName := "" hostNames := make([]string, len(csr.DNSNames)) copy(hostNames, csr.DNSNames) if len(csr.Subject.CommonName) > 0 { commonName = strings.ToLower(csr.Subject.CommonName) hostNames = append(hostNames, commonName) } else if len(hostNames) > 0 { commonName = strings.ToLower(hostNames[0]) } else { err = core.MalformedRequestError("Cannot issue a certificate without a hostname.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Collapse any duplicate names. Note that this operation may re-order the names hostNames = core.UniqueLowerNames(hostNames) if ca.MaxNames > 0 && len(hostNames) > ca.MaxNames { err = core.MalformedRequestError(fmt.Sprintf("Certificate request has %d > %d names", len(hostNames), ca.MaxNames)) ca.log.WarningErr(err) return emptyCert, err } // Verify that names are allowed by policy identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: commonName} if err = ca.PA.WillingToIssue(identifier, regID); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Policy forbids issuing for name %s", commonName)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } for _, name := range hostNames { identifier = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name} if err = ca.PA.WillingToIssue(identifier, regID); err != nil { err = core.MalformedRequestError(fmt.Sprintf("Policy forbids issuing for name %s", name)) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } } notAfter := ca.Clk.Now().Add(ca.ValidityPeriod) if ca.NotAfter.Before(notAfter) { err = core.InternalServerError("Cannot issue a certificate that expires after the intermediate certificate.") // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) return emptyCert, err } // Convert the CSR to PEM csrPEM := string(pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE REQUEST", Bytes: csr.Raw, })) // We want 136 bits of random number, plus an 8-bit instance id prefix. const randBits = 136 serialBytes := make([]byte, randBits/8+1) serialBytes[0] = byte(ca.Prefix) _, err = rand.Read(serialBytes[1:]) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Serial randomness failed, err=[%v]", err)) return emptyCert, err } serialHex := hex.EncodeToString(serialBytes) serialBigInt := big.NewInt(0) serialBigInt = serialBigInt.SetBytes(serialBytes) // Send the cert off for signing req := signer.SignRequest{ Request: csrPEM, Profile: ca.profile, Hosts: hostNames, Subject: &signer.Subject{ CN: commonName, }, Serial: serialBigInt, } certPEM, err := ca.Signer.Sign(req) ca.noteHSMFault(err) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Signer failed, rolling back: serial=[%s] err=[%v]", serialHex, err)) return emptyCert, err } if len(certPEM) == 0 { err = core.InternalServerError("No certificate returned by server") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM empty from Signer, rolling back: serial=[%s] err=[%v]", serialHex, err)) return emptyCert, err } block, _ := pem.Decode(certPEM) if block == nil || block.Type != "CERTIFICATE" { err = core.InternalServerError("Invalid certificate value returned") // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("PEM decode error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } certDER := block.Bytes cert := core.Certificate{ DER: certDER, } // This is one last check for uncaught errors if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Uncaught error, aborting and rolling back issuance: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } // Store the cert with the certificate authority, if provided _, err = ca.SA.AddCertificate(certDER, regID) if err != nil { err = core.InternalServerError(err.Error()) // AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3 ca.log.Audit(fmt.Sprintf("Failed RPC to store at SA, orphaning certificate: pem=[%s] err=[%v]", certPEM, err)) return emptyCert, err } // Submit the certificate to any configured CT logs go ca.Publisher.SubmitToCT(certDER) // Do not return an err at this point; caller must know that the Certificate // was issued. (Also, it should be impossible for err to be non-nil here) return cert, nil }
// verifyPOST reads and parses the request body, looks up the Registration // corresponding to its JWK, verifies the JWS signature, // checks that the resource field is present and correct in the JWS protected // header, and returns the JWS payload bytes, the key used to verify, and the // corresponding Registration (or error). // If regCheck is false, verifyPOST will still try to look up a registration // object, and will return it if found. However, if no registration object is // found, verifyPOST will attempt to verify the JWS using the key in the JWS // headers, and return the key plus a dummy registration if successful. If a // caller passes regCheck = false, it should plan on validating the key itself. func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, resource core.AcmeResource) ([]byte, *jose.JsonWebKey, core.Registration, error) { var err error // TODO: We should return a pointer to a registration, which can be nil, // rather the a registration value with a sentinel value. // https://github.com/letsencrypt/boulder/issues/877 reg := core.Registration{ID: -1} if _, ok := request.Header["Content-Length"]; !ok { err = core.LengthRequiredError("Content-Length header is required for POST.") wfe.log.Debug(err.Error()) return nil, nil, reg, err } // Read body if request.Body == nil { err = core.MalformedRequestError("No body on POST") wfe.log.Debug(err.Error()) return nil, nil, reg, err } bodyBytes, err := ioutil.ReadAll(request.Body) if err != nil { err = core.InternalServerError(err.Error()) wfe.log.Debug(err.Error()) return nil, nil, reg, err } body := string(bodyBytes) // Parse as JWS parsedJws, err := jose.ParseSigned(body) if err != nil { puberr := core.SignatureValidationError("Parse error reading JWS") wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) return nil, nil, reg, puberr } // Verify JWS // NOTE: It might seem insecure for the WFE to be trusted to verify // client requests, i.e., that the verification should be done at the // RA. However the WFE is the RA's only view of the outside world // *anyway*, so it could always lie about what key was used by faking // the signature itself. if len(parsedJws.Signatures) > 1 { err = core.SignatureValidationError("Too many signatures on POST") wfe.log.Debug(err.Error()) return nil, nil, reg, err } if len(parsedJws.Signatures) == 0 { err = core.SignatureValidationError("POST JWS not signed") wfe.log.Debug(err.Error()) return nil, nil, reg, err } submittedKey := parsedJws.Signatures[0].Header.JsonWebKey if submittedKey == nil { err = core.SignatureValidationError("No JWK in JWS header") wfe.log.Debug(err.Error()) return nil, nil, reg, err } var key *jose.JsonWebKey reg, err = wfe.SA.GetRegistrationByKey(*submittedKey) // Special case: If no registration was found, but regCheck is false, use an // empty registration and the submitted key. The caller is expected to do some // validation on the returned key. if _, ok := err.(core.NoSuchRegistrationError); ok && !regCheck { // When looking up keys from the registrations DB, we can be confident they // are "good". But when we are verifying against any submitted key, we want // to check its quality before doing the verify. if err = core.GoodKey(submittedKey.Key); err != nil { return nil, nil, reg, err } key = submittedKey } else if err != nil { // For all other errors, or if regCheck is true, return error immediately. return nil, nil, reg, err } else { // If the lookup was successful, use that key. key = ®.Key } payload, header, err := parsedJws.Verify(key) if err != nil { puberr := core.SignatureValidationError("JWS verification error") wfe.log.Debug(string(body)) wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) return nil, nil, reg, puberr } // Check that the request has a known anti-replay nonce // i.e., Nonce is in protected header and if err != nil || len(header.Nonce) == 0 { err = core.SignatureValidationError("JWS has no anti-replay nonce") wfe.log.Debug(err.Error()) return nil, nil, reg, err } else if !wfe.nonceService.Valid(header.Nonce) { err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce")) wfe.log.Debug(err.Error()) return nil, nil, reg, err } // Check that the "resource" field is present and has the correct value var parsedRequest struct { Resource string `json:"resource"` } err = json.Unmarshal([]byte(payload), &parsedRequest) if err != nil { puberr := core.SignatureValidationError("Request payload did not parse as JSON") wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) return nil, nil, reg, puberr } if parsedRequest.Resource == "" { err = core.MalformedRequestError("Request payload does not specify a resource") wfe.log.Debug(err.Error()) return nil, nil, reg, err } else if resource != core.AcmeResource(parsedRequest.Resource) { err = core.MalformedRequestError(fmt.Sprintf("Request payload has invalid resource: %s != %s", parsedRequest.Resource, resource)) wfe.log.Debug(err.Error()) return nil, nil, reg, err } return []byte(payload), key, reg, nil }