def _test_decode_cert(path): cert = lib.BIO_new(lib.BIO_s_file()) if cert is ffi.NULL: lib.BIO_free(cert) raise ssl_error("Can't malloc memory to read file") epath = path.encode() if lib.BIO_read_filename(cert, epath) <= 0: lib.BIO_free(cert) raise ssl_error("Can't open file") x = lib.PEM_read_bio_X509(cert, ffi.NULL, ffi.NULL, ffi.NULL) if x is ffi.NULL: ssl_error("Error decoding PEM-encoded file") retval = _decode_certificate(x) lib.X509_free(x) if cert != ffi.NULL: lib.BIO_free(cert) return retval
def _get_peer_alt_names(certificate): # this code follows the procedure outlined in # OpenSSL's crypto/x509v3/v3_prn.c:X509v3_EXT_print() # function to extract the STACK_OF(GENERAL_NAME), # then iterates through the stack to add the # names. peer_alt_names = [] if certificate == ffi.NULL: return None # get a memory buffer biobuf = lib.BIO_new(lib.BIO_s_mem()) i = -1 while True: i = lib.X509_get_ext_by_NID(certificate, lib.NID_subject_alt_name, i) if i < 0: break # now decode the altName ext = lib.X509_get_ext(certificate, i) method = lib.X509V3_EXT_get(ext) if method is ffi.NULL: raise ssl_error("No method for internalizing subjectAltName!") ext_data = lib.X509_EXTENSION_get_data(ext) ext_data_len = ext_data.length ext_data_value = ffi.new("unsigned char**", ffi.NULL) ext_data_value[0] = ext_data.data if method.it != ffi.NULL: names = lib.ASN1_item_d2i(ffi.NULL, ext_data_value, ext_data_len, lib.ASN1_ITEM_ptr(method.it)) else: names = method.d2i(ffi.NULL, ext_data_value, ext_data_len) names = ffi.cast("GENERAL_NAMES*", names) count = lib.sk_GENERAL_NAME_num(names) for j in range(count): # get a rendering of each name in the set of names name = lib.sk_GENERAL_NAME_value(names, j) _type = name.type if _type == lib.GEN_DIRNAME: # we special-case DirName as a tuple of # tuples of attributes v = _create_tuple_for_X509_NAME(name.d.dirn) peer_alt_names.append(("DirName", v)) # GENERAL_NAME_print() doesn't handle NULL bytes in ASN1_string # correctly, CVE-2013-4238 elif _type == lib.GEN_EMAIL: v = _string_from_asn1(name.d.rfc822Name) peer_alt_names.append(("email", v)) elif _type == lib.GEN_DNS: v = _string_from_asn1(name.d.dNSName) peer_alt_names.append(("DNS", v)) elif _type == lib.GEN_URI: v = _string_from_asn1(name.d.uniformResourceIdentifier) peer_alt_names.append(("URI", v)) elif _type == lib.GEN_RID: v = "Registered ID" buf = ffi.new("char[2048]") length = lib.OBJ_obj2txt(buf, 2047, name.d.rid, 0) if length < 0: # TODO _setSSLError(NULL, 0, __FILE__, __LINE__); raise NotImplementedError elif length >= 2048: v = "<INVALID>" else: v = _str_with_len(buf, length) peer_alt_names.append(("Registered ID", v)) else: # for everything else, we use the OpenSSL print form if _type not in (lib.GEN_OTHERNAME, lib.GEN_X400, \ lib.GEN_EDIPARTY, lib.GEN_IPADD, lib.GEN_RID): warnings.warn("Unknown general type %d" % _type, RuntimeWarning) continue lib.BIO_reset(biobuf) lib.GENERAL_NAME_print(biobuf, name) v = _bio_get_str(biobuf) idx = v.find(":") if idx == -1: raise ValueError("Invalid value %s", v) peer_alt_names.append((v[:idx], v[idx + 1:])) free_func_addr = ffi.addressof(lib, "GENERAL_NAME_free") lib.sk_GENERAL_NAME_pop_free(names, free_func_addr) lib.BIO_free(biobuf) if peer_alt_names is not None: return tuple(peer_alt_names) return peer_alt_names
def _decode_certificate(certificate): retval = {} peer = _create_tuple_for_X509_NAME(lib.X509_get_subject_name(certificate)) if not peer: return None retval["subject"] = peer issuer = _create_tuple_for_X509_NAME(lib.X509_get_issuer_name(certificate)) if not issuer: return None retval["issuer"] = issuer version = lib.X509_get_version(certificate) + 1 if version == 0: return None retval["version"] = version try: biobuf = lib.BIO_new(lib.BIO_s_mem()) lib.BIO_reset(biobuf) serialNumber = lib.X509_get_serialNumber(certificate) # should not exceed 20 octets, 160 bits, so buf is big enough lib.i2a_ASN1_INTEGER(biobuf, serialNumber) buf = ffi.new("char[]", 2048) length = lib.BIO_gets(biobuf, buf, len(buf) - 1) if length < 0: raise ssl_error(None) retval["serialNumber"] = _str_with_len(buf, length) lib.BIO_reset(biobuf) notBefore = lib.X509_get_notBefore(certificate) lib.ASN1_TIME_print(biobuf, notBefore) length = lib.BIO_gets(biobuf, buf, len(buf) - 1) if length < 0: raise ssl_error(None) retval["notBefore"] = _str_with_len(buf, length) lib.BIO_reset(biobuf) notAfter = lib.X509_get_notAfter(certificate) lib.ASN1_TIME_print(biobuf, notAfter) length = lib.BIO_gets(biobuf, buf, len(buf) - 1) if length < 0: raise ssl_error(None) retval["notAfter"] = _str_with_len(buf, length) # Now look for subjectAltName peer_alt_names = _get_peer_alt_names(certificate) if peer_alt_names is None: return None if len(peer_alt_names) > 0: retval["subjectAltName"] = peer_alt_names # Authority Information Access: OCSP URIs obj = _get_aia_uri(certificate, lib.NID_ad_OCSP) if obj: retval["OCSP"] = obj obj = _get_aia_uri(certificate, lib.NID_ad_ca_issuers) if obj: retval["caIssuers"] = obj # CDP (CRL distribution points) obj = _get_crl_dp(certificate) if obj: retval["crlDistributionPoints"] = obj finally: lib.BIO_free(biobuf) return retval