Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 75bbe71

Browse files
committedDec 22, 2014
Merge pull request #51 from mark-adams/change-to-cryptography
Switch to cryptography for RSA and ECDSA algorithms
2 parents 5ce6d44 + 0d647eb commit 75bbe71

File tree

5 files changed

+151
-107
lines changed

5 files changed

+151
-107
lines changed
 

‎AUTHORS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ Patches and Suggestions
1010

1111
- Åsmund Ødegård <asmund@xal.no> <ao@mcash.no>
1212
Adding support for RSA-SHA256 privat/public signature.
13+
14+
- Mark Adams <mark@markadams.me>

‎README.md

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,11 @@ $ pip install PyJWT
1010

1111
**A Note on Dependencies**:
1212

13-
The RSASSA-PKCS1-v1_5 algorithms depend on PyCrypto. If you plan on
13+
RSA and ECDSA signatures depend on the cryptography package. If you plan on
1414
using any of those algorithms, you'll need to install it as well.
1515

1616
```
17-
$ pip install PyCrypto
18-
```
19-
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
17+
$ pip install cryptography
2518
```
2619

2720
## Usage
@@ -73,11 +66,11 @@ jwt.encode({'some': 'payload'}, 'secret', 'HS512')
7366

7467
When using the RSASSA-PKCS1-v1_5 algorithms, the `key` argument in both
7568
`jwt.encode()` and `jwt.decode()` (`"secret"` in the examples) is expected to
76-
be an RSA public or private key as imported with `Crypto.PublicKey.RSA.importKey()`.
69+
be either an RSA public or private key in PEM format.
7770

7871
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()`.
72+
be an Elliptic Curve private key or an Elliptic Curve public
73+
key in PEM foramt.
8174

8275
## Tests
8376

‎jwt/__init__.py

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
basestring = str
2626

2727

28-
__version__ = '0.3.2'
28+
__version__ = '0.4'
2929
__all__ = ['encode', 'decode', 'DecodeError']
3030

3131

@@ -76,35 +76,68 @@ def prepare_HS_key(key):
7676
}
7777

7878
try:
79-
from Crypto.Signature import PKCS1_v1_5
80-
from Crypto.Hash import SHA256
81-
from Crypto.Hash import SHA384
82-
from Crypto.Hash import SHA512
83-
from Crypto.PublicKey import RSA
79+
from cryptography.hazmat.primitives import interfaces, hashes
80+
from cryptography.hazmat.primitives.serialization import (
81+
load_pem_private_key, load_pem_public_key, load_ssh_public_key
82+
)
83+
from cryptography.hazmat.primitives.asymmetric import ec, rsa, padding
84+
from cryptography.hazmat.backends import default_backend
85+
from cryptography.exceptions import InvalidSignature
86+
87+
def sign_rsa(msg, key, hashalg):
88+
signer = key.signer(
89+
padding.PKCS1v15(),
90+
hashalg
91+
)
92+
93+
signer.update(msg)
94+
return signer.finalize()
95+
96+
def verify_rsa(msg, key, hashalg, sig):
97+
verifier = key.verifier(
98+
sig,
99+
padding.PKCS1v15(),
100+
hashalg
101+
)
102+
103+
verifier.update(msg)
104+
105+
try:
106+
verifier.verify()
107+
return True
108+
except InvalidSignature:
109+
return False
84110

85111
signing_methods.update({
86-
'RS256': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA256.new(msg)),
87-
'RS384': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA384.new(msg)),
88-
'RS512': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA512.new(msg))
112+
'RS256': lambda msg, key: sign_rsa(msg, key, hashes.SHA256()),
113+
'RS384': lambda msg, key: sign_rsa(msg, key, hashes.SHA384()),
114+
'RS512': lambda msg, key: sign_rsa(msg, key, hashes.SHA512())
89115
})
90116

91117
verify_methods.update({
92-
'RS256': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA256.new(msg), sig),
93-
'RS384': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA384.new(msg), sig),
94-
'RS512': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA512.new(msg), sig)
118+
'RS256': lambda msg, key, sig: verify_rsa(msg, key, hashes.SHA256(), sig),
119+
'RS384': lambda msg, key, sig: verify_rsa(msg, key, hashes.SHA384(), sig),
120+
'RS512': lambda msg, key, sig: verify_rsa(msg, key, hashes.SHA512(), sig)
95121
})
96122

97123
def prepare_RS_key(key):
98-
if isinstance(key, RSA._RSAobj):
124+
if isinstance(key, interfaces.RSAPrivateKey) or \
125+
isinstance(key, interfaces.RSAPublicKey):
99126
return key
100127

101128
if isinstance(key, basestring):
102129
if isinstance(key, unicode):
103130
key = key.encode('utf-8')
104131

105-
key = RSA.importKey(key)
132+
try:
133+
if key.startswith(b'ssh-rsa'):
134+
key = load_ssh_public_key(key, backend=default_backend())
135+
else:
136+
key = load_pem_private_key(key, password=None, backend=default_backend())
137+
except ValueError:
138+
key = load_pem_public_key(key, backend=default_backend())
106139
else:
107-
raise TypeError('Expecting a PEM- or RSA-formatted key.')
140+
raise TypeError('Expecting a PEM-formatted key.')
108141

109142
return key
110143

@@ -114,27 +147,38 @@ def prepare_RS_key(key):
114147
'RS512': prepare_RS_key
115148
})
116149

117-
except ImportError:
118-
pass
150+
def sign_ecdsa(msg, key, hashalg):
151+
signer = key.signer(ec.ECDSA(hashalg))
119152

120-
try:
121-
import ecdsa
153+
signer.update(msg)
154+
return signer.finalize()
155+
156+
def verify_ecdsa(msg, key, hashalg, sig):
157+
verifier = key.verifier(sig, ec.ECDSA(hashalg))
158+
159+
verifier.update(msg)
160+
161+
try:
162+
verifier.verify()
163+
return True
164+
except InvalidSignature:
165+
return False
122166

123167
signing_methods.update({
124-
'ES256': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der),
125-
'ES384': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha384, sigencode=ecdsa.util.sigencode_der),
126-
'ES512': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha512, sigencode=ecdsa.util.sigencode_der),
168+
'ES256': lambda msg, key: sign_ecdsa(msg, key, hashes.SHA256()),
169+
'ES384': lambda msg, key: sign_ecdsa(msg, key, hashes.SHA384()),
170+
'ES512': lambda msg, key: sign_ecdsa(msg, key, hashes.SHA512()),
127171
})
128172

129173
verify_methods.update({
130-
'ES256': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der),
131-
'ES384': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha384, sigdecode=ecdsa.util.sigdecode_der),
132-
'ES512': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha512, sigdecode=ecdsa.util.sigdecode_der),
174+
'ES256': lambda msg, key, sig: verify_ecdsa(msg, key, hashes.SHA256(), sig),
175+
'ES384': lambda msg, key, sig: verify_ecdsa(msg, key, hashes.SHA384(), sig),
176+
'ES512': lambda msg, key, sig: verify_ecdsa(msg, key, hashes.SHA512(), sig),
133177
})
134178

135179
def prepare_ES_key(key):
136-
if isinstance(key, ecdsa.SigningKey) or \
137-
isinstance(key, ecdsa.VerifyingKey):
180+
if isinstance(key, interfaces.EllipticCurvePrivateKey) or \
181+
isinstance(key, interfaces.EllipticCurvePublicKey):
138182
return key
139183

140184
if isinstance(key, basestring):
@@ -145,12 +189,10 @@ def prepare_ES_key(key):
145189
# a Signing Key or a Verifying Key, so we try
146190
# the Verifying Key first.
147191
try:
148-
key = ecdsa.VerifyingKey.from_pem(key)
149-
except ecdsa.der.UnexpectedDER:
150-
try:
151-
key = ecdsa.SigningKey.from_pem(key)
152-
except:
153-
raise
192+
key = load_pem_public_key(key, backend=default_backend())
193+
except ValueError:
194+
key = load_pem_private_key(key, password=None, backend=default_backend())
195+
154196
else:
155197
raise TypeError('Expecting a PEM-formatted key.')
156198

0 commit comments

Comments
 (0)
Please sign in to comment.