def parse(self, backend, x509_obj): extensions = [] seen_oids = set() for i in range(self.ext_count(backend, x509_obj)): ext = self.get_ext(backend, x509_obj, i) backend.openssl_assert(ext != backend._ffi.NULL) crit = backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier( _obj2txt(backend, backend._lib.X509_EXTENSION_get_object(ext))) if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid) # This OID is only supported in OpenSSL 1.1.0+ but we want # to support it in all versions of OpenSSL so we decode it # ourselves. if oid == ExtensionOID.TLS_FEATURE: data = backend._lib.X509_EXTENSION_get_data(ext) parsed = _Integers.load(_asn1_string_to_bytes(backend, data)) value = x509.TLSFeature( [_TLS_FEATURE_TYPE_TO_ENUM[x.native] for x in parsed]) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) continue try: handler = self.handlers[oid] except KeyError: # Dump the DER payload into an UnrecognizedExtension object data = backend._lib.X509_EXTENSION_get_data(ext) backend.openssl_assert(data != backend._ffi.NULL) der = backend._ffi.buffer(data.data, data.length)[:] unrecognized = x509.UnrecognizedExtension(oid, der) extensions.append(x509.Extension(oid, critical, unrecognized)) else: ext_data = backend._lib.X509V3_EXT_d2i(ext) if ext_data == backend._ffi.NULL: backend._consume_errors() raise ValueError( "The {0} extension is invalid and can't be " "parsed".format(oid)) value = handler(backend, ext_data) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) return x509.Extensions(extensions)
def _decode_general_name(backend, gn): if gn.type == backend._lib.GEN_DNS: data = _asn1_string_to_bytes(backend, gn.d.dNSName).decode("utf8") return x509.DNSName._init_without_validation(data) elif gn.type == backend._lib.GEN_URI: data = _asn1_string_to_bytes( backend, gn.d.uniformResourceIdentifier ).decode("utf8") return x509.UniformResourceIdentifier._init_without_validation(data) elif gn.type == backend._lib.GEN_RID: oid = _asn1_string_to_bytes(backend, gn.d.iPAddress) data_len = len(data) if data_len == 8 or data_len == 32: base = ipaddress.ip_address(data[:data_len // 2]) netmask = ipaddress.ip_address(data[data_len // 2:]) bits = bin(int(netmask))[2:] prefix = bits.find('0') if prefix == -1: prefix = len(bits) if "1" in bits[prefix]: raise ValueError("Invalid netmask") ip = piaddress.ip_network(base.exploded + u"/{}".format(prefix)) else: ip = ipaddress.ip_address(data) return x509.IPAddress(ip) elif gn.type == backend._lib.GEN_DIRNAME: return x509.DirectoryName( _decode_x509_name(backend, gn.d.directoryName) ) elif gn.type == backend._lib.GEN_EMAIL: data = _asn1_string_to_bytes(backend, gn.d.rfc822Name).decode("utf8") return x509.RFC822Name._init_without_validation(data) elif gn.type == backend._lib.GEN_OTHERNAME: type_id = _obj2txt(backend, gn.d.otherName.type_id) value = _asn1_to_der(backend, gn.d.otherName.value) return x509.OtherName(x509.ObjectIdentifier(type_id), value) else: raise x509.UnsupportedGeneralnameType( "{} is not a supported type".format( x509._GENERAL_NAMES.get(gn.type, gn.type) ), gn.type )
def _build_general_name(backend, gn): if gn.type == backend._lib.GEN_DNS: data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:] return x509.DNSName(idna.decode(data)) elif gn.type == backend._lib.GEN_RID: oid = _obj2txt(backend, gn.d.registeredID) return x509.RegisteredID(x509.ObjectIdentifier(oid)) else: # otherName, x400Address or ediPartyName raise x509.UnsupportedGeneralNameType( "{0} is not a supported type".format( x509._GENERAL_NAMES.get(gn.type, gn.type) ), gn.type )
def _parse_nameattr(self, av): """Parse an X.509 name attribute/value pair""" try: attr, value = av.split('=', 1) except ValueError: raise ValueError('Invalid X.509 name attribute: ' + av) from None try: attr = attr.strip() oid = self._to_oid.get(attr) or x509.ObjectIdentifier(attr) except ValueError: raise ValueError('Unknown X.509 attribute: ' + attr) from None return x509.NameAttribute(oid, self._unescape.sub(r'\1', value))
def _decode_extended_key_usage(backend, ext): sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", backend._lib.X509V3_EXT_d2i(ext)) assert sk != backend._ffi.NULL sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free) num = backend._lib.sk_ASN1_OBJECT_num(sk) ekus = [] for i in range(num): obj = backend._lib.sk_ASN1_OBJECT_value(sk, i) assert obj != backend._ffi.NULL oid = x509.ObjectIdentifier(_obj2txt(backend, obj)) ekus.append(oid) return x509.ExtendedKeyUsage(ekus)
def test_sct_embedding(): order = chisel2.auth_and_issue([random_domain()]) cert = x509.load_pem_x509_certificate(str(order.fullchain_pem), default_backend()) # make sure there is no poison extension try: cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")) raise Exception("certificate contains CT poison extension") except x509.ExtensionNotFound: # do nothing pass # make sure there is a SCT list extension try: sctList = cert.extensions.get_extension_for_oid(x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")) except x509.ExtensionNotFound: raise Exception("certificate doesn't contain SCT list extension") if len(sctList.value) != 2: raise Exception("SCT list contains wrong number of SCTs") for sct in sctList.value: if sct.version != x509.certificate_transparency.Version.v1: raise Exception("SCT contains wrong version") if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE: raise Exception("SCT contains wrong entry type")
def _decode_certificate_policies(backend, cp): cp = backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", cp) cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free) num = backend._lib.sk_POLICYINFO_num(cp) certificate_policies = [] for i in range(num): qualifiers = None pi = backend._lib.sk_POLICYINFO_value(cp, i) oid = x509.ObjectIdentifier(_obj2txt(backend, pi.policyid)) if pi.qualifiers != backend._ffi.NULL: qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers) qualifiers = [] for j in range(qnum): pqi = backend._lib.sk_POLICYQUALINFO_value( pi.qualifiers, j ) pqualid = x509.ObjectIdentifier( _obj2txt(backend, pqi.pqualid) ) if pqualid == CertificatePoliciesOID.CPS_QUALIFIER: cpsuri = backend._ffi.buffer( pqi.d.cpsuri.data, pqi.d.cpsuri.length )[:].decode('ascii') qualifiers.append(cpsuri) else: assert pqualid == CertificatePoliciesOID.CPS_USER_NOTICE user_notice = _decode_user_notice( backend, pqi.d.usernotice ) qualifiers.append(user_notice) certificate_policies.append( x509.PolicyInformation(oid, qualifiers) ) return x509.CertificatePolicies(certificate_policies)
def test_numeric_string_x509_name_entry(self): cert = _load_cert(os.path.join("x509", "e-trust.ru.der"), x509.load_der_x509_certificate, backend) if backend._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_102I: with pytest.raises(ValueError) as exc: cert.subject # We assert on the message in this case because if the certificate # fails to load it will also raise a ValueError and this test could # erroneously pass. assert str(exc.value) == "Unsupported ASN1 string type. Type: 18" else: assert cert.subject.get_attributes_for_oid( x509.ObjectIdentifier( "1.2.643.3.131.1.1"))[0].value == "007710474375"
def test_sct_embedding(): if not os.environ.get('BOULDER_CONFIG_DIR', '').startswith("test/config-next"): return certr, authzs = auth_and_issue([random_domain()]) certBytes = urllib2.urlopen(certr.uri).read() cert = x509.load_der_x509_certificate(certBytes, default_backend()) # make sure there is no poison extension try: cert.extensions.get_extension_for_oid( x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3")) raise Exception("certificate contains CT poison extension") except x509.ExtensionNotFound: # do nothing pass # make sure there is a SCT list extension try: sctList = cert.extensions.get_extension_for_oid( x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2")) except x509.ExtensionNotFound: raise Exception("certificate doesn't contain SCT list extension") if len(sctList.value) != 2: raise Exception("SCT list contains wrong number of SCTs") for sct in sctList.value: if sct.version != x509.certificate_transparency.Version.v1: raise Exception("SCT contains wrong version") if sct.entry_type != x509.certificate_transparency.LogEntryType.PRE_CERTIFICATE: raise Exception("SCT contains wrong entry type") delta = sct.timestamp - datetime.datetime.now() if abs(delta) > datetime.timedelta(hours=1): raise Exception( "Delta between SCT timestamp and now was too great " "%s vs %s (%s)" % (sct.timestamp, datetime.datetime.now(), delta))
def test_single_extensions_sct(self, backend): resp = _load_data( os.path.join("x509", "ocsp", "resp-sct-extension.der"), ocsp.load_der_ocsp_response, ) assert len(resp.single_extensions) == 1 ext = resp.single_extensions[0] assert ext.oid == x509.ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") assert len(ext.value) == 4 log_ids = [base64.b64encode(sct.log_id) for sct in ext.value] assert log_ids == [ b"RJRlLrDuzq/EQAfYqP4owNrmgr7YyzG1P9MzlrW2gag=", b"b1N2rDHwMRnYmQCkURX/dxUcEdkCwQApBo2yCJo32RM=", b"u9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e0YU=", b"7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs=", ]
def _decode_authority_information_access(backend, ext): aia = backend._lib.X509V3_EXT_d2i(ext) assert aia != backend._ffi.NULL aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia) aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free) num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia) access_descriptions = [] for i in range(num): ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i) assert ad.method != backend._ffi.NULL oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method)) assert ad.location != backend._ffi.NULL gn = _decode_general_name(backend, ad.location) access_descriptions.append(x509.AccessDescription(oid, gn)) return x509.AuthorityInformationAccess(access_descriptions)
def _build_general_name(backend, gn): if gn.type == backend._lib.GEN_DNS: data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:] return x509.DNSName(idna.decode(data)) elif gn.type == backend._lib.GEN_URI: data = backend._ffi.buffer( gn.d.uniformResourceIdentifier.data, gn.d.uniformResourceIdentifier.length )[:].decode("ascii") parsed = urllib_parse.urlparse(data) hostname = idna.decode(parsed.hostname) if parsed.port: netloc = hostname + u":" + six.text_type(parsed.port) else: netloc = hostname # Note that building a URL in this fashion means it should be # semantically indistinguishable from the original but is not # guaranteed to be exactly the same. uri = urllib_parse.urlunparse(( parsed.scheme, netloc, parsed.path, parsed.params, parsed.query, parsed.fragment )) return x509.UniformResourceIdentifier(uri) elif gn.type == backend._lib.GEN_RID: oid = _obj2txt(backend, gn.d.registeredID) return x509.RegisteredID(x509.ObjectIdentifier(oid)) elif gn.type == backend._lib.GEN_IPADD: return x509.IPAddress( ipaddress.ip_address( backend._ffi.buffer( gn.d.iPAddress.data, gn.d.iPAddress.length )[:] ) ) else: # otherName, x400Address or ediPartyName raise x509.UnsupportedGeneralNameType( "{0} is not a supported type".format( x509._GENERAL_NAMES.get(gn.type, gn.type) ), gn.type )
def parse(self, backend, x509_obj): extensions = [] seen_oids = set() for i in range(self.ext_count(backend, x509_obj)): ext = self.get_ext(backend, x509_obj, i) backend.openssl_assert(ext != backend._ffi.NULL) crit = backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier( _obj2txt(backend, backend._lib.X509_EXTENSION_get_object(ext)) ) if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid ) try: handler = self.handlers[oid] except KeyError: if critical: raise x509.UnsupportedExtension( "Critical extension {0} is not currently supported" .format(oid), oid ) else: # Dump the DER payload into an UnrecognizedExtension object data = backend._lib.X509_EXTENSION_get_data(ext) backend.openssl_assert(data != backend._ffi.NULL) der = backend._ffi.buffer(data.data, data.length)[:] unrecognized = x509.UnrecognizedExtension(oid, der) extensions.append( x509.Extension(oid, critical, unrecognized) ) else: ext_data = backend._lib.X509V3_EXT_d2i(ext) if ext_data == backend._ffi.NULL: backend._consume_errors() raise ValueError( "The {0} extension is invalid and can't be " "parsed".format(oid) ) value = handler(backend, ext_data) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) return x509.Extensions(extensions)
def mk_signed_cert(cacert, ca_privkey, name, serialnum): """ Create a CA cert + server cert + server private key. """ cert_req, privkey = mk_request(config.getint("ca", "cert_bits"), common_name=name) pubkey = privkey.public_key() cert_req = cert_req.public_key(pubkey) cert_req = cert_req.serial_number(serialnum) cert_req = mk_cert_valid(cert_req) cert_req = cert_req.issuer_name(cacert.issuer) # Extensions. extensions = [ # OID 2.16.840.1.113730.1.13 is Netscape Comment. # http://oid-info.com/get/2.16.840.1.113730.1.13 x509.UnrecognizedExtension( oid=x509.ObjectIdentifier("2.16.840.1.113730.1.13"), value=b"SSL Server", ), # Subject Alternative Name. x509.SubjectAlternativeName([x509.DNSName(name)]), # CRL Distribution Points. x509.CRLDistributionPoints([ x509.DistributionPoint( full_name=[ x509.UniformResourceIdentifier("http://localhost/crl.pem"), ], relative_name=None, reasons=None, crl_issuer=None, ), ]), ] for ext in extensions: cert_req = cert_req.add_extension(ext, critical=False) cert = cert_req.sign( private_key=ca_privkey, algorithm=hashes.SHA256(), backend=default_backend(), ) return cert, privkey
def _decode_authority_information_access(backend, aia): aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia) aia = backend._ffi.gc( aia, lambda x: backend._lib.sk_ACCESS_DESCRIPTION_pop_free( x, backend._ffi.addressof(backend._lib._original_lib, "ACCESS_DESCRIPTION_free"))) num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia) access_descriptions = [] for i in range(num): ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i) backend.openssl_assert(ad.method != backend._ffi.NULL) oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method)) backend.openssl_assert(ad.location != backend._ffi.NULL) gn = _decode_general_name(backend, ad.location) access_descriptions.append(x509.AccessDescription(oid, gn)) return x509.AuthorityInformationAccess(access_descriptions)
def _xep_patched_parse(self, backend, x509_obj): extensions = [] seen_oids = set() for i in range(self.ext_count(backend, x509_obj)): ext = self.get_ext(backend, x509_obj, i) backend.openssl_assert(ext != backend._ffi.NULL) crit = backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier( _obj2txt(backend, backend._lib.X509_EXTENSION_get_object(ext))) # This OID is only supported in OpenSSL 1.1.0+ but we want # to support it in all versions of OpenSSL so we decode it # ourselves. if oid == ExtensionOID.TLS_FEATURE: data = backend._lib.X509_EXTENSION_get_data(ext) parsed = _Integers.load(_asn1_string_to_bytes(backend, data)) value = x509.TLSFeature( [_TLS_FEATURE_TYPE_TO_ENUM[x.native] for x in parsed]) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) continue try: handler = self.handlers[oid] except KeyError: # Dump the DER payload into an UnrecognizedExtension object der = dump_der(ext, backend) unrecognized = x509.UnrecognizedExtension(oid, der) extensions.append(x509.Extension(oid, critical, unrecognized)) else: ext_data = backend._lib.X509V3_EXT_d2i(ext) if ext_data == backend._ffi.NULL: backend._consume_errors() der = dump_der(ext, backend) unrecognized = x509.UnrecognizedExtension(oid, der) extensions.append(x509.Extension(oid, critical, unrecognized)) else: value = handler(backend, ext_data) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) return x509.Extensions(extensions)
def extensions(self): extensions = [] seen_oids = set() extcount = self._backend._lib.X509_get_ext_count(self._x509) for i in range(0, extcount): ext = self._backend._lib.X509_get_ext(self._x509, i) assert ext != self._backend._ffi.NULL crit = self._backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier(_obj2txt(self._backend, ext.object)) if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid) elif oid == x509.OID_BASIC_CONSTRAINTS: value = self._build_basic_constraints(ext) elif oid == x509.OID_SUBJECT_KEY_IDENTIFIER: value = self._build_subject_key_identifier(ext) elif oid == x509.OID_KEY_USAGE: value = self._build_key_usage(ext) elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME: value = self._build_subject_alt_name(ext) elif oid == x509.OID_EXTENDED_KEY_USAGE: value = self._build_extended_key_usage(ext) elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER: value = self._build_authority_key_identifier(ext) elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS: value = self._build_authority_information_access(ext) elif oid == x509.OID_CERTIFICATE_POLICIES: value = self._build_certificate_policies(ext) elif oid == x509.OID_CRL_DISTRIBUTION_POINTS: value = self._build_crl_distribution_points(ext) elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid) else: # Unsupported non-critical extension, silently skipping for now seen_oids.add(oid) continue seen_oids.add(oid) extensions.append(x509.Extension(oid, critical, value)) return x509.Extensions(extensions)
def _make_name(common_name): return x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, settings.VPN_KEYGEN_CONFIG.KEY_COUNTRY), x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, settings.VPN_KEYGEN_CONFIG.KEY_PROVINCE), x509.NameAttribute(NameOID.LOCALITY_NAME, settings.VPN_KEYGEN_CONFIG.KEY_CITY), x509.NameAttribute(NameOID.ORGANIZATION_NAME, settings.VPN_KEYGEN_CONFIG.KEY_ORG), x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, settings.VPN_KEYGEN_CONFIG.KEY_OU), x509.NameAttribute(NameOID.COMMON_NAME, common_name), x509.NameAttribute( x509.ObjectIdentifier("2.5.4.41"), # Name settings.VPN_KEYGEN_CONFIG.KEY_NAME), x509.NameAttribute(NameOID.EMAIL_ADDRESS, settings.VPN_KEYGEN_CONFIG.KEY_EMAIL) ])
def update_ca_cert(cert_name, cert_path, skip_checks=False, **kwargs): with open(cert_path) as f: cert_pem = f.read() if not skip_checks: try: cert = x509.load_pem_x509_certificate( cert_pem.encode(), crypto_backends.default_backend()) except Exception as exc: raise ValueError("Cannot parse PEM certificate") from exc try: oid = x509.ObjectIdentifier(CERT_OID_SGX_QUOTE) _ = cert.extensions.get_extension_for_oid(oid) except x509.ExtensionNotFound as exc: raise ValueError( "X.509 extension with SGX quote not found in certificate" ) from exc args = {"name": cert_name, "cert": cert_pem} return build_proposal("update_ca_cert", args, **kwargs)
def _build_x509_name(backend, x509_name): count = backend._lib.X509_NAME_entry_count(x509_name) attributes = [] for x in range(count): entry = backend._lib.X509_NAME_get_entry(x509_name, x) obj = backend._lib.X509_NAME_ENTRY_get_object(entry) assert obj != backend._ffi.NULL data = backend._lib.X509_NAME_ENTRY_get_data(entry) assert data != backend._ffi.NULL buf = backend._ffi.new("unsigned char **") res = backend._lib.ASN1_STRING_to_UTF8(buf, data) assert res >= 0 assert buf[0] != backend._ffi.NULL buf = backend._ffi.gc( buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0])) value = backend._ffi.buffer(buf[0], res)[:].decode('utf8') oid = _obj2txt(backend, obj) attributes.append(x509.NameAttribute(x509.ObjectIdentifier(oid), value)) return x509.Name(attributes)
def parse(self, backend, x509_obj): extensions = [] seen_oids = set() for i in range(self.ext_count(backend, x509_obj)): ext = self.get_ext(backend, x509_obj, i) backend.openssl_assert(ext != backend._ffi.NULL) crit = backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier(_obj2txt(backend, ext.object)) if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid) try: handler = self.handlers[oid] except KeyError: if critical: raise x509.UnsupportedExtension( "Critical extension {0} is not currently supported". format(oid), oid) else: # For extensions which are not supported by OpenSSL we pass the # extension object directly to the parsing routine so it can # be decoded manually. if self.unsupported_exts and oid in self.unsupported_exts: ext_data = ext else: ext_data = backend._lib.X509V3_EXT_d2i(ext) if ext_data == backend._ffi.NULL: backend._consume_errors() raise ValueError( "The {0} extension is invalid and can't be " "parsed".format(oid)) value = handler(backend, ext_data) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) return x509.Extensions(extensions)
def test_get_curve_for_oid(): assert ec.get_curve_for_oid(ec.EllipticCurveOID.SECP256R1) == ec.SECP256R1 with pytest.raises(LookupError): ec.get_curve_for_oid(x509.ObjectIdentifier("1.1.1.1"))
def _pyasn1_to_cryptography_oid(oid): return crypto_x509.ObjectIdentifier(str(oid))
def _decode_general_name(backend, gn): if gn.type == backend._lib.GEN_DNS: # Convert to bytes and then decode to utf8. We don't use # asn1_string_to_utf8 here because it doesn't properly convert # utf8 from ia5strings. data = _asn1_string_to_bytes(backend, gn.d.dNSName).decode("utf8") # We don't use the constructor for DNSName so we can bypass validation # This allows us to create DNSName objects that have unicode chars # when a certificate (against the RFC) contains them. return x509.DNSName._init_without_validation(data) elif gn.type == backend._lib.GEN_URI: # Convert to bytes and then decode to utf8. We don't use # asn1_string_to_utf8 here because it doesn't properly convert # utf8 from ia5strings. data = _asn1_string_to_bytes( backend, gn.d.uniformResourceIdentifier).decode("utf8") # We don't use the constructor for URI so we can bypass validation # This allows us to create URI objects that have unicode chars # when a certificate (against the RFC) contains them. return x509.UniformResourceIdentifier._init_without_validation(data) elif gn.type == backend._lib.GEN_RID: oid = _obj2txt(backend, gn.d.registeredID) return x509.RegisteredID(x509.ObjectIdentifier(oid)) elif gn.type == backend._lib.GEN_IPADD: data = _asn1_string_to_bytes(backend, gn.d.iPAddress) data_len = len(data) if data_len == 8 or data_len == 32: # This is an IPv4 or IPv6 Network and not a single IP. This # type of data appears in Name Constraints. Unfortunately, # ipaddress doesn't support packed bytes + netmask. Additionally, # IPv6Network can only handle CIDR rather than the full 16 byte # netmask. To handle this we convert the netmask to integer, then # find the first 0 bit, which will be the prefix. If another 1 # bit is present after that the netmask is invalid. base = ipaddress.ip_address(data[:data_len // 2]) netmask = ipaddress.ip_address(data[data_len // 2:]) bits = bin(int(netmask))[2:] prefix = bits.find("0") # If no 0 bits are found it is a /32 or /128 if prefix == -1: prefix = len(bits) if "1" in bits[prefix:]: raise ValueError("Invalid netmask") ip = ipaddress.ip_network(base.exploded + "/{}".format(prefix)) else: ip = ipaddress.ip_address(data) return x509.IPAddress(ip) elif gn.type == backend._lib.GEN_DIRNAME: return x509.DirectoryName( _decode_x509_name(backend, gn.d.directoryName)) elif gn.type == backend._lib.GEN_EMAIL: # Convert to bytes and then decode to utf8. We don't use # asn1_string_to_utf8 here because it doesn't properly convert # utf8 from ia5strings. data = _asn1_string_to_bytes(backend, gn.d.rfc822Name).decode("utf8") # We don't use the constructor for RFC822Name so we can bypass # validation. This allows us to create RFC822Name objects that have # unicode chars when a certificate (against the RFC) contains them. return x509.RFC822Name._init_without_validation(data) elif gn.type == backend._lib.GEN_OTHERNAME: type_id = _obj2txt(backend, gn.d.otherName.type_id) value = _asn1_to_der(backend, gn.d.otherName.value) return x509.OtherName(x509.ObjectIdentifier(type_id), value) else: # x400Address or ediPartyName raise x509.UnsupportedGeneralNameType( "{} is not a supported type".format( x509._GENERAL_NAMES.get(gn.type, gn.type)), gn.type, )
def test_ne(self): oid1 = x509.ObjectIdentifier('oid') assert oid1 != x509.ObjectIdentifier('oid1') assert oid1 != object()
def parse(self, x509_obj): extensions = [] seen_oids = set() for i in range(self.ext_count(x509_obj)): ext = self.get_ext(x509_obj, i) self._backend.openssl_assert(ext != self._backend._ffi.NULL) crit = self._backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier( _obj2txt(self._backend, self._backend._lib.X509_EXTENSION_get_object(ext))) if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {} extension found".format(oid), oid) # These OIDs are only supported in OpenSSL 1.1.0+ but we want # to support them in all versions of OpenSSL so we decode them # ourselves. if oid == ExtensionOID.TLS_FEATURE: # The extension contents are a SEQUENCE OF INTEGERs. data = self._backend._lib.X509_EXTENSION_get_data(ext) data_bytes = _asn1_string_to_bytes(self._backend, data) features = DERReader(data_bytes).read_single_element(SEQUENCE) parsed = [] while not features.is_empty(): parsed.append(features.read_element(INTEGER).as_integer()) # Map the features to their enum value. value = x509.TLSFeature( [_TLS_FEATURE_TYPE_TO_ENUM[x] for x in parsed]) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) continue elif oid == ExtensionOID.PRECERT_POISON: data = self._backend._lib.X509_EXTENSION_get_data(ext) # The contents of the extension must be an ASN.1 NULL. reader = DERReader(_asn1_string_to_bytes(self._backend, data)) reader.read_single_element(NULL).check_empty() extensions.append( x509.Extension(oid, critical, x509.PrecertPoison())) seen_oids.add(oid) continue try: handler = self.handlers[oid] except KeyError: # Dump the DER payload into an UnrecognizedExtension object data = self._backend._lib.X509_EXTENSION_get_data(ext) self._backend.openssl_assert(data != self._backend._ffi.NULL) der = self._backend._ffi.buffer(data.data, data.length)[:] unrecognized = x509.UnrecognizedExtension(oid, der) extensions.append(x509.Extension(oid, critical, unrecognized)) else: ext_data = self._backend._lib.X509V3_EXT_d2i(ext) if ext_data == self._backend._ffi.NULL: self._backend._consume_errors() raise ValueError( "The {} extension is invalid and can't be " "parsed".format(oid)) value = handler(self._backend, ext_data) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) return x509.Extensions(extensions)
def test_repr(self): oid = x509.ObjectIdentifier("2.5.4.3") assert repr(oid) == "<ObjectIdentifier(oid=2.5.4.3, name=commonName)>" oid = x509.ObjectIdentifier("oid1") assert repr(oid) == "<ObjectIdentifier(oid=oid1, name=Unknown OID)>"
from cryptography.hazmat.primitives.hashes import MD5, SHA1, SHA224 from cryptography.hazmat.primitives.hashes import SHA256, SHA384, SHA512 from cryptography.hazmat.primitives.serialization import Encoding from cryptography.hazmat.primitives.serialization import PublicFormat from cryptography import x509 from OpenSSL import crypto from ...asn1 import IA5String, der_decode, der_encode # pylint: disable=bad-whitespace _purpose_to_oid = { 'serverAuth': x509.ExtendedKeyUsageOID.SERVER_AUTH, 'clientAuth': x509.ExtendedKeyUsageOID.CLIENT_AUTH, 'secureShellClient': x509.ObjectIdentifier('1.3.6.1.5.5.7.3.21'), 'secureShellServer': x509.ObjectIdentifier('1.3.6.1.5.5.7.3.22') } # pylint: enable=bad-whitespace _purpose_any = x509.ObjectIdentifier('2.5.29.37.0') _hashes = {h.name: h for h in (MD5, SHA1, SHA224, SHA256, SHA384, SHA512)} _nscomment_oid = x509.ObjectIdentifier('2.16.840.1.113730.1.13') if sys.platform == 'win32': # pragma: no cover # Windows' datetime.max is year 9999, but timestamps that large don't work _gen_time_max = datetime( 2999, 12, 31, 23, 59, 59, 999999, tzinfo=timezone.utc).timestamp() - 1
from binascii import a2b_hex from enum import Enum, IntEnum, unique import struct import json import six import os __all__ = [ 'Transport', 'Type', 'RegistrationData', 'SignatureData', 'RegisteredKey', 'DeviceRegistration', 'ClientData', 'RegisterRequest', 'RegisterResponse', 'SignResponse', 'U2fRegisterRequest', 'U2fSignRequest' ] U2F_V2 = 'U2F_V2' TRANSPORTS_EXT_OID = x509.ObjectIdentifier('1.3.6.1.4.1.45724.2.1.1') PUB_KEY_DER_PREFIX = a2b_hex( '3059301306072a8648ce3d020106082a8648ce3d030107034200') CERTS_TO_FIX = [ a2b_hex( '349bca1031f8c82c4ceca38b9cebf1a69df9fb3b94eed99eb3fb9aa3822d26e8'), a2b_hex( 'dd574527df608e47ae45fbba75a2afdd5c20fd94a02419381813cd55a2a3398f'), a2b_hex( '1d8764f0f7cd1352df6150045c8f638e517270e8b5dda1c63ade9c2280240cae'), a2b_hex( 'd0edc9a91a1677435a953390865d208c55b3183c6759c9b5a7ff494c322558eb'), a2b_hex( '6073c436dcd064a48127ddbf6032ac1a66fd59a0c24434f070d4e564c124c897'), a2b_hex('ca993121846c464d666096d35f13bf44c1b05af205f9b4a1e00cf6cc10c5e511')
def parse_general_name(name: ParsableGeneralName) -> x509.GeneralName: """Parse a general name from user input. This function will do its best to detect the intended type of any value passed to it: >>> parse_general_name('example.com') <DNSName(value='example.com')> >>> parse_general_name('*.example.com') <DNSName(value='*.example.com')> >>> parse_general_name('.example.com') # Syntax used e.g. for NameConstraints: All levels of subdomains <DNSName(value='.example.com')> >>> parse_general_name('*****@*****.**') <RFC822Name(value='*****@*****.**')> >>> parse_general_name('https://example.com') <UniformResourceIdentifier(value='https://example.com')> >>> parse_general_name('1.2.3.4') <IPAddress(value=1.2.3.4)> >>> parse_general_name('fd00::1') <IPAddress(value=fd00::1)> >>> parse_general_name('/CN=example.com') <DirectoryName(value=<Name(CN=example.com)>)> The default fallback is to assume a :py:class:`~cg:cryptography.x509.DNSName`. If this doesn't work, an exception will be raised: >>> parse_general_name('foo..bar`*123') # doctest: +ELLIPSIS Traceback (most recent call last): ... ValueError: Could not parse name: foo..bar`*123 If you want to override detection, you can prefix the name to match :py:const:`GENERAL_NAME_RE`: >>> parse_general_name('email:[email protected]') <RFC822Name(value='*****@*****.**')> >>> parse_general_name('URI:https://example.com') <UniformResourceIdentifier(value='https://example.com')> >>> parse_general_name('dirname:/CN=example.com') <DirectoryName(value=<Name(CN=example.com)>)> Some more exotic values can only be generated by using this prefix: >>> parse_general_name('rid:2.5.4.3') <RegisteredID(value=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>)> >>> parse_general_name('otherName:2.5.4.3;UTF8:example.com') <OtherName(type_id=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value=b'example.com')> If you give a prefixed value, this function is less forgiving of any typos and does not catch any exceptions: >>> parse_general_name('email:foo@bar com') Traceback (most recent call last): ... ValueError: Invalid domain: bar com """ # pylint: disable=too-many-return-statements,too-many-branches,too-many-statements if isinstance(name, x509.GeneralName): return name if not isinstance(name, str): raise ValueError( "Cannot parse general name %s: Must be of type str (was: %s)." % (name, type(name).__name__) ) typ = None match = GENERAL_NAME_RE.match(name) if match is not None: typ, name = match.groups() typ = typ.lower() if typ is None: if re.match("[a-z0-9]{2,}://", name): # Looks like a URI try: return x509.UniformResourceIdentifier(encode_url(name)) except idna.IDNAError: pass if "@" in name: # Looks like an Email address try: return x509.RFC822Name(validate_email(name)) except ValueError: pass if name.strip().startswith("/"): # maybe it's a dirname? return x509.DirectoryName(x509_name(name)) # Try to parse this as IPAddress/Network try: return x509.IPAddress(ip_address(name)) except ValueError: pass try: return x509.IPAddress(ip_network(name)) except ValueError: pass # Almost anything passes as DNS name, so this is our default fallback try: return x509.DNSName(encode_dns(name)) except idna.IDNAError as e: raise ValueError("Could not parse name: %s" % name) from e if typ == "uri": try: return x509.UniformResourceIdentifier(encode_url(name)) except idna.IDNAError as e: raise ValueError("Could not parse DNS name in URL: %s" % name) from e elif typ == "email": return x509.RFC822Name(validate_email(name)) # validate_email already raises ValueError elif typ == "ip": try: return x509.IPAddress(ip_address(name)) except ValueError: pass try: return x509.IPAddress(ip_network(name)) except ValueError: pass raise ValueError("Could not parse IP address.") elif typ == "rid": return x509.RegisteredID(x509.ObjectIdentifier(name)) elif typ == "othername": match = re.match("(.*);(.*):(.*)", name) if match is not None: oid, asn_typ, val = match.groups() if asn_typ == "UTF8": parsed_value = val.encode("utf-8") elif asn_typ == "OctetString": parsed_value = OctetString(bytes(bytearray.fromhex(val))).dump() else: raise ValueError("Unsupported ASN type in otherName: %s" % asn_typ) return x509.OtherName(x509.ObjectIdentifier(oid), parsed_value) raise ValueError("Incorrect otherName format: %s" % name) elif typ == "dirname": return x509.DirectoryName(x509_name(name)) else: try: return x509.DNSName(encode_dns(name)) except idna.IDNAError as e: raise ValueError("Could not parse DNS name: %s" % name) from e