|
|
@@ -6,6 +6,7 @@ package acme
|
|
|
|
|
|
import (
|
|
|
"crypto"
|
|
|
+ "crypto/ecdsa"
|
|
|
"crypto/rand"
|
|
|
"crypto/rsa"
|
|
|
"crypto/sha256"
|
|
|
@@ -18,8 +19,11 @@ import (
|
|
|
// 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 *rsa.PrivateKey, nonce string) ([]byte, error) {
|
|
|
- jwk := jwkEncode(&key.PublicKey)
|
|
|
+func jwsEncodeJSON(claimset interface{}, key crypto.Signer, nonce string) ([]byte, error) {
|
|
|
+ jwk, err := jwkEncode(key.Public())
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
phead := fmt.Sprintf(`{"alg":"RS256","jwk":%s,"nonce":%q}`, jwk, nonce)
|
|
|
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
|
|
|
cs, err := json.Marshal(claimset)
|
|
|
@@ -29,7 +33,7 @@ func jwsEncodeJSON(claimset interface{}, key *rsa.PrivateKey, nonce string) ([]b
|
|
|
payload := base64.RawURLEncoding.EncodeToString(cs)
|
|
|
h := sha256.New()
|
|
|
h.Write([]byte(phead + "." + payload))
|
|
|
- sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
|
|
|
+ sig, err := key.Sign(rand.Reader, h.Sum(nil), crypto.SHA256)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
@@ -45,23 +49,54 @@ func jwsEncodeJSON(claimset interface{}, key *rsa.PrivateKey, nonce string) ([]b
|
|
|
return json.Marshal(&enc)
|
|
|
}
|
|
|
|
|
|
-// jwkEncode encodes public part of an RSA key into a JWK.
|
|
|
+// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
|
|
|
// The result is also suitable for creating a JWK thumbprint.
|
|
|
-func jwkEncode(pub *rsa.PublicKey) string {
|
|
|
- n := pub.N
|
|
|
- e := big.NewInt(int64(pub.E))
|
|
|
- // fields 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()),
|
|
|
- )
|
|
|
+// 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
|
|
|
}
|
|
|
|
|
|
// JWKThumbprint creates a JWK thumbprint out of pub
|
|
|
// as specified in https://tools.ietf.org/html/rfc7638.
|
|
|
-func JWKThumbprint(pub *rsa.PublicKey) string {
|
|
|
- jwk := jwkEncode(pub)
|
|
|
+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[:])
|
|
|
+ return base64.RawURLEncoding.EncodeToString(b[:]), nil
|
|
|
}
|