jws: add EncodeWithSigner function.

This permits controlling the mechanism for signing the token;
for instance, one can use EncodeWithSigner in an App Engine app
to use the App Identity API to perform the signing (you don't have
direct access to the private key there).

An alternate would be to replace Encode with EncodeWithSigner,
and add a little wrapper type that turns a *rsa.PrivateKey into
a Signer. That's probably what I'd do if this were being written
from scratch, but I wasn't keen on breaking existing code.

Change-Id: Id48f5dfa15c179832e613268d4a4098b96648f9a
Reviewed-on: https://go-review.googlesource.com/16711
Reviewed-by: Burcu Dogan <jbd@google.com>
Run-TryBot: David Symonds <dsymonds@golang.org>
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/jws/jws.go b/jws/jws.go
index 6b86a3e..e300001 100644
--- a/jws/jws.go
+++ b/jws/jws.go
@@ -119,8 +119,11 @@
 	return c, err
 }
 
-// Encode encodes a signed JWS with provided header and claim set.
-func Encode(header *Header, c *ClaimSet, signature *rsa.PrivateKey) (string, error) {
+// Signer returns a signature for the given data.
+type Signer func(data []byte) (sig []byte, err error)
+
+// EncodeWithSigner encodes a header and claim set with the provided signer.
+func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) {
 	head, err := header.encode()
 	if err != nil {
 		return "", err
@@ -130,14 +133,22 @@
 		return "", err
 	}
 	ss := fmt.Sprintf("%s.%s", head, cs)
-	h := sha256.New()
-	h.Write([]byte(ss))
-	b, err := rsa.SignPKCS1v15(rand.Reader, signature, crypto.SHA256, h.Sum(nil))
+	sig, err := sg([]byte(ss))
 	if err != nil {
 		return "", err
 	}
-	sig := base64Encode(b)
-	return fmt.Sprintf("%s.%s", ss, sig), nil
+	return fmt.Sprintf("%s.%s", ss, base64Encode(sig)), nil
+}
+
+// Encode encodes a signed JWS with provided header and claim set.
+// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key.
+func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) {
+	sg := func(data []byte) (sig []byte, err error) {
+		h := sha256.New()
+		h.Write([]byte(data))
+		return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
+	}
+	return EncodeWithSigner(header, c, sg)
 }
 
 // base64Encode returns and Base64url encoded version of the input string with any