github.com/letsencrypt/boulder@v0.20251208.0/test/integration/issuance_test.go (about)

     1  //go:build integration
     2  
     3  package integration
     4  
     5  import (
     6  	"crypto/ecdsa"
     7  	"crypto/elliptic"
     8  	"crypto/rand"
     9  	"crypto/x509"
    10  	"crypto/x509/pkix"
    11  	"fmt"
    12  	"net"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/eggsampler/acme/v3"
    17  
    18  	"github.com/letsencrypt/boulder/test"
    19  )
    20  
    21  // TestCommonNameInCSR ensures that CSRs which have a CN set result in certs
    22  // with the same CN set.
    23  func TestCommonNameInCSR(t *testing.T) {
    24  	t.Parallel()
    25  
    26  	// Create an account.
    27  	client, err := makeClient("mailto:example@letsencrypt.org")
    28  	test.AssertNotError(t, err, "creating acme client")
    29  
    30  	// Create a private key.
    31  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    32  	test.AssertNotError(t, err, "creating random cert key")
    33  
    34  	// Put together some names.
    35  	cn := random_domain()
    36  	san1 := random_domain()
    37  	san2 := random_domain()
    38  	idents := []acme.Identifier{
    39  		{Type: "dns", Value: cn},
    40  		{Type: "dns", Value: san1},
    41  		{Type: "dns", Value: san2},
    42  	}
    43  
    44  	// Issue a cert. authAndIssue includes the 0th name as the CN by default.
    45  	ir, err := authAndIssue(client, key, idents, true, "")
    46  	test.AssertNotError(t, err, "failed to issue test cert")
    47  	cert := ir.certs[0]
    48  
    49  	// Ensure that the CN is incorporated into the SANs.
    50  	test.AssertSliceContains(t, cert.DNSNames, cn)
    51  	test.AssertSliceContains(t, cert.DNSNames, san1)
    52  	test.AssertSliceContains(t, cert.DNSNames, san2)
    53  
    54  	// Ensure that the CN is preserved as the CN.
    55  	test.AssertEquals(t, cert.Subject.CommonName, cn)
    56  }
    57  
    58  // TestFirstCSRSANHoistedToCN ensures that CSRs which have no CN set result in
    59  // certs with the first CSR SAN hoisted into the CN field.
    60  func TestFirstCSRSANHoistedToCN(t *testing.T) {
    61  	t.Parallel()
    62  
    63  	// Create an account.
    64  	client, err := makeClient("mailto:example@letsencrypt.org")
    65  	test.AssertNotError(t, err, "creating acme client")
    66  
    67  	// Create a private key.
    68  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    69  	test.AssertNotError(t, err, "creating random cert key")
    70  
    71  	// Create some names that we can sort.
    72  	san1 := "a" + random_domain()
    73  	san2 := "b" + random_domain()
    74  	idents := []acme.Identifier{
    75  		{Type: "dns", Value: san2},
    76  		{Type: "dns", Value: san1},
    77  	}
    78  
    79  	// Issue a cert using a CSR with no CN set, and the SANs in *non*-alpha order.
    80  	ir, err := authAndIssue(client, key, idents, false, "")
    81  	test.AssertNotError(t, err, "failed to issue test cert")
    82  	cert := ir.certs[0]
    83  
    84  	// Ensure that the SANs are correct, and sorted alphabetically.
    85  	test.AssertEquals(t, cert.DNSNames[0], san1)
    86  	test.AssertEquals(t, cert.DNSNames[1], san2)
    87  
    88  	// Ensure that the first SAN from the CSR is the CN.
    89  	test.Assert(t, cert.Subject.CommonName == san2, "first SAN should have been hoisted")
    90  }
    91  
    92  // TestCommonNameSANsTooLong tests that, when the names in an order and CSR are
    93  // too long to be hoisted into the CN, the correct behavior results.
    94  func TestCommonNameSANsTooLong(t *testing.T) {
    95  	t.Parallel()
    96  
    97  	// Create an account.
    98  	client, err := makeClient("mailto:example@letsencrypt.org")
    99  	test.AssertNotError(t, err, "creating acme client")
   100  
   101  	// Create a private key.
   102  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   103  	test.AssertNotError(t, err, "creating random cert key")
   104  
   105  	// Put together some names.
   106  	san1 := fmt.Sprintf("thisdomainnameis.morethan64characterslong.forthesakeoftesting.%s", random_domain())
   107  	san2 := fmt.Sprintf("thisdomainnameis.morethan64characterslong.forthesakeoftesting.%s", random_domain())
   108  	idents := []acme.Identifier{
   109  		{Type: "dns", Value: san1},
   110  		{Type: "dns", Value: san2},
   111  	}
   112  
   113  	// Issue a cert using a CSR with no CN set.
   114  	ir, err := authAndIssue(client, key, idents, false, "")
   115  	test.AssertNotError(t, err, "failed to issue test cert")
   116  	cert := ir.certs[0]
   117  
   118  	// Ensure that the SANs are correct.
   119  	test.AssertSliceContains(t, cert.DNSNames, san1)
   120  	test.AssertSliceContains(t, cert.DNSNames, san2)
   121  
   122  	// Ensure that the CN is empty.
   123  	test.AssertEquals(t, cert.Subject.CommonName, "")
   124  }
   125  
   126  // TestIssuanceProfiles verifies that profile selection works, and results in
   127  // measurable differences between certificates issued under different profiles.
   128  // It does not test the omission of the keyEncipherment KU, because all of our
   129  // integration test framework assumes ECDSA pubkeys for the sake of speed,
   130  // and ECDSA certs don't get the keyEncipherment KU in either profile.
   131  func TestIssuanceProfiles(t *testing.T) {
   132  	t.Parallel()
   133  
   134  	// Create an account.
   135  	client, err := makeClient("mailto:example@letsencrypt.org")
   136  	test.AssertNotError(t, err, "creating acme client")
   137  
   138  	profiles := client.Directory().Meta.Profiles
   139  	if len(profiles) < 2 {
   140  		t.Fatal("ACME server not advertising multiple profiles")
   141  	}
   142  
   143  	// Create a private key.
   144  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   145  	test.AssertNotError(t, err, "creating random cert key")
   146  
   147  	// Create a set of identifiers to request.
   148  	idents := []acme.Identifier{
   149  		{Type: "dns", Value: random_domain()},
   150  	}
   151  
   152  	// Get one cert for each profile that we know the test server advertises.
   153  	res, err := authAndIssue(client, key, idents, true, "legacy")
   154  	test.AssertNotError(t, err, "failed to issue under legacy profile")
   155  	test.AssertEquals(t, res.Order.Profile, "legacy")
   156  	legacy := res.certs[0]
   157  
   158  	res, err = authAndIssue(client, key, idents, true, "modern")
   159  	test.AssertNotError(t, err, "failed to issue under modern profile")
   160  	test.AssertEquals(t, res.Order.Profile, "modern")
   161  	modern := res.certs[0]
   162  
   163  	// Check that each profile worked as expected.
   164  	test.AssertEquals(t, legacy.Subject.CommonName, idents[0].Value)
   165  	test.AssertEquals(t, modern.Subject.CommonName, "")
   166  
   167  	test.AssertDeepEquals(t, legacy.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth})
   168  	test.AssertDeepEquals(t, modern.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
   169  
   170  	test.AssertEquals(t, len(legacy.SubjectKeyId), 20)
   171  	test.AssertEquals(t, len(modern.SubjectKeyId), 0)
   172  }
   173  
   174  // TestIPShortLived verifies that we will allow IP address identifiers only in
   175  // orders that use the shortlived profile.
   176  func TestIPShortLived(t *testing.T) {
   177  	t.Parallel()
   178  
   179  	// Create an account.
   180  	client, err := makeClient("mailto:example@letsencrypt.org")
   181  	if err != nil {
   182  		t.Fatalf("creating acme client: %s", err)
   183  	}
   184  
   185  	// Create a private key.
   186  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   187  	if err != nil {
   188  		t.Fatalf("creating random cert key: %s", err)
   189  	}
   190  
   191  	// Create an IP address identifier to request.
   192  	ip := "64.112.117.122"
   193  	idents := []acme.Identifier{
   194  		{Type: "ip", Value: ip},
   195  	}
   196  
   197  	// Ensure we fail under each other profile that we know the test server advertises.
   198  	_, err = authAndIssue(client, key, idents, false, "legacy")
   199  	if err == nil {
   200  		t.Error("issued for IP address identifier under legacy profile")
   201  	}
   202  	if !strings.Contains(err.Error(), "Profile \"legacy\" does not permit ip type identifiers") {
   203  		t.Fatalf("issuing under legacy profile failed for the wrong reason: %s", err)
   204  	}
   205  
   206  	_, err = authAndIssue(client, key, idents, false, "modern")
   207  	if err == nil {
   208  		t.Error("issued for IP address identifier under modern profile")
   209  	}
   210  	if !strings.Contains(err.Error(), "Profile \"modern\" does not permit ip type identifiers") {
   211  		t.Fatalf("issuing under legacy profile failed for the wrong reason: %s", err)
   212  	}
   213  
   214  	// Get one cert for the shortlived profile.
   215  	res, err := authAndIssue(client, key, idents, false, "shortlived")
   216  	if err != nil {
   217  		t.Errorf("issuing under shortlived profile: %s", err)
   218  	}
   219  	if res.Order.Profile != "shortlived" {
   220  		t.Errorf("got '%s' profile, wanted 'shortlived'", res.Order.Profile)
   221  	}
   222  	cert := res.certs[0]
   223  
   224  	// Check that the shortlived profile worked as expected.
   225  	if cert.IPAddresses[0].String() != ip {
   226  		t.Errorf("got cert with first IP SAN '%s', wanted '%s'", cert.IPAddresses[0], ip)
   227  	}
   228  }
   229  
   230  // TestIPCNRejected verifies that we will reject IP address identifiers when
   231  // they occur in the Subject CommonName.
   232  func TestIPCNRejected(t *testing.T) {
   233  	t.Parallel()
   234  
   235  	// Create an account.
   236  	client, err := makeClient("mailto:example@letsencrypt.org")
   237  	if err != nil {
   238  		t.Fatalf("creating acme client: %s", err)
   239  	}
   240  
   241  	// Create an IP address identifier to request.
   242  	ip := "64.112.117.122"
   243  	ipParsed := net.ParseIP(ip)
   244  	idents := []acme.Identifier{
   245  		{Type: "ip", Value: ip},
   246  	}
   247  
   248  	order, err := client.Client.NewOrderExtension(client.Account, idents, acme.OrderExtension{Profile: "shortlived"})
   249  	if err != nil {
   250  		t.Fatalf("creating order: %s", err)
   251  	}
   252  
   253  	if len(order.Authorizations) != 1 {
   254  		t.Fatalf("Got %d authorizations, expected 1", len(order.Authorizations))
   255  	}
   256  	auth, err := client.Client.FetchAuthorization(client.Account, order.Authorizations[0])
   257  	chal, ok := auth.ChallengeMap[acme.ChallengeTypeHTTP01]
   258  	if !ok {
   259  		t.Fatalf("no HTTP challenge at %s", order.Authorizations[0])
   260  	}
   261  
   262  	_, err = testSrvClient.AddHTTP01Response(chal.Token, chal.KeyAuthorization)
   263  	if err != nil {
   264  		t.Fatalf("adding HTTP challenge response: %s", err)
   265  	}
   266  	defer testSrvClient.RemoveHTTP01Response(chal.Token)
   267  
   268  	chal, err = client.Client.UpdateChallenge(client.Account, chal)
   269  	if err != nil {
   270  		t.Fatalf("updating challenge: %s", err)
   271  	}
   272  
   273  	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   274  	if err != nil {
   275  		t.Fatalf("creating random cert key: %s", err)
   276  	}
   277  	csrTemplate := &x509.CertificateRequest{
   278  		Subject:            pkix.Name{CommonName: ip},
   279  		SignatureAlgorithm: x509.ECDSAWithSHA256,
   280  		PublicKeyAlgorithm: x509.ECDSA,
   281  		PublicKey:          key.Public(),
   282  		IPAddresses:        []net.IP{ipParsed},
   283  	}
   284  	csrDer, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, key)
   285  	if err != nil {
   286  		t.Fatalf("making csr: %s", err)
   287  	}
   288  	csr, err := x509.ParseCertificateRequest(csrDer)
   289  	if err != nil {
   290  		t.Fatalf("parsing csr: %s", err)
   291  	}
   292  
   293  	_, err = client.Client.FinalizeOrder(client.Account, order, csr)
   294  	if err == nil {
   295  		t.Errorf("Finalizing order with IP in CN: got nil error, want badCSR error")
   296  	}
   297  	if !strings.Contains(err.Error(), "CSR contains IP address in Common Name") {
   298  		t.Errorf("issuing with IP in CN failed for the wrong reason: %s", err)
   299  	}
   300  
   301  }