aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py90
-rw-r--r--tests/test_x509.py89
2 files changed, 179 insertions, 0 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 58587b94..e1ef63be 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -94,6 +94,20 @@ def _encode_asn1_str(backend, data, length):
return s
+def _encode_asn1_utf8_str(backend, string):
+ """
+ Create an ASN1_UTF8STRING from a Python unicode string.
+ This object will be a ASN1_STRING with UTF8 type in OpenSSL and
+ can be decoded with ASN1_STRING_to_UTF8.
+ """
+ s = backend._lib.ASN1_UTF8STRING_new()
+ res = backend._lib.ASN1_STRING_set(
+ s, string.encode("utf8"), len(string.encode("utf8"))
+ )
+ assert res == 1
+ return s
+
+
def _encode_asn1_str_gc(backend, data, length):
s = _encode_asn1_str(backend, data, length)
s = backend._ffi.gc(s, backend._lib.ASN1_OCTET_STRING_free)
@@ -136,6 +150,81 @@ def _encode_name_gc(backend, attributes):
return subject
+def _encode_certificate_policies(backend, certificate_policies):
+ cp = backend._lib.sk_POLICYINFO_new_null()
+ assert cp != backend._ffi.NULL
+ cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
+ for policy_info in certificate_policies:
+ pi = backend._lib.POLICYINFO_new()
+ assert pi != backend._ffi.NULL
+ res = backend._lib.sk_POLICYINFO_push(cp, pi)
+ assert res >= 1
+ oid = _txt2obj(backend, policy_info.policy_identifier.dotted_string)
+ pi.policyid = oid
+ if policy_info.policy_qualifiers:
+ pqis = backend._lib.sk_POLICYQUALINFO_new_null()
+ assert pqis != backend._ffi.NULL
+ for qualifier in policy_info.policy_qualifiers:
+ pqi = backend._lib.POLICYQUALINFO_new()
+ assert pqi != backend._ffi.NULL
+ res = backend._lib.sk_POLICYQUALINFO_push(pqis, pqi)
+ assert res >= 1
+ if isinstance(qualifier, six.text_type):
+ pqi.pqualid = _txt2obj(
+ backend, x509.OID_CPS_QUALIFIER.dotted_string
+ )
+ pqi.d.cpsuri = _encode_asn1_str(
+ backend,
+ qualifier.encode("ascii"),
+ len(qualifier.encode("ascii"))
+ )
+ else:
+ assert isinstance(qualifier, x509.UserNotice)
+ pqi.pqualid = _txt2obj(
+ backend, x509.OID_CPS_USER_NOTICE.dotted_string
+ )
+ un = backend._lib.USERNOTICE_new()
+ assert un != backend._ffi.NULL
+ pqi.d.usernotice = un
+ if qualifier.explicit_text:
+ un.exptext = _encode_asn1_utf8_str(
+ backend, qualifier.explicit_text
+ )
+
+ un.noticeref = _encode_notice_reference(
+ backend, qualifier.notice_reference
+ )
+
+ pi.qualifiers = pqis
+
+ pp = backend._ffi.new('unsigned char **')
+ r = backend._lib.i2d_CERTIFICATEPOLICIES(cp, pp)
+ assert r > 0
+ pp = backend._ffi.gc(
+ pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ )
+ return pp, r
+
+
+def _encode_notice_reference(backend, notice):
+ if notice is None:
+ return backend._ffi.NULL
+ else:
+ nr = backend._lib.NOTICEREF_new()
+ assert nr != backend._ffi.NULL
+ # organization is a required field
+ nr.organization = _encode_asn1_utf8_str(backend, notice.organization)
+
+ notice_stack = backend._lib.sk_ASN1_INTEGER_new_null()
+ nr.noticenos = notice_stack
+ for number in notice.notice_numbers:
+ num = _encode_asn1_int(backend, number)
+ res = backend._lib.sk_ASN1_INTEGER_push(notice_stack, num)
+ assert res >= 1
+
+ return nr
+
+
def _txt2obj(backend, name):
"""
Converts a Python string with an ASN.1 object ID in dotted form to a
@@ -487,6 +576,7 @@ _EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name,
ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage,
ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier,
+ x509.OID_CERTIFICATE_POLICIES: _encode_certificate_policies,
ExtensionOID.AUTHORITY_INFORMATION_ACCESS: (
_encode_authority_information_access
),
diff --git a/tests/test_x509.py b/tests/test_x509.py
index b9ea139b..a54cdc56 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -1694,6 +1694,95 @@ class TestCertificateBuilder(object):
with pytest.raises(ValueError):
builder.sign(issuer_private_key, hashes.SHA512(), backend)
+ @pytest.mark.parametrize(
+ "cp",
+ [
+ x509.CertificatePolicies([
+ x509.PolicyInformation(
+ x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+ [u"http://other.com/cps"]
+ )
+ ]),
+ x509.CertificatePolicies([
+ x509.PolicyInformation(
+ x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+ None
+ )
+ ]),
+ x509.CertificatePolicies([
+ x509.PolicyInformation(
+ x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+ [
+ u"http://example.com/cps",
+ u"http://other.com/cps",
+ x509.UserNotice(
+ x509.NoticeReference(u"my org", [1, 2, 3, 4]),
+ u"thing"
+ )
+ ]
+ )
+ ]),
+ x509.CertificatePolicies([
+ x509.PolicyInformation(
+ x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+ [
+ u"http://example.com/cps",
+ x509.UserNotice(
+ x509.NoticeReference(u"UTF8\u2122'", [1, 2, 3, 4]),
+ u"We heart UTF8!\u2122"
+ )
+ ]
+ )
+ ]),
+ x509.CertificatePolicies([
+ x509.PolicyInformation(
+ x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+ [x509.UserNotice(None, u"thing")]
+ )
+ ]),
+ x509.CertificatePolicies([
+ x509.PolicyInformation(
+ x509.ObjectIdentifier("2.16.840.1.12345.1.2.3.4.1"),
+ [
+ x509.UserNotice(
+ x509.NoticeReference(u"my org", [1, 2, 3, 4]),
+ None
+ )
+ ]
+ )
+ ])
+ ]
+ )
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_certificate_policies(self, cp, backend):
+ issuer_private_key = RSA_KEY_2048.private_key(backend)
+ subject_private_key = RSA_KEY_2048.private_key(backend)
+
+ not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+ not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+ cert = x509.CertificateBuilder().subject_name(
+ x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+ ).issuer_name(
+ x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')])
+ ).not_valid_before(
+ not_valid_before
+ ).not_valid_after(
+ not_valid_after
+ ).public_key(
+ subject_private_key.public_key()
+ ).serial_number(
+ 123
+ ).add_extension(
+ cp, critical=False
+ ).sign(issuer_private_key, hashes.SHA256(), backend)
+
+ ext = cert.extensions.get_extension_for_oid(
+ x509.OID_CERTIFICATE_POLICIES
+ )
+ assert ext.value == cp
+
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
def test_issuer_alt_name(self, backend):