|
|
- // Copyright 2015 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
-
- package acme
-
- import (
- "crypto"
- "crypto/ecdsa"
- "crypto/rand"
- "crypto/rsa"
- "crypto/sha256"
- _ "crypto/sha512" // need for EC keys
- "encoding/base64"
- "encoding/json"
- "fmt"
- "math/big"
- )
-
- // jwsEncodeJSON signs claimset using provided key and a nonce.
- // The result is serialized in JSON format.
- // See https://tools.ietf.org/html/rfc7515#section-7.
- func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
- jwk, err := jwkEncode(key.Public())
- if err != nil {
- return nil, err
- }
- alg, sha := jwsHasher(key)
- if alg == "" || !sha.Available() {
- return nil, ErrUnsupportedKey
- }
- phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
- phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
- cs, err := json.Marshal(claimset)
- if err != nil {
- return nil, err
- }
- payload := base64.RawURLEncoding.EncodeToString(cs)
- hash := sha.New()
- hash.Write([]byte(phead + "." + payload))
- sig, err := jwsSign(key, sha, hash.Sum(nil))
- if err != nil {
- return nil, err
- }
-
- enc := struct {
- Protected string `json:"protected"`
- Payload string `json:"payload"`
- Sig string `json:"signature"`
- }{
- Protected: phead,
- Payload: payload,
- Sig: base64.RawURLEncoding.EncodeToString(sig),
- }
- return json.Marshal(&enc)
- }
-
- // jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
- // The result is also suitable for creating a JWK thumbprint.
- // https://tools.ietf.org/html/rfc7517
- func jwkEncode(pub crypto.PublicKey) (string, error) {
- switch pub := pub.(type) {
- case *rsa.PublicKey:
- // https://tools.ietf.org/html/rfc7518#section-6.3.1
- n := pub.N
- e := big.NewInt(int64(pub.E))
- // Field order is important.
- // See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
- return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
- base64.RawURLEncoding.EncodeToString(e.Bytes()),
- base64.RawURLEncoding.EncodeToString(n.Bytes()),
- ), nil
- case *ecdsa.PublicKey:
- // https://tools.ietf.org/html/rfc7518#section-6.2.1
- p := pub.Curve.Params()
- n := p.BitSize / 8
- if p.BitSize%8 != 0 {
- n++
- }
- x := pub.X.Bytes()
- if n > len(x) {
- x = append(make([]byte, n-len(x)), x...)
- }
- y := pub.Y.Bytes()
- if n > len(y) {
- y = append(make([]byte, n-len(y)), y...)
- }
- // Field order is important.
- // See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
- return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
- p.Name,
- base64.RawURLEncoding.EncodeToString(x),
- base64.RawURLEncoding.EncodeToString(y),
- ), nil
- }
- return "", ErrUnsupportedKey
- }
-
- // jwsSign signs the digest using the given key.
- // It returns ErrUnsupportedKey if the key type is unknown.
- // The hash is used only for RSA keys.
- func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
- switch key := key.(type) {
- case *rsa.PrivateKey:
- return key.Sign(rand.Reader, digest, hash)
- case *ecdsa.PrivateKey:
- r, s, err := ecdsa.Sign(rand.Reader, key, digest)
- if err != nil {
- return nil, err
- }
- rb, sb := r.Bytes(), s.Bytes()
- size := key.Params().BitSize / 8
- if size%8 > 0 {
- size++
- }
- sig := make([]byte, size*2)
- copy(sig[size-len(rb):], rb)
- copy(sig[size*2-len(sb):], sb)
- return sig, nil
- }
- return nil, ErrUnsupportedKey
- }
-
- // jwsHasher indicates suitable JWS algorithm name and a hash function
- // to use for signing a digest with the provided key.
- // It returns ("", 0) if the key is not supported.
- func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
- switch key := key.(type) {
- case *rsa.PrivateKey:
- return "RS256", crypto.SHA256
- case *ecdsa.PrivateKey:
- switch key.Params().Name {
- case "P-256":
- return "ES256", crypto.SHA256
- case "P-384":
- return "ES384", crypto.SHA384
- case "P-521":
- return "ES512", crypto.SHA512
- }
- }
- return "", 0
- }
-
- // JWKThumbprint creates a JWK thumbprint out of pub
- // as specified in https://tools.ietf.org/html/rfc7638.
- func JWKThumbprint(pub crypto.PublicKey) (string, error) {
- jwk, err := jwkEncode(pub)
- if err != nil {
- return "", err
- }
- b := sha256.Sum256([]byte(jwk))
- return base64.RawURLEncoding.EncodeToString(b[:]), nil
- }
|