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 storedSerial, err := core.StringToSerial(cert.Serial) if err != nil { problems = append(problems, "Stored serial is invalid") } else if parsedCert.SerialNumber.Cmp(storedSerial) != 0 { 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) { id := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name} if err = c.pa.WillingToIssue(id, cert.RegistrationID); 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 }
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 storedSerial, err := core.StringToSerial(cert.Serial) if err != nil { problems = append(problems, "Stored serial is invalid") } else if parsedCert.SerialNumber.Cmp(storedSerial) != 0 { 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 > expectedValidityPeriod { problems = append(problems, fmt.Sprintf("Certificate has a validity period longer than %s", expectedValidityPeriod)) } else if validityPeriod < expectedValidityPeriod { problems = append(problems, fmt.Sprintf("Certificate has a validity period shorter than %s", expectedValidityPeriod)) } // Check the stored issuance time isn't too far back/forward dated 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 CommonName is <= 64 characters if len(parsedCert.Subject.CommonName) > 64 { problems = append( problems, fmt.Sprintf("Certificate has common name >64 characters long (%d)", len(parsedCert.Subject.CommonName)), ) } // Check that the PA is still willing to issue for each name in DNSNames + CommonName for _, name := range append(parsedCert.DNSNames, parsedCert.Subject.CommonName) { id := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: name} if err = c.pa.WillingToIssue(id); err != nil { problems = append(problems, fmt.Sprintf("Policy Authority isn't willing to issue for '%s': %s", name, err)) } else { // For defense-in-depth, even if the PA was willing to issue for a name // we double check it against a list of forbidden domains. This way even // if the hostnamePolicyFile malfunctions we will flag the forbidden // domain matches if forbidden, pattern := isForbiddenDomain(name); forbidden { problems = append(problems, fmt.Sprintf( "Policy Authority was willing to issue but domain '%s' matches "+ "forbiddenDomains entry %q", name, pattern)) } } } // 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 }