Skip to content

Commit

Permalink
Merge pull request #1715 from reaperhulk/rsa-pkcs1-public-key
Browse files Browse the repository at this point in the history
RSA PKCS1 public key loading support
  • Loading branch information
alex committed Mar 7, 2015
2 parents e37a0a5 + 791afc0 commit 7d5483b
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ Changelog
* Fixed building against LibreSSL, a compile-time substitute for OpenSSL.
* FreeBSD 9.2 was removed from the continuous integration system.
* Updated Windows wheels to be compiled against OpenSSL 1.0.2.
* :func:`~cryptography.hazmat.primitives.serialization.load_pem_public_key`
and :func:`~cryptography.hazmat.primitives.serialization.load_der_public_key`
now support PKCS1 RSA public keys (in addition to the previous support for
SubjectPublicKeyInfo format for RSA, EC, and DSA).
* Added
:class:`~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKeyWithSerialization`
and deprecated
Expand Down
48 changes: 38 additions & 10 deletions src/cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,12 +692,28 @@ def load_pem_private_key(self, data, password):
)

def load_pem_public_key(self, data):
return self._load_key(
self._lib.PEM_read_bio_PUBKEY,
self._evp_pkey_to_public_key,
data,
None,
mem_bio = self._bytes_to_bio(data)
evp_pkey = self._lib.PEM_read_bio_PUBKEY(
mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
)
if evp_pkey != self._ffi.NULL:
evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
return self._evp_pkey_to_public_key(evp_pkey)
else:
# It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still
# need to check to see if it is a pure PKCS1 RSA public key (not
# embedded in a subjectPublicKeyInfo)
self._consume_errors()
res = self._lib.BIO_reset(mem_bio.bio)
assert res == 1
rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey(
mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL
)
if rsa_cdata != self._ffi.NULL:
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
return _RSAPublicKey(self, rsa_cdata)
else:
self._handle_key_loading_error()

def load_der_private_key(self, data, password):
# OpenSSL has a function called d2i_AutoPrivateKey that can simplify
Expand Down Expand Up @@ -762,12 +778,24 @@ def _evp_pkey_from_der_unencrypted_pkcs8(self, bio_data, password):
def load_der_public_key(self, data):
mem_bio = self._bytes_to_bio(data)
evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL)
if evp_pkey == self._ffi.NULL:
if evp_pkey != self._ffi.NULL:
evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
return self._evp_pkey_to_public_key(evp_pkey)
else:
# It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still
# need to check to see if it is a pure PKCS1 RSA public key (not
# embedded in a subjectPublicKeyInfo)
self._consume_errors()
raise ValueError("Could not unserialize key data.")

evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free)
return self._evp_pkey_to_public_key(evp_pkey)
res = self._lib.BIO_reset(mem_bio.bio)
assert res == 1
rsa_cdata = self._lib.d2i_RSAPublicKey_bio(
mem_bio.bio, self._ffi.NULL
)
if rsa_cdata != self._ffi.NULL:
rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free)
return _RSAPublicKey(self, rsa_cdata)
else:
self._handle_key_loading_error()

def load_pem_x509_certificate(self, data):
mem_bio = self._bytes_to_bio(data)
Expand Down
10 changes: 9 additions & 1 deletion tests/hazmat/primitives/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ def test_corrupt_traditional_format_der(self, backend):
"asymmetric", "DER_Serialization", "unenc-rsa-pkcs8.pub.der"),
os.path.join(
"asymmetric", "DER_Serialization", "rsa_public_key.der"),
os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.der"),
]
)
@pytest.mark.requires_backend_interface(interface=RSABackend)
Expand Down Expand Up @@ -384,6 +385,7 @@ def test_load_pem_ec_private_key(self, key_path, password, backend):
os.path.join("asymmetric", "PKCS8", "unenc-rsa-pkcs8.pub.pem"),
os.path.join(
"asymmetric", "PEM_Serialization", "rsa_public_key.pem"),
os.path.join("asymmetric", "public", "PKCS1", "rsa.pub.pem"),
]
)
def test_load_pem_rsa_public_key(self, key_file, backend):
Expand Down Expand Up @@ -537,7 +539,7 @@ def test_missing_password(self, key_path, password, backend):
)
)

def test_wrong_format(self, backend):
def test_wrong_private_format(self, backend):
key_data = b"---- NOT A KEY ----\n"

with pytest.raises(ValueError):
Expand All @@ -550,6 +552,12 @@ def test_wrong_format(self, backend):
key_data, b"this password will not be used", backend
)

def test_wrong_public_format(self, backend):
key_data = b"---- NOT A KEY ----\n"

with pytest.raises(ValueError):
load_pem_public_key(key_data, backend)

def test_corrupt_traditional_format(self, backend):
# privkey.pem with a bunch of data missing.
key_data = textwrap.dedent("""\
Expand Down

0 comments on commit 7d5483b

Please sign in to comment.