You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

jws.go 5.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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"
  7. "crypto/ecdsa"
  8. "crypto/rand"
  9. "crypto/rsa"
  10. "crypto/sha256"
  11. _ "crypto/sha512" // need for EC keys
  12. "encoding/asn1"
  13. "encoding/base64"
  14. "encoding/json"
  15. "fmt"
  16. "math/big"
  17. )
  18. // keyID is the account identity provided by a CA during registration.
  19. type keyID string
  20. // noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID.
  21. // See jwsEncodeJSON for details.
  22. const noKeyID = keyID("")
  23. // noPayload indicates jwsEncodeJSON will encode zero-length octet string
  24. // in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make
  25. // authenticated GET requests via POSTing with an empty payload.
  26. // See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
  27. const noPayload = ""
  28. // jwsEncodeJSON signs claimset using provided key and a nonce.
  29. // The result is serialized in JSON format containing either kid or jwk
  30. // fields based on the provided keyID value.
  31. //
  32. // If kid is non-empty, its quoted value is inserted in the protected head
  33. // as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted
  34. // as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive.
  35. //
  36. // See https://tools.ietf.org/html/rfc7515#section-7.
  37. func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) {
  38. alg, sha := jwsHasher(key.Public())
  39. if alg == "" || !sha.Available() {
  40. return nil, ErrUnsupportedKey
  41. }
  42. var phead string
  43. switch kid {
  44. case noKeyID:
  45. jwk, err := jwkEncode(key.Public())
  46. if err != nil {
  47. return nil, err
  48. }
  49. phead = fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q,"url":%q}`, alg, jwk, nonce, url)
  50. default:
  51. phead = fmt.Sprintf(`{"alg":%q,"kid":%q,"nonce":%q,"url":%q}`, alg, kid, nonce, url)
  52. }
  53. phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
  54. var payload string
  55. if claimset != noPayload {
  56. cs, err := json.Marshal(claimset)
  57. if err != nil {
  58. return nil, err
  59. }
  60. payload = base64.RawURLEncoding.EncodeToString(cs)
  61. }
  62. hash := sha.New()
  63. hash.Write([]byte(phead + "." + payload))
  64. sig, err := jwsSign(key, sha, hash.Sum(nil))
  65. if err != nil {
  66. return nil, err
  67. }
  68. enc := struct {
  69. Protected string `json:"protected"`
  70. Payload string `json:"payload"`
  71. Sig string `json:"signature"`
  72. }{
  73. Protected: phead,
  74. Payload: payload,
  75. Sig: base64.RawURLEncoding.EncodeToString(sig),
  76. }
  77. return json.Marshal(&enc)
  78. }
  79. // jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
  80. // The result is also suitable for creating a JWK thumbprint.
  81. // https://tools.ietf.org/html/rfc7517
  82. func jwkEncode(pub crypto.PublicKey) (string, error) {
  83. switch pub := pub.(type) {
  84. case *rsa.PublicKey:
  85. // https://tools.ietf.org/html/rfc7518#section-6.3.1
  86. n := pub.N
  87. e := big.NewInt(int64(pub.E))
  88. // Field order is important.
  89. // See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
  90. return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
  91. base64.RawURLEncoding.EncodeToString(e.Bytes()),
  92. base64.RawURLEncoding.EncodeToString(n.Bytes()),
  93. ), nil
  94. case *ecdsa.PublicKey:
  95. // https://tools.ietf.org/html/rfc7518#section-6.2.1
  96. p := pub.Curve.Params()
  97. n := p.BitSize / 8
  98. if p.BitSize%8 != 0 {
  99. n++
  100. }
  101. x := pub.X.Bytes()
  102. if n > len(x) {
  103. x = append(make([]byte, n-len(x)), x...)
  104. }
  105. y := pub.Y.Bytes()
  106. if n > len(y) {
  107. y = append(make([]byte, n-len(y)), y...)
  108. }
  109. // Field order is important.
  110. // See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
  111. return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
  112. p.Name,
  113. base64.RawURLEncoding.EncodeToString(x),
  114. base64.RawURLEncoding.EncodeToString(y),
  115. ), nil
  116. }
  117. return "", ErrUnsupportedKey
  118. }
  119. // jwsSign signs the digest using the given key.
  120. // The hash is unused for ECDSA keys.
  121. func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
  122. switch pub := key.Public().(type) {
  123. case *rsa.PublicKey:
  124. return key.Sign(rand.Reader, digest, hash)
  125. case *ecdsa.PublicKey:
  126. sigASN1, err := key.Sign(rand.Reader, digest, hash)
  127. if err != nil {
  128. return nil, err
  129. }
  130. var rs struct{ R, S *big.Int }
  131. if _, err := asn1.Unmarshal(sigASN1, &rs); err != nil {
  132. return nil, err
  133. }
  134. rb, sb := rs.R.Bytes(), rs.S.Bytes()
  135. size := pub.Params().BitSize / 8
  136. if size%8 > 0 {
  137. size++
  138. }
  139. sig := make([]byte, size*2)
  140. copy(sig[size-len(rb):], rb)
  141. copy(sig[size*2-len(sb):], sb)
  142. return sig, nil
  143. }
  144. return nil, ErrUnsupportedKey
  145. }
  146. // jwsHasher indicates suitable JWS algorithm name and a hash function
  147. // to use for signing a digest with the provided key.
  148. // It returns ("", 0) if the key is not supported.
  149. func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
  150. switch pub := pub.(type) {
  151. case *rsa.PublicKey:
  152. return "RS256", crypto.SHA256
  153. case *ecdsa.PublicKey:
  154. switch pub.Params().Name {
  155. case "P-256":
  156. return "ES256", crypto.SHA256
  157. case "P-384":
  158. return "ES384", crypto.SHA384
  159. case "P-521":
  160. return "ES512", crypto.SHA512
  161. }
  162. }
  163. return "", 0
  164. }
  165. // JWKThumbprint creates a JWK thumbprint out of pub
  166. // as specified in https://tools.ietf.org/html/rfc7638.
  167. func JWKThumbprint(pub crypto.PublicKey) (string, error) {
  168. jwk, err := jwkEncode(pub)
  169. if err != nil {
  170. return "", err
  171. }
  172. b := sha256.Sum256([]byte(jwk))
  173. return base64.RawURLEncoding.EncodeToString(b[:]), nil
  174. }