aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2017-09-26 10:23:24 +0800
committerAlex Gaynor <alex.gaynor@gmail.com>2017-09-25 22:23:24 -0400
commit72c92f5ed1a3fe1b5196e0247bbe4cbe5e93c1a7 (patch)
tree2d7c2ddc1174a8185ac1a0d13b4189ae1b70b3ad /src
parent7bb0210ef9e4cd7c822ea3864bd7b18f3877c24b (diff)
downloadcryptography-72c92f5ed1a3fe1b5196e0247bbe4cbe5e93c1a7.tar.gz
cryptography-72c92f5ed1a3fe1b5196e0247bbe4cbe5e93c1a7.tar.bz2
cryptography-72c92f5ed1a3fe1b5196e0247bbe4cbe5e93c1a7.zip
both parse and encode the ASN1 string type for Name attributes (#3896)
* both parse and encode the ASN1 string type for Name attributes Previously cryptography encoded everything (except country names) as UTF8String. This caused problems with chain building in libraries like NSS where the subject and issuer are expected to match byte-for-byte. With this change we now parse and store the ASN1 string type as a private _type in NameAttribute. We then use this to encode when issuing a new certificate. This allows the CertificateBuilder to properly construct an identical issuer and fixes the issue with NSS. * make the sentinel private too
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py6
-rw-r--r--src/cryptography/hazmat/backends/openssl/decode_asn1.py4
-rw-r--r--src/cryptography/hazmat/backends/openssl/encode_asn1.py14
-rw-r--r--src/cryptography/x509/name.py46
4 files changed, 47 insertions, 23 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 3a889344..ede35ec0 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -95,12 +95,6 @@ class Backend(object):
self._ffi = self._binding.ffi
self._lib = self._binding.lib
- # Set the default string mask for encoding ASN1 strings to UTF8. This
- # is the default for newer OpenSSLs for several years (1.0.1h+) and is
- # recommended in RFC 2459.
- res = self._lib.ASN1_STRING_set_default_mask_asc(b"utf8only")
- self.openssl_assert(res == 1)
-
self._cipher_registry = {}
self._register_default_ciphers()
self.activate_osrandom_engine()
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
index ec55a9e8..2665fb22 100644
--- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
@@ -11,6 +11,7 @@ from asn1crypto.core import Integer, SequenceOf
from cryptography import x509
from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM
+from cryptography.x509.name import _ASN1_TYPE_TO_ENUM
from cryptography.x509.oid import (
CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID
)
@@ -51,8 +52,9 @@ def _decode_x509_name_entry(backend, x509_name_entry):
backend.openssl_assert(data != backend._ffi.NULL)
value = _asn1_string_to_utf8(backend, data)
oid = _obj2txt(backend, obj)
+ type = _ASN1_TYPE_TO_ENUM[data.type]
- return x509.NameAttribute(x509.ObjectIdentifier(oid), value)
+ return x509.NameAttribute(x509.ObjectIdentifier(oid), value, type)
def _decode_x509_name(backend, x509_name):
diff --git a/src/cryptography/hazmat/backends/openssl/encode_asn1.py b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
index 6b867683..e45e1050 100644
--- a/src/cryptography/hazmat/backends/openssl/encode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/encode_asn1.py
@@ -14,7 +14,7 @@ from cryptography.hazmat.backends.openssl.decode_asn1 import (
_CRL_ENTRY_REASON_ENUM_TO_CODE, _DISTPOINT_TYPE_FULLNAME,
_DISTPOINT_TYPE_RELATIVENAME
)
-from cryptography.x509.oid import CRLEntryExtensionOID, ExtensionOID, NameOID
+from cryptography.x509.oid import CRLEntryExtensionOID, ExtensionOID
def _encode_asn1_int(backend, x):
@@ -118,17 +118,9 @@ def _encode_sk_name_entry(backend, attributes):
def _encode_name_entry(backend, attribute):
value = attribute.value.encode('utf8')
obj = _txt2obj_gc(backend, attribute.oid.dotted_string)
- if attribute.oid in [
- NameOID.COUNTRY_NAME, NameOID.JURISDICTION_COUNTRY_NAME
- ]:
- # Per RFC5280 Appendix A.1 countryName should be encoded as
- # PrintableString, not UTF8String. EV Guidelines section 9.2.5 says
- # jurisdictionCountryName follows the same rules as countryName.
- type = backend._lib.MBSTRING_ASC
- else:
- type = backend._lib.MBSTRING_UTF8
+
name_entry = backend._lib.X509_NAME_ENTRY_create_by_OBJ(
- backend._ffi.NULL, obj, type, value, -1
+ backend._ffi.NULL, obj, attribute._type.value, value, -1
)
return name_entry
diff --git a/src/cryptography/x509/name.py b/src/cryptography/x509/name.py
index 108b60cc..2fbaee91 100644
--- a/src/cryptography/x509/name.py
+++ b/src/cryptography/x509/name.py
@@ -4,14 +4,33 @@
from __future__ import absolute_import, division, print_function
+from enum import Enum
+
import six
from cryptography import utils
from cryptography.x509.oid import NameOID, ObjectIdentifier
+class _ASN1Type(Enum):
+ UTF8String = 12
+ NumericString = 18
+ PrintableString = 19
+ T61String = 20
+ IA5String = 22
+ UTCTime = 23
+ GeneralizedTime = 24
+ VisibleString = 26
+ UniversalString = 28
+ BMPString = 30
+
+
+_ASN1_TYPE_TO_ENUM = dict((i.value, i) for i in _ASN1Type)
+_SENTINEL = object()
+
+
class NameAttribute(object):
- def __init__(self, oid, value):
+ def __init__(self, oid, value, _type=_SENTINEL):
if not isinstance(oid, ObjectIdentifier):
raise TypeError(
"oid argument must be an ObjectIdentifier instance."
@@ -22,16 +41,33 @@ class NameAttribute(object):
"value argument must be a text type."
)
- if oid == NameOID.COUNTRY_NAME and len(value.encode("utf8")) != 2:
- raise ValueError(
- "Country name must be a 2 character country code"
- )
+ if (
+ oid == NameOID.COUNTRY_NAME or
+ oid == NameOID.JURISDICTION_COUNTRY_NAME
+ ):
+ if len(value.encode("utf8")) != 2:
+ raise ValueError(
+ "Country name must be a 2 character country code"
+ )
+
+ if _type == _SENTINEL:
+ _type = _ASN1Type.PrintableString
if len(value) == 0:
raise ValueError("Value cannot be an empty string")
+ # Set the default string type for encoding ASN1 strings to UTF8. This
+ # is the default for newer OpenSSLs for several years (1.0.1h+) and is
+ # recommended in RFC 2459.
+ if _type == _SENTINEL:
+ _type = _ASN1Type.UTF8String
+
+ if not isinstance(_type, _ASN1Type):
+ raise TypeError("_type must be from the _ASN1Type enum")
+
self._oid = oid
self._value = value
+ self._type = _type
oid = utils.read_only_property("_oid")
value = utils.read_only_property("_value")