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 "strings"
13 "testing"
14
15 "github.com/letsencrypt/boulder/core"
16 berrors "github.com/letsencrypt/boulder/errors"
17 "github.com/letsencrypt/boulder/features"
18 "github.com/letsencrypt/boulder/goodkey"
19 "github.com/letsencrypt/boulder/identifier"
20 "github.com/letsencrypt/boulder/test"
21 )
22
23 var testingPolicy = &goodkey.KeyPolicy{
24 AllowRSA: true,
25 AllowECDSANISTP256: true,
26 AllowECDSANISTP384: true,
27 }
28
29 type mockPA struct{}
30
31 func (pa *mockPA) ChallengesFor(identifier identifier.ACMEIdentifier) (challenges []core.Challenge, err error) {
32 return
33 }
34
35 func (pa *mockPA) WillingToIssueWildcards(idents []identifier.ACMEIdentifier) error {
36 for _, ident := range idents {
37 if ident.Value == "bad-name.com" || ident.Value == "other-bad-name.com" {
38 return errors.New("policy forbids issuing for identifier")
39 }
40 }
41 return nil
42 }
43
44 func (pa *mockPA) ChallengeTypeEnabled(t core.AcmeChallenge) bool {
45 return true
46 }
47
48 func (pa *mockPA) CheckAuthz(a *core.Authorization) error {
49 return nil
50 }
51
52 func TestVerifyCSR(t *testing.T) {
53 private, err := rsa.GenerateKey(rand.Reader, 2048)
54 test.AssertNotError(t, err, "error generating test key")
55 signedReqBytes, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{PublicKey: private.PublicKey, SignatureAlgorithm: x509.SHA256WithRSA}, private)
56 test.AssertNotError(t, err, "error generating test CSR")
57 signedReq, err := x509.ParseCertificateRequest(signedReqBytes)
58 test.AssertNotError(t, err, "error parsing test CSR")
59 brokenSignedReq := new(x509.CertificateRequest)
60 *brokenSignedReq = *signedReq
61 brokenSignedReq.Signature = []byte{1, 1, 1, 1}
62 signedReqWithHosts := new(x509.CertificateRequest)
63 *signedReqWithHosts = *signedReq
64 signedReqWithHosts.DNSNames = []string{"a.com", "b.com"}
65 signedReqWithLongCN := new(x509.CertificateRequest)
66 *signedReqWithLongCN = *signedReq
67 signedReqWithLongCN.Subject.CommonName = strings.Repeat("a", maxCNLength+1)
68 signedReqWithBadNames := new(x509.CertificateRequest)
69 *signedReqWithBadNames = *signedReq
70 signedReqWithBadNames.DNSNames = []string{"bad-name.com", "other-bad-name.com"}
71 signedReqWithEmailAddress := new(x509.CertificateRequest)
72 *signedReqWithEmailAddress = *signedReq
73 signedReqWithEmailAddress.EmailAddresses = []string{"foo@bar.com"}
74 signedReqWithIPAddress := new(x509.CertificateRequest)
75 *signedReqWithIPAddress = *signedReq
76 signedReqWithIPAddress.IPAddresses = []net.IP{net.IPv4(1, 2, 3, 4)}
77 signedReqWithAllLongSANs := new(x509.CertificateRequest)
78 *signedReqWithAllLongSANs = *signedReq
79 signedReqWithAllLongSANs.DNSNames = []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"}
80
81 cases := []struct {
82 csr *x509.CertificateRequest
83 maxNames int
84 keyPolicy *goodkey.KeyPolicy
85 pa core.PolicyAuthority
86 expectedError error
87 }{
88 {
89 &x509.CertificateRequest{},
90 100,
91 testingPolicy,
92 &mockPA{},
93 invalidPubKey,
94 },
95 {
96 &x509.CertificateRequest{PublicKey: &private.PublicKey},
97 100,
98 testingPolicy,
99 &mockPA{},
100 unsupportedSigAlg,
101 },
102 {
103 brokenSignedReq,
104 100,
105 testingPolicy,
106 &mockPA{},
107 invalidSig,
108 },
109 {
110 signedReq,
111 100,
112 testingPolicy,
113 &mockPA{},
114 invalidNoDNS,
115 },
116 {
117 signedReqWithLongCN,
118 100,
119 testingPolicy,
120 &mockPA{},
121 berrors.BadCSRError("CN was longer than %d bytes", maxCNLength),
122 },
123 {
124 signedReqWithHosts,
125 1,
126 testingPolicy,
127 &mockPA{},
128 berrors.BadCSRError("CSR contains more than 1 DNS names"),
129 },
130 {
131 signedReqWithBadNames,
132 100,
133 testingPolicy,
134 &mockPA{},
135 errors.New("policy forbids issuing for identifier"),
136 },
137 {
138 signedReqWithEmailAddress,
139 100,
140 testingPolicy,
141 &mockPA{},
142 invalidEmailPresent,
143 },
144 {
145 signedReqWithIPAddress,
146 100,
147 testingPolicy,
148 &mockPA{},
149 invalidIPPresent,
150 },
151 {
152 signedReqWithAllLongSANs,
153 100,
154 testingPolicy,
155 &mockPA{},
156 invalidAllSANTooLong,
157 },
158 }
159
160 for _, c := range cases {
161 err := VerifyCSR(context.Background(), c.csr, c.maxNames, c.keyPolicy, c.pa)
162 test.AssertDeepEquals(t, c.expectedError, err)
163 }
164 }
165
166 func TestNamesFromCSR(t *testing.T) {
167 tooLongString := strings.Repeat("a", maxCNLength+1)
168
169 cases := []struct {
170 name string
171 csr *x509.CertificateRequest
172 expectedCN string
173 expectedNames []string
174 }{
175 {
176 "no explicit CN",
177 &x509.CertificateRequest{DNSNames: []string{"a.com"}},
178 "a.com",
179 []string{"a.com"},
180 },
181 {
182 "explicit uppercase CN",
183 &x509.CertificateRequest{Subject: pkix.Name{CommonName: "A.com"}, DNSNames: []string{"a.com"}},
184 "a.com",
185 []string{"a.com"},
186 },
187 {
188 "no explicit CN, uppercase SAN",
189 &x509.CertificateRequest{DNSNames: []string{"A.com"}},
190 "a.com",
191 []string{"a.com"},
192 },
193 {
194 "duplicate SANs",
195 &x509.CertificateRequest{DNSNames: []string{"b.com", "b.com", "a.com", "a.com"}},
196 "b.com",
197 []string{"a.com", "b.com"},
198 },
199 {
200 "explicit CN not found in SANs",
201 &x509.CertificateRequest{Subject: pkix.Name{CommonName: "a.com"}, DNSNames: []string{"b.com"}},
202 "a.com",
203 []string{"a.com", "b.com"},
204 },
205 {
206 "no explicit CN, too long leading SANs",
207 &x509.CertificateRequest{DNSNames: []string{
208 tooLongString + ".a.com",
209 tooLongString + ".b.com",
210 "a.com",
211 "b.com",
212 }},
213 "a.com",
214 []string{"a.com", tooLongString + ".a.com", tooLongString + ".b.com", "b.com"},
215 },
216 {
217 "explicit CN, too long leading SANs",
218 &x509.CertificateRequest{
219 Subject: pkix.Name{CommonName: "A.com"},
220 DNSNames: []string{
221 tooLongString + ".a.com",
222 tooLongString + ".b.com",
223 "a.com",
224 "b.com",
225 }},
226 "a.com",
227 []string{"a.com", tooLongString + ".a.com", tooLongString + ".b.com", "b.com"},
228 },
229 }
230 for _, tc := range cases {
231 t.Run(tc.name, func(t *testing.T) {
232 names := NamesFromCSR(tc.csr)
233 test.AssertEquals(t, names.CN, tc.expectedCN)
234 test.AssertDeepEquals(t, names.SANs, tc.expectedNames)
235 })
236 }
237 }
238
239 func TestSHA1Deprecation(t *testing.T) {
240 features.Reset()
241
242 private, err := rsa.GenerateKey(rand.Reader, 2048)
243 test.AssertNotError(t, err, "error generating test key")
244
245 makeAndVerifyCsr := func(alg x509.SignatureAlgorithm) error {
246 csrBytes, err := x509.CreateCertificateRequest(rand.Reader,
247 &x509.CertificateRequest{
248 DNSNames: []string{"example.com"},
249 SignatureAlgorithm: alg,
250 PublicKey: &private.PublicKey,
251 }, private)
252 test.AssertNotError(t, err, "creating test CSR")
253
254 csr, err := x509.ParseCertificateRequest(csrBytes)
255 test.AssertNotError(t, err, "parsing test CSR")
256
257 return VerifyCSR(context.Background(), csr, 100, testingPolicy, &mockPA{})
258 }
259
260 err = makeAndVerifyCsr(x509.SHA256WithRSA)
261 test.AssertNotError(t, err, "SHA256 CSR should verify")
262
263 err = makeAndVerifyCsr(x509.SHA1WithRSA)
264 test.AssertError(t, err, "SHA1 CSR should not verify")
265 }
266
267 func TestDuplicateExtensionRejection(t *testing.T) {
268 private, err := rsa.GenerateKey(rand.Reader, 2048)
269 test.AssertNotError(t, err, "error generating test key")
270
271 csrBytes, err := x509.CreateCertificateRequest(rand.Reader,
272 &x509.CertificateRequest{
273 DNSNames: []string{"example.com"},
274 SignatureAlgorithm: x509.SHA256WithRSA,
275 PublicKey: &private.PublicKey,
276 ExtraExtensions: []pkix.Extension{
277 {Id: asn1.ObjectIdentifier{2, 5, 29, 1}, Value: []byte("hello")},
278 {Id: asn1.ObjectIdentifier{2, 5, 29, 1}, Value: []byte("world")},
279 },
280 }, private)
281 test.AssertNotError(t, err, "creating test CSR")
282
283 _, err = x509.ParseCertificateRequest(csrBytes)
284 test.AssertError(t, err, "CSR with duplicate extension OID should fail to parse")
285 }
286
View as plain text