blob: 1def87397d016151e6968f65ce27557b66ffd1e6 [file] [log] [blame]
Alex Vaghin1777f3b2016-03-18 12:12:46 +00001// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package acme
6
7import (
Anmol Sethie0d166c2016-08-04 00:47:19 -04008 "crypto/ecdsa"
9 "crypto/elliptic"
Alex Vaghin1777f3b2016-03-18 12:12:46 +000010 "crypto/rsa"
11 "crypto/x509"
12 "encoding/base64"
13 "encoding/json"
14 "encoding/pem"
15 "math/big"
16 "testing"
17)
18
19const testKeyPEM = `
20-----BEGIN RSA PRIVATE KEY-----
21MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq
22WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30
23Ab7+zawrfRaFONa1HwEzPY1KHnGVkxJc85gNkwYI9SY2RHXtvln3zs5wITNrdosq
24EXeaIkVYBEhbhNu54pp3kxo6TuWLi9e6pXeWetEwmlBwtWZlPoib2j3TxLBksKZf
25oyFyek380mHgJAumQ/I2fjj98/97mk3ihOY4AgVdCDj1z/GCoZkG5Rq7nbCGyosy
26KWyDX00Zs+nNqVhoLeIvXC4nnWdJMZ6rogxyQQIDAQABAoIBACIEZTOI1Kao9nmV
279IeIsuaR1Y61b9neOF/MLmIVIZu+AAJFCMB4Iw11FV6sFodwpEyeZhx2WkpWVN+H
28r19eGiLX3zsL0DOdqBJoSIHDWCCMxgnYJ6nvS0nRxX3qVrBp8R2g12Ub+gNPbmFm
29ecf/eeERIVxfifd9VsyRu34eDEvcmKFuLYbElFcPh62xE3x12UZvV/sN7gXbawpP
30G+w255vbE5MoaKdnnO83cTFlcHvhn24M/78qP7Te5OAeelr1R89kYxQLpuGe4fbS
31zc6E3ym5Td6urDetGGrSY1Eu10/8sMusX+KNWkm+RsBRbkyKq72ks/qKpOxOa+c6
329gm+Y8ECgYEA/iNUyg1ubRdH11p82l8KHtFC1DPE0V1gSZsX29TpM5jS4qv46K+s
338Ym1zmrORM8x+cynfPx1VQZQ34EYeCMIX212ryJ+zDATl4NE0I4muMvSiH9vx6Xc
347FmhNnaYzPsBL5Tm9nmtQuP09YEn8poiOJFiDs/4olnD5ogA5O4THGkCgYEA5MIL
35qWYBUuqbEWLRtMruUtpASclrBqNNsJEsMGbeqBJmoMxdHeSZckbLOrqm7GlMyNRJ
36Ne/5uWRGSzaMYuGmwsPpERzqEvYFnSrpjW5YtXZ+JtxFXNVfm9Z1gLLgvGpOUCIU
37RbpoDckDe1vgUuk3y5+DjZihs+rqIJ45XzXTzBkCgYBWuf3segruJZy5rEKhTv+o
38JqeUvRn0jNYYKFpLBeyTVBrbie6GkbUGNIWbrK05pC+c3K9nosvzuRUOQQL1tJbd
394gA3oiD9U4bMFNr+BRTHyZ7OQBcIXdz3t1qhuHVKtnngIAN1p25uPlbRFUNpshnt
40jgeVoHlsBhApcs5DUc+pyQKBgDzeHPg/+g4z+nrPznjKnktRY1W+0El93kgi+J0Q
41YiJacxBKEGTJ1MKBb8X6sDurcRDm22wMpGfd9I5Cv2v4GsUsF7HD/cx5xdih+G73
42c4clNj/k0Ff5Nm1izPUno4C+0IOl7br39IPmfpSuR6wH/h6iHQDqIeybjxyKvT1G
43N0rRAoGBAKGD+4ZI/E1MoJ5CXB8cDDMHagbE3cq/DtmYzE2v1DFpQYu5I4PCm5c7
44EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO
459XWsXpbSTsRPj0sv1rB+UzBJ0PgjK4q2zOF0sNo7b1+6nlM3BWPx
Alex Vaghin47ff8df2016-05-06 18:46:57 +010046-----END RSA PRIVATE KEY-----
Alex Vaghin1777f3b2016-03-18 12:12:46 +000047`
48
49// This thumbprint is for the testKey defined above.
50const testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ"
51
Alex Vaghin3461a682016-08-21 13:39:17 +020052const (
53 // openssl ecparam -name secp256k1 -genkey -noout
54 testKeyECPEM = `
55-----BEGIN EC PRIVATE KEY-----
56MHcCAQEEIK07hGLr0RwyUdYJ8wbIiBS55CjnkMD23DWr+ccnypWLoAoGCCqGSM49
57AwEHoUQDQgAE5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HThqIrvawF5
58QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ==
59-----END EC PRIVATE KEY-----
60`
61 // 1. opnessl ec -in key.pem -noout -text
62 // 2. remove first byte, 04 (the header); the rest is X and Y
63 // 3. covert each with: echo <val> | xxd -r -p | base64 | tr -d '=' | tr '/+' '_-'
64 testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ"
65 testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk"
Alex Vaghin7e016f12016-08-21 18:20:10 +020066 // echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \
67 // openssl dgst -binary -sha256 | base64 | tr -d '=' | tr '/+' '_-'
68 testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU"
Alex Vaghin3461a682016-08-21 13:39:17 +020069)
70
71var (
72 testKey *rsa.PrivateKey
73 testKeyEC *ecdsa.PrivateKey
74)
Alex Vaghin1777f3b2016-03-18 12:12:46 +000075
76func init() {
77 d, _ := pem.Decode([]byte(testKeyPEM))
78 if d == nil {
79 panic("no block found in testKeyPEM")
80 }
81 var err error
82 testKey, err = x509.ParsePKCS1PrivateKey(d.Bytes)
83 if err != nil {
84 panic(err.Error())
85 }
Alex Vaghin3461a682016-08-21 13:39:17 +020086
87 if d, _ = pem.Decode([]byte(testKeyECPEM)); d == nil {
88 panic("no block found in testKeyECPEM")
89 }
90 testKeyEC, err = x509.ParseECPrivateKey(d.Bytes)
91 if err != nil {
92 panic(err.Error())
93 }
Alex Vaghin1777f3b2016-03-18 12:12:46 +000094}
95
96func TestJWSEncodeJSON(t *testing.T) {
97 claims := struct{ Msg string }{"Hello JWS"}
98 // JWS signed with testKey and "nonce" as the nonce value
99 // JSON-serialized JWS fields are split for easier testing
100 const (
101 // {"alg":"RS256","jwk":{"e":"AQAB","kty":"RSA","n":"..."},"nonce":"nonce"}
102 protected = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6" +
103 "IlJTQSIsIm4iOiI0eGdaM2VSUGt3b1J2eTdxZVJVYm1NRGUwVi14" +
104 "SDllV0xkdTBpaGVlTGxybUQybXFXWGZQOUllU0tBcGJuMzRnOFR1" +
105 "QVM5ZzV6aHE4RUxRM2ttanItS1Y4NkdBTWdJNlZBY0dscTNRcnpw" +
106 "VENmXzMwQWI3LXphd3JmUmFGT05hMUh3RXpQWTFLSG5HVmt4SmM4" +
107 "NWdOa3dZSTlTWTJSSFh0dmxuM3pzNXdJVE5yZG9zcUVYZWFJa1ZZ" +
108 "QkVoYmhOdTU0cHAza3hvNlR1V0xpOWU2cFhlV2V0RXdtbEJ3dFda" +
109 "bFBvaWIyajNUeExCa3NLWmZveUZ5ZWszODBtSGdKQXVtUV9JMmZq" +
110 "ajk4Xzk3bWszaWhPWTRBZ1ZkQ0RqMXpfR0NvWmtHNVJxN25iQ0d5" +
111 "b3N5S1d5RFgwMFpzLW5OcVZob0xlSXZYQzRubldkSk1aNnJvZ3h5" +
112 "UVEifSwibm9uY2UiOiJub25jZSJ9"
113 // {"Msg":"Hello JWS"}
114 payload = "eyJNc2ciOiJIZWxsbyBKV1MifQ"
115 signature = "eAGUikStX_UxyiFhxSLMyuyBcIB80GeBkFROCpap2sW3EmkU_ggF" +
116 "knaQzxrTfItICSAXsCLIquZ5BbrSWA_4vdEYrwWtdUj7NqFKjHRa" +
117 "zpLHcoR7r1rEHvkoP1xj49lS5fc3Wjjq8JUhffkhGbWZ8ZVkgPdC" +
118 "4tMBWiQDoth-x8jELP_3LYOB_ScUXi2mETBawLgOT2K8rA0Vbbmx" +
119 "hWNlOWuUf-8hL5YX4IOEwsS8JK_TrTq5Zc9My0zHJmaieqDV0UlP" +
120 "k0onFjPFkGm7MrPSgd0MqRG-4vSAg2O4hDo7rKv4n8POjjXlNQvM" +
121 "9IPLr8qZ7usYBKhEGwX3yq_eicAwBw"
122 )
123
124 b, err := jwsEncodeJSON(claims, testKey, "nonce")
125 if err != nil {
126 t.Fatal(err)
127 }
128 var jws struct{ Protected, Payload, Signature string }
129 if err := json.Unmarshal(b, &jws); err != nil {
130 t.Fatal(err)
131 }
132 if jws.Protected != protected {
133 t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected)
134 }
135 if jws.Payload != payload {
136 t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload)
137 }
138 if jws.Signature != signature {
139 t.Errorf("signature:\n%s\nwant:\n%s", jws.Signature, signature)
140 }
141}
142
Alex Vaghin3461a682016-08-21 13:39:17 +0200143func TestJWSEncodeJSONEC(t *testing.T) {
144 claims := struct{ Msg string }{"Hello JWS"}
145
146 b, err := jwsEncodeJSON(claims, testKeyEC, "nonce")
147 if err != nil {
148 t.Fatal(err)
149 }
150 var jws struct{ Protected, Payload, Signature string }
151 if err := json.Unmarshal(b, &jws); err != nil {
152 t.Fatal(err)
153 }
154
155 if b, err = base64.RawURLEncoding.DecodeString(jws.Protected); err != nil {
156 t.Fatalf("jws.Protected: %v", err)
157 }
158 var head struct {
159 Alg string
160 Nonce string
161 JWK struct {
162 Crv string
163 Kty string
164 X string
165 Y string
166 } `json:"jwk"`
167 }
168 if err := json.Unmarshal(b, &head); err != nil {
169 t.Fatalf("jws.Protected: %v", err)
170 }
171 if head.Alg != "ES256" {
172 t.Errorf("head.Alg = %q; want ES256", head.Alg)
173 }
174 if head.Nonce != "nonce" {
175 t.Errorf("head.Nonce = %q; want nonce", head.Nonce)
176 }
177 if head.JWK.Crv != "P-256" {
178 t.Errorf("head.JWK.Crv = %q; want P-256", head.JWK.Crv)
179 }
180 if head.JWK.Kty != "EC" {
181 t.Errorf("head.JWK.Kty = %q; want EC", head.JWK.Kty)
182 }
183 if head.JWK.X != testKeyECPubX {
184 t.Errorf("head.JWK.X = %q; want %q", head.JWK.X, testKeyECPubX)
185 }
186 if head.JWK.Y != testKeyECPubY {
187 t.Errorf("head.JWK.Y = %q; want %q", head.JWK.Y, testKeyECPubY)
188 }
189}
190
Anmol Sethie0d166c2016-08-04 00:47:19 -0400191func TestJWKThumbprintRSA(t *testing.T) {
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000192 // Key example from RFC 7638
193 const base64N = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" +
194 "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" +
195 "4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" +
196 "W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" +
197 "1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" +
198 "aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
199 const base64E = "AQAB"
200 const expected = "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
201
Anmol Sethie0d166c2016-08-04 00:47:19 -0400202 b, err := base64.RawURLEncoding.DecodeString(base64N)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000203 if err != nil {
204 t.Fatalf("Error parsing example key N: %v", err)
205 }
Anmol Sethie0d166c2016-08-04 00:47:19 -0400206 n := new(big.Int).SetBytes(b)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000207
Anmol Sethie0d166c2016-08-04 00:47:19 -0400208 b, err = base64.RawURLEncoding.DecodeString(base64E)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000209 if err != nil {
210 t.Fatalf("Error parsing example key E: %v", err)
211 }
Anmol Sethie0d166c2016-08-04 00:47:19 -0400212 e := new(big.Int).SetBytes(b)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000213
214 pub := &rsa.PublicKey{N: n, E: int(e.Uint64())}
Anmol Sethie0d166c2016-08-04 00:47:19 -0400215 th, err := JWKThumbprint(pub)
216 if err != nil {
217 t.Error(err)
218 }
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000219 if th != expected {
Anmol Sethie0d166c2016-08-04 00:47:19 -0400220 t.Errorf("thumbprint = %q; want %q", th, expected)
221 }
222}
223
224func TestJWKThumbprintEC(t *testing.T) {
225 // Key example from RFC 7520
226 // expected was computed with
227 // echo -n '{"crv":"P-521","kty":"EC","x":"<base64X>","y":"<base64Y>"}' | \
228 // openssl dgst -binary -sha256 | \
229 // base64 | \
230 // tr -d '=' | tr '/+' '_-'
231 const (
232 base64X = "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkT" +
233 "KqjqvjyekWF-7ytDyRXYgCF5cj0Kt"
234 base64Y = "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUda" +
235 "QkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
236 expected = "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M"
237 )
238
239 b, err := base64.RawURLEncoding.DecodeString(base64X)
240 if err != nil {
241 t.Fatalf("Error parsing example key X: %v", err)
242 }
243 x := new(big.Int).SetBytes(b)
244
245 b, err = base64.RawURLEncoding.DecodeString(base64Y)
246 if err != nil {
247 t.Fatalf("Error parsing example key Y: %v", err)
248 }
249 y := new(big.Int).SetBytes(b)
250
251 pub := &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}
252 th, err := JWKThumbprint(pub)
253 if err != nil {
254 t.Error(err)
255 }
256 if th != expected {
257 t.Errorf("thumbprint = %q; want %q", th, expected)
258 }
259}
260
261func TestJWKThumbprintErrUnsupportedKey(t *testing.T) {
262 _, err := JWKThumbprint(struct{}{})
263 if err != ErrUnsupportedKey {
264 t.Errorf("err = %q; want %q", err, ErrUnsupportedKey)
Alex Vaghin1777f3b2016-03-18 12:12:46 +0000265 }
266}