acme: support custom crypto.Signer implementations

Currently, only rsa.PrivateKey and ecdsa.PrivateKey are supported
when creating JWS signatures. However, it is unnecessarily limiting
because any crypto.Signer implementation can sign a digest
in the appropriate format.

This change uses key.Public() instead of type-asserting the private
key which allows for a custom crypto.Signer implementation.
For instance, a key stored in a hardware module where the latter
does the actual signing without the key ever leaving its boundaries.

Change-Id: Ie7930ea2ba8c49dde7107ff074ae34abec05bdb9
Reviewed-on: https://go-review.googlesource.com/c/145137
Run-TryBot: Alex Vaghin <ddos@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/acme/jws.go b/acme/jws.go
index 6cbca25..1093b50 100644
--- a/acme/jws.go
+++ b/acme/jws.go
@@ -25,7 +25,7 @@
 	if err != nil {
 		return nil, err
 	}
-	alg, sha := jwsHasher(key)
+	alg, sha := jwsHasher(key.Public())
 	if alg == "" || !sha.Available() {
 		return nil, ErrUnsupportedKey
 	}
@@ -97,13 +97,16 @@
 }
 
 // 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.
+// The hash is unused for ECDSA keys.
+//
+// Note: non-stdlib crypto.Signer implementations are expected to return
+// the signature in the format as specified in RFC7518.
+// See https://tools.ietf.org/html/rfc7518 for more details.
 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:
+	if key, ok := key.(*ecdsa.PrivateKey); ok {
+		// The key.Sign method of ecdsa returns ASN1-encoded signature.
+		// So, we use the package Sign function instead
+		// to get R and S values directly and format the result accordingly.
 		r, s, err := ecdsa.Sign(rand.Reader, key, digest)
 		if err != nil {
 			return nil, err
@@ -118,18 +121,18 @@
 		copy(sig[size*2-len(sb):], sb)
 		return sig, nil
 	}
-	return nil, ErrUnsupportedKey
+	return key.Sign(rand.Reader, digest, hash)
 }
 
 // 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:
+func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) {
+	switch pub := pub.(type) {
+	case *rsa.PublicKey:
 		return "RS256", crypto.SHA256
-	case *ecdsa.PrivateKey:
-		switch key.Params().Name {
+	case *ecdsa.PublicKey:
+		switch pub.Params().Name {
 		case "P-256":
 			return "ES256", crypto.SHA256
 		case "P-384":