github.com/letsencrypt/boulder@v0.20251208.0/csr/csr_test.go (about)

     1  package csr
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"crypto/rsa"
     7  	"crypto/x509"
     8  	"crypto/x509/pkix"
     9  	"encoding/asn1"
    10  	"errors"
    11  	"net"
    12  	"net/netip"
    13  	"net/url"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/letsencrypt/boulder/core"
    18  	berrors "github.com/letsencrypt/boulder/errors"
    19  	"github.com/letsencrypt/boulder/features"
    20  	"github.com/letsencrypt/boulder/goodkey"
    21  	"github.com/letsencrypt/boulder/identifier"
    22  	"github.com/letsencrypt/boulder/test"
    23  )
    24  
    25  type mockPA struct{}
    26  
    27  func (pa *mockPA) ChallengeTypesFor(ident identifier.ACMEIdentifier) ([]core.AcmeChallenge, error) {
    28  	return []core.AcmeChallenge{}, nil
    29  }
    30  
    31  func (pa *mockPA) WillingToIssue(idents identifier.ACMEIdentifiers) error {
    32  	for _, ident := range idents {
    33  		if ident.Value == "bad-name.com" || ident.Value == "other-bad-name.com" {
    34  			return errors.New("policy forbids issuing for identifier")
    35  		}
    36  	}
    37  	return nil
    38  }
    39  
    40  func (pa *mockPA) ChallengeTypeEnabled(t core.AcmeChallenge) bool {
    41  	return true
    42  }
    43  
    44  func (pa *mockPA) CheckAuthzChallenges(a *core.Authorization) error {
    45  	return nil
    46  }
    47  
    48  func TestVerifyCSR(t *testing.T) {
    49  	private, err := rsa.GenerateKey(rand.Reader, 2048)
    50  	test.AssertNotError(t, err, "error generating test key")
    51  	signedReqBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{PublicKey: private.PublicKey, SignatureAlgorithm: x509.SHA256WithRSA}, private)
    52  	test.AssertNotError(t, err, "error generating test CSR")
    53  	signedReq, err := x509.ParseCertificateRequest(signedReqBytes)
    54  	test.AssertNotError(t, err, "error parsing test CSR")
    55  	brokenSignedReq := new(x509.CertificateRequest)
    56  	*brokenSignedReq = *signedReq
    57  	brokenSignedReq.Signature = []byte{1, 1, 1, 1}
    58  	signedReqWithHosts := new(x509.CertificateRequest)
    59  	*signedReqWithHosts = *signedReq
    60  	signedReqWithHosts.DNSNames = []string{"a.com", "b.com"}
    61  	signedReqWithLongCN := new(x509.CertificateRequest)
    62  	*signedReqWithLongCN = *signedReq
    63  	signedReqWithLongCN.Subject.CommonName = strings.Repeat("a", maxCNLength+1)
    64  	signedReqWithBadNames := new(x509.CertificateRequest)
    65  	*signedReqWithBadNames = *signedReq
    66  	signedReqWithBadNames.DNSNames = []string{"bad-name.com", "other-bad-name.com"}
    67  	signedReqWithEmailAddress := new(x509.CertificateRequest)
    68  	*signedReqWithEmailAddress = *signedReq
    69  	signedReqWithEmailAddress.EmailAddresses = []string{"foo@bar.com"}
    70  	signedReqWithIPAddress := new(x509.CertificateRequest)
    71  	*signedReqWithIPAddress = *signedReq
    72  	signedReqWithIPAddress.IPAddresses = []net.IP{net.IPv4(1, 2, 3, 4)}
    73  	signedReqWithIPCN := new(x509.CertificateRequest)
    74  	*signedReqWithIPCN = *signedReq
    75  	signedReqWithIPCN.Subject.CommonName = "1.2.3.4"
    76  	signedReqWithURI := new(x509.CertificateRequest)
    77  	*signedReqWithURI = *signedReq
    78  	testURI, _ := url.ParseRequestURI("https://example.com/")
    79  	signedReqWithURI.URIs = []*url.URL{testURI}
    80  	signedReqWithAllLongSANs := new(x509.CertificateRequest)
    81  	*signedReqWithAllLongSANs = *signedReq
    82  	signedReqWithAllLongSANs.DNSNames = []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"}
    83  
    84  	keyPolicy, err := goodkey.NewPolicy(nil, nil)
    85  	test.AssertNotError(t, err, "creating test keypolicy")
    86  
    87  	cases := []struct {
    88  		csr           *x509.CertificateRequest
    89  		maxNames      int
    90  		pa            core.PolicyAuthority
    91  		expectedError error
    92  	}{
    93  		{
    94  			&x509.CertificateRequest{},
    95  			100,
    96  			&mockPA{},
    97  			invalidPubKey,
    98  		},
    99  		{
   100  			&x509.CertificateRequest{PublicKey: &private.PublicKey},
   101  			100,
   102  			&mockPA{},
   103  			unsupportedSigAlg,
   104  		},
   105  		{
   106  			brokenSignedReq,
   107  			100,
   108  			&mockPA{},
   109  			invalidSig,
   110  		},
   111  		{
   112  			signedReq,
   113  			100,
   114  			&mockPA{},
   115  			invalidNoIdent,
   116  		},
   117  		{
   118  			signedReqWithLongCN,
   119  			100,
   120  			&mockPA{},
   121  			nil,
   122  		},
   123  		{
   124  			signedReqWithHosts,
   125  			1,
   126  			&mockPA{},
   127  			berrors.BadCSRError("CSR contains more than 1 identifiers"),
   128  		},
   129  		{
   130  			signedReqWithBadNames,
   131  			100,
   132  			&mockPA{},
   133  			errors.New("policy forbids issuing for identifier"),
   134  		},
   135  		{
   136  			signedReqWithEmailAddress,
   137  			100,
   138  			&mockPA{},
   139  			invalidEmailPresent,
   140  		},
   141  		{
   142  			signedReqWithIPAddress,
   143  			100,
   144  			&mockPA{},
   145  			nil,
   146  		},
   147  		{
   148  			signedReqWithIPCN,
   149  			100,
   150  			&mockPA{},
   151  			invalidIPCN,
   152  		},
   153  		{
   154  			signedReqWithURI,
   155  			100,
   156  			&mockPA{},
   157  			invalidURIPresent,
   158  		},
   159  		{
   160  			signedReqWithAllLongSANs,
   161  			100,
   162  			&mockPA{},
   163  			nil,
   164  		},
   165  	}
   166  
   167  	for _, c := range cases {
   168  		err := VerifyCSR(context.Background(), c.csr, c.maxNames, &keyPolicy, c.pa)
   169  		test.AssertDeepEquals(t, c.expectedError, err)
   170  	}
   171  }
   172  
   173  func TestCNFromCSR(t *testing.T) {
   174  	tooLongString := strings.Repeat("a", maxCNLength+1)
   175  
   176  	cases := []struct {
   177  		name       string
   178  		csr        *x509.CertificateRequest
   179  		expectedCN string
   180  	}{
   181  		{
   182  			"no explicit CN",
   183  			&x509.CertificateRequest{DNSNames: []string{"a.com"}},
   184  			"a.com",
   185  		},
   186  		{
   187  			"explicit uppercase CN",
   188  			&x509.CertificateRequest{Subject: pkix.Name{CommonName: "A.com"}, DNSNames: []string{"a.com"}},
   189  			"a.com",
   190  		},
   191  		{
   192  			"no explicit CN, uppercase SAN",
   193  			&x509.CertificateRequest{DNSNames: []string{"A.com"}},
   194  			"a.com",
   195  		},
   196  		{
   197  			"duplicate SANs",
   198  			&x509.CertificateRequest{DNSNames: []string{"b.com", "b.com", "a.com", "a.com"}},
   199  			"b.com",
   200  		},
   201  		{
   202  			"explicit CN not found in SANs",
   203  			&x509.CertificateRequest{Subject: pkix.Name{CommonName: "a.com"}, DNSNames: []string{"b.com"}},
   204  			"a.com",
   205  		},
   206  		{
   207  			"no explicit CN, all SANs too long to be the CN",
   208  			&x509.CertificateRequest{DNSNames: []string{
   209  				tooLongString + ".a.com",
   210  				tooLongString + ".b.com",
   211  			}},
   212  			"",
   213  		},
   214  		{
   215  			"no explicit CN, leading SANs too long to be the CN",
   216  			&x509.CertificateRequest{DNSNames: []string{
   217  				tooLongString + ".a.com",
   218  				tooLongString + ".b.com",
   219  				"a.com",
   220  				"b.com",
   221  			}},
   222  			"a.com",
   223  		},
   224  		{
   225  			"explicit CN, leading SANs too long to be the CN",
   226  			&x509.CertificateRequest{
   227  				Subject: pkix.Name{CommonName: "A.com"},
   228  				DNSNames: []string{
   229  					tooLongString + ".a.com",
   230  					tooLongString + ".b.com",
   231  					"a.com",
   232  					"b.com",
   233  				}},
   234  			"a.com",
   235  		},
   236  		{
   237  			"explicit CN that's too long to be the CN",
   238  			&x509.CertificateRequest{
   239  				Subject: pkix.Name{CommonName: tooLongString + ".a.com"},
   240  			},
   241  			"",
   242  		},
   243  		{
   244  			"explicit CN that's too long to be the CN, with a SAN",
   245  			&x509.CertificateRequest{
   246  				Subject: pkix.Name{CommonName: tooLongString + ".a.com"},
   247  				DNSNames: []string{
   248  					"b.com",
   249  				}},
   250  			"",
   251  		},
   252  		{
   253  			"explicit CN that's an IP",
   254  			&x509.CertificateRequest{
   255  				Subject: pkix.Name{CommonName: "127.0.0.1"},
   256  			},
   257  			"",
   258  		},
   259  		{
   260  			"no CN, only IP SANs",
   261  			&x509.CertificateRequest{
   262  				IPAddresses: []net.IP{
   263  					netip.MustParseAddr("127.0.0.1").AsSlice(),
   264  				},
   265  			},
   266  			"",
   267  		},
   268  	}
   269  	for _, tc := range cases {
   270  		t.Run(tc.name, func(t *testing.T) {
   271  			test.AssertEquals(t, CNFromCSR(tc.csr), tc.expectedCN)
   272  		})
   273  	}
   274  }
   275  
   276  func TestSHA1Deprecation(t *testing.T) {
   277  	features.Reset()
   278  
   279  	keyPolicy, err := goodkey.NewPolicy(nil, nil)
   280  	test.AssertNotError(t, err, "creating test keypolicy")
   281  
   282  	private, err := rsa.GenerateKey(rand.Reader, 2048)
   283  	test.AssertNotError(t, err, "error generating test key")
   284  
   285  	makeAndVerifyCsr := func(alg x509.SignatureAlgorithm) error {
   286  		csrBytes, err := x509.CreateCertificateRequest(rand.Reader,
   287  			&x509.CertificateRequest{
   288  				DNSNames:           []string{"example.com"},
   289  				SignatureAlgorithm: alg,
   290  				PublicKey:          &private.PublicKey,
   291  			}, private)
   292  		test.AssertNotError(t, err, "creating test CSR")
   293  
   294  		csr, err := x509.ParseCertificateRequest(csrBytes)
   295  		test.AssertNotError(t, err, "parsing test CSR")
   296  
   297  		return VerifyCSR(context.Background(), csr, 100, &keyPolicy, &mockPA{})
   298  	}
   299  
   300  	err = makeAndVerifyCsr(x509.SHA256WithRSA)
   301  	test.AssertNotError(t, err, "SHA256 CSR should verify")
   302  
   303  	err = makeAndVerifyCsr(x509.SHA1WithRSA)
   304  	test.AssertError(t, err, "SHA1 CSR should not verify")
   305  }
   306  
   307  func TestDuplicateExtensionRejection(t *testing.T) {
   308  	private, err := rsa.GenerateKey(rand.Reader, 2048)
   309  	test.AssertNotError(t, err, "error generating test key")
   310  
   311  	csrBytes, err := x509.CreateCertificateRequest(rand.Reader,
   312  		&x509.CertificateRequest{
   313  			DNSNames:           []string{"example.com"},
   314  			SignatureAlgorithm: x509.SHA256WithRSA,
   315  			PublicKey:          &private.PublicKey,
   316  			ExtraExtensions: []pkix.Extension{
   317  				{Id: asn1.ObjectIdentifier{2, 5, 29, 1}, Value: []byte("hello")},
   318  				{Id: asn1.ObjectIdentifier{2, 5, 29, 1}, Value: []byte("world")},
   319  			},
   320  		}, private)
   321  	test.AssertNotError(t, err, "creating test CSR")
   322  
   323  	_, err = x509.ParseCertificateRequest(csrBytes)
   324  	test.AssertError(t, err, "CSR with duplicate extension OID should fail to parse")
   325  }