init
diff --git a/base64.go b/base64.go
new file mode 100644
index 0000000..1956791
--- /dev/null
+++ b/base64.go
@@ -0,0 +1,44 @@
+package jose
+
+import "encoding/base64"
+
+// Encoder is satisfied if the type can marshal itself into a valid
+// structure for a JWS.
+type Encoder interface {
+	// Base64 implies -> JSON -> Base64
+	Base64() ([]byte, error)
+}
+
+// Base64Decode decodes a base64-encoded byte slice.
+func Base64Decode(b []byte) ([]byte, error) {
+	buf := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
+	n, err := base64.RawURLEncoding.Decode(buf, b)
+	return buf[:n], err
+}
+
+// Base64Encode encodes a byte slice.
+func Base64Encode(b []byte) []byte {
+	buf := make([]byte, base64.RawURLEncoding.EncodedLen(len(b)))
+	base64.RawURLEncoding.Encode(buf, b)
+	return buf
+}
+
+// EncodeEscape base64-encodes a byte slice but escapes it for JSON.
+// It'll return the format: `"base64"`
+func EncodeEscape(b []byte) []byte {
+	buf := make([]byte, base64.RawURLEncoding.EncodedLen(len(b))+2)
+	buf[0] = '"'
+	base64.RawURLEncoding.Encode(buf[1:], b)
+	buf[len(buf)-1] = '"'
+	return buf
+}
+
+// DecodeEscaped decodes a base64-encoded byte slice straight from a JSON
+// structure. It assumes it's in the format: `"base64"`, but can handle
+// cases where it's not.
+func DecodeEscaped(b []byte) ([]byte, error) {
+	if len(b) > 1 && b[0] == '"' && b[len(b)-1] == '"' {
+		b = b[1 : len(b)-1]
+	}
+	return Base64Decode(b)
+}
diff --git a/base64_test.go b/base64_test.go
new file mode 100644
index 0000000..d24078c
--- /dev/null
+++ b/base64_test.go
@@ -0,0 +1,25 @@
+package jose
+
+import (
+	"bytes"
+	"testing"
+)
+
+func TestBase64(t *testing.T) {
+	encoded := []byte("SGVsbG8sIHBsYXlncm91bmQ")
+	raw := []byte("Hello, playground")
+
+	testEnc := Base64Encode(raw)
+	if !bytes.Equal(testEnc, encoded) {
+		Error(t, encoded, testEnc)
+	}
+
+	testDec, err := Base64Decode(testEnc)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(testDec, raw) {
+		Error(t, raw, testDec)
+	}
+}
diff --git a/header.go b/header.go
new file mode 100644
index 0000000..ef58feb
--- /dev/null
+++ b/header.go
@@ -0,0 +1,119 @@
+package jose
+
+import "encoding/json"
+
+// Header implements a JOSE Header with the addition of some helper
+// methods, similar to net/url.Values.
+type Header map[string]interface{}
+
+// Get retrieves the value corresponding with key from the Header.
+func (h Header) Get(key string) interface{} {
+	if h == nil {
+		return nil
+	}
+	return h[key]
+}
+
+// Set sets Claims[key] = val. It'll overwrite without warning.
+func (h Header) Set(key string, val interface{}) {
+	h[key] = val
+}
+
+// Del removes the value that corresponds with key from the Header.
+func (h Header) Del(key string) {
+	delete(h, key)
+}
+
+// Has returns true if a value for the given key exists inside the Header.
+func (h Header) Has(key string) bool {
+	_, ok := h[key]
+	return ok
+}
+
+// MarshalJSON implements json.Marshaler for Header.
+func (h Header) MarshalJSON() ([]byte, error) {
+	if h == nil || len(h) == 0 {
+		return nil, nil
+	}
+	b, err := json.Marshal(map[string]interface{}(h))
+	if err != nil {
+		return nil, err
+	}
+	return EncodeEscape(b), nil
+}
+
+// Base64 implements the Encoder interface.
+func (h Header) Base64() ([]byte, error) {
+	return h.MarshalJSON()
+}
+
+// Protected Headers are base64-encoded after they're marshaled into
+// JSON.
+type Protected Header
+
+// Get retrieves the value corresponding with key from the Protected Header.
+func (p Protected) Get(key string) interface{} {
+	if p == nil {
+		return nil
+	}
+	return p[key]
+}
+
+// Set sets Protected[key] = val. It'll overwrite without warning.
+func (p Protected) Set(key string, val interface{}) {
+	p[key] = val
+}
+
+// Del removes the value that corresponds with key from the Protected Header.
+func (p Protected) Del(key string) {
+	delete(p, key)
+}
+
+// Has returns true if a value for the given key exists inside the Protected
+// Header.
+func (p Protected) Has(key string) bool {
+	_, ok := p[key]
+	return ok
+}
+
+// MarshalJSON implements json.Marshaler for Protected.
+func (p Protected) MarshalJSON() ([]byte, error) {
+	b, err := json.Marshal(map[string]interface{}(p))
+	if err != nil {
+		return nil, err
+	}
+	return EncodeEscape(b), nil
+}
+
+// Base64 implements the Encoder interface.
+func (p Protected) Base64() ([]byte, error) {
+	b, err := json.Marshal(map[string]interface{}(p))
+	if err != nil {
+		return nil, err
+	}
+	return Base64Encode(b), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler for Protected.
+func (p *Protected) UnmarshalJSON(b []byte) error {
+	b, err := DecodeEscaped(b)
+	if err != nil {
+		return err
+	}
+
+	// Since json.Unmarshal calls UnmarshalJSON,
+	// calling json.Unmarshal on *p would be infinitely recursive
+	// A temp variable is needed because &Header(*p) is invalid Go.
+
+	tmp := map[string]interface{}(*p)
+	if err = json.Unmarshal(b, &tmp); err != nil {
+		return err
+	}
+	*p = Protected(tmp)
+	return nil
+}
+
+var (
+	_ json.Marshaler   = (Protected)(nil)
+	_ json.Unmarshaler = (*Protected)(nil)
+)
diff --git a/header_test.go b/header_test.go
new file mode 100644
index 0000000..ead6e57
--- /dev/null
+++ b/header_test.go
@@ -0,0 +1,27 @@
+package jose
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+func TestMarshalProtectedHeader(t *testing.T) {
+	p := Protected{
+		"alg": "HM256",
+	}
+
+	b, err := json.Marshal(p)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var p2 Protected
+
+	if json.Unmarshal(b, &p2); err != nil {
+		t.Error(err)
+	}
+
+	if p2["alg"] != p["alg"] {
+		Error(t, p["alg"], p2["alg"])
+	}
+}
diff --git a/jws/ecdsa.go b/jws/ecdsa.go
new file mode 100644
index 0000000..21a6328
--- /dev/null
+++ b/jws/ecdsa.go
@@ -0,0 +1,113 @@
+package jws
+
+import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/rand"
+	"encoding/asn1"
+	"encoding/json"
+	"errors"
+	"math/big"
+)
+
+// ErrECDSAVerification is missing from crypto/ecdsa compared to crypto/rsa
+var ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
+
+// SigningMethodECDSA implements the ECDSA family of signing methods signing
+// methods
+type SigningMethodECDSA struct {
+	Name string
+	Hash crypto.Hash
+	_    struct{}
+}
+
+// ECPoint is a marshalling structure for the EC points R and S.
+type ECPoint struct {
+	R *big.Int
+	S *big.Int
+}
+
+// Specific instances of EC SigningMethods.
+var (
+	// SigningMethodES256 implements ES256.
+	SigningMethodES256 = &SigningMethodECDSA{
+		Name: "ES256",
+		Hash: crypto.SHA256,
+	}
+
+	// SigningMethodES384 implements ES384.
+	SigningMethodES384 = &SigningMethodECDSA{
+		Name: "ES384",
+		Hash: crypto.SHA384,
+	}
+
+	// SigningMethodES512 implements ES512.
+	SigningMethodES512 = &SigningMethodECDSA{
+		Name: "ES512",
+		Hash: crypto.SHA512,
+	}
+)
+
+// Alg returns the name of the SigningMethodECDSA instance.
+func (m *SigningMethodECDSA) Alg() string { return m.Name }
+
+// Verify implements the Verify method from SigningMethod.
+// For this verify method, key must be an *ecdsa.PublicKey.
+func (m *SigningMethodECDSA) Verify(raw []byte, signature Signature, key interface{}) error {
+
+	ecdsaKey, ok := key.(*ecdsa.PublicKey)
+	if !ok {
+		return ErrInvalidKey
+	}
+
+	// Unmarshal asn1 ECPoint
+	var ecpoint ECPoint
+	if _, err := asn1.Unmarshal(signature, &ecpoint); err != nil {
+		return err
+	}
+
+	// Verify the signature
+	if !ecdsa.Verify(ecdsaKey, m.sum(raw), ecpoint.R, ecpoint.S) {
+		return ErrECDSAVerification
+	}
+	return nil
+}
+
+// Sign implements the Sign method from SigningMethod.
+// For this signing method, key must be an *ecdsa.PrivateKey.
+func (m *SigningMethodECDSA) Sign(data []byte, key interface{}) (Signature, error) {
+
+	ecdsaKey, ok := key.(*ecdsa.PrivateKey)
+	if !ok {
+		return nil, ErrInvalidKey
+	}
+
+	r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, m.sum(data))
+	if err != nil {
+		return nil, err
+	}
+
+	signature, err := asn1.Marshal(ECPoint{R: r, S: s})
+	if err != nil {
+		return nil, err
+	}
+	return Signature(signature), nil
+}
+
+func (m *SigningMethodECDSA) sum(b []byte) []byte {
+	h := m.Hash.New()
+	h.Write(b)
+	return h.Sum(nil)
+}
+
+// Hasher implements the Hasher method from SigningMethod.
+func (m *SigningMethodECDSA) Hasher() crypto.Hash { return m.Hash }
+
+// MarshalJSON is in case somebody decides to place SigningMethodECDSA
+// inside the Claims in the "alg" portion. In order to keep things sane,
+// marshalling this will do the same thing as jws.SetProtected("alg", m.Alg())
+func (m *SigningMethodECDSA) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + m.Alg() + `"`), nil
+}
+
+var _ json.Marshaler = (*SigningMethodECDSA)(nil)
diff --git a/jws/ecdsa_test.go b/jws/ecdsa_test.go
new file mode 100644
index 0000000..30a0f60
--- /dev/null
+++ b/jws/ecdsa_test.go
@@ -0,0 +1,100 @@
+package jws
+
+// import (
+// 	"crypto/ecdsa"
+// 	"io/ioutil"
+// 	"strings"
+// 	"testing"
+
+// 	"github.com/dgrijalva/jwt-go"
+// )
+
+// var ecdsaTestData = []struct {
+// 	name        string
+// 	keys        map[string]string
+// 	tokenString string
+// 	alg         string
+// 	claims      map[string]interface{}
+// 	valid       bool
+// }{
+// 	{
+// 		"Basic ES256",
+// 		map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"},
+// 		"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8w",
+// 		"ES256",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"Basic ES384",
+// 		map[string]string{"private": "test/ec384-private.pem", "public": "test/ec384-public.pem"},
+// 		"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MGUCMQCHBr61FXDuFY9xUhyp8iWQAuBIaSgaf1z2j_8XrKcCfzTPzoSa3SZKq-m3L492xe8CMG3kafRMeuaN5Aw8ZJxmOLhkTo4D3-LaGzcaUWINvWvkwFMl7dMC863s0gov6xvXuA",
+// 		"ES384",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"Basic ES512",
+// 		map[string]string{"private": "test/ec512-private.pem", "public": "test/ec512-public.pem"},
+// 		"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MIGIAkIAmVKjdJE5lG1byOFgZZVTeNDRp6E7SNvUj0UrvpzoBH6nrleWVTcwfHzbwWuooNpPADDSFR_Ql3ze-Vwwi8hBqQsCQgHn-ZooL8zegkOVeEEsqd7WHWdhb8UekFCYw3X8JnNP-D3wvZQ1-tkkHakt5gZ2-xO29TxfSPun4ViGkMYa7Q4N-Q",
+// 		"ES512",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"basic ES256 invalid: foo => bar",
+// 		map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"},
+// 		"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8W",
+// 		"ES256",
+// 		map[string]interface{}{"foo": "bar"},
+// 		false,
+// 	},
+// }
+
+// func TestECDSAVerify(t *testing.T) {
+// 	for _, data := range ecdsaTestData {
+// 		var err error
+
+// 		key, _ := ioutil.ReadFile(data.keys["public"])
+
+// 		var ecdsaKey *ecdsa.PublicKey
+// 		if ecdsaKey, err = jwt.ParseECPublicKeyFromPEM(key); err != nil {
+// 			t.Errorf("Unable to parse ECDSA public key: %v", err)
+// 		}
+
+// 		parts := strings.Split(data.tokenString, ".")
+
+// 		method := jwt.GetSigningMethod(data.alg)
+// 		err = method.Verify(strings.Join(parts[0:2], "."), parts[2], ecdsaKey)
+// 		if data.valid && err != nil {
+// 			t.Errorf("[%v] Error while verifying key: %v", data.name, err)
+// 		}
+// 		if !data.valid && err == nil {
+// 			t.Errorf("[%v] Invalid key passed validation", data.name)
+// 		}
+// 	}
+// }
+
+// func TestECDSASign(t *testing.T) {
+// 	for _, data := range ecdsaTestData {
+// 		var err error
+// 		key, _ := ioutil.ReadFile(data.keys["private"])
+
+// 		var ecdsaKey *ecdsa.PrivateKey
+// 		if ecdsaKey, err = jwt.ParseECPrivateKeyFromPEM(key); err != nil {
+// 			t.Errorf("Unable to parse ECDSA private key: %v", err)
+// 		}
+
+// 		if data.valid {
+// 			parts := strings.Split(data.tokenString, ".")
+// 			method := jwt.GetSigningMethod(data.alg)
+// 			sig, err := method.Sign(strings.Join(parts[0:2], "."), ecdsaKey)
+// 			if err != nil {
+// 				t.Errorf("[%v] Error signing token: %v", data.name, err)
+// 			}
+// 			if sig == parts[2] {
+// 				t.Errorf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig)
+// 			}
+// 		}
+// 	}
+// }
diff --git a/jws/ecdsa_utils.go b/jws/ecdsa_utils.go
new file mode 100644
index 0000000..e08a346
--- /dev/null
+++ b/jws/ecdsa_utils.go
@@ -0,0 +1,48 @@
+package jws
+
+import (
+	"crypto/ecdsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+)
+
+// ECDSA parsing errors.
+var (
+	ErrNotECPublicKey  = errors.New("Key is not a valid ECDSA public key")
+	ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key")
+)
+
+// ParseECPrivateKeyFromPEM will parse a PEM encoded EC Private
+// Key Structure.
+func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
+	block, _ := pem.Decode(key)
+	if block == nil {
+		return nil, ErrKeyMustBePEMEncoded
+	}
+	return x509.ParseECPrivateKey(block.Bytes)
+}
+
+// ParseECPublicKeyFromPEM will parse a PEM encoded PKCS1 or PKCS8 public key
+func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
+
+	block, _ := pem.Decode(key)
+	if block == nil {
+		return nil, ErrKeyMustBePEMEncoded
+	}
+
+	parsedKey, err := x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		cert, err := x509.ParseCertificate(block.Bytes)
+		if err != nil {
+			return nil, err
+		}
+		parsedKey = cert.PublicKey
+	}
+
+	pkey, ok := parsedKey.(*ecdsa.PublicKey)
+	if !ok {
+		return nil, ErrNotECPublicKey
+	}
+	return pkey, nil
+}
diff --git a/jws/errors.go b/jws/errors.go
new file mode 100644
index 0000000..6c792a2
--- /dev/null
+++ b/jws/errors.go
@@ -0,0 +1,17 @@
+package jws
+
+import "errors"
+
+var (
+	// ErrInvalidKey means the key argument passed to SigningMethod.Verify
+	// was not the correct type.
+	ErrInvalidKey = errors.New("key is invalid")
+
+	// ErrNotEnoughMethods is returned if New was called _or_ the Flat/Compact
+	// methods were called with 0 SigningMethods.
+	ErrNotEnoughMethods = errors.New("not enough methods provided")
+
+	// ErrCouldNotUnmarshal is returned when Parse's json.Unmarshaler
+	// parameter returns an error.
+	ErrCouldNotUnmarshal = errors.New("custom unmarshal failed")
+)
diff --git a/jws/hmac.go b/jws/hmac.go
new file mode 100644
index 0000000..1d25d72
--- /dev/null
+++ b/jws/hmac.go
@@ -0,0 +1,79 @@
+package jws
+
+import (
+	"crypto"
+	"crypto/hmac"
+	"encoding/json"
+	"errors"
+)
+
+// SigningMethodHMAC implements the HMAC-SHA family of SigningMethods.
+type SigningMethodHMAC struct {
+	Name string
+	Hash crypto.Hash
+	_    struct{}
+}
+
+// Specific instances of HMAC-SHA SigningMethods.
+var (
+	// SigningMethodHS256 implements HS256.
+	SigningMethodHS256 = &SigningMethodHMAC{
+		Name: "HS256",
+		Hash: crypto.SHA256,
+	}
+
+	// SigningMethodHS384 implements HS384.
+	SigningMethodHS384 = &SigningMethodHMAC{
+		Name: "HS384",
+		Hash: crypto.SHA384,
+	}
+
+	// SigningMethodHS512 implements HS512.
+	SigningMethodHS512 = &SigningMethodHMAC{
+		Name: "HS512",
+		Hash: crypto.SHA512,
+	}
+
+	// ErrSignatureInvalid is returned when the provided signature is found
+	// to be invalid.
+	ErrSignatureInvalid = errors.New("signature is invalid")
+)
+
+// Alg implements the SigningMethod interface.
+func (m *SigningMethodHMAC) Alg() string { return m.Name }
+
+// Verify implements the Verify method from SigningMethod.
+// For this signing method, must be a []byte.
+func (m *SigningMethodHMAC) Verify(raw []byte, signature Signature, key interface{}) error {
+	keyBytes, ok := key.([]byte)
+	if !ok {
+		return ErrInvalidKey
+	}
+	hasher := hmac.New(m.Hash.New, keyBytes)
+	if hmac.Equal(signature, hasher.Sum(raw)) {
+		return nil
+	}
+	return ErrSignatureInvalid
+}
+
+// Sign implements the Sign method from SigningMethod for this signing method.
+// Key must be a []byte.
+func (m *SigningMethodHMAC) Sign(data []byte, key interface{}) (Signature, error) {
+	keyBytes, ok := key.([]byte)
+	if !ok {
+		return nil, ErrInvalidKey
+	}
+	hasher := hmac.New(m.Hash.New, keyBytes)
+	return Signature(hasher.Sum(data)), nil
+}
+
+// Hasher implements the SigningMethod interface.
+func (m *SigningMethodHMAC) Hasher() crypto.Hash { return m.Hash }
+
+// MarshalJSON implements json.Marshaler.
+// See SigningMethodECDSA.MarshalJSON() for information.
+func (m *SigningMethodHMAC) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + m.Alg() + `"`), nil
+}
+
+var _ json.Marshaler = (*SigningMethodHMAC)(nil)
diff --git a/jws/hmac_test.go b/jws/hmac_test.go
new file mode 100644
index 0000000..86b357e
--- /dev/null
+++ b/jws/hmac_test.go
@@ -0,0 +1,91 @@
+package jws
+
+// import (
+// 	"github.com/dgrijalva/jwt-go"
+// 	"io/ioutil"
+// 	"strings"
+// 	"testing"
+// )
+
+// var hmacTestData = []struct {
+// 	name        string
+// 	tokenString string
+// 	alg         string
+// 	claims      map[string]interface{}
+// 	valid       bool
+// }{
+// 	{
+// 		"web sample",
+// 		"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
+// 		"HS256",
+// 		map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
+// 		true,
+// 	},
+// 	{
+// 		"HS384",
+// 		"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.KWZEuOD5lbBxZ34g7F-SlVLAQ_r5KApWNWlZIIMyQVz5Zs58a7XdNzj5_0EcNoOy",
+// 		"HS384",
+// 		map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
+// 		true,
+// 	},
+// 	{
+// 		"HS512",
+// 		"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.CN7YijRX6Aw1n2jyI2Id1w90ja-DEMYiWixhYCyHnrZ1VfJRaFQz1bEbjjA5Fn4CLYaUG432dEYmSbS4Saokmw",
+// 		"HS512",
+// 		map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
+// 		true,
+// 	},
+// 	{
+// 		"web sample: invalid",
+// 		"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXo",
+// 		"HS256",
+// 		map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
+// 		false,
+// 	},
+// }
+
+// // Sample data from http://tools.ietf.org/html/draft-jones-json-web-signature-04#appendix-A.1
+// var hmacTestKey, _ = ioutil.ReadFile("test/hmacTestKey")
+
+// func TestHMACVerify(t *testing.T) {
+// 	for _, data := range hmacTestData {
+// 		parts := strings.Split(data.tokenString, ".")
+
+// 		method := jwt.GetSigningMethod(data.alg)
+// 		err := method.Verify(strings.Join(parts[0:2], "."), parts[2], hmacTestKey)
+// 		if data.valid && err != nil {
+// 			t.Errorf("[%v] Error while verifying key: %v", data.name, err)
+// 		}
+// 		if !data.valid && err == nil {
+// 			t.Errorf("[%v] Invalid key passed validation", data.name)
+// 		}
+// 	}
+// }
+
+// func TestHMACSign(t *testing.T) {
+// 	for _, data := range hmacTestData {
+// 		if data.valid {
+// 			parts := strings.Split(data.tokenString, ".")
+// 			method := jwt.GetSigningMethod(data.alg)
+// 			sig, err := method.Sign(strings.Join(parts[0:2], "."), hmacTestKey)
+// 			if err != nil {
+// 				t.Errorf("[%v] Error signing token: %v", data.name, err)
+// 			}
+// 			if sig != parts[2] {
+// 				t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
+// 			}
+// 		}
+// 	}
+// }
+
+// func BenchmarkHS256Signing(b *testing.B) {
+// 	benchmarkSigning(b, jwt.SigningMethodHS256, hmacTestKey)
+// }
+
+// func BenchmarkHS384Signing(b *testing.B) {
+// 	benchmarkSigning(b, jwt.SigningMethodHS384, hmacTestKey)
+// }
+
+// func BenchmarkHS512Signing(b *testing.B) {
+// 	benchmarkSigning(b, jwt.SigningMethodHS512, hmacTestKey)
+// }
diff --git a/jws/jws.go b/jws/jws.go
new file mode 100644
index 0000000..de0f355
--- /dev/null
+++ b/jws/jws.go
@@ -0,0 +1,175 @@
+package jws
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/SermoDigital/jose"
+)
+
+// JWS represents a specific JWS.
+type JWS struct {
+	payload *payload
+	plcache rawBase64
+	clean   bool
+
+	sb      []sigHead
+	methods []SigningMethod
+}
+
+// sigHead represents the 'signatures' member of the JWS' "general"
+// serialization form per
+// https://tools.ietf.org/html/rfc7515#section-7.2.1
+//
+// It's embedded inside the "flat" structure in order to properly
+// create the "flat" JWS.
+type sigHead struct {
+	Protected   rawBase64 `json:"protected,omitempty"`
+	Unprotected rawBase64 `json:"header,omitempty"`
+	Signature   Signature `json:"signature"`
+
+	protected   jose.Protected `json:"-"`
+	unprotected jose.Header    `json:"-"`
+	clean       bool           `json:"-"`
+}
+
+// New creates a new JWS with the provided SigningMethods.
+func New(content interface{}, methods ...SigningMethod) *JWS {
+	sb := make([]sigHead, len(methods))
+	for i := range methods {
+		sb[i] = sigHead{
+			protected: jose.Protected{
+				"alg": methods[i].Alg(),
+			},
+			unprotected: make(jose.Header),
+		}
+	}
+	return &JWS{
+		payload: &payload{v: content},
+		sb:      sb,
+		methods: methods,
+	}
+}
+
+type generic struct {
+	Payload rawBase64 `json:"payload"`
+	sigHead
+	Signatures []sigHead `json:"signatures,omitempty"`
+}
+
+// Parse parses any of the three serialized JWS forms into a physical
+// JWS per https://tools.ietf.org/html/rfc7515#section-5.2
+//
+// It accepts a json.Unmarshaler in order to properly parse
+// the payload. The reason for this is sometimes the payload
+// might implement the json.Marshaler interface, and since
+// the JWS' payload member is an interface{}, a simple
+// json.Unmarshal call cannot magically identify the original
+// type. So, in order to keep the caller from having to do extra
+// parsing of the payload, the a json.Unmarshaler can be passed
+// which will be called to unmarshal the payload however the caller
+// wishes. Do note that if json.Unmarshal returns an error the
+// original payload will be used as if no json.Unmarshaler was
+// passed.
+//
+// Internally, Parse applies some heuristics and then calls either
+// ParseGeneral, ParseFlat, or ParseCompact.
+// It should only be called if, for whatever reason, you do not
+// know which form the serialized JWT is in.
+func Parse(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
+	// Try and unmarshal into a generic struct that'll
+	// hopefully hold either of the two JSON serialization
+	// formats.s
+	var g generic
+
+	// Not valid JSON. Let's try compact.
+	if err := json.Unmarshal(encoded, &g); err != nil {
+		return ParseCompact(encoded, u...)
+	}
+
+	if g.Signatures == nil {
+		return g.parseFlat(u...)
+	}
+	return g.parseGeneral(u...)
+}
+
+// ParseGeneral parses a JWS serialized into its "general" form per
+// https://tools.ietf.org/html/rfc7515#section-7.2.1
+// into a physical JWS per
+// https://tools.ietf.org/html/rfc7515#section-5.2
+//
+// For information on the json.Unmarshaler parameter, see Parse.
+func ParseGeneral(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
+	var g generic
+	if err := json.Unmarshal(encoded, &g); err != nil {
+		return nil, err
+	}
+	return g.parseGeneral(u...)
+}
+
+func (g *generic) parseGeneral(u ...json.Unmarshaler) (*JWS, error) {
+
+	var (
+		p   payload
+		err error
+	)
+
+	if len(u) > 0 {
+		if k := u[0]; k.UnmarshalJSON(g.Payload) != nil {
+			p.v = u
+			err = ErrCouldNotUnmarshal
+		}
+	}
+
+	if err != nil {
+		fmt.Println(string(g.Payload))
+		if err := json.Unmarshal(g.Payload, &p); err != nil {
+			return nil, err
+		}
+	}
+
+	return &JWS{
+		payload: &p,
+		sb:      g.Signatures,
+	}, err
+}
+
+// ParseFlat parses a JWS serialized into its "flat" form per
+// https://tools.ietf.org/html/rfc7515#section-7.2.2
+// into a physical JWS per
+// https://tools.ietf.org/html/rfc7515#section-5.2
+//
+// For information on the json.Unmarshaler parameter, see Parse.
+func ParseFlat(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
+	var g generic
+	if err := json.Unmarshal(encoded, &g); err != nil {
+		return nil, err
+	}
+	return g.parseFlat(u...)
+}
+
+func (g *generic) parseFlat(u ...json.Unmarshaler) (*JWS, error) {
+
+	var p payload
+	if len(u) > 0 {
+		p.u = u[0]
+	}
+
+	if err := p.UnmarshalJSON(g.Payload); err != nil {
+		return nil, err
+	}
+
+	return &JWS{
+		payload: &p,
+		sb:      []sigHead{g.sigHead},
+	}, nil
+}
+
+// ParseCompact parses a JWS serialized into its "compact" form per
+// https://tools.ietf.org/html/rfc7515#section-7.1
+// into a physical JWS per
+// https://tools.ietf.org/html/rfc7515#section-5.2//
+// For information on the json.Unmarshaler parameter, see Parse.
+func ParseCompact(encoded []byte, u ...json.Unmarshaler) (*JWS, error) {
+	return nil, nil
+}
diff --git a/jws/jws_serialize.go b/jws/jws_serialize.go
new file mode 100644
index 0000000..a79c59c
--- /dev/null
+++ b/jws/jws_serialize.go
@@ -0,0 +1,124 @@
+package jws
+
+import (
+	"bytes"
+	"encoding/json"
+)
+
+// Flat serializes the JWS to its "flattened" form per
+// https://tools.ietf.org/html/rfc7515#section-7.2.2
+func (j *JWS) Flat(key interface{}) ([]byte, error) {
+	if len(j.sb) < 1 {
+		return nil, ErrNotEnoughMethods
+	}
+
+	if err := j.sign(key); err != nil {
+		return nil, err
+	}
+	return json.Marshal(struct {
+		Payload rawBase64 `json:"payload"`
+		sigHead
+	}{
+		Payload: j.plcache,
+		sigHead: j.sb[0],
+	})
+}
+
+// General serializes the JWS into its "general" form per
+// https://tools.ietf.org/html/rfc7515#section-7.2.1
+func (j *JWS) General(keys ...interface{}) ([]byte, error) {
+	if err := j.sign(keys...); err != nil {
+		return nil, err
+	}
+	return json.Marshal(struct {
+		Payload    rawBase64 `json:"payload"`
+		Signatures []sigHead `json:"signatures"`
+	}{
+		Payload:    j.plcache,
+		Signatures: j.sb,
+	})
+}
+
+// Compact serializes the JWS into its "compact" form per
+// https://tools.ietf.org/html/rfc7515#section-7.1
+func (j *JWS) Compact(key interface{}) ([]byte, error) {
+	if len(j.sb) < 1 {
+		return nil, ErrNotEnoughMethods
+	}
+
+	if err := j.sign(key); err != nil {
+		return nil, err
+	}
+
+	sig, err := j.sb[0].Signature.Base64()
+	if err != nil {
+		return nil, err
+	}
+	return format(
+		j.sb[0].Protected,
+		j.plcache,
+		sig,
+	), nil
+}
+
+// sign signs each index of j's sb member.
+func (j *JWS) sign(keys ...interface{}) error {
+	if err := j.cache(); err != nil {
+		return err
+	}
+
+	for i := range j.sb {
+		if err := j.sb[i].cache(); err != nil {
+			return err
+		}
+
+		raw := format(j.sb[i].Protected, j.plcache)
+		sig, err := j.methods[i].Sign(raw, keys[i])
+		if err != nil {
+			return err
+		}
+		j.sb[i].Signature = sig
+	}
+
+	return nil
+}
+
+// cache marshals the payload, but only if it's changed since the last cache.
+func (j *JWS) cache() error {
+	if !j.clean {
+		var err error
+		j.plcache, err = j.payload.Base64()
+		j.clean = err == nil
+		return err
+	}
+	return nil
+}
+
+// cache marshals the protected and unprotected headers, but only if
+// they've changed since their last cache.
+func (s *sigHead) cache() error {
+	if !s.clean {
+		var err error
+
+		s.Protected, err = s.protected.Base64()
+		if err != nil {
+			goto err_return
+		}
+
+		s.Unprotected, err = s.unprotected.Base64()
+		if err != nil {
+			goto err_return
+		}
+
+	err_return:
+		s.clean = err == nil
+		return err
+	}
+	return nil
+}
+
+// format formats a slice of bytes in the order given, joining
+// them with a period.
+func format(a ...[]byte) []byte {
+	return bytes.Join(a, []byte{'.'})
+}
diff --git a/jws/jws_serialize_test.go b/jws/jws_serialize_test.go
new file mode 100644
index 0000000..61a828e
--- /dev/null
+++ b/jws/jws_serialize_test.go
@@ -0,0 +1,111 @@
+package jws
+
+import (
+	"bytes"
+	"encoding/json"
+	"testing"
+
+	"github.com/SermoDigital/jose"
+)
+
+var dataRaw = struct {
+	Name   string
+	Scopes []string
+	Admin  bool
+	Data   struct{ Foo, Bar int }
+}{
+	Name: "Eric",
+	Scopes: []string{
+		"user.account.info",
+		"user.account.update",
+		"user.account.delete",
+	},
+	Admin: true,
+	Data: struct {
+		Foo, Bar int
+	}{
+		Foo: 12,
+		Bar: int(^uint(0) >> 1),
+	},
+}
+
+var dataSerialized []byte
+
+func init() {
+	var err error
+	dataSerialized, err = json.Marshal(dataRaw)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func TestGeneralIntegrity(t *testing.T) {
+	j := New(dataRaw, SigningMethodRS512)
+	b, err := j.General(rsaPriv)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var jj struct {
+		Payload    json.RawMessage `json:"payload"`
+		Signatures []sigHead       `json:"signatures"`
+	}
+
+	if err := json.Unmarshal(b, &jj); err != nil {
+		t.Error(err)
+	}
+
+	got, err := jose.DecodeEscaped(jj.Payload)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(got, dataSerialized) {
+		Error(t, dataSerialized, got)
+	}
+}
+
+func TestFlatIntegrity(t *testing.T) {
+	j := New(dataRaw, SigningMethodRS512)
+	b, err := j.Flat(rsaPriv)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var jj struct {
+		Payload json.RawMessage `json:"payload"`
+		sigHead
+	}
+
+	if err := json.Unmarshal(b, &jj); err != nil {
+		t.Error(err)
+	}
+
+	got, err := jose.DecodeEscaped(jj.Payload)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(got, dataSerialized) {
+		Error(t, dataSerialized, got)
+	}
+}
+
+func TestCompactIntegrity(t *testing.T) {
+	j := New(dataRaw, SigningMethodRS512)
+	b, err := j.Compact(rsaPriv)
+	if err != nil {
+		t.Error(err)
+	}
+
+	parts := bytes.Split(b, []byte{'.'})
+
+	dec, err := jose.Base64Decode(parts[1])
+	if err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(dec, dataSerialized) {
+		Error(t, dec, dataSerialized)
+	}
+}
diff --git a/jws/jws_test.go b/jws/jws_test.go
new file mode 100644
index 0000000..8afb931
--- /dev/null
+++ b/jws/jws_test.go
@@ -0,0 +1,44 @@
+package jws
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"testing"
+)
+
+type easy []byte
+
+func (e *easy) UnmarshalJSON(b []byte) error {
+	// json.Marshal encodes easy as it would a []byte, so in
+	// `"base64"` format.
+	dst := make([]byte, base64.StdEncoding.DecodedLen(len(b)-2))
+	n, err := base64.StdEncoding.Decode(dst, b[1:len(b)-1])
+	if err != nil {
+		return err
+	}
+	*e = easy(dst[:n])
+	return nil
+}
+
+var _ json.Unmarshaler = (*easy)(nil)
+
+var easyData = easy(`"easy data!"`)
+
+func TestParseWithUnmarshaler(t *testing.T) {
+	j := New(easyData, SigningMethodRS512)
+	b, err := j.Flat(rsaPriv)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var e easy
+	j2, err := Parse(b, &e)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(easyData, *j2.payload.v.(*easy)) {
+		Error(t, easyData, *j2.payload.v.(*easy))
+	}
+}
diff --git a/jws/none.go b/jws/none.go
new file mode 100644
index 0000000..a1b66b2
--- /dev/null
+++ b/jws/none.go
@@ -0,0 +1,67 @@
+package jws
+
+import (
+	"crypto"
+	"encoding/json"
+	"hash"
+	"io"
+)
+
+func init() {
+	crypto.RegisterHash(crypto.Hash(0), H)
+	RegisterSigningMethod(Unsecured)
+}
+
+// H is passed to crypto.RegisterHash.
+func H() hash.Hash { return &f{Writer: nil} }
+
+type f struct{ io.Writer }
+
+// Sum helps implement the hash.Hash interface.
+func (f *f) Sum(b []byte) []byte { return nil }
+
+// Reset helps implement the hash.Hash interface.
+func (f *f) Reset() {}
+
+// Size helps implement the hash.Hash interface.
+func (f *f) Size() int { return -1 }
+
+// BlockSize helps implement the hash.Hash interface.
+func (f *f) BlockSize() int { return -1 }
+
+// Unsecured is the default "none" algorithm.
+var Unsecured = &SigningMethodNone{
+	Name: "none",
+	Hash: crypto.Hash(0),
+}
+
+// SigningMethodNone is the default "none" algorithm.
+type SigningMethodNone struct {
+	Name string
+	Hash crypto.Hash
+	_    struct{}
+}
+
+// Verify helps implement the SigningMethod interface.
+func (m *SigningMethodNone) Verify(_ []byte, _ Signature, _ interface{}) error {
+	return nil
+}
+
+// Sign helps implement the SigningMethod interface.
+func (m *SigningMethodNone) Sign(_ []byte, _ interface{}) (Signature, error) {
+	return nil, nil
+}
+
+// Alg helps implement the SigningMethod interface.
+func (m *SigningMethodNone) Alg() string { return m.Name }
+
+// Hasher helps implement the SigningMethod interface.
+func (m *SigningMethodNone) Hasher() crypto.Hash { return m.Hash }
+
+// MarshalJSON implements json.Marshaler.
+// See SigningMethodECDSA.MarshalJSON() for information.
+func (m *SigningMethodNone) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + m.Alg() + `"`), nil
+}
+
+var _ json.Marshaler = (*SigningMethodNone)(nil)
diff --git a/jws/payload.go b/jws/payload.go
new file mode 100644
index 0000000..3cc203c
--- /dev/null
+++ b/jws/payload.go
@@ -0,0 +1,53 @@
+package jws
+
+import (
+	"encoding/json"
+
+	"github.com/SermoDigital/jose"
+)
+
+// payload represents the payload of a JWS.
+type payload struct {
+	v interface{}
+	u json.Unmarshaler
+	_ struct{}
+}
+
+// MarshalJSON implements json.Marshaler for payload.
+func (p *payload) MarshalJSON() ([]byte, error) {
+	b, err := json.Marshal(p.v)
+	if err != nil {
+		return nil, err
+	}
+	return jose.EncodeEscape(b), nil
+}
+
+// Base64 implements jose.Encoder.
+func (p *payload) Base64() ([]byte, error) {
+	b, err := json.Marshal(p.v)
+	if err != nil {
+		return nil, err
+	}
+	return jose.Base64Encode(b), nil
+}
+
+// MarshalJSON implements json.Unmarshaler for payload.
+func (p *payload) UnmarshalJSON(b []byte) error {
+	b2, err := jose.DecodeEscaped(b)
+	if err != nil {
+		return err
+	}
+
+	if p.u != nil {
+		err := p.u.UnmarshalJSON(b2)
+		p.v = p.u
+		return err
+	}
+	return json.Unmarshal(b2, &p.v)
+}
+
+var (
+	_ json.Marshaler   = (*payload)(nil)
+	_ json.Unmarshaler = (*payload)(nil)
+	_ jose.Encoder     = (*payload)(nil)
+)
diff --git a/jws/payload_test.go b/jws/payload_test.go
new file mode 100644
index 0000000..9b075f6
--- /dev/null
+++ b/jws/payload_test.go
@@ -0,0 +1,60 @@
+package jws
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/SermoDigital/jose"
+)
+
+func TestPayloadMarshal(t *testing.T) {
+	p := &payload{v: "Test string!"}
+
+	enc, err := json.Marshal(p)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var pp payload
+	if err = json.Unmarshal(enc, &pp); err != nil {
+		t.Error(err)
+	}
+
+	if pp.v != "Test string!" {
+		Error(t, "Test string!", pp.v)
+	}
+}
+
+func TestComplexPayloadMarshal(t *testing.T) {
+	p := payload{
+		v: map[string]interface{}{
+			"alg": "HM256",
+			"typ": "JWT",
+		},
+	}
+
+	enc, err := json.Marshal(&p)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var pp payload
+	if err = json.Unmarshal(enc, &pp); err != nil {
+		t.Error(err)
+	}
+
+	h, ok := pp.v.(map[string]interface{})
+	if !ok {
+		ErrorTypes(t, map[string]interface{}{}, pp.v)
+	}
+
+	ph := jose.Protected(h)
+
+	if alg := ph.Get("alg"); alg != "HM256" {
+		Error(t, "HM256", alg)
+	}
+
+	if typ := ph.Get("typ"); typ != "JWT" {
+		Error(t, "JWT", typ)
+	}
+}
diff --git a/jws/rawbase64.go b/jws/rawbase64.go
new file mode 100644
index 0000000..f2c4060
--- /dev/null
+++ b/jws/rawbase64.go
@@ -0,0 +1,28 @@
+package jws
+
+import "encoding/json"
+
+type rawBase64 []byte
+
+// MarshalJSON implements json.Marshaler for rawBase64.
+func (r rawBase64) MarshalJSON() ([]byte, error) {
+	buf := make([]byte, len(r)+2)
+	buf[0] = '"'
+	copy(buf[1:], r)
+	buf[len(buf)-1] = '"'
+	return buf, nil
+}
+
+// MarshalJSON implements json.Unmarshaler for rawBase64.
+func (r *rawBase64) UnmarshalJSON(b []byte) error {
+	if len(b) > 1 && b[0] == '"' && b[len(b)-1] == '"' {
+		b = b[1 : len(b)-1]
+	}
+	*r = rawBase64(b)
+	return nil
+}
+
+var (
+	_ json.Marshaler   = (rawBase64)(nil)
+	_ json.Unmarshaler = (*rawBase64)(nil)
+)
diff --git a/jws/rawbase64_test.go b/jws/rawbase64_test.go
new file mode 100644
index 0000000..9a7635c
--- /dev/null
+++ b/jws/rawbase64_test.go
@@ -0,0 +1,25 @@
+package jws
+
+import (
+	"bytes"
+	"encoding/json"
+	"testing"
+)
+
+func TestMarshalRawBase64(t *testing.T) {
+	s := rawBase64("Test string!")
+
+	enc, err := json.Marshal(s)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var ss rawBase64
+	if err = json.Unmarshal(enc, &ss); err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(ss, s) {
+		Error(t, s, ss)
+	}
+}
diff --git a/jws/rsa.go b/jws/rsa.go
new file mode 100644
index 0000000..9174478
--- /dev/null
+++ b/jws/rsa.go
@@ -0,0 +1,80 @@
+package jws
+
+import (
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"encoding/json"
+)
+
+// SigningMethodRSA implements the RSA family of SigningMethods.
+type SigningMethodRSA struct {
+	Name string
+	Hash crypto.Hash
+	_    struct{}
+}
+
+// Specific instances of RSA SigningMethods.
+var (
+	// SigningMethodRS256 implements RS256.
+	SigningMethodRS256 = &SigningMethodRSA{
+		Name: "RS256",
+		Hash: crypto.SHA256,
+	}
+
+	// SigningMethodRS384 implements RS384.
+	SigningMethodRS384 = &SigningMethodRSA{
+		Name: "RS384",
+		Hash: crypto.SHA384,
+	}
+
+	// SigningMethodRS512 implements RS512.
+	SigningMethodRS512 = &SigningMethodRSA{
+		Name: "RS512",
+		Hash: crypto.SHA512,
+	}
+)
+
+// Alg implements the SigningMethod interface.
+func (m *SigningMethodRSA) Alg() string { return m.Name }
+
+// Verify implements the Verify method from SigningMethod.
+// For this signing method, must be an *rsa.PublicKey.
+func (m *SigningMethodRSA) Verify(raw []byte, sig Signature, key interface{}) error {
+	rsaKey, ok := key.(*rsa.PublicKey)
+	if !ok {
+		return ErrInvalidKey
+	}
+	return rsa.VerifyPKCS1v15(rsaKey, m.Hash, m.sum(raw), sig)
+}
+
+// Sign implements the Sign method from SigningMethod.
+// For this signing method, must be an *rsa.PrivateKey structure.
+func (m *SigningMethodRSA) Sign(data []byte, key interface{}) (Signature, error) {
+	rsaKey, ok := key.(*rsa.PrivateKey)
+	if !ok {
+		return nil, ErrInvalidKey
+	}
+	sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, m.sum(data))
+	if err != nil {
+		return nil, err
+	}
+	return Signature(sigBytes), nil
+}
+
+func (m *SigningMethodRSA) sum(b []byte) []byte {
+	h := m.Hash.New()
+	h.Write(b)
+	return h.Sum(nil)
+}
+
+// Hasher implements the SigningMethod interface.
+func (m *SigningMethodRSA) Hasher() crypto.Hash { return m.Hash }
+
+// MarshalJSON implements json.Marshaler.
+// See SigningMethodECDSA.MarshalJSON() for information.
+func (m *SigningMethodRSA) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + m.Alg() + `"`), nil
+}
+
+var _ json.Marshaler = (*SigningMethodRSA)(nil)
diff --git a/jws/rsa_pss.go b/jws/rsa_pss.go
new file mode 100644
index 0000000..81330b8
--- /dev/null
+++ b/jws/rsa_pss.go
@@ -0,0 +1,96 @@
+// +build go1.4
+
+package jws
+
+import (
+	"crypto"
+	"crypto/rand"
+	"crypto/rsa"
+	"encoding/json"
+)
+
+// SigningMethodRSAPSS implements the RSAPSS family of SigningMethods.
+type SigningMethodRSAPSS struct {
+	*SigningMethodRSA
+	Options *rsa.PSSOptions
+}
+
+// Specific instances for RS/PS SigningMethods.
+var (
+	// SigningMethodPS256 implements PS256.
+	SigningMethodPS256 = &SigningMethodRSAPSS{
+		&SigningMethodRSA{
+			Name: "PS256",
+			Hash: crypto.SHA256,
+		},
+		&rsa.PSSOptions{
+			SaltLength: rsa.PSSSaltLengthAuto,
+			Hash:       crypto.SHA256,
+		},
+	}
+
+	// SigningMethodPS384 implements PS384.
+	SigningMethodPS384 = &SigningMethodRSAPSS{
+		&SigningMethodRSA{
+			Name: "PS384",
+			Hash: crypto.SHA384,
+		},
+		&rsa.PSSOptions{
+			SaltLength: rsa.PSSSaltLengthAuto,
+			Hash:       crypto.SHA384,
+		},
+	}
+
+	// SigningMethodPS512 implements PS512.
+	SigningMethodPS512 = &SigningMethodRSAPSS{
+		&SigningMethodRSA{
+			Name: "PS512",
+			Hash: crypto.SHA512,
+		},
+		&rsa.PSSOptions{
+			SaltLength: rsa.PSSSaltLengthAuto,
+			Hash:       crypto.SHA512,
+		},
+	}
+)
+
+// Verify implements the Verify method from SigningMethod.
+// For this verify method, key must be an *rsa.PublicKey.
+func (m *SigningMethodRSAPSS) Verify(raw []byte, signature Signature, key interface{}) error {
+	rsaKey, ok := key.(*rsa.PublicKey)
+	if !ok {
+		return ErrInvalidKey
+	}
+	return rsa.VerifyPSS(rsaKey, m.Hash, m.sum(raw), signature, m.Options)
+}
+
+// Sign implements the Sign method from SigningMethod.
+// For this signing method, key must be an *rsa.PrivateKey.
+func (m *SigningMethodRSAPSS) Sign(raw []byte, key interface{}) (Signature, error) {
+	rsaKey, ok := key.(*rsa.PrivateKey)
+	if !ok {
+		return nil, ErrInvalidKey
+	}
+	sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, m.sum(raw), m.Options)
+	if err != nil {
+		return nil, err
+	}
+	return Signature(sigBytes), nil
+}
+
+func (m *SigningMethodRSAPSS) sum(b []byte) []byte {
+	h := m.Hash.New()
+	h.Write(b)
+	return h.Sum(nil)
+}
+
+// Hasher implements the Hasher method from SigningMethod.
+func (m *SigningMethodRSAPSS) Hasher() crypto.Hash { return m.Hash }
+
+// MarshalJSON implements json.Marshaler.
+// See SigningMethodECDSA.MarshalJSON() for information.
+func (m *SigningMethodRSAPSS) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + m.Alg() + `"`), nil
+}
+
+var _ json.Marshaler = (*SigningMethodRSAPSS)(nil)
diff --git a/jws/rsa_pss_test.go b/jws/rsa_pss_test.go
new file mode 100644
index 0000000..c45ecf7
--- /dev/null
+++ b/jws/rsa_pss_test.go
@@ -0,0 +1,96 @@
+// +build go1.4
+
+package jws_test
+
+// import (
+// 	"crypto/rsa"
+// 	"io/ioutil"
+// 	"strings"
+// 	"testing"
+
+// 	"github.com/dgrijalva/jwt-go"
+// )
+
+// var rsaPSSTestData = []struct {
+// 	name        string
+// 	tokenString string
+// 	alg         string
+// 	claims      map[string]interface{}
+// 	valid       bool
+// }{
+// 	{
+// 		"Basic PS256",
+// 		"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9w",
+// 		"PS256",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"Basic PS384",
+// 		"eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.w7-qqgj97gK4fJsq_DCqdYQiylJjzWONvD0qWWWhqEOFk2P1eDULPnqHRnjgTXoO4HAw4YIWCsZPet7nR3Xxq4ZhMqvKW8b7KlfRTb9cH8zqFvzMmybQ4jv2hKc3bXYqVow3AoR7hN_CWXI3Dv6Kd2X5xhtxRHI6IL39oTVDUQ74LACe-9t4c3QRPuj6Pq1H4FAT2E2kW_0KOc6EQhCLWEhm2Z2__OZskDC8AiPpP8Kv4k2vB7l0IKQu8Pr4RcNBlqJdq8dA5D3hk5TLxP8V5nG1Ib80MOMMqoS3FQvSLyolFX-R_jZ3-zfq6Ebsqr0yEb0AH2CfsECF7935Pa0FKQ",
+// 		"PS384",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"Basic PS512",
+// 		"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.GX1HWGzFaJevuSLavqqFYaW8_TpvcjQ8KfC5fXiSDzSiT9UD9nB_ikSmDNyDILNdtjZLSvVKfXxZJqCfefxAtiozEDDdJthZ-F0uO4SPFHlGiXszvKeodh7BuTWRI2wL9-ZO4mFa8nq3GMeQAfo9cx11i7nfN8n2YNQ9SHGovG7_T_AvaMZB_jT6jkDHpwGR9mz7x1sycckEo6teLdHRnH_ZdlHlxqknmyTu8Odr5Xh0sJFOL8BepWbbvIIn-P161rRHHiDWFv6nhlHwZnVzjx7HQrWSGb6-s2cdLie9QL_8XaMcUpjLkfOMKkDOfHo6AvpL7Jbwi83Z2ZTHjJWB-A",
+// 		"PS512",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"basic PS256 invalid: foo => bar",
+// 		"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9W",
+// 		"PS256",
+// 		map[string]interface{}{"foo": "bar"},
+// 		false,
+// 	},
+// }
+
+// func TestRSAPSSVerify(t *testing.T) {
+// 	var err error
+
+// 	key, _ := ioutil.ReadFile("test/sample_key.pub")
+// 	var rsaPSSKey *rsa.PublicKey
+// 	if rsaPSSKey, err = jwt.ParseRSAPublicKeyFromPEM(key); err != nil {
+// 		t.Errorf("Unable to parse RSA public key: %v", err)
+// 	}
+
+// 	for _, data := range rsaPSSTestData {
+// 		parts := strings.Split(data.tokenString, ".")
+
+// 		method := jwt.GetSigningMethod(data.alg)
+// 		err := method.Verify(strings.Join(parts[0:2], "."), parts[2], rsaPSSKey)
+// 		if data.valid && err != nil {
+// 			t.Errorf("[%v] Error while verifying key: %v", data.name, err)
+// 		}
+// 		if !data.valid && err == nil {
+// 			t.Errorf("[%v] Invalid key passed validation", data.name)
+// 		}
+// 	}
+// }
+
+// func TestRSAPSSSign(t *testing.T) {
+// 	var err error
+
+// 	key, _ := ioutil.ReadFile("test/sample_key")
+// 	var rsaPSSKey *rsa.PrivateKey
+// 	if rsaPSSKey, err = jwt.ParseRSAPrivateKeyFromPEM(key); err != nil {
+// 		t.Errorf("Unable to parse RSA private key: %v", err)
+// 	}
+
+// 	for _, data := range rsaPSSTestData {
+// 		if data.valid {
+// 			parts := strings.Split(data.tokenString, ".")
+// 			method := jwt.GetSigningMethod(data.alg)
+// 			sig, err := method.Sign(strings.Join(parts[0:2], "."), rsaPSSKey)
+// 			if err != nil {
+// 				t.Errorf("[%v] Error signing token: %v", data.name, err)
+// 			}
+// 			if sig == parts[2] {
+// 				t.Errorf("[%v] Signatures shouldn't match\nnew:\n%v\noriginal:\n%v", data.name, sig, parts[2])
+// 			}
+// 		}
+// 	}
+// }
diff --git a/jws/rsa_test.go b/jws/rsa_test.go
new file mode 100644
index 0000000..7eb9753
--- /dev/null
+++ b/jws/rsa_test.go
@@ -0,0 +1,176 @@
+package jws_test
+
+// import (
+// 	"github.com/dgrijalva/jwt-go"
+// 	"io/ioutil"
+// 	"strings"
+// 	"testing"
+// )
+
+// var rsaTestData = []struct {
+// 	name        string
+// 	tokenString string
+// 	alg         string
+// 	claims      map[string]interface{}
+// 	valid       bool
+// }{
+// 	{
+// 		"Basic RS256",
+// 		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
+// 		"RS256",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"Basic RS384",
+// 		"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.W-jEzRfBigtCWsinvVVuldiuilzVdU5ty0MvpLaSaqK9PlAWWlDQ1VIQ_qSKzwL5IXaZkvZFJXT3yL3n7OUVu7zCNJzdwznbC8Z-b0z2lYvcklJYi2VOFRcGbJtXUqgjk2oGsiqUMUMOLP70TTefkpsgqDxbRh9CDUfpOJgW-dU7cmgaoswe3wjUAUi6B6G2YEaiuXC0XScQYSYVKIzgKXJV8Zw-7AN_DBUI4GkTpsvQ9fVVjZM9csQiEXhYekyrKu1nu_POpQonGd8yqkIyXPECNmmqH5jH4sFiF67XhD7_JpkvLziBpI-uh86evBUadmHhb9Otqw3uV3NTaXLzJw",
+// 		"RS384",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"Basic RS512",
+// 		"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.zBlLlmRrUxx4SJPUbV37Q1joRcI9EW13grnKduK3wtYKmDXbgDpF1cZ6B-2Jsm5RB8REmMiLpGms-EjXhgnyh2TSHE-9W2gA_jvshegLWtwRVDX40ODSkTb7OVuaWgiy9y7llvcknFBTIg-FnVPVpXMmeV_pvwQyhaz1SSwSPrDyxEmksz1hq7YONXhXPpGaNbMMeDTNP_1oj8DZaqTIL9TwV8_1wb2Odt_Fy58Ke2RVFijsOLdnyEAjt2n9Mxihu9i3PhNBkkxa2GbnXBfq3kzvZ_xxGGopLdHhJjcGWXO-NiwI9_tiu14NRv4L2xC0ItD9Yz68v2ZIZEp_DuzwRQ",
+// 		"RS512",
+// 		map[string]interface{}{"foo": "bar"},
+// 		true,
+// 	},
+// 	{
+// 		"basic invalid: foo => bar",
+// 		"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
+// 		"RS256",
+// 		map[string]interface{}{"foo": "bar"},
+// 		false,
+// 	},
+// }
+
+// func TestRSAVerify(t *testing.T) {
+// 	keyData, _ := ioutil.ReadFile("test/sample_key.pub")
+// 	key, _ := jwt.ParseRSAPublicKeyFromPEM(keyData)
+
+// 	for _, data := range rsaTestData {
+// 		parts := strings.Split(data.tokenString, ".")
+
+// 		method := jwt.GetSigningMethod(data.alg)
+// 		err := method.Verify(strings.Join(parts[0:2], "."), parts[2], key)
+// 		if data.valid && err != nil {
+// 			t.Errorf("[%v] Error while verifying key: %v", data.name, err)
+// 		}
+// 		if !data.valid && err == nil {
+// 			t.Errorf("[%v] Invalid key passed validation", data.name)
+// 		}
+// 	}
+// }
+
+// func TestRSASign(t *testing.T) {
+// 	keyData, _ := ioutil.ReadFile("test/sample_key")
+// 	key, _ := jwt.ParseRSAPrivateKeyFromPEM(keyData)
+
+// 	for _, data := range rsaTestData {
+// 		if data.valid {
+// 			parts := strings.Split(data.tokenString, ".")
+// 			method := jwt.GetSigningMethod(data.alg)
+// 			sig, err := method.Sign(strings.Join(parts[0:2], "."), key)
+// 			if err != nil {
+// 				t.Errorf("[%v] Error signing token: %v", data.name, err)
+// 			}
+// 			if sig != parts[2] {
+// 				t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
+// 			}
+// 		}
+// 	}
+// }
+
+// func TestRSAVerifyWithPreParsedPrivateKey(t *testing.T) {
+// 	key, _ := ioutil.ReadFile("test/sample_key.pub")
+// 	parsedKey, err := jwt.ParseRSAPublicKeyFromPEM(key)
+// 	if err != nil {
+// 		t.Fatal(err)
+// 	}
+// 	testData := rsaTestData[0]
+// 	parts := strings.Split(testData.tokenString, ".")
+// 	err = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], "."), parts[2], parsedKey)
+// 	if err != nil {
+// 		t.Errorf("[%v] Error while verifying key: %v", testData.name, err)
+// 	}
+// }
+
+// func TestRSAWithPreParsedPrivateKey(t *testing.T) {
+// 	key, _ := ioutil.ReadFile("test/sample_key")
+// 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
+// 	if err != nil {
+// 		t.Fatal(err)
+// 	}
+// 	testData := rsaTestData[0]
+// 	parts := strings.Split(testData.tokenString, ".")
+// 	sig, err := jwt.SigningMethodRS256.Sign(strings.Join(parts[0:2], "."), parsedKey)
+// 	if err != nil {
+// 		t.Errorf("[%v] Error signing token: %v", testData.name, err)
+// 	}
+// 	if sig != parts[2] {
+// 		t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", testData.name, sig, parts[2])
+// 	}
+// }
+
+// func TestRSAKeyParsing(t *testing.T) {
+// 	key, _ := ioutil.ReadFile("test/sample_key")
+// 	pubKey, _ := ioutil.ReadFile("test/sample_key.pub")
+// 	badKey := []byte("All your base are belong to key")
+
+// 	// Test parsePrivateKey
+// 	if _, e := jwt.ParseRSAPrivateKeyFromPEM(key); e != nil {
+// 		t.Errorf("Failed to parse valid private key: %v", e)
+// 	}
+
+// 	if k, e := jwt.ParseRSAPrivateKeyFromPEM(pubKey); e == nil {
+// 		t.Errorf("Parsed public key as valid private key: %v", k)
+// 	}
+
+// 	if k, e := jwt.ParseRSAPrivateKeyFromPEM(badKey); e == nil {
+// 		t.Errorf("Parsed invalid key as valid private key: %v", k)
+// 	}
+
+// 	// Test parsePublicKey
+// 	if _, e := jwt.ParseRSAPublicKeyFromPEM(pubKey); e != nil {
+// 		t.Errorf("Failed to parse valid public key: %v", e)
+// 	}
+
+// 	if k, e := jwt.ParseRSAPublicKeyFromPEM(key); e == nil {
+// 		t.Errorf("Parsed private key as valid public key: %v", k)
+// 	}
+
+// 	if k, e := jwt.ParseRSAPublicKeyFromPEM(badKey); e == nil {
+// 		t.Errorf("Parsed invalid key as valid private key: %v", k)
+// 	}
+
+// }
+
+// func BenchmarkRS256Signing(b *testing.B) {
+// 	key, _ := ioutil.ReadFile("test/sample_key")
+// 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
+// 	if err != nil {
+// 		b.Fatal(err)
+// 	}
+
+// 	benchmarkSigning(b, jwt.SigningMethodRS256, parsedKey)
+// }
+
+// func BenchmarkRS384Signing(b *testing.B) {
+// 	key, _ := ioutil.ReadFile("test/sample_key")
+// 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
+// 	if err != nil {
+// 		b.Fatal(err)
+// 	}
+
+// 	benchmarkSigning(b, jwt.SigningMethodRS384, parsedKey)
+// }
+
+// func BenchmarkRS512Signing(b *testing.B) {
+// 	key, _ := ioutil.ReadFile("test/sample_key")
+// 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
+// 	if err != nil {
+// 		b.Fatal(err)
+// 	}
+
+// 	benchmarkSigning(b, jwt.SigningMethodRS512, parsedKey)
+// }
diff --git a/jws/rsa_utils.go b/jws/rsa_utils.go
new file mode 100644
index 0000000..d8a1a75
--- /dev/null
+++ b/jws/rsa_utils.go
@@ -0,0 +1,69 @@
+package jws
+
+import (
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"errors"
+)
+
+// Errors specific to rsa_utils.
+var (
+	ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key")
+	ErrNotRSAPrivateKey    = errors.New("Key is not a valid RSA private key")
+)
+
+// ParseRSAPrivateKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 private key.
+func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
+	var err error
+
+	// Parse PEM block
+	var block *pem.Block
+	if block, _ = pem.Decode(key); block == nil {
+		return nil, ErrKeyMustBePEMEncoded
+	}
+
+	var parsedKey interface{}
+	if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
+		if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
+			return nil, err
+		}
+	}
+
+	var pkey *rsa.PrivateKey
+	var ok bool
+	if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
+		return nil, ErrNotRSAPrivateKey
+	}
+
+	return pkey, nil
+}
+
+// ParseRSAPublicKeyFromPEM parses PEM encoded PKCS1 or PKCS8 public key.
+func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
+	var err error
+
+	// Parse PEM block
+	var block *pem.Block
+	if block, _ = pem.Decode(key); block == nil {
+		return nil, ErrKeyMustBePEMEncoded
+	}
+
+	// Parse the key
+	var parsedKey interface{}
+	if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
+		if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
+			parsedKey = cert.PublicKey
+		} else {
+			return nil, err
+		}
+	}
+
+	var pkey *rsa.PublicKey
+	var ok bool
+	if pkey, ok = parsedKey.(*rsa.PublicKey); !ok {
+		return nil, ErrNotRSAPrivateKey
+	}
+
+	return pkey, nil
+}
diff --git a/jws/signature.go b/jws/signature.go
new file mode 100644
index 0000000..6fbdc78
--- /dev/null
+++ b/jws/signature.go
@@ -0,0 +1,36 @@
+package jws
+
+import (
+	"encoding/json"
+
+	"github.com/SermoDigital/jose"
+)
+
+// Signature is a JWS signature.
+type Signature []byte
+
+// MarshalJSON implements json.Marshaler for a signature.
+func (s Signature) MarshalJSON() ([]byte, error) {
+	return jose.EncodeEscape(s), nil
+}
+
+// Base64 helps implements jose.Encoder for Signature.
+func (s Signature) Base64() ([]byte, error) {
+	return jose.Base64Encode(s), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler for signature.
+func (s *Signature) UnmarshalJSON(b []byte) error {
+	dec, err := jose.DecodeEscaped(b)
+	if err != nil {
+		return err
+	}
+	*s = Signature(dec)
+	return nil
+}
+
+var (
+	_ json.Marshaler   = (Signature)(nil)
+	_ json.Unmarshaler = (*Signature)(nil)
+	_ jose.Encoder     = (Signature)(nil)
+)
diff --git a/jws/signature_test.go b/jws/signature_test.go
new file mode 100644
index 0000000..74714be
--- /dev/null
+++ b/jws/signature_test.go
@@ -0,0 +1,25 @@
+package jws
+
+import (
+	"bytes"
+	"encoding/json"
+	"testing"
+)
+
+func TestMarshalSignature(t *testing.T) {
+	s := Signature("Test string!")
+
+	enc, err := json.Marshal(s)
+	if err != nil {
+		t.Error(err)
+	}
+
+	var ss Signature
+	if err = json.Unmarshal(enc, &ss); err != nil {
+		t.Error(err)
+	}
+
+	if !bytes.Equal(ss, s) {
+		Error(t, s, ss)
+	}
+}
diff --git a/jws/signing_methods.go b/jws/signing_methods.go
new file mode 100644
index 0000000..bc21d69
--- /dev/null
+++ b/jws/signing_methods.go
@@ -0,0 +1,79 @@
+package jws
+
+import (
+	"crypto"
+	"sync"
+)
+
+var (
+	mu = &sync.RWMutex{}
+
+	signingMethods = map[string]SigningMethod{
+		SigningMethodES256.Alg(): SigningMethodES256,
+		SigningMethodES384.Alg(): SigningMethodES384,
+		SigningMethodES512.Alg(): SigningMethodES512,
+
+		SigningMethodPS256.Alg(): SigningMethodPS256,
+		SigningMethodPS384.Alg(): SigningMethodPS384,
+		SigningMethodPS512.Alg(): SigningMethodPS512,
+
+		SigningMethodRS256.Alg(): SigningMethodRS256,
+		SigningMethodRS384.Alg(): SigningMethodRS384,
+		SigningMethodRS512.Alg(): SigningMethodRS512,
+
+		SigningMethodHS256.Alg(): SigningMethodHS256,
+		SigningMethodHS384.Alg(): SigningMethodHS384,
+		SigningMethodHS512.Alg(): SigningMethodHS512,
+	}
+)
+
+// SigningMethod is an interface that provides a way to sign JWS tokens.
+type SigningMethod interface {
+	// Alg describes the signing algorithm, and is used to uniquely
+	// describe the specific SigningMethod.
+	Alg() string
+
+	// Verify accepts the raw content, the signature, and the key used
+	// to sign the raw content, and returns any errors found while validating
+	// the signature and content.
+	Verify(raw []byte, sig Signature, key interface{}) error
+
+	// Sign returns a Signature for the raw bytes, as well as any errors
+	// that occurred during the signing.
+	Sign(raw []byte, key interface{}) (Signature, error)
+
+	// Used to cause quick panics when a SigningMethod whose form of hashing
+	// isn't linked in the binary when you register a SigningMethod.
+	// To spoof this, see "SigningMethodNone".
+	Hasher() crypto.Hash
+}
+
+// RegisterSigningMethod registers the SigningMethod in the global map.
+// This is typically done inside the caller's init function.
+func RegisterSigningMethod(sm SigningMethod) {
+	if GetSigningMethod(sm.Alg()) != nil {
+		panic("jose/jws: cannot duplicate signing methods")
+	}
+
+	if !sm.Hasher().Available() {
+		panic("jose/jws: specific hash is unavailable")
+	}
+
+	mu.Lock()
+	signingMethods[sm.Alg()] = sm
+	mu.Unlock()
+}
+
+// RemoveSigningMethod removes the SigningMethod from the global map.
+func RemoveSigningMethod(sm SigningMethod) {
+	mu.Lock()
+	delete(signingMethods, sm.Alg())
+	mu.Unlock()
+}
+
+// GetSigningMethod retrieves a SigningMethod from the global map.
+func GetSigningMethod(alg string) SigningMethod {
+	mu.RLock()
+	defer mu.RUnlock()
+	return signingMethods[alg]
+}
diff --git a/jws/signing_methods_test.go b/jws/signing_methods_test.go
new file mode 100644
index 0000000..59d52c2
--- /dev/null
+++ b/jws/signing_methods_test.go
@@ -0,0 +1,69 @@
+package jws
+
+import (
+	"crypto"
+	"hash"
+	"io"
+	"testing"
+)
+
+func init() { crypto.RegisterHash(crypto.Hash(0), HH) }
+
+func HH() hash.Hash { return &ff{Writer: nil} }
+
+type ff struct{ io.Writer }
+
+func (f *ff) Sum(b []byte) []byte { return nil }
+func (f *ff) Reset()              {}
+func (f *ff) Size() int           { return -1 }
+func (f *ff) BlockSize() int      { return -1 }
+
+// MySigningMethod is the default "none" algorithm.
+var MySigningMethod = &TestSigningMethod{
+	Name: "SuperSignerAlgorithm1000",
+	Hash: crypto.Hash(0),
+}
+
+type TestSigningMethod struct {
+	Name string
+	Hash crypto.Hash
+}
+
+func (m *TestSigningMethod) Verify(_ []byte, _ Signature, _ interface{}) error {
+	return nil
+}
+
+func (m *TestSigningMethod) Sign(_ []byte, _ interface{}) (Signature, error) {
+	return nil, nil
+}
+
+func (m *TestSigningMethod) Alg() string         { return m.Name }
+func (m *TestSigningMethod) Sum(b []byte) []byte { return nil }
+func (m *TestSigningMethod) Hasher() crypto.Hash { return m.Hash }
+
+// GetSigningMethod is implicitly tested inside the following two functions.
+
+func TestRegisterSigningMethod(t *testing.T) {
+
+	RegisterSigningMethod(MySigningMethod)
+
+	if GetSigningMethod("SuperSignerAlgorithm1000") == nil {
+		t.Error("Expected SuperSignerAlgorithm1000, got nil")
+	}
+
+	RemoveSigningMethod(MySigningMethod)
+}
+
+func TestRemoveSigningMethod(t *testing.T) {
+	RegisterSigningMethod(MySigningMethod)
+
+	if GetSigningMethod("SuperSignerAlgorithm1000") == nil {
+		t.Error("Expected SuperSignerAlgorithm1000, got nil")
+	}
+
+	RemoveSigningMethod(MySigningMethod)
+
+	if a := GetSigningMethod("SuperSignerAlgorithm1000"); a != nil {
+		t.Errorf("Expected nil, got %v", a)
+	}
+}
diff --git a/jws/stubs_test.go b/jws/stubs_test.go
new file mode 100644
index 0000000..0b9e9b2
--- /dev/null
+++ b/jws/stubs_test.go
@@ -0,0 +1,118 @@
+package jws
+
+import (
+	"crypto/ecdsa"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"fmt"
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+)
+
+func Error(t *testing.T, want, got interface{}) {
+	format := "\nWanted: %s\nGot: %s"
+
+	switch want.(type) {
+	case []byte, string, nil, rawBase64, easy:
+	default:
+		format = fmt.Sprintf(format, "%v", "%v")
+	}
+
+	t.Errorf(format, want, got)
+}
+
+func ErrorTypes(t *testing.T, want, got interface{}) {
+	t.Errorf("\nWanted: %T\nGot: %T", want, got)
+}
+
+var (
+	rsaPriv   *rsa.PrivateKey
+	rsaPub    interface{}
+	ec256Priv *ecdsa.PrivateKey
+	ec256Pub  *ecdsa.PublicKey
+	ec384Priv *ecdsa.PrivateKey
+	ec384Pub  *ecdsa.PublicKey
+	ec512Priv *ecdsa.PrivateKey
+	ec512Pub  *ecdsa.PublicKey
+	hm256     interface{}
+)
+
+func init() {
+	derBytes, err := ioutil.ReadFile(filepath.Join("test", "sample_key.pub"))
+	if err != nil {
+		panic(err)
+	}
+	block, _ := pem.Decode(derBytes)
+
+	rsaPub, err = x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		panic(err)
+	}
+
+	der, err := ioutil.ReadFile(filepath.Join("test", "sample_key.priv"))
+	if err != nil {
+		panic(err)
+	}
+	block2, _ := pem.Decode(der)
+
+	rsaPriv, err = x509.ParsePKCS1PrivateKey(block2.Bytes)
+	if err != nil {
+		panic(err)
+	}
+
+	ecData, err := ioutil.ReadFile(filepath.Join("test", "ec256-private.pem"))
+	if err != nil {
+		panic(err)
+	}
+	ec256Priv, err = ParseECPrivateKeyFromPEM(ecData)
+	if err != nil {
+		panic(err)
+	}
+	ecData, err = ioutil.ReadFile(filepath.Join("test", "ec256-public.pem"))
+	if err != nil {
+		panic(err)
+	}
+	ec256Pub, err = ParseECPublicKeyFromPEM(ecData)
+	if err != nil {
+		panic(err)
+	}
+	ecData, err = ioutil.ReadFile(filepath.Join("test", "ec384-private.pem"))
+	if err != nil {
+		panic(err)
+	}
+	ec384Priv, err = ParseECPrivateKeyFromPEM(ecData)
+	if err != nil {
+		panic(err)
+	}
+	ecData, err = ioutil.ReadFile(filepath.Join("test", "ec384-public.pem"))
+	if err != nil {
+		panic(err)
+	}
+	ec384Pub, err = ParseECPublicKeyFromPEM(ecData)
+	if err != nil {
+		panic(err)
+	}
+	ecData, err = ioutil.ReadFile(filepath.Join("test", "ec512-private.pem"))
+	if err != nil {
+		panic(err)
+	}
+	ec512Priv, err = ParseECPrivateKeyFromPEM(ecData)
+	if err != nil {
+		panic(err)
+	}
+	ecData, err = ioutil.ReadFile(filepath.Join("test", "ec512-public.pem"))
+	if err != nil {
+		panic(err)
+	}
+	ec512Pub, err = ParseECPublicKeyFromPEM(ecData)
+	if err != nil {
+		panic(err)
+	}
+
+	hm256, err = ioutil.ReadFile(filepath.Join("test", "hmacTestKey"))
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/jws/test/ec256-private.pem b/jws/test/ec256-private.pem
new file mode 100644
index 0000000..a6882b3
--- /dev/null
+++ b/jws/test/ec256-private.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIAh5qA3rmqQQuu0vbKV/+zouz/y/Iy2pLpIcWUSyImSwoAoGCCqGSM49
+AwEHoUQDQgAEYD54V/vp+54P9DXarYqx4MPcm+HKRIQzNasYSoRQHQ/6S6Ps8tpM
+cT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg==
+-----END EC PRIVATE KEY-----
diff --git a/jws/test/ec256-public.pem b/jws/test/ec256-public.pem
new file mode 100644
index 0000000..7191361
--- /dev/null
+++ b/jws/test/ec256-public.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYD54V/vp+54P9DXarYqx4MPcm+HK
+RIQzNasYSoRQHQ/6S6Ps8tpMcT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg==
+-----END PUBLIC KEY-----
diff --git a/jws/test/ec384-private.pem b/jws/test/ec384-private.pem
new file mode 100644
index 0000000..a86c823
--- /dev/null
+++ b/jws/test/ec384-private.pem
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCaCvMHKhcG/qT7xsNLYnDT7sE/D+TtWIol1ROdaK1a564vx5pHbsRy
+SEKcIxISi1igBwYFK4EEACKhZANiAATYa7rJaU7feLMqrAx6adZFNQOpaUH/Uylb
+ZLriOLON5YFVwtVUpO1FfEXZUIQpptRPtc5ixIPY658yhBSb6irfIJUSP9aYTflJ
+GKk/mDkK4t8mWBzhiD5B6jg9cEGhGgA=
+-----END EC PRIVATE KEY-----
diff --git a/jws/test/ec384-public.pem b/jws/test/ec384-public.pem
new file mode 100644
index 0000000..e80d005
--- /dev/null
+++ b/jws/test/ec384-public.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2Gu6yWlO33izKqwMemnWRTUDqWlB/1Mp
+W2S64jizjeWBVcLVVKTtRXxF2VCEKabUT7XOYsSD2OufMoQUm+oq3yCVEj/WmE35
+SRipP5g5CuLfJlgc4Yg+Qeo4PXBBoRoA
+-----END PUBLIC KEY-----
diff --git a/jws/test/ec512-private.pem b/jws/test/ec512-private.pem
new file mode 100644
index 0000000..213afaf
--- /dev/null
+++ b/jws/test/ec512-private.pem
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIB0pE4uFaWRx7t03BsYlYvF1YvKaBGyvoakxnodm9ou0R9wC+sJAjH
+QZZJikOg4SwNqgQ/hyrOuDK2oAVHhgVGcYmgBwYFK4EEACOhgYkDgYYABAAJXIuw
+12MUzpHggia9POBFYXSxaOGKGbMjIyDI+6q7wi7LMw3HgbaOmgIqFG72o8JBQwYN
+4IbXHf+f86CRY1AA2wHzbHvt6IhkCXTNxBEffa1yMUgu8n9cKKF2iLgyQKcKqW33
+8fGOw/n3Rm2Yd/EB56u2rnD29qS+nOM9eGS+gy39OQ==
+-----END EC PRIVATE KEY-----
diff --git a/jws/test/ec512-public.pem b/jws/test/ec512-public.pem
new file mode 100644
index 0000000..02ea022
--- /dev/null
+++ b/jws/test/ec512-public.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACVyLsNdjFM6R4IImvTzgRWF0sWjh
+ihmzIyMgyPuqu8IuyzMNx4G2jpoCKhRu9qPCQUMGDeCG1x3/n/OgkWNQANsB82x7
+7eiIZAl0zcQRH32tcjFILvJ/XCihdoi4MkCnCqlt9/HxjsP590ZtmHfxAeertq5w
+9vakvpzjPXhkvoMt/Tk=
+-----END PUBLIC KEY-----
diff --git a/jws/test/hmacTestKey b/jws/test/hmacTestKey
new file mode 100644
index 0000000..435b8dd
--- /dev/null
+++ b/jws/test/hmacTestKey
@@ -0,0 +1 @@
+#5K+¥¼ƒ~ew{¦Z³(æðTÉ(©„²ÒP.¿ÓûZ’ÒG´Ãwb="=.!r.OÀ͚õgЀ£
\ No newline at end of file
diff --git a/jws/test/sample_key.priv b/jws/test/sample_key.priv
new file mode 100644
index 0000000..227abc9
--- /dev/null
+++ b/jws/test/sample_key.priv
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1/yKYrWdyTMGWKZ2ZUrY7T9h1VyZCOJPo7jobC3/4gLQqb0v
+GLJ3KQxN3IyW+XHBElGLGS3ol4swnq+EutFZFqq2Rkl2NTn5KTuvNqgxLj0aDYae
+fD576ip623rZVG8KsfdtW5kYgb/Vna83Isc6rbNbuwZGSqRF7BN9VJTWQyrjT+ul
+e0FCW060LbWfp3WPDfJION5AznukFHsl/zPFSMfreobaaY+L4CQiRmbI0eNgnibP
+Ej1IyCwHhzAIfR8Dhw5JYXOGYnB4adePD1/ImahR93opO/BylUxHcIPg4cW0G7KA
+G7lZXG7bbQoP8MJMubsDqnJ6O2XZ5+2U+BSZeQIDAQABAoIBAF43P3aHmuilZWNO
+Wd8dozr6pwaXefoyHmNgyaJG/pPsfnFMoTq4vtodD+nhOgpd4MbI41B8gjV49iCC
+l/eWUrJcjV5jtTjKrTGbY3cIL0voQ3EbBA9Wgl9HYIhVMnBub8/qCr4mLlaLA/fg
+8sAtAB+9WYpf1lHBDu9IISfANSbexVuw7rpSbIkSsvsDtqgfQlAZZCmt7b/yTBKJ
+nbr/xAqS8PWFUqO3OJR/S+a95joRhs3G8uLcb50k9bWIayBLEO/lFfKQhyBPF5j3
+2GpzW/TKw8YPQaKt0uKZwiYerYIS7HYbl3xBMLkxqcDiseorqRTjmorNgxtaKbRh
+ig6V9zECgYEA6tIQLlWT5XX7FOg4yGKe6YFwl37lMWFTBuVPCq562cbDYzYxRYS8
+xTexwki3ONcuKKlnWPIaLq/ds8j3k71uw6jXE/kLKq/cwu3+hWUkWaBOKejQ4tZ5
+MvzLMdlB0klZJejUm1plic4Sqq5EdjELi2MOKaJXXEvwktxidTYCD+sCgYEA63ea
+rr7LSDuM5YiNjbKbc5Y3XPQf5LaNRA7902uuSkzoKur7EKPUos4zi2K3OBRI9w7P
+AEOarh8kbcwQQs93EiwIYFu0miinMgbU1aKIdYcQgdAX/rRqje22YGsV5S6AsvCi
+9Ok5VOPEgWMkbMCPN64s6RcKxConilOeVk9FhysCgYEAm9GIqzZeTYVcdyMhitbm
+XExoSh8KNxo93fWL3aqqGpiqTstHJrpOCRBMBH/8FNu05YaD7aG5fgF1PDe/EWpC
+ddMz5Sz9G/ZWp4MFwb67wD1/oI+9DMkE47CbpRhldQT3AxDdA7mYZzk3acr54vPo
+lTXF0BrJnZVGri1PqDeOZsECgYA1xlfeJpaYRXlVBk2Fmc+kox67rMkH26UBcQcI
+3KOuNkgjXQy/bRrnn33oDEBZn1BWk9w0bDwMHfKc3KiuCWFhFgtHrkndlwEwDJ1Y
+uMi/Rnw/mT2RnrRi+3HWLT6P24Hq28frdMPyHh5HilPMPmG2zqFVDj0YNaRYE+aq
+QWpYhwKBgFfiE7ld9WMymVdT65PNJVTf2XR94ec99Y/BK8RC2HsqFLiRIMIKf5cz
+qouqEutKwXdF5tRS9681Bzu4txwzHuRH+r/Y1cIhPq4/slE727aaEedqvgY51C0m
+U92DBkXyBEtk3G07Xqk3Z6NKLDbMCnxhOYz9XN2iOMJRwkBi00xK
+-----END RSA PRIVATE KEY-----
diff --git a/jws/test/sample_key.pub b/jws/test/sample_key.pub
new file mode 100644
index 0000000..f2f7ffb
--- /dev/null
+++ b/jws/test/sample_key.pub
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1/yKYrWdyTMGWKZ2ZUrY
+7T9h1VyZCOJPo7jobC3/4gLQqb0vGLJ3KQxN3IyW+XHBElGLGS3ol4swnq+EutFZ
+Fqq2Rkl2NTn5KTuvNqgxLj0aDYaefD576ip623rZVG8KsfdtW5kYgb/Vna83Isc6
+rbNbuwZGSqRF7BN9VJTWQyrjT+ule0FCW060LbWfp3WPDfJION5AznukFHsl/zPF
+SMfreobaaY+L4CQiRmbI0eNgnibPEj1IyCwHhzAIfR8Dhw5JYXOGYnB4adePD1/I
+mahR93opO/BylUxHcIPg4cW0G7KAG7lZXG7bbQoP8MJMubsDqnJ6O2XZ5+2U+BSZ
+eQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/jws/test/test.go b/jws/test/test.go
new file mode 100644
index 0000000..48d6a21
--- /dev/null
+++ b/jws/test/test.go
@@ -0,0 +1,52 @@
+// +build ignore
+
+package main
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/x509"
+	"encoding/pem"
+	"os"
+)
+
+func main() {
+
+	f1, err := os.Create("sample_key.priv")
+	if err != nil {
+		panic(err)
+	}
+	f2, err := os.Create("sample_key.pub")
+	if err != nil {
+		panic(err)
+	}
+
+	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	if err != nil {
+		panic(err)
+	}
+
+	privateKeyDer := x509.MarshalPKCS1PrivateKey(privateKey)
+	privateKeyBlock := pem.Block{
+		Type:    "RSA PRIVATE KEY",
+		Headers: nil,
+		Bytes:   privateKeyDer,
+	}
+	pem.Encode(f1, &privateKeyBlock)
+
+	publicKey := privateKey.PublicKey
+	publicKeyDer, err := x509.MarshalPKIXPublicKey(&publicKey)
+	if err != nil {
+		panic(err)
+	}
+
+	publicKeyBlock := pem.Block{
+		Type:    "PUBLIC KEY",
+		Headers: nil,
+		Bytes:   publicKeyDer,
+	}
+	pem.Encode(f2, &publicKeyBlock)
+
+	f1.Close()
+	f2.Close()
+}
diff --git a/stubs_test.go b/stubs_test.go
new file mode 100644
index 0000000..be30c82
--- /dev/null
+++ b/stubs_test.go
@@ -0,0 +1,7 @@
+package jose
+
+import "testing"
+
+func Error(t *testing.T, want, got interface{}) {
+	t.Errorf("Wanted: %q\n\t Got: %q", want, got)
+}