Skip to content

Commit 2c117c6

Browse files
author
Gasper Zejn
committed
Add SubjectPublicKeyInfo format for PEM output and cross implementation tests.
1 parent 9044354 commit 2c117c6

File tree

4 files changed

+66
-11
lines changed

4 files changed

+66
-11
lines changed

jose/backends/cryptography_backend.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,17 @@ def public_key(self):
284284

285285
def to_pem(self, pem_format='PKCS8'):
286286
if self.is_public():
287-
return self.prepared_key.public_bytes(
287+
if pem_format == 'PKCS8':
288+
fmt = serialization.PublicFormat.SubjectPublicKeyInfo
289+
elif pem_format == 'PKCS1':
290+
fmt = serialization.PublicFormat.PKCS1
291+
else:
292+
raise ValueError("Invalid format specified: %r" % pem_format)
293+
pem = self.prepared_key.public_bytes(
288294
encoding=serialization.Encoding.PEM,
289-
format=serialization.PublicFormat.SubjectPublicKeyInfo
295+
format=fmt
290296
)
297+
return pem
291298

292299
if pem_format == 'PKCS8':
293300
fmt = serialization.PrivateFormat.PKCS8

jose/backends/pycrypto_backend.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from Crypto.Util.asn1 import DerSequence
1010

1111
from jose.backends.base import Key
12+
from jose.backends.rsa_backend import pem_to_spki
1213
from jose.utils import base64_to_long, long_to_base64
1314
from jose.constants import ALGORITHMS
1415
from jose.exceptions import JWKError
@@ -148,7 +149,15 @@ def to_pem(self, pem_format='PKCS8'):
148149
else:
149150
raise ValueError("Invalid pem format specified: %r" % (pem_format,))
150151

151-
pem = self.prepared_key.exportKey('PEM', pkcs=pkcs)
152+
if self.is_public():
153+
pem = self.prepared_key.exportKey('PEM', pkcs=1)
154+
if pkcs == 8:
155+
pem = pem_to_spki(pem, fmt='PKCS8')
156+
else:
157+
pem = pem_to_spki(pem, fmt='PKCS1')
158+
return pem
159+
else:
160+
pem = self.prepared_key.exportKey('PEM', pkcs=pkcs)
152161
return pem
153162

154163
def to_dict(self):

jose/backends/rsa_backend.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
import six
2+
from pyasn1.codec.der import encoder
3+
from pyasn1.type import univ
4+
15
import rsa as pyrsa
26
import rsa.pem as pyrsa_pem
3-
import six
7+
from rsa.asn1 import OpenSSLPubKey, AsnPubKey, PubKeyHeader
48

59
from jose.backends.base import Key
610
from jose.constants import ALGORITHMS
@@ -73,6 +77,11 @@ def _rsa_recover_prime_factors(n, e, d):
7377
return (p, q)
7478

7579

80+
def pem_to_spki(pem, fmt='PKCS8'):
81+
key = RSAKey(pem, ALGORITHMS.RS256)
82+
return key.to_pem(fmt)
83+
84+
7685
class RSAKey(Key):
7786
SHA256 = 'SHA-256'
7887
SHA384 = 'SHA-384'
@@ -171,21 +180,35 @@ def public_key(self):
171180
return self.__class__(pyrsa.PublicKey(n=self._prepared_key.n, e=self._prepared_key.e), self._algorithm)
172181

173182
def to_pem(self, pem_format='PKCS8'):
174-
import rsa.pem
175183

176184
if isinstance(self._prepared_key, pyrsa.PrivateKey):
177185
der = self._prepared_key.save_pkcs1(format='DER')
178186
if pem_format == 'PKCS8':
179-
pem = rsa.pem.save_pem(PKCS8_RSA_HEADER + der, pem_marker='PRIVATE KEY')
187+
pem = pyrsa_pem.save_pem(PKCS8_RSA_HEADER + der, pem_marker='PRIVATE KEY')
180188
elif pem_format == 'PKCS1':
181-
pem = rsa.pem.save_pem(der, pem_marker='RSA PRIVATE KEY')
189+
pem = pyrsa_pem.save_pem(der, pem_marker='RSA PRIVATE KEY')
182190
else:
183191
raise ValueError("Invalid pem format specified: %r" % (pem_format,))
184192
else:
185-
# this is a PKCS#8 DER header to identify rsaEncryption
186-
header = b'0\x82\x04\xbd\x02\x01\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00'
187-
der = self._prepared_key.save_pkcs1(format='DER')
188-
pem = rsa.pem.save_pem(header + der, pem_marker='PUBLIC KEY')
193+
if pem_format == 'PKCS8':
194+
asn_key = AsnPubKey()
195+
asn_key.setComponentByName('modulus', self._prepared_key.n)
196+
asn_key.setComponentByName('publicExponent', self._prepared_key.e)
197+
der = encoder.encode(asn_key)
198+
199+
header = PubKeyHeader()
200+
header['oid'] = univ.ObjectIdentifier('1.2.840.113549.1.1.1')
201+
pub_key = OpenSSLPubKey()
202+
pub_key['header'] = header
203+
pub_key['key'] = univ.BitString.fromOctetString(der)
204+
205+
der = encoder.encode(pub_key)
206+
pem = pyrsa_pem.save_pem(der, pem_marker='PUBLIC KEY')
207+
elif pem_format == 'PKCS1':
208+
der = self._prepared_key.save_pkcs1(format='DER')
209+
pem = pyrsa_pem.save_pem(der, pem_marker='RSA PUBLIC KEY')
210+
else:
211+
raise ValueError("Invalid pem format specified: %r" % (pem_format,))
189212
return pem
190213

191214
def to_dict(self):

tests/algorithms/test_RSA.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,22 @@ def test_to_pem(self, Backend):
184184
newkey = Backend(pkcs8, ALGORITHMS.RS256)
185185
assert newkey.to_pem(pem_format='PKCS1').strip() == private_key.strip()
186186

187+
@pytest.mark.parametrize("BackendFrom", [RSAKey, CryptographyRSAKey, PurePythonRSAKey])
188+
@pytest.mark.parametrize("BackendTo", [RSAKey, CryptographyRSAKey, PurePythonRSAKey])
189+
def test_public_key_to_pem(self, BackendFrom, BackendTo):
190+
key = BackendFrom(private_key, ALGORITHMS.RS256)
191+
pubkey = key.public_key()
192+
193+
pkcs1_pub = pubkey.to_pem(pem_format='PKCS1').strip()
194+
pkcs8_pub = pubkey.to_pem(pem_format='PKCS8').strip()
195+
assert pkcs1_pub != pkcs8_pub, BackendFrom
196+
197+
pub1 = BackendTo(pkcs1_pub, ALGORITHMS.RS256)
198+
pub8 = BackendTo(pkcs8_pub, ALGORITHMS.RS256)
199+
200+
assert pkcs8_pub == pub1.to_pem(pem_format='PKCS8').strip()
201+
assert pkcs1_pub == pub8.to_pem(pem_format='PKCS1').strip()
202+
187203
def assert_parameters(self, as_dict, private):
188204
assert isinstance(as_dict, dict)
189205

0 commit comments

Comments
 (0)