jws_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // 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. package acme
  5. import (
  6. "crypto/ecdsa"
  7. "crypto/elliptic"
  8. "crypto/rsa"
  9. "crypto/x509"
  10. "encoding/base64"
  11. "encoding/json"
  12. "encoding/pem"
  13. "fmt"
  14. "math/big"
  15. "testing"
  16. )
  17. const (
  18. testKeyPEM = `
  19. -----BEGIN RSA PRIVATE KEY-----
  20. MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq
  21. WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30
  22. Ab7+zawrfRaFONa1HwEzPY1KHnGVkxJc85gNkwYI9SY2RHXtvln3zs5wITNrdosq
  23. EXeaIkVYBEhbhNu54pp3kxo6TuWLi9e6pXeWetEwmlBwtWZlPoib2j3TxLBksKZf
  24. oyFyek380mHgJAumQ/I2fjj98/97mk3ihOY4AgVdCDj1z/GCoZkG5Rq7nbCGyosy
  25. KWyDX00Zs+nNqVhoLeIvXC4nnWdJMZ6rogxyQQIDAQABAoIBACIEZTOI1Kao9nmV
  26. 9IeIsuaR1Y61b9neOF/MLmIVIZu+AAJFCMB4Iw11FV6sFodwpEyeZhx2WkpWVN+H
  27. r19eGiLX3zsL0DOdqBJoSIHDWCCMxgnYJ6nvS0nRxX3qVrBp8R2g12Ub+gNPbmFm
  28. ecf/eeERIVxfifd9VsyRu34eDEvcmKFuLYbElFcPh62xE3x12UZvV/sN7gXbawpP
  29. G+w255vbE5MoaKdnnO83cTFlcHvhn24M/78qP7Te5OAeelr1R89kYxQLpuGe4fbS
  30. zc6E3ym5Td6urDetGGrSY1Eu10/8sMusX+KNWkm+RsBRbkyKq72ks/qKpOxOa+c6
  31. 9gm+Y8ECgYEA/iNUyg1ubRdH11p82l8KHtFC1DPE0V1gSZsX29TpM5jS4qv46K+s
  32. 8Ym1zmrORM8x+cynfPx1VQZQ34EYeCMIX212ryJ+zDATl4NE0I4muMvSiH9vx6Xc
  33. 7FmhNnaYzPsBL5Tm9nmtQuP09YEn8poiOJFiDs/4olnD5ogA5O4THGkCgYEA5MIL
  34. qWYBUuqbEWLRtMruUtpASclrBqNNsJEsMGbeqBJmoMxdHeSZckbLOrqm7GlMyNRJ
  35. Ne/5uWRGSzaMYuGmwsPpERzqEvYFnSrpjW5YtXZ+JtxFXNVfm9Z1gLLgvGpOUCIU
  36. RbpoDckDe1vgUuk3y5+DjZihs+rqIJ45XzXTzBkCgYBWuf3segruJZy5rEKhTv+o
  37. JqeUvRn0jNYYKFpLBeyTVBrbie6GkbUGNIWbrK05pC+c3K9nosvzuRUOQQL1tJbd
  38. 4gA3oiD9U4bMFNr+BRTHyZ7OQBcIXdz3t1qhuHVKtnngIAN1p25uPlbRFUNpshnt
  39. jgeVoHlsBhApcs5DUc+pyQKBgDzeHPg/+g4z+nrPznjKnktRY1W+0El93kgi+J0Q
  40. YiJacxBKEGTJ1MKBb8X6sDurcRDm22wMpGfd9I5Cv2v4GsUsF7HD/cx5xdih+G73
  41. c4clNj/k0Ff5Nm1izPUno4C+0IOl7br39IPmfpSuR6wH/h6iHQDqIeybjxyKvT1G
  42. N0rRAoGBAKGD+4ZI/E1MoJ5CXB8cDDMHagbE3cq/DtmYzE2v1DFpQYu5I4PCm5c7
  43. EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO
  44. 9XWsXpbSTsRPj0sv1rB+UzBJ0PgjK4q2zOF0sNo7b1+6nlM3BWPx
  45. -----END RSA PRIVATE KEY-----
  46. `
  47. // This thumbprint is for the testKey defined above.
  48. testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ"
  49. // openssl ecparam -name secp256k1 -genkey -noout
  50. testKeyECPEM = `
  51. -----BEGIN EC PRIVATE KEY-----
  52. MHcCAQEEIK07hGLr0RwyUdYJ8wbIiBS55CjnkMD23DWr+ccnypWLoAoGCCqGSM49
  53. AwEHoUQDQgAE5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HThqIrvawF5
  54. QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ==
  55. -----END EC PRIVATE KEY-----
  56. `
  57. // openssl ecparam -name secp384r1 -genkey -noout
  58. testKeyEC384PEM = `
  59. -----BEGIN EC PRIVATE KEY-----
  60. MIGkAgEBBDAQ4lNtXRORWr1bgKR1CGysr9AJ9SyEk4jiVnlUWWUChmSNL+i9SLSD
  61. Oe/naPqXJ6CgBwYFK4EEACKhZANiAAQzKtj+Ms0vHoTX5dzv3/L5YMXOWuI5UKRj
  62. JigpahYCqXD2BA1j0E/2xt5vlPf+gm0PL+UHSQsCokGnIGuaHCsJAp3ry0gHQEke
  63. WYXapUUFdvaK1R2/2hn5O+eiQM8YzCg=
  64. -----END EC PRIVATE KEY-----
  65. `
  66. // openssl ecparam -name secp521r1 -genkey -noout
  67. testKeyEC512PEM = `
  68. -----BEGIN EC PRIVATE KEY-----
  69. MIHcAgEBBEIBSNZKFcWzXzB/aJClAb305ibalKgtDA7+70eEkdPt28/3LZMM935Z
  70. KqYHh/COcxuu3Kt8azRAUz3gyr4zZKhlKUSgBwYFK4EEACOhgYkDgYYABAHUNKbx
  71. 7JwC7H6pa2sV0tERWhHhB3JmW+OP6SUgMWryvIKajlx73eS24dy4QPGrWO9/ABsD
  72. FqcRSkNVTXnIv6+0mAF25knqIBIg5Q8M9BnOu9GGAchcwt3O7RDHmqewnJJDrbjd
  73. GGnm6rb+NnWR9DIopM0nKNkToWoF/hzopxu4Ae/GsQ==
  74. -----END EC PRIVATE KEY-----
  75. `
  76. // 1. openssl ec -in key.pem -noout -text
  77. // 2. remove first byte, 04 (the header); the rest is X and Y
  78. // 3. convert each with: echo <val> | xxd -r -p | base64 -w 100 | tr -d '=' | tr '/+' '_-'
  79. testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ"
  80. testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk"
  81. testKeyEC384PubX = "MyrY_jLNLx6E1-Xc79_y-WDFzlriOVCkYyYoKWoWAqlw9gQNY9BP9sbeb5T3_oJt"
  82. testKeyEC384PubY = "Dy_lB0kLAqJBpyBrmhwrCQKd68tIB0BJHlmF2qVFBXb2itUdv9oZ-TvnokDPGMwo"
  83. testKeyEC512PubX = "AdQ0pvHsnALsfqlraxXS0RFaEeEHcmZb44_pJSAxavK8gpqOXHvd5Lbh3LhA8atY738AGwMWpxFKQ1VNeci_r7SY"
  84. testKeyEC512PubY = "AXbmSeogEiDlDwz0Gc670YYByFzC3c7tEMeap7CckkOtuN0Yaebqtv42dZH0MiikzSco2ROhagX-HOinG7gB78ax"
  85. // echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \
  86. // openssl dgst -binary -sha256 | base64 | tr -d '=' | tr '/+' '_-'
  87. testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU"
  88. )
  89. var (
  90. testKey *rsa.PrivateKey
  91. testKeyEC *ecdsa.PrivateKey
  92. testKeyEC384 *ecdsa.PrivateKey
  93. testKeyEC512 *ecdsa.PrivateKey
  94. )
  95. func init() {
  96. testKey = parseRSA(testKeyPEM, "testKeyPEM")
  97. testKeyEC = parseEC(testKeyECPEM, "testKeyECPEM")
  98. testKeyEC384 = parseEC(testKeyEC384PEM, "testKeyEC384PEM")
  99. testKeyEC512 = parseEC(testKeyEC512PEM, "testKeyEC512PEM")
  100. }
  101. func decodePEM(s, name string) []byte {
  102. d, _ := pem.Decode([]byte(s))
  103. if d == nil {
  104. panic("no block found in " + name)
  105. }
  106. return d.Bytes
  107. }
  108. func parseRSA(s, name string) *rsa.PrivateKey {
  109. b := decodePEM(s, name)
  110. k, err := x509.ParsePKCS1PrivateKey(b)
  111. if err != nil {
  112. panic(fmt.Sprintf("%s: %v", name, err))
  113. }
  114. return k
  115. }
  116. func parseEC(s, name string) *ecdsa.PrivateKey {
  117. b := decodePEM(s, name)
  118. k, err := x509.ParseECPrivateKey(b)
  119. if err != nil {
  120. panic(fmt.Sprintf("%s: %v", name, err))
  121. }
  122. return k
  123. }
  124. func TestJWSEncodeJSON(t *testing.T) {
  125. claims := struct{ Msg string }{"Hello JWS"}
  126. // JWS signed with testKey and "nonce" as the nonce value
  127. // JSON-serialized JWS fields are split for easier testing
  128. const (
  129. // {"alg":"RS256","jwk":{"e":"AQAB","kty":"RSA","n":"..."},"nonce":"nonce"}
  130. protected = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6" +
  131. "IlJTQSIsIm4iOiI0eGdaM2VSUGt3b1J2eTdxZVJVYm1NRGUwVi14" +
  132. "SDllV0xkdTBpaGVlTGxybUQybXFXWGZQOUllU0tBcGJuMzRnOFR1" +
  133. "QVM5ZzV6aHE4RUxRM2ttanItS1Y4NkdBTWdJNlZBY0dscTNRcnpw" +
  134. "VENmXzMwQWI3LXphd3JmUmFGT05hMUh3RXpQWTFLSG5HVmt4SmM4" +
  135. "NWdOa3dZSTlTWTJSSFh0dmxuM3pzNXdJVE5yZG9zcUVYZWFJa1ZZ" +
  136. "QkVoYmhOdTU0cHAza3hvNlR1V0xpOWU2cFhlV2V0RXdtbEJ3dFda" +
  137. "bFBvaWIyajNUeExCa3NLWmZveUZ5ZWszODBtSGdKQXVtUV9JMmZq" +
  138. "ajk4Xzk3bWszaWhPWTRBZ1ZkQ0RqMXpfR0NvWmtHNVJxN25iQ0d5" +
  139. "b3N5S1d5RFgwMFpzLW5OcVZob0xlSXZYQzRubldkSk1aNnJvZ3h5" +
  140. "UVEifSwibm9uY2UiOiJub25jZSJ9"
  141. // {"Msg":"Hello JWS"}
  142. payload = "eyJNc2ciOiJIZWxsbyBKV1MifQ"
  143. signature = "eAGUikStX_UxyiFhxSLMyuyBcIB80GeBkFROCpap2sW3EmkU_ggF" +
  144. "knaQzxrTfItICSAXsCLIquZ5BbrSWA_4vdEYrwWtdUj7NqFKjHRa" +
  145. "zpLHcoR7r1rEHvkoP1xj49lS5fc3Wjjq8JUhffkhGbWZ8ZVkgPdC" +
  146. "4tMBWiQDoth-x8jELP_3LYOB_ScUXi2mETBawLgOT2K8rA0Vbbmx" +
  147. "hWNlOWuUf-8hL5YX4IOEwsS8JK_TrTq5Zc9My0zHJmaieqDV0UlP" +
  148. "k0onFjPFkGm7MrPSgd0MqRG-4vSAg2O4hDo7rKv4n8POjjXlNQvM" +
  149. "9IPLr8qZ7usYBKhEGwX3yq_eicAwBw"
  150. )
  151. b, err := jwsEncodeJSON(claims, testKey, "nonce")
  152. if err != nil {
  153. t.Fatal(err)
  154. }
  155. var jws struct{ Protected, Payload, Signature string }
  156. if err := json.Unmarshal(b, &jws); err != nil {
  157. t.Fatal(err)
  158. }
  159. if jws.Protected != protected {
  160. t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected)
  161. }
  162. if jws.Payload != payload {
  163. t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload)
  164. }
  165. if jws.Signature != signature {
  166. t.Errorf("signature:\n%s\nwant:\n%s", jws.Signature, signature)
  167. }
  168. }
  169. func TestJWSEncodeJSONEC(t *testing.T) {
  170. tt := []struct {
  171. key *ecdsa.PrivateKey
  172. x, y string
  173. alg, crv string
  174. }{
  175. {testKeyEC, testKeyECPubX, testKeyECPubY, "ES256", "P-256"},
  176. {testKeyEC384, testKeyEC384PubX, testKeyEC384PubY, "ES384", "P-384"},
  177. {testKeyEC512, testKeyEC512PubX, testKeyEC512PubY, "ES512", "P-521"},
  178. }
  179. for i, test := range tt {
  180. claims := struct{ Msg string }{"Hello JWS"}
  181. b, err := jwsEncodeJSON(claims, test.key, "nonce")
  182. if err != nil {
  183. t.Errorf("%d: %v", i, err)
  184. continue
  185. }
  186. var jws struct{ Protected, Payload, Signature string }
  187. if err := json.Unmarshal(b, &jws); err != nil {
  188. t.Errorf("%d: %v", i, err)
  189. continue
  190. }
  191. b, err = base64.RawURLEncoding.DecodeString(jws.Protected)
  192. if err != nil {
  193. t.Errorf("%d: jws.Protected: %v", i, err)
  194. }
  195. var head struct {
  196. Alg string
  197. Nonce string
  198. JWK struct {
  199. Crv string
  200. Kty string
  201. X string
  202. Y string
  203. } `json:"jwk"`
  204. }
  205. if err := json.Unmarshal(b, &head); err != nil {
  206. t.Errorf("%d: jws.Protected: %v", i, err)
  207. }
  208. if head.Alg != test.alg {
  209. t.Errorf("%d: head.Alg = %q; want %q", i, head.Alg, test.alg)
  210. }
  211. if head.Nonce != "nonce" {
  212. t.Errorf("%d: head.Nonce = %q; want nonce", i, head.Nonce)
  213. }
  214. if head.JWK.Crv != test.crv {
  215. t.Errorf("%d: head.JWK.Crv = %q; want %q", i, head.JWK.Crv, test.crv)
  216. }
  217. if head.JWK.Kty != "EC" {
  218. t.Errorf("%d: head.JWK.Kty = %q; want EC", i, head.JWK.Kty)
  219. }
  220. if head.JWK.X != test.x {
  221. t.Errorf("%d: head.JWK.X = %q; want %q", i, head.JWK.X, test.x)
  222. }
  223. if head.JWK.Y != test.y {
  224. t.Errorf("%d: head.JWK.Y = %q; want %q", i, head.JWK.Y, test.y)
  225. }
  226. }
  227. }
  228. func TestJWKThumbprintRSA(t *testing.T) {
  229. // Key example from RFC 7638
  230. const base64N = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" +
  231. "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" +
  232. "4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" +
  233. "W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" +
  234. "1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" +
  235. "aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"
  236. const base64E = "AQAB"
  237. const expected = "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
  238. b, err := base64.RawURLEncoding.DecodeString(base64N)
  239. if err != nil {
  240. t.Fatalf("Error parsing example key N: %v", err)
  241. }
  242. n := new(big.Int).SetBytes(b)
  243. b, err = base64.RawURLEncoding.DecodeString(base64E)
  244. if err != nil {
  245. t.Fatalf("Error parsing example key E: %v", err)
  246. }
  247. e := new(big.Int).SetBytes(b)
  248. pub := &rsa.PublicKey{N: n, E: int(e.Uint64())}
  249. th, err := JWKThumbprint(pub)
  250. if err != nil {
  251. t.Error(err)
  252. }
  253. if th != expected {
  254. t.Errorf("thumbprint = %q; want %q", th, expected)
  255. }
  256. }
  257. func TestJWKThumbprintEC(t *testing.T) {
  258. // Key example from RFC 7520
  259. // expected was computed with
  260. // echo -n '{"crv":"P-521","kty":"EC","x":"<base64X>","y":"<base64Y>"}' | \
  261. // openssl dgst -binary -sha256 | \
  262. // base64 | \
  263. // tr -d '=' | tr '/+' '_-'
  264. const (
  265. base64X = "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkT" +
  266. "KqjqvjyekWF-7ytDyRXYgCF5cj0Kt"
  267. base64Y = "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUda" +
  268. "QkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
  269. expected = "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M"
  270. )
  271. b, err := base64.RawURLEncoding.DecodeString(base64X)
  272. if err != nil {
  273. t.Fatalf("Error parsing example key X: %v", err)
  274. }
  275. x := new(big.Int).SetBytes(b)
  276. b, err = base64.RawURLEncoding.DecodeString(base64Y)
  277. if err != nil {
  278. t.Fatalf("Error parsing example key Y: %v", err)
  279. }
  280. y := new(big.Int).SetBytes(b)
  281. pub := &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y}
  282. th, err := JWKThumbprint(pub)
  283. if err != nil {
  284. t.Error(err)
  285. }
  286. if th != expected {
  287. t.Errorf("thumbprint = %q; want %q", th, expected)
  288. }
  289. }
  290. func TestJWKThumbprintErrUnsupportedKey(t *testing.T) {
  291. _, err := JWKThumbprint(struct{}{})
  292. if err != ErrUnsupportedKey {
  293. t.Errorf("err = %q; want %q", err, ErrUnsupportedKey)
  294. }
  295. }