Skip to content

Commit 8e78302

Browse files
committed
move asn1 private key processing into separate module
1 parent c28f15a commit 8e78302

6 files changed

Lines changed: 93 additions & 62 deletions

File tree

jose/backends/_asn1.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""ASN1 encoding helpers for converting between PKCS1 and PKCS8.
2+
3+
Required by rsa_backend and pycrypto_backend but not cryptography_backend.
4+
"""
5+
from pyasn1.codec.der import decoder, encoder
6+
from pyasn1.error import PyAsn1Error
7+
from pyasn1.type import namedtype, univ
8+
9+
RSA_ENCRYPTION_ASN1_OID = "1.2.840.113549.1.1.1"
10+
11+
12+
class PKCS8RsaPrivateKeyAlgorithm(univ.Sequence):
13+
"""ASN1 structure for recording RSA PrivateKeyAlgorithm identifiers."""
14+
componentType = namedtype.NamedTypes(
15+
namedtype.NamedType("rsaEncryption", univ.ObjectIdentifier()),
16+
namedtype.NamedType("parameters", univ.Null())
17+
)
18+
19+
20+
class PKCS8PrivateKey(univ.Sequence):
21+
"""ASN1 structure for recording PKCS8 private keys."""
22+
componentType = namedtype.NamedTypes(
23+
namedtype.NamedType("version", univ.Integer()),
24+
namedtype.NamedType("privateKeyAlgorithm", PKCS8RsaPrivateKeyAlgorithm()),
25+
namedtype.NamedType("privateKey", univ.OctetString())
26+
)
27+
28+
29+
def rsa_private_key_pkcs8_to_pkcs1(pkcs8_key):
30+
"""Convert a PKCS8-encoded RSA private key to PKCS1."""
31+
decoded_values = decoder.decode(pkcs8_key, asn1Spec=PKCS8PrivateKey())
32+
33+
try:
34+
decoded_key = decoded_values[0]
35+
except IndexError:
36+
raise ValueError("Invalid private key encoding")
37+
38+
return decoded_key["privateKey"]
39+
40+
41+
def rsa_private_key_pkcs1_to_pkcs8(pkcs1_key):
42+
"""Convert a PKCS1-encoded RSA private key to PKCS8."""
43+
algorithm = PKCS8RsaPrivateKeyAlgorithm()
44+
algorithm["rsaEncryption"] = RSA_ENCRYPTION_ASN1_OID
45+
46+
pkcs8_key = PKCS8PrivateKey()
47+
pkcs8_key["version"] = 0
48+
pkcs8_key["privateKeyAlgorithm"] = algorithm
49+
pkcs8_key["privateKey"] = pkcs1_key
50+
51+
return encoder.encode(pkcs8_key)

jose/backends/pycrypto_backend.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ def to_pem(self, pem_format='PKCS8'):
152152
raise ValueError("Invalid pem format specified: %r" % (pem_format,))
153153

154154
if self.is_public():
155-
pem = self.prepared_key.exportKey('PEM', pkcs=1)
155+
# PyCrypto/dome always export public keys as PKCS8
156+
pem = self.prepared_key.exportKey('PEM')
156157
if pkcs == 8:
157158
pem = pem_to_spki(pem, fmt='PKCS8')
158159
else:

jose/backends/rsa_backend.py

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import binascii
22

33
import six
4-
from pyasn1.codec.der import decoder, encoder
4+
from pyasn1.codec.der import encoder
55
from pyasn1.error import PyAsn1Error
6-
from pyasn1.type import namedtype, univ
6+
from pyasn1.type import univ
77

88
import rsa as pyrsa
99
import rsa.pem as pyrsa_pem
1010
from rsa.asn1 import OpenSSLPubKey, AsnPubKey, PubKeyHeader
1111

1212
from jose.backends.base import Key
13+
from jose.backends._asn1 import rsa_private_key_pkcs1_to_pkcs8, rsa_private_key_pkcs8_to_pkcs1
1314
from jose.constants import ALGORITHMS
1415
from jose.exceptions import JWKError
1516
from jose.utils import base64_to_long, long_to_base64
@@ -114,48 +115,6 @@ def _legacy_private_key_pkcs8_to_pkcs1(pkcs8_key):
114115
return pkcs8_key[len(LEGACY_INVALID_PKCS8_RSA_HEADER):]
115116

116117

117-
class PKCS8RsaPrivateKeyAlgorithm(univ.Sequence):
118-
"""ASN1 structure for recording RSA PrivateKeyAlgorithm identifiers."""
119-
componentType = namedtype.NamedTypes(
120-
namedtype.NamedType("rsaEncryption", univ.ObjectIdentifier()),
121-
namedtype.NamedType("parameters", univ.Null())
122-
)
123-
124-
125-
class PKCS8PrivateKey(univ.Sequence):
126-
"""ASN1 structure for recording PKCS8 private keys."""
127-
componentType = namedtype.NamedTypes(
128-
namedtype.NamedType("version", univ.Integer()),
129-
namedtype.NamedType("privateKeyAlgorithm", PKCS8RsaPrivateKeyAlgorithm()),
130-
namedtype.NamedType("privateKey", univ.OctetString())
131-
)
132-
133-
134-
def _private_key_pkcs8_to_pkcs1(pkcs8_key):
135-
"""Convert a PKCS8-encoded RSA private key to PKCS1."""
136-
decoded_values = decoder.decode(pkcs8_key, asn1Spec=PKCS8PrivateKey())
137-
138-
try:
139-
decoded_key = decoded_values[0]
140-
except IndexError:
141-
raise ValueError("Invalid private key encoding")
142-
143-
return decoded_key["privateKey"]
144-
145-
146-
def _private_key_pkcs1_to_pkcs8(pkcs1_key):
147-
"""Convert a PKCS1-encoded RSA private key to PKCS8."""
148-
algorithm = PKCS8RsaPrivateKeyAlgorithm()
149-
algorithm["rsaEncryption"] = RSA_ENCRYPTION_ASN1_OID
150-
151-
pkcs8_key = PKCS8PrivateKey()
152-
pkcs8_key["version"] = 0
153-
pkcs8_key["privateKeyAlgorithm"] = algorithm
154-
pkcs8_key["privateKey"] = pkcs1_key
155-
156-
return encoder.encode(pkcs8_key)
157-
158-
159118
class RSAKey(Key):
160119
SHA256 = 'SHA-256'
161120
SHA384 = 'SHA-384'
@@ -196,7 +155,7 @@ def __init__(self, key, algorithm):
196155
try:
197156
der = pyrsa_pem.load_pem(key, b'PRIVATE KEY')
198157
try:
199-
pkcs1_key = _private_key_pkcs8_to_pkcs1(der)
158+
pkcs1_key = rsa_private_key_pkcs8_to_pkcs1(der)
200159
except PyAsn1Error:
201160
# If the key was encoded using the old, invalid,
202161
# encoding then pyasn1 will throw an error attempting
@@ -259,7 +218,7 @@ def to_pem(self, pem_format='PKCS8'):
259218
if isinstance(self._prepared_key, pyrsa.PrivateKey):
260219
der = self._prepared_key.save_pkcs1(format='DER')
261220
if pem_format == 'PKCS8':
262-
pkcs8_der = _private_key_pkcs1_to_pkcs8(der)
221+
pkcs8_der = rsa_private_key_pkcs1_to_pkcs8(der)
263222
pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker='PRIVATE KEY')
264223
elif pem_format == 'PKCS1':
265224
pem = pyrsa_pem.save_pem(der, pem_marker='RSA PRIVATE KEY')

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ def get_packages(package):
2121
]
2222

2323

24+
pyasn1 = ['pyasn1']
2425
extras_require = {
2526
'cryptography': ['cryptography'],
26-
'pycrypto': ['pycrypto >=2.6.0, <2.7.0'],
27-
'pycryptodome': ['pycryptodome >=3.3.1, <4.0.0'],
27+
'pycrypto': ['pycrypto >=2.6.0, <2.7.0'] + pyasn1,
28+
'pycryptodome': ['pycryptodome >=3.3.1, <4.0.0'] + pyasn1,
2829
}
29-
legacy_backend_requires = ['ecdsa <1.0', 'rsa', 'pyasn1']
30+
legacy_backend_requires = ['ecdsa <1.0', 'rsa'] + pyasn1
3031
install_requires = ['six <2.0', 'future <1.0']
3132

3233
# TODO: work this into the extras selection instead.

tests/algorithms/test_RSA.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -318,18 +318,6 @@ def test_python_rsa_legacy_private_key_pkcs8_to_pkcs1_invalid(self):
318318

319319
excinfo.match("Invalid private key encoding")
320320

321-
def test_python_rsa_private_key_pkcs1_to_pkcs8(self):
322-
pkcs1 = base64.b64decode(PKCS1_PRIVATE_KEY)
323-
pkcs8 = base64.b64decode(PKCS8_PRIVATE_KEY)
324-
325-
assert rsa_backend._private_key_pkcs1_to_pkcs8(pkcs1) == pkcs8
326-
327-
def test_python_rsa_private_key_pkcs8_to_pkcs1(self):
328-
pkcs1 = base64.b64decode(PKCS1_PRIVATE_KEY)
329-
pkcs8 = base64.b64decode(PKCS8_PRIVATE_KEY)
330-
331-
assert rsa_backend._private_key_pkcs8_to_pkcs1(pkcs8) == pkcs1
332-
333321

334322
@pytest.mark.pycrypto
335323
@pytest.mark.pycryptodome

tests/test_asn1.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""Tests for ``jose.backends._asn1``."""
2+
import base64
3+
4+
import pytest
5+
6+
try:
7+
from jose.backends import _asn1
8+
except ImportError:
9+
_asn1 = None
10+
11+
from .algorithms.test_RSA import PKCS1_PRIVATE_KEY, PKCS8_PRIVATE_KEY
12+
13+
pytestmark = [
14+
pytest.mark.pycrypto,
15+
pytest.mark.pycryptodome,
16+
pytest.mark.skipif(_asn1 is None, reason="ASN1 backend not available")
17+
]
18+
19+
20+
def test_rsa_private_key_pkcs1_to_pkcs8():
21+
pkcs1 = base64.b64decode(PKCS1_PRIVATE_KEY)
22+
pkcs8 = base64.b64decode(PKCS8_PRIVATE_KEY)
23+
24+
assert _asn1.rsa_private_key_pkcs1_to_pkcs8(pkcs1) == pkcs8
25+
26+
27+
def test_rsa_private_key_pkcs8_to_pkcs1():
28+
pkcs1 = base64.b64decode(PKCS1_PRIVATE_KEY)
29+
pkcs8 = base64.b64decode(PKCS8_PRIVATE_KEY)
30+
31+
assert _asn1.rsa_private_key_pkcs8_to_pkcs1(pkcs8) == pkcs1

0 commit comments

Comments
 (0)