Skip to content

Commit

Permalink
Merge pull request #1963 from AndreLouisCaron/csr-encoding
Browse files Browse the repository at this point in the history
Adds support for writing CSRs.
  • Loading branch information
alex committed May 19, 2015
2 parents 5f1f65c + acb1897 commit 9acecae
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Changelog

.. note:: This version is not yet released and is under active development.

* Support serialization of certificate signing requests using the
``public_bytes`` method of
:class:`~cryptography.x509.CertificateSigningRequest`.

0.9 - 2015-05-13
~~~~~~~~~~~~~~~~

Expand Down
11 changes: 11 additions & 0 deletions docs/x509.rst
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,17 @@ X.509 CSR (Certificate Signing Request) Object
>>> isinstance(csr.signature_hash_algorithm, hashes.SHA1)
True

.. method:: public_bytes(encoding)

:param encoding: The
:class:`~cryptography.hazmat.primitives.serialization.Encoding`
that will be used to serialize the certificate request.

:return bytes: The data that can be written to a file or sent
over the network to be signed by the certificate
authority.


.. class:: Name

.. versionadded:: 0.8
Expand Down
16 changes: 15 additions & 1 deletion src/cryptography/hazmat/backends/openssl/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import hashes, serialization


def _obj2txt(backend, obj):
Expand Down Expand Up @@ -689,3 +689,17 @@ def extensions(self):
extensions.append(x509.Extension(oid, critical, value))

return x509.Extensions(extensions)

def public_bytes(self, encoding):
if not isinstance(encoding, serialization.Encoding):
raise TypeError("encoding must be an item from the Encoding enum")

bio = self._backend._create_mem_bio()
if encoding is serialization.Encoding.PEM:
res = self._backend._lib.PEM_write_bio_X509_REQ(
bio, self._x509_req
)
elif encoding is serialization.Encoding.DER:
res = self._backend._lib.i2d_X509_REQ_bio(bio, self._x509_req)
assert res == 1
return self._backend._read_mem_bio(bio)
6 changes: 6 additions & 0 deletions src/cryptography/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,3 +1194,9 @@ def extensions(self):
"""
Returns the extensions in the signing request.
"""

@abc.abstractmethod
def public_bytes(self, encoding):
"""
Encodes the request to PEM or DER format.
"""
90 changes: 89 additions & 1 deletion tests/test_x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from cryptography.hazmat.backends.interfaces import (
DSABackend, EllipticCurveBackend, RSABackend, X509Backend
)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa

from .hazmat.primitives.test_ec import _skip_curve_unsupported
Expand Down Expand Up @@ -471,6 +471,94 @@ def test_request_basic_constraints(self, backend):
),
]

def test_public_bytes_pem(self, backend):
# Load an existing CSR.
request = _load_cert(
os.path.join("x509", "requests", "rsa_sha1.pem"),
x509.load_pem_x509_csr,
backend
)

# Encode it to PEM and load it back.
request = x509.load_pem_x509_csr(request.public_bytes(
encoding=serialization.Encoding.PEM,
), backend)

# We should recover what we had to start with.
assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
public_key = request.public_key()
assert isinstance(public_key, rsa.RSAPublicKey)
subject = request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'),
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
]

def test_public_bytes_der(self, backend):
# Load an existing CSR.
request = _load_cert(
os.path.join("x509", "requests", "rsa_sha1.pem"),
x509.load_pem_x509_csr,
backend
)

# Encode it to DER and load it back.
request = x509.load_der_x509_csr(request.public_bytes(
encoding=serialization.Encoding.DER,
), backend)

# We should recover what we had to start with.
assert isinstance(request.signature_hash_algorithm, hashes.SHA1)
public_key = request.public_key()
assert isinstance(public_key, rsa.RSAPublicKey)
subject = request.subject
assert isinstance(subject, x509.Name)
assert list(subject) == [
x509.NameAttribute(x509.OID_COUNTRY_NAME, 'US'),
x509.NameAttribute(x509.OID_STATE_OR_PROVINCE_NAME, 'Texas'),
x509.NameAttribute(x509.OID_LOCALITY_NAME, 'Austin'),
x509.NameAttribute(x509.OID_ORGANIZATION_NAME, 'PyCA'),
x509.NameAttribute(x509.OID_COMMON_NAME, 'cryptography.io'),
]

def test_public_bytes_invalid_encoding(self, backend):
request = _load_cert(
os.path.join("x509", "requests", "rsa_sha1.pem"),
x509.load_pem_x509_csr,
backend
)

with pytest.raises(TypeError):
request.public_bytes('NotAnEncoding')

@pytest.mark.parametrize(
("request_path", "loader_func", "encoding"),
[
(
os.path.join("x509", "requests", "rsa_sha1.pem"),
x509.load_pem_x509_csr,
serialization.Encoding.PEM,
),
(
os.path.join("x509", "requests", "rsa_sha1.der"),
x509.load_der_x509_csr,
serialization.Encoding.DER,
),
]
)
def test_public_bytes_match(self, request_path, loader_func, encoding,
backend):
request_bytes = load_vectors_from_file(
request_path, lambda pemfile: pemfile.read(), mode="rb"
)
request = loader_func(request_bytes, backend)
serialized = request.public_bytes(encoding)
assert serialized == request_bytes


@pytest.mark.requires_backend_interface(interface=DSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
Expand Down

0 comments on commit 9acecae

Please sign in to comment.