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.
13 _ "crypto/sha512" // need for EC keys
20 // jwsEncodeJSON signs claimset using provided key and a nonce.
21 // The result is serialized in JSON format.
22 // See https://tools.ietf.org/html/rfc7515#section-7.
23 func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
24 jwk, err := jwkEncode(key.Public())
28 alg, sha := jwsHasher(key)
29 if alg == "" || !sha.Available() {
30 return nil, ErrUnsupportedKey
32 phead := fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q}`, alg, jwk, nonce)
33 phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
34 cs, err := json.Marshal(claimset)
38 payload := base64.RawURLEncoding.EncodeToString(cs)
40 hash.Write([]byte(phead + "." + payload))
41 sig, err := jwsSign(key, sha, hash.Sum(nil))
47 Protected string `json:"protected"`
48 Payload string `json:"payload"`
49 Sig string `json:"signature"`
53 Sig: base64.RawURLEncoding.EncodeToString(sig),
55 return json.Marshal(&enc)
58 // jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
59 // The result is also suitable for creating a JWK thumbprint.
60 // https://tools.ietf.org/html/rfc7517
61 func jwkEncode(pub crypto.PublicKey) (string, error) {
62 switch pub := pub.(type) {
64 // https://tools.ietf.org/html/rfc7518#section-6.3.1
66 e := big.NewInt(int64(pub.E))
67 // Field order is important.
68 // See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
69 return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`,
70 base64.RawURLEncoding.EncodeToString(e.Bytes()),
71 base64.RawURLEncoding.EncodeToString(n.Bytes()),
73 case *ecdsa.PublicKey:
74 // https://tools.ietf.org/html/rfc7518#section-6.2.1
75 p := pub.Curve.Params()
82 x = append(make([]byte, n-len(x)), x...)
86 y = append(make([]byte, n-len(y)), y...)
88 // Field order is important.
89 // See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
90 return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`,
92 base64.RawURLEncoding.EncodeToString(x),
93 base64.RawURLEncoding.EncodeToString(y),
96 return "", ErrUnsupportedKey
99 // jwsSign signs the digest using the given key.
100 // It returns ErrUnsupportedKey if the key type is unknown.
101 // The hash is used only for RSA keys.
102 func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) {
103 switch key := key.(type) {
104 case *rsa.PrivateKey:
105 return key.Sign(rand.Reader, digest, hash)
106 case *ecdsa.PrivateKey:
107 r, s, err := ecdsa.Sign(rand.Reader, key, digest)
111 rb, sb := r.Bytes(), s.Bytes()
112 size := key.Params().BitSize / 8
116 sig := make([]byte, size*2)
117 copy(sig[size-len(rb):], rb)
118 copy(sig[size*2-len(sb):], sb)
121 return nil, ErrUnsupportedKey
124 // jwsHasher indicates suitable JWS algorithm name and a hash function
125 // to use for signing a digest with the provided key.
126 // It returns ("", 0) if the key is not supported.
127 func jwsHasher(key crypto.Signer) (string, crypto.Hash) {
128 switch key := key.(type) {
129 case *rsa.PrivateKey:
130 return "RS256", crypto.SHA256
131 case *ecdsa.PrivateKey:
132 switch key.Params().Name {
134 return "ES256", crypto.SHA256
136 return "ES384", crypto.SHA384
138 return "ES512", crypto.SHA512
144 // JWKThumbprint creates a JWK thumbprint out of pub
145 // as specified in https://tools.ietf.org/html/rfc7638.
146 func JWKThumbprint(pub crypto.PublicKey) (string, error) {
147 jwk, err := jwkEncode(pub)
151 b := sha256.Sum256([]byte(jwk))
152 return base64.RawURLEncoding.EncodeToString(b[:]), nil