aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/x509/reference.rst19
-rw-r--r--src/_cffi_src/openssl/x509.py4
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py15
-rw-r--r--src/cryptography/x509/base.py13
-rw-r--r--tests/test_x509.py132
5 files changed, 183 insertions, 0 deletions
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 35c00e61..0fea6167 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -734,6 +734,25 @@ X.509 CSR (Certificate Signing Request) Object
over the network to be signed by the certificate
authority.
+ .. attribute:: signature
+
+ .. versionadded:: 1.2
+
+ :type: bytes
+
+ The bytes of the certificate signing request's signature.
+
+ .. attribute:: tbs_certrequest_bytes
+
+ .. versionadded:: 1.2
+
+ :type: bytes
+
+ The DER encoded bytes payload (as defined by :rfc:`2986`) that is
+ hashed and then signed by the private key (corresponding to the public
+ key embedded in the CSR). This data may be used to validate the CSR
+ signature.
+
X.509 Revoked Certificate Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py
index b5d461ab..0fc49ac5 100644
--- a/src/_cffi_src/openssl/x509.py
+++ b/src/_cffi_src/openssl/x509.py
@@ -43,9 +43,12 @@ typedef struct {
} X509_EXTENSION;
typedef ... X509_EXTENSIONS;
+typedef ... X509_REQ_INFO;
typedef struct {
+ X509_REQ_INFO *req_info;
X509_ALGOR *sig_alg;
+ ASN1_BIT_STRING *signature;
...;
} X509_REQ;
@@ -267,6 +270,7 @@ void PKCS8_PRIV_KEY_INFO_free(PKCS8_PRIV_KEY_INFO *);
MACROS = """
int i2d_X509_CINF(X509_CINF *, unsigned char **);
int i2d_X509_CRL_INFO(X509_CRL_INFO *, unsigned char **);
+int i2d_X509_REQ_INFO(X509_REQ_INFO *, unsigned char **);
long X509_get_version(X509 *);
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 8fa43ea8..01232df2 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -921,6 +921,21 @@ class _CertificateSigningRequest(object):
self._backend.openssl_assert(res == 1)
return self._backend._read_mem_bio(bio)
+ @property
+ def tbs_certrequest_bytes(self):
+ pp = self._backend._ffi.new("unsigned char **")
+ # the X509_CINF struct holds the tbsCertificate data
+ res = self._backend._lib.i2d_X509_REQ_INFO(self._x509_req.req_info, pp)
+ self._backend.openssl_assert(res > 0)
+ pp = self._backend._ffi.gc(
+ pp, lambda pointer: self._backend._lib.OPENSSL_free(pointer[0])
+ )
+ return self._backend._ffi.buffer(pp[0], res)[:]
+
+ @property
+ def signature(self):
+ return self._backend._asn1_string_to_bytes(self._x509_req.signature)
+
_EXTENSION_HANDLERS = {
ExtensionOID.BASIC_CONSTRAINTS: _decode_basic_constraints,
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
index 6c2386f6..c56ca5ee 100644
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -270,6 +270,19 @@ class CertificateSigningRequest(object):
Encodes the request to PEM or DER format.
"""
+ @abc.abstractproperty
+ def signature(self):
+ """
+ Returns the signature bytes.
+ """
+
+ @abc.abstractproperty
+ def tbs_certrequest_bytes(self):
+ """
+ Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC
+ 2986.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class RevokedCertificate(object):
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 5a1c4c54..51c48f43 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -1051,6 +1051,52 @@ class TestRSACertificateRequest(object):
x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
]
+ def test_signature(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ assert request.signature == binascii.unhexlify(
+ b"8364c86ffbbfe0bfc9a21f831256658ca8989741b80576d36f08a934603a43b1"
+ b"837246d00167a518abb1de7b51a1e5b7ebea14944800818b1a923c804f120a0d"
+ b"624f6310ef79e8612755c2b01dcc7f59dfdbce0db3f2630f185f504b8c17af80"
+ b"cbd364fa5fda68337153930948226cd4638287a0aed6524d3006885c19028a1e"
+ b"e2f5a91d6e77dbaa0b49996ee0a0c60b55b61bd080a08bb34aa7f3e07e91f37f"
+ b"6a11645be2d8654c1570dcda145ed7cc92017f7d53225d7f283f3459ec5bda41"
+ b"cf6dd75d43676c543483385226b7e4fa29c8739f1b0eaf199613593991979862"
+ b"e36181e8c4c270c354b7f52c128db1b70639823324c7ea24791b7bc3d7005f3b"
+ )
+
+ def test_tbs_certrequest_bytes(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "rsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ assert request.tbs_certrequest_bytes == binascii.unhexlify(
+ b"308201840201003057310b3009060355040613025553310e300c060355040813"
+ b"055465786173310f300d0603550407130641757374696e310d300b060355040a"
+ b"130450794341311830160603550403130f63727970746f6772617068792e696f"
+ b"30820122300d06092a864886f70d01010105000382010f003082010a02820101"
+ b"00a840a78460cb861066dfa3045a94ba6cf1b7ab9d24c761cffddcc2cb5e3f1d"
+ b"c3e4be253e7039ef14fe9d6d2304f50d9f2e1584c51530ab75086f357138bff7"
+ b"b854d067d1d5f384f1f2f2c39cc3b15415e2638554ef8402648ae3ef08336f22"
+ b"b7ecc6d4331c2b21c3091a7f7a9518180754a646640b60419e4cc6f5c798110a"
+ b"7f030a639fe87e33b4776dfcd993940ec776ab57a181ad8598857976dc303f9a"
+ b"573ca619ab3fe596328e92806b828683edc17cc256b41948a2bfa8d047d2158d"
+ b"3d8e069aa05fa85b3272abb1c4b4422b6366f3b70e642377b145cd6259e5d3e7"
+ b"db048d51921e50766a37b1b130ee6b11f507d20a834001e8de16a92c14f2e964"
+ b"a30203010001a000"
+ )
+ verifier = request.public_key().verifier(
+ request.signature,
+ padding.PKCS1v15(),
+ request.signature_hash_algorithm
+ )
+ verifier.update(request.tbs_certrequest_bytes)
+ verifier.verify()
+
def test_public_bytes_invalid_encoding(self, backend):
request = _load_cert(
os.path.join("x509", "requests", "rsa_sha1.pem"),
@@ -2884,6 +2930,10 @@ class TestDSACertificate(object):
verifier.update(cert.tbs_certificate_bytes)
verifier.verify()
+
+@pytest.mark.requires_backend_interface(interface=DSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestDSACertificateRequest(object):
@pytest.mark.parametrize(
("path", "loader_func"),
[
@@ -2912,6 +2962,49 @@ class TestDSACertificate(object):
x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
]
+ def test_signature(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "dsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ assert request.signature == binascii.unhexlify(
+ b"302c021461d58dc028d0110818a7d817d74235727c4acfdf0214097b52e198e"
+ b"ce95de17273f0a924df23ce9d8188"
+ )
+
+ def test_tbs_certrequest_bytes(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "dsa_sha1.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ assert request.tbs_certrequest_bytes == binascii.unhexlify(
+ b"3082021802010030573118301606035504030c0f63727970746f677261706879"
+ b"2e696f310d300b060355040a0c0450794341310b300906035504061302555331"
+ b"0e300c06035504080c055465786173310f300d06035504070c0641757374696e"
+ b"308201b63082012b06072a8648ce3804013082011e028181008d7fadbc09e284"
+ b"aafa69154cea24177004909e519f8b35d685cde5b4ecdc9583e74d370a0f88ad"
+ b"a98f026f27762fb3d5da7836f986dfcdb3589e5b925bea114defc03ef81dae30"
+ b"c24bbc6df3d588e93427bba64203d4a5b1687b2b5e3b643d4c614976f89f95a3"
+ b"8d3e4c89065fba97514c22c50adbbf289163a74b54859b35b7021500835de56b"
+ b"d07cf7f82e2032fe78949aed117aa2ef0281801f717b5a07782fc2e4e68e311f"
+ b"ea91a54edd36b86ac634d14f05a68a97eae9d2ef31fb1ef3de42c3d100df9ca6"
+ b"4f5bdc2aec7bfdfb474cf831fea05853b5e059f2d24980a0ac463f1e818af352"
+ b"3e3cb79a39d45fa92731897752842469cf8540b01491024eaafbce6018e8a1f4"
+ b"658c343f4ba7c0b21e5376a21f4beb8491961e038184000281800713f07641f6"
+ b"369bb5a9545274a2d4c01998367fb371bb9e13436363672ed68f82174c2de05c"
+ b"8e839bc6de568dd50ba28d8d9d8719423aaec5557df10d773ab22d6d65cbb878"
+ b"04a697bc8fd965b952f9f7e850edf13c8acdb5d753b6d10e59e0b5732e3c82ba"
+ b"fa140342bc4a3bba16bd0681c8a6a2dbbb7efe6ce2b8463b170ba000"
+ )
+ verifier = request.public_key().verifier(
+ request.signature,
+ request.signature_hash_algorithm
+ )
+ verifier.update(request.tbs_certrequest_bytes)
+ verifier.verify()
+
@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
@@ -3001,6 +3094,10 @@ class TestECDSACertificate(object):
with pytest.raises(NotImplementedError):
cert.public_key()
+
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+@pytest.mark.requires_backend_interface(interface=EllipticCurveBackend)
+class TestECDSACertificateRequest(object):
@pytest.mark.parametrize(
("path", "loader_func"),
[
@@ -3030,6 +3127,41 @@ class TestECDSACertificate(object):
x509.NameAttribute(NameOID.LOCALITY_NAME, u'Austin'),
]
+ def test_signature(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "ec_sha256.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ assert request.signature == binascii.unhexlify(
+ b"306502302c1a9f7de8c1787332d2307a886b476a59f172b9b0e250262f3238b1"
+ b"b45ee112bb6eb35b0fb56a123b9296eb212dffc302310094cf440c95c52827d5"
+ b"56ae6d76500e3008255d47c29f7ee782ed7558e51bfd76aa45df6d999ed5c463"
+ b"347fe2382d1751"
+ )
+
+ def test_tbs_certrequest_bytes(self, backend):
+ request = _load_cert(
+ os.path.join("x509", "requests", "ec_sha256.pem"),
+ x509.load_pem_x509_csr,
+ backend
+ )
+ assert request.tbs_certrequest_bytes == binascii.unhexlify(
+ b"3081d602010030573118301606035504030c0f63727970746f6772617068792"
+ b"e696f310d300b060355040a0c0450794341310b300906035504061302555331"
+ b"0e300c06035504080c055465786173310f300d06035504070c0641757374696"
+ b"e3076301006072a8648ce3d020106052b8104002203620004de19b514c0b3c3"
+ b"ae9b398ea3e26b5e816bdcf9102cad8f12fe02f9e4c9248724b39297ed7582e"
+ b"04d8b32a551038d09086803a6d3fb91a1a1167ec02158b00efad39c9396462f"
+ b"accff0ffaf7155812909d3726bd59fde001cff4bb9b2f5af8cbaa000"
+ )
+ verifier = request.public_key().verifier(
+ request.signature,
+ ec.ECDSA(request.signature_hash_algorithm)
+ )
+ verifier.update(request.tbs_certrequest_bytes)
+ verifier.verify()
+
@pytest.mark.requires_backend_interface(interface=X509Backend)
class TestOtherCertificate(object):