jws: add RS256 Verification for JWS

Provides helper method for verifying a signed JWT against a provided
public key.

Change-Id: I498ecfce07862c372fd5f81c1fcdc09692ed0f5c
Reviewed-on: https://go-review.googlesource.com/21762
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/jws/jws.go b/jws/jws.go
index 8ca5978..29887ea 100644
--- a/jws/jws.go
+++ b/jws/jws.go
@@ -145,12 +145,31 @@
 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))
+		h.Write(data)
 		return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
 	}
 	return EncodeWithSigner(header, c, sg)
 }
 
+// Verify tests whether the provided JWT token's signature was produced by the private key
+// associated with the supplied public key.
+func Verify(token string, key *rsa.PublicKey) error {
+	parts := strings.Split(token, ".")
+	if len(parts) != 3 {
+		return errors.New("jws: invalid token received, token must have 3 parts")
+	}
+
+	signedContent := parts[0] + "." + parts[1]
+	signatureString, err := base64Decode(parts[2])
+	if err != nil {
+		return err
+	}
+
+	h := sha256.New()
+	h.Write([]byte(signedContent))
+	return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString))
+}
+
 // base64Encode returns and Base64url encoded version of the input string with any
 // trailing "=" stripped.
 func base64Encode(b []byte) string {
diff --git a/jws/jws_test.go b/jws/jws_test.go
new file mode 100644
index 0000000..d8369f6
--- /dev/null
+++ b/jws/jws_test.go
@@ -0,0 +1,46 @@
+// Copyright 2016 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 jws
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"testing"
+)
+
+func TestSignAndVerify(t *testing.T) {
+	header := &Header{
+		Algorithm: "RS256",
+		Typ:       "JWT",
+	}
+	payload := &ClaimSet{
+		Iss: "http://google.com/",
+		Aud: "",
+		Exp: 3610,
+		Iat: 10,
+	}
+
+	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	token, err := Encode(header, payload, privateKey)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err := Verify(token, &privateKey.PublicKey)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestVerifyFailsOnMalformedClaim(t *testing.T) {
+	err := Verify("abc.def", nil)
+	if err == nil {
+		t.Error("Improperly formed JWT should fail.")
+	}
+}