forked from golang-jwt/jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Patrick Baker
committed
Jul 16, 2015
1 parent
2e53eb6
commit bf3befa
Showing
9 changed files
with
336 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package jwt | ||
|
||
import ( | ||
"crypto" | ||
"crypto/ecdsa" | ||
"crypto/rand" | ||
"encoding/asn1" | ||
"errors" | ||
"math/big" | ||
) | ||
|
||
var ( | ||
// Sadly this is missing from crypto/ecdsa compared to crypto/rsa | ||
ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") | ||
) | ||
|
||
// Implements the ECDSA family of signing methods signing methods | ||
type SigningMethodECDSA struct { | ||
Name string | ||
Hash crypto.Hash | ||
} | ||
|
||
// Marshalling structure for r, s EC point | ||
type ECPoint struct { | ||
R *big.Int | ||
S *big.Int | ||
} | ||
|
||
// Specific instances for EC256 and company | ||
var ( | ||
SigningMethodES256 *SigningMethodECDSA | ||
SigningMethodES384 *SigningMethodECDSA | ||
SigningMethodES512 *SigningMethodECDSA | ||
) | ||
|
||
func init() { | ||
// ES256 | ||
SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256} | ||
RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { | ||
return SigningMethodES256 | ||
}) | ||
|
||
// ES384 | ||
SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384} | ||
RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { | ||
return SigningMethodES384 | ||
}) | ||
|
||
// ES512 | ||
SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512} | ||
RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { | ||
return SigningMethodES512 | ||
}) | ||
} | ||
|
||
func (m *SigningMethodECDSA) Alg() string { | ||
return m.Name | ||
} | ||
|
||
// Implements the Verify method from SigningMethod | ||
// For this verify method, key must be an ecdsa.PublicKey struct | ||
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { | ||
var err error | ||
|
||
// Decode the signature | ||
var sig []byte | ||
if sig, err = DecodeSegment(signature); err != nil { | ||
return err | ||
} | ||
|
||
// Get the key | ||
var ecdsaKey *ecdsa.PublicKey | ||
switch k := key.(type) { | ||
case *ecdsa.PublicKey: | ||
ecdsaKey = k | ||
default: | ||
return ErrInvalidKey | ||
} | ||
|
||
// Unmarshal asn1 ECPoint | ||
var ecpoint = new(ECPoint) | ||
if _, err := asn1.Unmarshal(sig, ecpoint); err != nil { | ||
return err | ||
} | ||
|
||
// Create hasher | ||
if !m.Hash.Available() { | ||
return ErrHashUnavailable | ||
} | ||
hasher := m.Hash.New() | ||
hasher.Write([]byte(signingString)) | ||
|
||
// Verify the signature | ||
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), ecpoint.R, ecpoint.S); verifystatus == true { | ||
return nil | ||
} else { | ||
return ErrECDSAVerification | ||
} | ||
} | ||
|
||
// Implements the Sign method from SigningMethod | ||
// For this signing method, key must be an ecdsa.PrivateKey struct | ||
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { | ||
// Get the key | ||
var ecdsaKey *ecdsa.PrivateKey | ||
switch k := key.(type) { | ||
case *ecdsa.PrivateKey: | ||
ecdsaKey = k | ||
default: | ||
return "", ErrInvalidKey | ||
} | ||
|
||
// Create the hasher | ||
if !m.Hash.Available() { | ||
return "", ErrHashUnavailable | ||
} | ||
|
||
hasher := m.Hash.New() | ||
hasher.Write([]byte(signingString)) | ||
|
||
// Sign the string and return r, s | ||
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { | ||
// asn1 marhsal r, s using ecPoint as the structure | ||
var ecpoint = new(ECPoint) | ||
ecpoint.R = r | ||
ecpoint.S = s | ||
|
||
if signature, err := asn1.Marshal(*ecpoint); err != nil { | ||
return "", err | ||
} else { | ||
return EncodeSegment(signature), nil | ||
} | ||
} else { | ||
return "", err | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package jwt_test | ||
|
||
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) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package jwt | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"errors" | ||
) | ||
|
||
var ( | ||
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key") | ||
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") | ||
) | ||
|
||
// Parse PEM encoded Elliptic Curve Private Key Structure | ||
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, 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.ParseECPrivateKey(block.Bytes); err != nil { | ||
return nil, err | ||
} | ||
|
||
var pkey *ecdsa.PrivateKey | ||
var ok bool | ||
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { | ||
return nil, ErrNotECPrivateKey | ||
} | ||
|
||
return pkey, nil | ||
} | ||
|
||
// Parse PEM encoded PKCS1 or PKCS8 public key | ||
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.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 *ecdsa.PublicKey | ||
var ok bool | ||
if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { | ||
return nil, ErrNotECPublicKey | ||
} | ||
|
||
return pkey, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
-----BEGIN PUBLIC KEY----- | ||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYD54V/vp+54P9DXarYqx4MPcm+HK | ||
RIQzNasYSoRQHQ/6S6Ps8tpMcT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg== | ||
-----END PUBLIC KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
-----BEGIN EC PRIVATE KEY----- | ||
MIGkAgEBBDCaCvMHKhcG/qT7xsNLYnDT7sE/D+TtWIol1ROdaK1a564vx5pHbsRy | ||
SEKcIxISi1igBwYFK4EEACKhZANiAATYa7rJaU7feLMqrAx6adZFNQOpaUH/Uylb | ||
ZLriOLON5YFVwtVUpO1FfEXZUIQpptRPtc5ixIPY658yhBSb6irfIJUSP9aYTflJ | ||
GKk/mDkK4t8mWBzhiD5B6jg9cEGhGgA= | ||
-----END EC PRIVATE KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
-----BEGIN PUBLIC KEY----- | ||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2Gu6yWlO33izKqwMemnWRTUDqWlB/1Mp | ||
W2S64jizjeWBVcLVVKTtRXxF2VCEKabUT7XOYsSD2OufMoQUm+oq3yCVEj/WmE35 | ||
SRipP5g5CuLfJlgc4Yg+Qeo4PXBBoRoA | ||
-----END PUBLIC KEY----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
-----BEGIN PUBLIC KEY----- | ||
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACVyLsNdjFM6R4IImvTzgRWF0sWjh | ||
ihmzIyMgyPuqu8IuyzMNx4G2jpoCKhRu9qPCQUMGDeCG1x3/n/OgkWNQANsB82x7 | ||
7eiIZAl0zcQRH32tcjFILvJ/XCihdoi4MkCnCqlt9/HxjsP590ZtmHfxAeertq5w | ||
9vakvpzjPXhkvoMt/Tk= | ||
-----END PUBLIC KEY----- |