// VerifyCSR checks the validity of a x509.CertificateRequest. Before doing checks it normalizes // the CSR which lowers the case of DNS names and subject CN, and if forceCNFromSAN is true it // will hoist a DNS name into the CN if it is empty. func VerifyCSR(csr *x509.CertificateRequest, maxNames int, keyPolicy *goodkey.KeyPolicy, pa core.PolicyAuthority, forceCNFromSAN bool, regID int64) error { normalizeCSR(csr, forceCNFromSAN) key, ok := csr.PublicKey.(crypto.PublicKey) if !ok { return invalidPubKey } if err := keyPolicy.GoodKey(key); err != nil { return fmt.Errorf("invalid public key in CSR: %s", err) } if badSignatureAlgorithms[csr.SignatureAlgorithm] { // go1.6 provides a stringer for x509.SignatureAlgorithm but 1.5.x // does not return unsupportedSigAlg } if err := csr.CheckSignature(); err != nil { return invalidSig } if len(csr.EmailAddresses) > 0 { return invalidEmailPresent } if len(csr.IPAddresses) > 0 { return invalidIPPresent } if len(csr.DNSNames) == 0 && csr.Subject.CommonName == "" { return invalidNoDNS } if len(csr.Subject.CommonName) > maxCNLength { return fmt.Errorf("CN was longer than %d bytes", maxCNLength) } if maxNames > 0 && len(csr.DNSNames) > maxNames { return fmt.Errorf("CSR contains more than %d DNS names", maxNames) } badNames := []string{} for _, name := range csr.DNSNames { if err := pa.WillingToIssue(core.AcmeIdentifier{ Type: core.IdentifierDNS, Value: name, }); err != nil { badNames = append(badNames, fmt.Sprintf("%q", name)) } } if len(badNames) > 0 { return fmt.Errorf("policy forbids issuing for: %s", strings.Join(badNames, ", ")) } return nil }
// Performs the heavy lifting of generating a certificate from a CSR. // Returns a ParsedCertBundle sans private keys. func signCertificate(creationInfo *creationBundle, csr *x509.CertificateRequest) (*certutil.ParsedCertBundle, error) { switch { case creationInfo == nil: return nil, errutil.UserError{Err: "nil creation info given to signCertificate"} case creationInfo.SigningBundle == nil: return nil, errutil.UserError{Err: "nil signing bundle given to signCertificate"} case csr == nil: return nil, errutil.UserError{Err: "nil csr given to signCertificate"} } err := csr.CheckSignature() if err != nil { return nil, errutil.UserError{Err: "request signature invalid"} } result := &certutil.ParsedCertBundle{} serialNumber, err := certutil.GenerateSerialNumber() if err != nil { return nil, err } marshaledKey, err := x509.MarshalPKIXPublicKey(csr.PublicKey) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("error marshalling public key: %s", err)} } subjKeyID := sha1.Sum(marshaledKey) subject := pkix.Name{ CommonName: creationInfo.CommonName, } certTemplate := &x509.Certificate{ SerialNumber: serialNumber, Subject: subject, NotBefore: time.Now().Add(-30 * time.Second), NotAfter: time.Now().Add(creationInfo.TTL), SubjectKeyId: subjKeyID[:], } switch creationInfo.SigningBundle.PrivateKeyType { case certutil.RSAPrivateKey: certTemplate.SignatureAlgorithm = x509.SHA256WithRSA case certutil.ECPrivateKey: certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256 } if creationInfo.UseCSRValues { certTemplate.Subject = csr.Subject certTemplate.DNSNames = csr.DNSNames certTemplate.EmailAddresses = csr.EmailAddresses certTemplate.IPAddresses = csr.IPAddresses certTemplate.ExtraExtensions = csr.Extensions } else { certTemplate.DNSNames = creationInfo.DNSNames certTemplate.EmailAddresses = creationInfo.EmailAddresses certTemplate.IPAddresses = creationInfo.IPAddresses } addKeyUsages(creationInfo, certTemplate) var certBytes []byte caCert := creationInfo.SigningBundle.Certificate certTemplate.IssuingCertificateURL = creationInfo.URLs.IssuingCertificates certTemplate.CRLDistributionPoints = creationInfo.URLs.CRLDistributionPoints certTemplate.OCSPServer = creationInfo.SigningBundle.URLs.OCSPServers if creationInfo.IsCA { certTemplate.BasicConstraintsValid = true certTemplate.IsCA = true if creationInfo.SigningBundle.Certificate.MaxPathLen == 0 && creationInfo.SigningBundle.Certificate.MaxPathLenZero { return nil, errutil.UserError{Err: "signing certificate has a max path length of zero, and cannot issue further CA certificates"} } certTemplate.MaxPathLen = creationInfo.MaxPathLength if certTemplate.MaxPathLen == 0 { certTemplate.MaxPathLenZero = true } } certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, csr.PublicKey, creationInfo.SigningBundle.PrivateKey) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)} } result.CertificateBytes = certBytes result.Certificate, err = x509.ParseCertificate(certBytes) if err != nil { return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)} } result.IssuingCABytes = creationInfo.SigningBundle.CertificateBytes result.IssuingCA = creationInfo.SigningBundle.Certificate return result, nil }
// Performs the heavy lifting of generating a certificate from a CSR. // Returns a ParsedCertBundle sans private keys. func signCertificate(creationInfo *creationBundle, csr *x509.CertificateRequest) (*certutil.ParsedCertBundle, error) { switch { case creationInfo == nil: return nil, certutil.UserError{Err: "nil creation info given to signCertificate"} case creationInfo.SigningBundle == nil: return nil, certutil.UserError{Err: "nil signing bundle given to signCertificate"} case csr == nil: return nil, certutil.UserError{Err: "nil csr given to signCertificate"} } err := csr.CheckSignature() if err != nil { return nil, certutil.UserError{Err: "request signature invalid"} } result := &certutil.ParsedCertBundle{} serialNumber, err := certutil.GenerateSerialNumber() if err != nil { return nil, err } marshaledKey, err := x509.MarshalPKIXPublicKey(csr.PublicKey) if err != nil { return nil, certutil.InternalError{Err: fmt.Sprintf("error marshalling public key: %s", err)} } subjKeyID := sha1.Sum(marshaledKey) subject := pkix.Name{ CommonName: creationInfo.CommonName, } certTemplate := &x509.Certificate{ SerialNumber: serialNumber, Subject: subject, NotBefore: time.Now(), NotAfter: time.Now().Add(creationInfo.TTL), SubjectKeyId: subjKeyID[:], } switch creationInfo.SigningBundle.PrivateKeyType { case certutil.RSAPrivateKey: certTemplate.SignatureAlgorithm = x509.SHA256WithRSA case certutil.ECPrivateKey: certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256 } if creationInfo.UseCSRValues { certTemplate.Subject = csr.Subject certTemplate.DNSNames = csr.DNSNames certTemplate.EmailAddresses = csr.EmailAddresses certTemplate.IPAddresses = csr.IPAddresses certTemplate.ExtraExtensions = csr.Extensions // Do not sign a CA certificate if they didn't go through the sign-intermediate // endpoint if !creationInfo.IsCA && oidInExtensions(oidExtensionBasicConstraints, certTemplate.ExtraExtensions) { return nil, certutil.UserError{Err: "will not sign a CSR asking for CA rights through this endpoint"} } } else { certTemplate.DNSNames = creationInfo.DNSNames certTemplate.EmailAddresses = creationInfo.EmailAddresses certTemplate.IPAddresses = creationInfo.IPAddresses certTemplate.KeyUsage = x509.KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement) if creationInfo.Usage&serverUsage != 0 { certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageServerAuth) } if creationInfo.Usage&clientUsage != 0 { certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageClientAuth) } if creationInfo.Usage&codeSigningUsage != 0 { certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageCodeSigning) } if creationInfo.Usage&emailProtectionUsage != 0 { certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageEmailProtection) } if creationInfo.IsCA { certTemplate.KeyUsage = x509.KeyUsage(certTemplate.KeyUsage | x509.KeyUsageCertSign | x509.KeyUsageCRLSign) certTemplate.ExtKeyUsage = append(certTemplate.ExtKeyUsage, x509.ExtKeyUsageOCSPSigning) } } var certBytes []byte caCert := creationInfo.SigningBundle.Certificate certTemplate.IssuingCertificateURL = creationInfo.URLs.IssuingCertificates certTemplate.CRLDistributionPoints = creationInfo.URLs.CRLDistributionPoints certTemplate.OCSPServer = creationInfo.SigningBundle.URLs.OCSPServers if creationInfo.IsCA { certTemplate.BasicConstraintsValid = true certTemplate.IsCA = true if creationInfo.SigningBundle.Certificate.MaxPathLen == 0 && creationInfo.SigningBundle.Certificate.MaxPathLenZero { return nil, certutil.UserError{Err: "signing certificate has a max path length of zero, and cannot issue further CA certificates"} } certTemplate.MaxPathLen = creationInfo.MaxPathLength if certTemplate.MaxPathLen == 0 { certTemplate.MaxPathLenZero = true } } certBytes, err = x509.CreateCertificate(rand.Reader, certTemplate, caCert, csr.PublicKey, creationInfo.SigningBundle.PrivateKey) if err != nil { return nil, certutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)} } result.CertificateBytes = certBytes result.Certificate, err = x509.ParseCertificate(certBytes) if err != nil { return nil, certutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)} } result.IssuingCABytes = creationInfo.SigningBundle.CertificateBytes result.IssuingCA = creationInfo.SigningBundle.Certificate return result, nil }