Skip to content

Commit d81c10b

Browse files
committed
Merge pull request jpadilla#40 from sullivanmatt/master
Support for Elliptic Curve signatures (ES256 / ES384 / ES512)
2 parents fb7d4ac + 491e0a7 commit d81c10b

File tree

8 files changed

+274
-23
lines changed

8 files changed

+274
-23
lines changed

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ A Python implementation of [JSON Web Token draft 01](http://self-issued.info/doc
88
$ pip install PyJWT
99
```
1010

11-
**Note**: The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
12-
using any of those algorithms you'll need to install it as well.
11+
**A Note on Dependencies**:
12+
13+
The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
14+
using any of those algorithms, you'll need to install it as well.
1315

1416
```
1517
$ pip install PyCrypto
1618
```
1719

20+
The Elliptic Curve Digital Signature algorithms depend on Python-ECDSA. If
21+
you plan on using any of those algorithms, you'll need to install it as well.
22+
23+
```
24+
$ pip install ecdsa
25+
```
26+
1827
## Usage
1928

2029
```python
@@ -49,6 +58,9 @@ currently supports:
4958
* HS256 - HMAC using SHA-256 hash algorithm (default)
5059
* HS384 - HMAC using SHA-384 hash algorithm
5160
* HS512 - HMAC using SHA-512 hash algorithm
61+
* ES256 - ECDSA signature algorithm using SHA-256 hash algorithm
62+
* ES384 - ECDSA signature algorithm using SHA-384 hash algorithm
63+
* ES512 - ECDSA signature algorithm using SHA-512 hash algorithm
5264
* RS256 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-256 hash algorithm
5365
* RS384 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-384 hash algorithm
5466
* RS512 - RSASSA-PKCS1-v1_5 signature algorithm using SHA-512 hash algorithm
@@ -61,7 +73,11 @@ jwt.encode({'some': 'payload'}, 'secret', 'HS512')
6173

6274
When using the RSASSA-PKCS1-v1_5 algorithms, the `key` argument in both
6375
`jwt.encode()` and `jwt.decode()` (`"secret"` in the examples) is expected to
64-
be an RSA private key as imported with `Crypto.PublicKey.RSA.importKey()`.
76+
be an RSA public or private key as imported with `Crypto.PublicKey.RSA.importKey()`.
77+
78+
When using the ECDSA algorithms, the `key` argument is expected to
79+
be an Elliptic Curve private key as imported with `ecdsa.SigningKey.from_pem()`,
80+
or a public key as imported with `ecdsa.VerifyingKey.from_pem()`.
6581

6682
## Tests
6783

jwt/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,55 @@ def prepare_RS_key(key):
108108
except ImportError:
109109
pass
110110

111+
try:
112+
import ecdsa
113+
from Crypto.Hash import SHA256
114+
from Crypto.Hash import SHA384
115+
from Crypto.Hash import SHA512
116+
117+
signing_methods.update({
118+
'ES256': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der),
119+
'ES384': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha384, sigencode=ecdsa.util.sigencode_der),
120+
'ES512': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha512, sigencode=ecdsa.util.sigencode_der),
121+
})
122+
123+
verify_methods.update({
124+
'ES256': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der),
125+
'ES384': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha384, sigdecode=ecdsa.util.sigdecode_der),
126+
'ES512': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha512, sigdecode=ecdsa.util.sigdecode_der),
127+
})
128+
129+
def prepare_ES_key(key):
130+
if isinstance(key, basestring):
131+
if isinstance(key, unicode):
132+
key = key.encode('utf-8')
133+
134+
# Attempt to load key. We don't know if it's
135+
# a Signing Key or a Verifying Key, so we try
136+
# the Verifying Key first.
137+
try:
138+
key = ecdsa.VerifyingKey.from_pem(key)
139+
except ecdsa.der.UnexpectedDER:
140+
try:
141+
key = ecdsa.SigningKey.from_pem(key)
142+
except:
143+
raise
144+
elif isinstance(key, ecdsa.SigningKey) or isinstance(key, ecdsa.VerifyingKey):
145+
pass
146+
else:
147+
raise TypeError("Expecting a PEM-formatted key.")
148+
return key
149+
150+
prepare_key_methods.update({
151+
'ES256': prepare_ES_key,
152+
'ES384': prepare_ES_key,
153+
'ES512': prepare_ES_key
154+
})
155+
156+
except ImportError:
157+
pass
158+
159+
111160

112161
def constant_time_compare(val1, val2):
113162
"""

0 commit comments

Comments
 (0)