...

Source file src/github.com/letsencrypt/boulder/csr/csr.go

Documentation: github.com/letsencrypt/boulder/csr

     1  package csr
     2  
     3  import (
     4  	"context"
     5  	"crypto"
     6  	"crypto/x509"
     7  	"errors"
     8  	"strings"
     9  
    10  	"github.com/letsencrypt/boulder/core"
    11  	berrors "github.com/letsencrypt/boulder/errors"
    12  	"github.com/letsencrypt/boulder/features"
    13  	"github.com/letsencrypt/boulder/goodkey"
    14  	"github.com/letsencrypt/boulder/identifier"
    15  )
    16  
    17  // maxCNLength is the maximum length allowed for the common name as specified in RFC 5280
    18  const maxCNLength = 64
    19  
    20  // This map is used to decide which CSR signing algorithms we consider
    21  // strong enough to use. Significantly the missing algorithms are:
    22  // * No algorithms using MD2, MD5, or SHA-1
    23  // * No DSA algorithms
    24  var goodSignatureAlgorithms = map[x509.SignatureAlgorithm]bool{
    25  	x509.SHA256WithRSA:   true,
    26  	x509.SHA384WithRSA:   true,
    27  	x509.SHA512WithRSA:   true,
    28  	x509.ECDSAWithSHA256: true,
    29  	x509.ECDSAWithSHA384: true,
    30  	x509.ECDSAWithSHA512: true,
    31  }
    32  
    33  var (
    34  	invalidPubKey        = berrors.BadCSRError("invalid public key in CSR")
    35  	unsupportedSigAlg    = berrors.BadCSRError("signature algorithm not supported")
    36  	invalidSig           = berrors.BadCSRError("invalid signature on CSR")
    37  	invalidEmailPresent  = berrors.BadCSRError("CSR contains one or more email address fields")
    38  	invalidIPPresent     = berrors.BadCSRError("CSR contains one or more IP address fields")
    39  	invalidNoDNS         = berrors.BadCSRError("at least one DNS name is required")
    40  	invalidAllSANTooLong = berrors.BadCSRError("CSR doesn't contain a SAN short enough to fit in CN")
    41  )
    42  
    43  // VerifyCSR checks the validity of a x509.CertificateRequest. Before doing checks it normalizes
    44  // the CSR which lowers the case of DNS names and subject CN, and hoist a DNS name into the CN
    45  // if it is empty.
    46  func VerifyCSR(ctx context.Context, csr *x509.CertificateRequest, maxNames int, keyPolicy *goodkey.KeyPolicy, pa core.PolicyAuthority) error {
    47  	key, ok := csr.PublicKey.(crypto.PublicKey)
    48  	if !ok {
    49  		return invalidPubKey
    50  	}
    51  	err := keyPolicy.GoodKey(ctx, key)
    52  	if err != nil {
    53  		if errors.Is(err, goodkey.ErrBadKey) {
    54  			return berrors.BadCSRError("invalid public key in CSR: %s", err)
    55  		}
    56  		return berrors.InternalServerError("error checking key validity: %s", err)
    57  	}
    58  	if !goodSignatureAlgorithms[csr.SignatureAlgorithm] {
    59  		return unsupportedSigAlg
    60  	}
    61  
    62  	err = csr.CheckSignature()
    63  	if err != nil {
    64  		return invalidSig
    65  	}
    66  	if len(csr.EmailAddresses) > 0 {
    67  		return invalidEmailPresent
    68  	}
    69  	if len(csr.IPAddresses) > 0 {
    70  		return invalidIPPresent
    71  	}
    72  
    73  	names := NamesFromCSR(csr)
    74  
    75  	if len(names.SANs) == 0 && names.CN == "" {
    76  		return invalidNoDNS
    77  	}
    78  	if names.CN == "" && features.Enabled(features.RequireCommonName) {
    79  		return invalidAllSANTooLong
    80  	}
    81  	if len(names.CN) > maxCNLength {
    82  		return berrors.BadCSRError("CN was longer than %d bytes", maxCNLength)
    83  	}
    84  	if len(names.SANs) > maxNames {
    85  		return berrors.BadCSRError("CSR contains more than %d DNS names", maxNames)
    86  	}
    87  
    88  	idents := make([]identifier.ACMEIdentifier, len(names.SANs))
    89  	for i, name := range names.SANs {
    90  		idents[i] = identifier.DNSIdentifier(name)
    91  	}
    92  	err = pa.WillingToIssueWildcards(idents)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	return nil
    97  }
    98  
    99  type names struct {
   100  	SANs []string
   101  	CN   string
   102  }
   103  
   104  // NamesFromCSR deduplicates and lower-cases the Subject Common Name and Subject
   105  // Alternative Names from the CSR. If the CSR contains a CN, then it preserves
   106  // it and guarantees that the SANs also include it. If the CSR does not contain
   107  // a CN, then it also attempts to promote a SAN to the CN (if any is short
   108  // enough to fit).
   109  func NamesFromCSR(csr *x509.CertificateRequest) names {
   110  	// Produce a new "sans" slice with the same memory address as csr.DNSNames
   111  	// but force a new allocation if an append happens so that we don't
   112  	// accidentally mutate the underlying csr.DNSNames array.
   113  	sans := csr.DNSNames[0:len(csr.DNSNames):len(csr.DNSNames)]
   114  	if csr.Subject.CommonName != "" {
   115  		sans = append(sans, csr.Subject.CommonName)
   116  	}
   117  
   118  	if csr.Subject.CommonName != "" {
   119  		return names{SANs: core.UniqueLowerNames(sans), CN: strings.ToLower(csr.Subject.CommonName)}
   120  	}
   121  
   122  	// If there's no CN already, but we want to set one, promote the first SAN
   123  	// which is shorter than the the maximum acceptable CN length (if any).
   124  	for _, name := range sans {
   125  		if len(name) <= maxCNLength {
   126  			return names{SANs: core.UniqueLowerNames(sans), CN: strings.ToLower(name)}
   127  		}
   128  	}
   129  
   130  	return names{SANs: core.UniqueLowerNames(sans)}
   131  }
   132  

View as plain text