diff options
-rw-r--r-- | dev-requirements.txt | 1 | ||||
-rw-r--r-- | docs/x509/reference.rst | 64 | ||||
-rw-r--r-- | setup.py | 1 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 16 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/binding.py | 8 | ||||
-rw-r--r-- | tests/test_x509.py | 41 | ||||
-rw-r--r-- | tox.ini | 1 |
7 files changed, 99 insertions, 33 deletions
diff --git a/dev-requirements.txt b/dev-requirements.txt index d82c13b6..f6eec132 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,6 +5,7 @@ invoke iso8601 pep8-naming pretend +pyasn1_modules pytest requests sphinx diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index e7e02de3..f056a727 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -896,7 +896,7 @@ General Name Classes This is the generic interface that all the following classes are registered against. -.. class:: RFC822Name +.. class:: RFC822Name(value) .. versionadded:: 0.9 @@ -906,7 +906,7 @@ General Name Classes :type: :term:`text` -.. class:: DNSName +.. class:: DNSName(value) .. versionadded:: 0.9 @@ -916,7 +916,7 @@ General Name Classes :type: :term:`text` -.. class:: DirectoryName +.. class:: DirectoryName(value) .. versionadded:: 0.9 @@ -926,7 +926,7 @@ General Name Classes :type: :class:`Name` -.. class:: UniformResourceIdentifier +.. class:: UniformResourceIdentifier(value) .. versionadded:: 0.9 @@ -942,7 +942,7 @@ General Name Classes :type: :term:`text` -.. class:: IPAddress +.. class:: IPAddress(value) .. versionadded:: 0.9 @@ -954,7 +954,7 @@ General Name Classes :class:`~ipaddress.IPv6Address`, :class:`~ipaddress.IPv4Network`, or :class:`~ipaddress.IPv6Network`. -.. class:: RegisteredID +.. class:: RegisteredID(value) .. versionadded:: 0.9 @@ -964,7 +964,7 @@ General Name Classes :type: :class:`ObjectIdentifier` -.. class:: OtherName +.. class:: OtherName(type_id, value) .. versionadded:: 1.0 @@ -1050,7 +1050,7 @@ X.509 Extensions This is the interface against which all the following extension types are registered. -.. class:: KeyUsage +.. class:: KeyUsage(digital_signature, content_commitment, key_encipherment, data_encipherment, key_agreement, key_cert_sign, crl_sign, encipher_only, decipher_only) .. versionadded:: 0.9 @@ -1149,7 +1149,7 @@ X.509 Extensions is false. -.. class:: BasicConstraints +.. class:: BasicConstraints(ca, path_length) .. versionadded:: 0.9 @@ -1184,7 +1184,7 @@ X.509 Extensions means the certificate can sign a subordinate CA, but the subordinate CA is not allowed to create subordinates with ``ca`` set to true. -.. class:: ExtendedKeyUsage +.. class:: ExtendedKeyUsage(usages) .. versionadded:: 0.9 @@ -1194,6 +1194,9 @@ X.509 Extensions iterable to obtain the list of :class:`~cryptography.x509.oid.ExtendedKeyUsageOID` OIDs present. + :param list usages: A list of + :class:`~cryptography.x509.oid.ExtendedKeyUsageOID` OIDs. + .. attribute:: oid .. versionadded:: 1.0 @@ -1203,7 +1206,7 @@ X.509 Extensions Returns :attr:`~cryptography.x509.oid.ExtensionOID.EXTENDED_KEY_USAGE`. -.. class:: OCSPNoCheck +.. class:: OCSPNoCheck() .. versionadded:: 1.0 @@ -1224,7 +1227,7 @@ X.509 Extensions Returns :attr:`~cryptography.x509.oid.ExtensionOID.OCSP_NO_CHECK`. -.. class:: NameConstraints +.. class:: NameConstraints(permitted_subtrees, excluded_subtrees) .. versionadded:: 1.0 @@ -1258,7 +1261,7 @@ X.509 Extensions ``permitted_subtrees``. At least one of ``permitted_subtrees`` and ``excluded_subtrees`` will be non-None. -.. class:: AuthorityKeyIdentifier +.. class:: AuthorityKeyIdentifier(key_identifier, authority_cert_issuer, authority_cert_serial_number) .. versionadded:: 0.9 @@ -1327,7 +1330,7 @@ X.509 Extensions >>> x509.AuthorityKeyIdentifier.from_issuer_public_key(issuer_cert.public_key()) <AuthorityKeyIdentifier(key_identifier='X\x01\x84$\x1b\xbc+R\x94J=\xa5\x10r\x14Q\xf5\xaf:\xc9', authority_cert_issuer=None, authority_cert_serial_number=None)> -.. class:: SubjectKeyIdentifier +.. class:: SubjectKeyIdentifier(digest) .. versionadded:: 0.9 @@ -1374,7 +1377,7 @@ X.509 Extensions >>> x509.SubjectKeyIdentifier.from_public_key(csr.public_key()) <SubjectKeyIdentifier(digest='\xdb\xaa\xf0\x06\x11\xdbD\xfe\xbf\x93\x03\x8av\x88WP7\xa6\x91\xf7')> -.. class:: SubjectAlternativeName +.. class:: SubjectAlternativeName(general_names) .. versionadded:: 0.9 @@ -1383,6 +1386,8 @@ X.509 Extensions of identities for which the certificate is valid. The object is iterable to get every element. + :param list general_names: A list of :class:`GeneralName` instances. + .. attribute:: oid .. versionadded:: 1.0 @@ -1413,7 +1418,7 @@ X.509 Extensions [u'www.cryptography.io', u'cryptography.io'] -.. class:: IssuerAlternativeName +.. class:: IssuerAlternativeName(general_names) .. versionadded:: 1.0 @@ -1422,6 +1427,8 @@ X.509 Extensions of identities for the certificate issuer. The object is iterable to get every element. + :param list general_names: A list of :class:`GeneralName` instances. + .. attribute:: oid .. versionadded:: 1.0 @@ -1439,7 +1446,7 @@ X.509 Extensions :returns: A list of values extracted from the matched general names. -.. class:: AuthorityInformationAccess +.. class:: AuthorityInformationAccess(descriptions) .. versionadded:: 0.9 @@ -1450,6 +1457,8 @@ X.509 Extensions containing one or more :class:`~cryptography.x509.AccessDescription` instances. + :param list descriptions: A list of :class:`AccessDescription` objects. + .. attribute:: oid .. versionadded:: 1.0 @@ -1460,7 +1469,7 @@ X.509 Extensions :attr:`~cryptography.x509.oid.ExtensionOID.AUTHORITY_INFORMATION_ACCESS`. -.. class:: AccessDescription +.. class:: AccessDescription(access_method, access_location) .. versionadded:: 0.9 @@ -1486,7 +1495,7 @@ X.509 Extensions Where to access the information defined by the access method. -.. class:: CRLDistributionPoints +.. class:: CRLDistributionPoints(distribution_points) .. versionadded:: 0.9 @@ -1494,6 +1503,9 @@ X.509 Extensions obtained. It is an iterable, containing one or more :class:`DistributionPoint` instances. + :param list distribution_points: A list of :class:`DistributionPoint` + instances. + .. attribute:: oid .. versionadded:: 1.0 @@ -1503,7 +1515,7 @@ X.509 Extensions Returns :attr:`~cryptography.x509.oid.ExtensionOID.CRL_DISTRIBUTION_POINTS`. -.. class:: DistributionPoint +.. class:: DistributionPoint(full_name, relative_name, reasons, crl_issuer) .. versionadded:: 0.9 @@ -1587,7 +1599,7 @@ X.509 Extensions removed from the CRL. This reason cannot be used as a reason flag in a :class:`DistributionPoint`. -.. class:: InhibitAnyPolicy +.. class:: InhibitAnyPolicy(skip_certs) .. versionadded:: 1.0 @@ -1616,13 +1628,15 @@ X.509 Extensions :type: int -.. class:: CertificatePolicies +.. class:: CertificatePolicies(policies) .. versionadded:: 0.9 The certificate policies extension is an iterable, containing one or more :class:`PolicyInformation` instances. + :param list policies: A list of :class:`PolicyInformation` instances. + .. attribute:: oid .. versionadded:: 1.0 @@ -1637,7 +1651,7 @@ Certificate Policies Classes These classes may be present within a :class:`CertificatePolicies` instance. -.. class:: PolicyInformation +.. class:: PolicyInformation(policy_identifier, policy_qualifiers) .. versionadded:: 0.9 @@ -1657,7 +1671,7 @@ These classes may be present within a :class:`CertificatePolicies` instance. meant for display to the relying party when the certificate is used. -.. class:: UserNotice +.. class:: UserNotice(notice_reference, explicit_text) .. versionadded:: 0.9 @@ -1679,7 +1693,7 @@ These classes may be present within a :class:`CertificatePolicies` instance. :type: :term:`text` -.. class:: NoticeReference +.. class:: NoticeReference(organization, notice_numbers) Notice reference can name an organization and provide information about notices related to the certificate. For example, it might identify the @@ -63,6 +63,7 @@ test_requirements = [ "pretend", "iso8601", "hypothesis", + "pyasn1_modules", ] # If there's no vectors locally that probably means we are in a tarball and diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f7b5408a..8e302a99 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -52,7 +52,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( from cryptography.hazmat.primitives.ciphers.modes import ( CBC, CFB, CFB8, CTR, ECB, GCM, OFB ) -from cryptography.x509.oid import ExtensionOID +from cryptography.x509.oid import ExtensionOID, NameOID _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) @@ -133,12 +133,14 @@ def _encode_name(backend, attributes): for attribute in attributes: value = attribute.value.encode('utf8') obj = _txt2obj_gc(backend, attribute.oid.dotted_string) + if attribute.oid == NameOID.COUNTRY_NAME: + # Per RFC5280 Appendix A.1 countryName should be encoded as + # PrintableString, not UTF8String + type = backend._lib.MBSTRING_ASC + else: + type = backend._lib.MBSTRING_UTF8 res = backend._lib.X509_NAME_add_entry_by_OBJ( - subject, - obj, - backend._lib.MBSTRING_UTF8, - value, - -1, -1, 0, + subject, obj, type, value, -1, -1, 0, ) backend.openssl_assert(res == 1) return subject @@ -614,8 +616,6 @@ class Backend(object): res = self._lib.ASN1_STRING_set_default_mask_asc(b"utf8only") self.openssl_assert(res == 1) - self._binding.init_static_locks() - self._cipher_registry = {} self._register_default_ciphers() self.activate_osrandom_engine() diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index 4fac11d8..a750cd6b 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -172,3 +172,11 @@ class Binding(object): mode, n, file, line ) ) + + +# OpenSSL is not thread safe until the locks are initialized. We call this +# method in module scope so that it executes with the import lock. On +# Pythons < 3.4 this import lock is a global lock, which can prevent a race +# condition registering the OpenSSL locks. On Python 3.4+ the import lock +# is per module so this approach will not work. +Binding.init_static_locks() diff --git a/tests/test_x509.py b/tests/test_x509.py index a54cdc56..4072ef15 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -9,6 +9,10 @@ import datetime import ipaddress import os +from pyasn1.codec.der import decoder + +from pyasn1_modules import rfc2459 + import pytest import six @@ -1083,6 +1087,43 @@ class TestRSACertificateRequest(object): x509.DNSName(u"cryptography.io"), ] + def test_build_cert_printable_string_country_name(self, 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) + + builder = x509.CertificateBuilder().serial_number( + 777 + ).issuer_name(x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'), + ])).subject_name(x509.Name([ + x509.NameAttribute(NameOID.COUNTRY_NAME, u'US'), + x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u'Texas'), + ])).public_key( + subject_private_key.public_key() + ).not_valid_before( + not_valid_before + ).not_valid_after( + not_valid_after + ) + + cert = builder.sign(issuer_private_key, hashes.SHA256(), backend) + + parsed, _ = decoder.decode( + cert.public_bytes(serialization.Encoding.DER), + asn1Spec=rfc2459.Certificate() + ) + tbs_cert = parsed.getComponentByName('tbsCertificate') + subject = tbs_cert.getComponentByName('subject') + issuer = tbs_cert.getComponentByName('issuer') + # \x13 is printable string. The first byte of the value of the + # node corresponds to the ASN.1 string type. + assert subject[0][0][0][1][0] == b"\x13"[0] + assert issuer[0][0][0][1][0] == b"\x13"[0] + class TestCertificateBuilder(object): @pytest.mark.requires_backend_interface(interface=RSABackend) @@ -9,6 +9,7 @@ deps = pretend pytest hypothesis>=1.11.4 + pyasn1_modules ./vectors passenv = ARCHFLAGS LDFLAGS CFLAGS INCLUDE LIB LD_LIBRARY_PATH USERNAME commands = |