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

176 lines
5.2KB

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