def parse_certificate(certificate_path): fqdns = set() substrate = pem.readPemFromFile(open(certificate_path)) cert = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())[0] core = cert['tbsCertificate'] # Hash public key der = encoder.encode(core.getComponentByName('subjectPublicKeyInfo')) hash_der = hashlib.sha256() hash_der.update(der) pkhash = hash_der.hexdigest() # Extract CommonName for rdnss in core['subject']: for rdns in rdnss: for name in rdns: if name.getComponentByName('type') == rfc2459.id_at_commonName: value = decoder.decode( name.getComponentByName('value'), asn1Spec=rfc2459.DirectoryString())[0] fqdns.add(str(value.getComponent())) # Extract SubjectAltName for extension in core['extensions']: if extension['extnID'] == rfc2459.id_ce_subjectAltName: octet_string = decoder.decode( extension.getComponentByName('extnValue'), asn1Spec=OctetString())[0] (san_list, r) = decoder.decode(octet_string, rfc2459.SubjectAltName()) for san_struct in san_list: if san_struct.getName() == 'dNSName': fqdns.add(str(san_struct.getComponent())) return (pkhash, fqdns)
def addSubjectAlternativeName(self, names, critical): IPV4_PREFIX = "ip4:" subjectAlternativeName = rfc2459.SubjectAltName() for count, name in enumerate(names.split(",")): generalName = rfc2459.GeneralName() if "/" in name: directoryName = stringToDN( name, tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4) ) generalName["directoryName"] = directoryName elif "@" in name: generalName["rfc822Name"] = name elif name.startswith(IPV4_PREFIX): generalName["iPAddress"] = socket.inet_pton( socket.AF_INET, name[len(IPV4_PREFIX) :] ) else: # The string may have things like '\0' (i.e. a slash # followed by the number zero) that have to be decoded into # the resulting '\x00' (i.e. a byte with value zero). generalName["dNSName"] = six.ensure_binary(name).decode( "unicode_escape" ) subjectAlternativeName.setComponentByPosition(count, generalName) self.addExtension( rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical )
def parse_certificate(certificate_path): fqdns = set() substrate = pem.readPemFromFile(open(certificate_path)) cert = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())[0] core = cert['tbsCertificate'] # Extract CommonName for rdnss in core['subject']: for rdns in rdnss: for name in rdns: if name.getComponentByName('type') == rfc2459.id_at_commonName: value = decoder.decode( name.getComponentByName('value'), asn1Spec=rfc2459.DirectoryString())[0] fqdns.add(str(value.getComponent())) # extract notAfter datetime notAfter = str(core['validity'].getComponentByName( 'notAfter').getComponent()).strip('Z') (year, month, day, hour, minute, seconds) = [int(notAfter[i:i + 2]) for i in range(0, len(notAfter), 2)] expiration_date = datetime(2000 + year, month, day, hour, minute, seconds) # Extract SubjectAltName for extension in core['extensions']: if extension['extnID'] == rfc2459.id_ce_subjectAltName: (san_list, r) = decoder.decode(extension.getComponentByName('extnValue'), rfc2459.SubjectAltName()) for san_struct in san_list: if san_struct.getName() == 'dNSName': fqdns.add(str(san_struct.getComponent())) return (fqdns, expiration_date)
def __init__(self, altnames): """Создание AltName :altnames: список вида [(тип, значение), (тип, значение), ] где значение в зависимости от типа: 'otherName' : ('OID', 'байтовая строка') 'ediPartyName' : ('строка', 'строка') или 'строка' 'x400Address' : 'байтовая строка' 'directoryName' : [('OID', 'строка'), ...] 'dNSName' : строка 'uniformResourceIdentifier' : строка 'iPAddress' : строка 'registeredID' : строка """ val = rfc2459.SubjectAltName() for (i, (t, v)) in enumerate(altnames): gn = rfc2459.GeneralName() elt = getattr(self, t, None) assert elt is not None, 'unsupported element type {0}'.format(t) gn.setComponentByName(t, elt(v)) val.setComponentByPosition(i, gn) super(SubjectAltName, self).__init__(rfc2459.id_ce_subjectAltName, encoder.encode(val))
def get_san_general_names(cert): """ Return SAN general names from a python-cryptography certificate object. If the SAN extension is not present, return an empty sequence. Because python-cryptography does not yet provide a way to handle unrecognised critical extensions (which may occur), we must parse the certificate and extract the General Names. For uniformity with other code, we manually construct values of python-crytography GeneralName subtypes. python-cryptography does not yet provide types for ediPartyName or x400Address, so we drop these name types. otherNames are NOT instantiated to more specific types where the type is known. Use ``process_othernames`` to do that. When python-cryptography can handle certs with unrecognised critical extensions and implements ediPartyName and x400Address, this function (and helpers) will be redundant and should go away. """ tbs = decoder.decode( cert.tbs_certificate_bytes, asn1Spec=rfc2459.TBSCertificate() )[0] OID_SAN = univ.ObjectIdentifier('2.5.29.17') gns = [] for ext in tbs['extensions']: if ext['extnID'] == OID_SAN: der = decoder.decode( ext['extnValue'], asn1Spec=univ.OctetString())[0] gns = decoder.decode(der, asn1Spec=rfc2459.SubjectAltName())[0] break GENERAL_NAME_CONSTRUCTORS = { 'rfc822Name': lambda x: cryptography.x509.RFC822Name(unicode(x)), 'dNSName': lambda x: cryptography.x509.DNSName(unicode(x)), 'directoryName': _pyasn1_to_cryptography_directoryname, 'registeredID': _pyasn1_to_cryptography_registeredid, 'iPAddress': _pyasn1_to_cryptography_ipaddress, 'uniformResourceIdentifier': lambda x: cryptography.x509.UniformResourceIdentifier(unicode(x)), 'otherName': _pyasn1_to_cryptography_othername, } result = [] for gn in gns: gn_type = gn.getName() if gn_type in GENERAL_NAME_CONSTRUCTORS: result.append( GENERAL_NAME_CONSTRUCTORS[gn_type](gn.getComponent())) return result
def addSubjectAlternativeName(self, dNSNames, critical): subjectAlternativeName = rfc2459.SubjectAltName() for count, dNSName in enumerate(dNSNames.split(',')): generalName = rfc2459.GeneralName() # The string may have things like '\0' (i.e. a slash # followed by the number zero) that have to be decoded into # the resulting '\x00' (i.e. a byte with value zero). generalName.setComponentByName('dNSName', dNSName.decode(encoding='string_escape')) subjectAlternativeName.setComponentByPosition(count, generalName) self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical)
def __pyasn1_get_san_general_names(self): # pyasn1 returns None when the key is not present in the certificate # but we need an iterable extensions = self.__get_pyasn1_field('extensions') or [] OID_SAN = univ.ObjectIdentifier('2.5.29.17') gns = [] for ext in extensions: if ext['extnID'] == OID_SAN: der = decoder.decode(ext['extnValue'], asn1Spec=univ.OctetString())[0] gns = decoder.decode(der, asn1Spec=rfc2459.SubjectAltName())[0] break return gns
def addSubjectAlternativeName(self, names, critical): subjectAlternativeName = rfc2459.SubjectAltName() for count, name in enumerate(names.split(',')): generalName = rfc2459.GeneralName() if '/' in name: directoryName = stringToDN(name, tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) generalName.setComponentByName('directoryName', directoryName) else: # The string may have things like '\0' (i.e. a slash # followed by the number zero) that have to be decoded into # the resulting '\x00' (i.e. a byte with value zero). generalName.setComponentByName('dNSName', name.decode(encoding='string_escape')) subjectAlternativeName.setComponentByPosition(count, generalName) self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical)
def __pyasn1_get_san_general_names(self): # pyasn1 returns None when the key is not present in the certificate # but we need an iterable extensions = self.__get_pyasn1_field('extensions') or [] OID_SAN = univ.ObjectIdentifier('2.5.29.17') gns = [] for ext in extensions: if ext['extnID'] == OID_SAN: der = ext['extnValue'] if pyasn1.__version__.startswith('0.3'): # pyasn1 <= 0.3.7 needs explicit unwrap of ANY container # see https://pagure.io/freeipa/issue/7685 der = decoder.decode(der, asn1Spec=univ.OctetString())[0] gns = decoder.decode(der, asn1Spec=rfc2459.SubjectAltName())[0] break return gns
def _pyasn1_get_san_general_names(cert): tbs = decoder.decode(cert.tbs_certificate_bytes, asn1Spec=rfc2459.TBSCertificate())[0] OID_SAN = univ.ObjectIdentifier('2.5.29.17') # One would expect KeyError or empty iterable when the key ('extensions' # in this particular case) is not pressent in the certificate but pyasn1 # returns None here extensions = tbs['extensions'] or [] gns = [] for ext in extensions: if ext['extnID'] == OID_SAN: der = decoder.decode(ext['extnValue'], asn1Spec=univ.OctetString())[0] gns = decoder.decode(der, asn1Spec=rfc2459.SubjectAltName())[0] break return gns
def get_subject(f): """ return a list of subjects frmo a tbsCertificate """ tbs = f.read() res = [] # based on the original ct tools cert, rest = decoder.decode(tbs, asn1Spec=TBSCertificate()) # First, try the CN field in the subject subject = cert['subject'] l = subject.getComponent() for i in l: for attr in i: if attr['type'] == rfc2459.id_at_commonName: val, rest = decoder.decode(attr.getComponentByName('value'), asn1Spec=rfc2459.X520name()) valt = val.getName() val = val.getComponent() s = decode_string(valt, val) if (" " not in s): res.append(s) # Now, try the SubjectAlternativeName in the extensions try: # based on https://github.com/google/certificate-transparency/blob/master/cpp/client/fix-chain.py ext = cert['extensions'] for i in ext: oid = i['extnID'] if oid != rfc2459.id_ce_subjectAltName: continue subject_alt_names_raw = decoder.decode( i.getComponentByName('extnValue'), asn1Spec=pyasn1.type.univ.OctetString())[0] subject_alt_names = decoder.decode( subject_alt_names_raw, asn1Spec=rfc2459.SubjectAltName())[0] for general_name in subject_alt_names: subject_alt_name_type = general_name.getName() subject_alt_name_value = general_name.getComponent() if (subject_alt_name_type == "dNSName"): res.append(str(subject_alt_name_value)) except: pass return res
def vanillaConnect(host, port=443, attempt_protocol=OpenSSL.SSL.SSLv23_METHOD): """ Return a list of connection parameters negotiated with a vanilla connect :return: clientCiphers, certificate, connection params, (host,port), openssl_version """ returnlist = [] ## time before we started connection scan_time = datetime.datetime.utcnow() ## configure SSL context ctx = SSL.Context(attempt_protocol) ##ctx.set_options(SSL.OP_NO_SSLv2) ##ctx.set_verify(SSL.VERIFY_FAIL_IF_NO_PEER_CER6T, verify_cb) # Demand a certificate ##ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb) # Demand a certificate ##ctx.use_privatekey_file (os.path.join(dir, 'server.pkey')) ##ctx.use_certificate_file(os.path.join(dir, 'server.cert')) ##ctx.load_verify_locations("server.crt") ##print("%s" % OpenSSL.crypto.get_elliptic_curves()) try: for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res try: rawsocket = socket.socket(af, socktype, proto) except socket.error as msg: rawsocket = None return "Socket Error: %s" % msg except socket.gaierror as msg: return "getaddrinfo failed: %s" % msg rawsocket.settimeout(5) sock = SSL.Connection(ctx, rawsocket) sock.set_tlsext_host_name(host.encode('utf-8')) try: sock.connect((host, port)) except Exception as inst: return "Connection Error: %s" % inst server_ip = sock._socket.getpeername() rawsocket.settimeout(None) try: sock.do_handshake() except Exception as inst: return "Handshake Error: %s" % inst returnlist.append((scan_time, sock.get_cipher_list())) servercert = sock.get_peer_certificate() servercert_serial = servercert.get_serial_number() servercert_subject = X509Name_to_str(servercert.get_subject()) servercert_issuer = X509Name_to_str(servercert.get_issuer()) servercert_version = servercert.get_version() servercert_algo = servercert.get_signature_algorithm().decode() servercert_validity = (servercert.get_notBefore().decode(), servercert.get_notAfter().decode()) dt_now = datetime.datetime.utcnow() notbefore = str(servercert_validity[0][2:14]) notafter = str(servercert_validity[1][2:14]) ## this should work for UTCtime, GeneralTime is YYYY so fix this near the year 2050 dt_notbefore = datetime.datetime(2000 + int(notbefore[0:2]), int(notbefore[2:4]), int(notbefore[4:6]), int(notbefore[6:8]), int(notbefore[8:10]), int(notbefore[10:12])) dt_notafter = datetime.datetime(2000 + int(notafter[0:2]), int(notafter[2:4]), int(notafter[4:6]), int(notafter[6:8]), int(notafter[8:10]), int(notafter[10:12])) servercert_pubkey = servercert.get_pubkey() evp_pkey = servercert_pubkey._pkey servercert_key_bits = servercert_pubkey.bits() returncertificate = {} bio = OpenSSL.crypto._new_mem_buf() lib.PEM_write_bio_X509(bio, servercert._x509) cert_pem = OpenSSL.crypto._bio_to_string(bio).decode().strip() returncertificate['pem'] = cert_pem returncertificate['version'] = (servercert_version + 1) returncertificate['serial'] = servercert_serial returncertificate['algo'] = servercert_algo returncertificate['issuer'] = servercert_issuer returncertificate['validity'] = [dt_notbefore, dt_notafter] returncertificate['subject'] = servercert_subject key_type = servercert_pubkey.type() ## Public Key Algo Specific Extractions returnpublickey = [] returnpublickey.append(key_type) if (key_type == 408): ##print(" EC") ec_key = lib.EVP_PKEY_get1_EC_KEY(evp_pkey) ec_point = lib.EC_KEY_get0_public_key(ec_key) ec_group = lib.EC_KEY_get0_group(ec_key) ec_group_nid = lib.EC_GROUP_get_curve_name(ec_group) ec_point_conversion_form = lib.EC_KEY_get_conv_form(ec_key) curve_string = ffi.string(lib.OBJ_nid2sn(ec_group_nid)).decode() point_string = ffi.string( lib.EC_POINT_point2hex(ec_group, ec_point, ec_point_conversion_form, ffi.NULL)).decode() ##print(" curve: %s" % curve_string) ##print(" public %s" % points_string) ##print(" bits: %d" % servercert_key_bits) returnpublickey.append(servercert_key_bits) returnpublickey.append(point_string) returnpublickey.append(curve_string) #print("%s " % lib.EC_POINT_point2oct(ec_point)) #print("%s " % lib.EVP_PKEY_print_public(evp_key)) ##bio = OpenSSL.crypto._new_mem_buf() #lib.i2d_EC_PUBKEY_bio(bio, ec_key) #publickey_string = OpenSSL.crypto._bio_to_string(bio) #print(binascii.hexlify(publickey_string)) returncertificate['pubkey'] = returnpublickey elif (key_type == OpenSSL.crypto.TYPE_RSA): #print(" type: RSA") rsa_key = lib.EVP_PKEY_get1_RSA(evp_pkey) bio = OpenSSL.crypto._new_mem_buf() lib.RSA_print(bio, rsa_key, 0) rsabiostring = OpenSSL.crypto._bio_to_string(bio).decode() openssl_rsa_print_regex = "Public-Key: \((\d+) bit\)\nModulus:\n(.*)Exponent: (\d+)" prog = re.compile(openssl_rsa_print_regex, re.DOTALL) rsa_data = prog.match(rsabiostring) rsa_size, rsa_mod, rsa_exp = rsa_data.groups() rsa_mod = rsa_mod.replace(" ", "") rsa_mod = rsa_mod.replace(":", "") rsa_mod = rsa_mod.replace("\n", "") returnpublickey.append(rsa_size) returnpublickey.append(rsa_mod) returnpublickey.append(rsa_exp) returncertificate['pubkey'] = returnpublickey else: return "unsupported: %s " % returncertificate ## SAN and ext server_cert_subjectaltname = "" server_cert_subjectaltname_list = [] bc, cp, crl, ku, eku, aki, aia = (), (), (), (), (), (), () for ext in range(0, servercert.get_extension_count()): ext_obj = servercert.get_extension(ext) ext_name = ext_obj.get_short_name() #print("n: %s d: %s %s" % (ext_name, ext_obj, type(ext_obj))) if (ext_name == b'subjectAltName'): ext_data = ext_obj.get_data() server_cert_subjectaltname = decoder.decode( ext_data, asn1Spec=rfc2459.SubjectAltName())[0] for san in server_cert_subjectaltname: santype = san.getName() sancomponent = san.getComponent() if isinstance(sancomponent, pyasn1.type.char.IA5String): sanuri = san.getComponent().asOctets().decode() elif isinstance(sancomponent, pyasn1_modules.rfc2459.AnotherName): san_other_oid = san.getComponent().getComponentByName( 'type-id') san_other_value = san.getComponent().getComponentByName( 'value') sanuri = san_other_oid.prettyPrint( ) + "\n" + san_other_value.prettyPrint() else: sanuri = san.getComponent().prettyPrint() server_cert_subjectaltname_list.append("%s:%s" % (santype, sanuri)) elif (ext_name == b'basicConstraints'): bc = ext_obj elif (ext_name == b'keyUsage'): ku = ext_obj elif (ext_name == b'extendedKeyUsage'): eku = ext_obj elif (ext_name == b'authorityKeyIdentifier'): aki = ext_obj elif (ext_name == b'crlDistributionPoints'): crl = ext_obj elif (ext_name == b'authorityInfoAccess'): aia = ext_obj elif (ext_name == b'certificatePolicies'): cp = ext_obj returncertificate['san'] = server_cert_subjectaltname_list returncertificate['bc'] = bc returncertificate['eku'] = eku returncertificate['aki'] = aki returncertificate['aia'] = aia returncertificate['crl'] = crl returncertificate['ku'] = ku returncertificate['cp'] = cp ## OK done with certificate dictionary items. push to return list returnlist.append(returncertificate) # get ServerHello technical specifics cipherinuse = lib.SSL_get_current_cipher(sock._ssl) cipherinuse_string = ffi.string( lib.SSL_CIPHER_get_name(cipherinuse)).decode() cipherversion = ffi.string( lib.SSL_CIPHER_get_version(cipherinuse)).decode() protocolversion = ffi.string(lib.SSL_get_version(sock._ssl)).decode() cipherdescription = ffi.string( lib.SSL_CIPHER_description(cipherinuse, ffi.NULL, 128)).decode().strip() serverrandom = binascii.hexlify(sock.server_random()) clientrandom = binascii.hexlify(sock.client_random()) masterkey = binascii.hexlify(sock.master_key()).decode() ## requires SSL_SESSION struct expanded binding in cryptography.binding session = sock.get_session() ## print out session using SSL_SESSION_print #bio = OpenSSL.crypto._new_mem_buf() #lib.SSL_SESSION_print(bio, session._session) #print(OpenSSL.crypto._bio_to_string(bio)) ## session params returnsession_params = dict() returnsession_params['cipher'] = cipherinuse_string returnsession_params['tls_version'] = protocolversion returnsession_params['cipher_description'] = cipherdescription returnsession_params['server_random'] = serverrandom returnsession_params['client_random'] = clientrandom returnsession_params['master_key'] = masterkey sessionid_length = session._session.session_id_length returnsession_params['session_id'] = binascii.hexlify( ffi.buffer(session._session.session_id)) ## are tickets supported? if (session._session.tlsext_tick): returnsession_params['session_ticket'] = binascii.hexlify( ffi.string(session._session.tlsext_tick)) returnsession_params[ 'session_ticket_lifetime'] = session._session.tlsext_tick_lifetime_hint else: returnsession_params['session_ticket'] = "0" returnsession_params['session_ticket_lifetime'] = "0" returnlist.append(returnsession_params) returnlist.append(server_ip) openssl_version = ffi.string(lib.SSLeay_version(0)).decode() #print(openssl_version ) returnlist.append(openssl_version) ## Geo Data language = 'en' server_geo = OrderedDict() ip_to_geo = server_ip[0] reader = geolite2.reader() match = reader.get(ip_to_geo) if (match != None): if (match.get('city') != None): server_geo['city'] = match['city']['names'][language] if (match.get('subdivisions') != None): server_geo['subdivisions'] = match['subdivisions'][0]['names'][ language] if (match.get('postal') != None): server_geo['postal'] = match['postal']['code'] if (match.get('country') != None): server_geo['country'] = match['country']['names'][language] if (match.get('continent') != None): server_geo['continent'] = match['continent']['names'][language] if (match.get('location') != None): server_geo['location'] = (match['location']['latitude'], match['location']['longitude']) test_geoip_resolution = float(server_geo['location'][0]) if (test_geoip_resolution % 1 == 0): server_geo['zoom'] = 3 else: server_geo['zoom'] = 8 if (match.get('time_zone') != None): server_geo['time_zone'] = match['location']['time_zone'] if (match.get('metro_code') != None): server_geo['metro_code'] = match['location']['metro_code'] if (match.get('registered_country') != None): server_geo['registered_country'] = match['registered_country'][ 'names'][language] returnlist.append(server_geo) ## Application data try: useragent = "TLSSecondOpinion/1.0 (+https://tls2o.com TLS Second Opinion Bot)" line = "GET / HTTP/1.1\r\nHost:%s\r\nAccept: */*\r\nConnection: keep-alive\r\nUser-Agent: %s\r\n\r\n" % ( host, useragent) sock.send(line) server_response = sock.recv(65535).decode() returnlist.append(server_response) except SSL.Error: server_response = 'Connection died unexpectedly' sock.shutdown() sock.close() return returnlist
def main(): parser = argparse.ArgumentParser( description='verifyutxoc.py by MiWCryptoCurrency for UTXOC UTXO based certificate verify' ) parser.add_argument('-f', '--filename', required=True, type=argparse.FileType('r'), help='Input UTXOC Filename') parser.add_argument('-n', "--network", help='specify network (default: BTC = Bitcoin)', default='BTC', choices=NETWORK_NAMES) args = parser.parse_args() network = args.network inputcert = "" while True: line = args.filename.readline() if not line: break inputcert += line parsedcert = None try: parsedcert = decoder.decode(read_pem(inputcert), ECCertificate() )[0] except: print "Problem parsing certificate. Is input x509 Elliptic Curve certificate?" return ############################### ## Check 1 - Is the current date and time within the validity period of the certificate? print "-- Check 1: Date validity--" tbs = parsedcert.getComponentByName("tbsCertificate") validity = tbs.getComponentByName("validity") validityperiod = (validity.getComponentByName("notBefore")[0], validity.getComponentByName("notAfter")[0]) dt_now = datetime.datetime.utcnow() notbefore = str(validityperiod[0][:12]) notafter = str(validityperiod[1][:12]) ## this should work for UTCtime, GeneralTime is YYYY so fix this near the year 2050 dt_notbefore = datetime.datetime(2000 + int(notbefore[0:2]), int(notbefore[2:4]), int(notbefore[4:6]), int(notbefore[6:8]), int(notbefore[8:10]), int(notbefore[10:12])) dt_notafter = datetime.datetime(2000 + int(notafter[0:2]), int(notafter[2:4]), int(notafter[4:6]), int(notafter[6:8]), int(notafter[8:10]), int(notafter[10:12])) timetoexpire = dt_notafter - dt_now if ( dt_now < dt_notbefore ): print "This certificate is not yet valid. Please wait until validity period has started in %s" % timetoexpire return elif ( dt_now > dt_notafter ): print "This certificate has expired %s ago. Please claim any unspent transactions and migrate value to another address" % timetoexpire return print "Certificate will expire on: %s, which is in %s" % (dt_notafter, timetoexpire) print "The date notBefore and notAfter validity checks passed. OK!" ############################### ## Check 2 -- is the public key valid to be used in cryptocurrency? does it match the coin address generated from the public key? print "-- Check 2: Public Key --" print "--- Check 2.1 : Public Key over secp256k1 ---" subjectpublickeyinfo = tbs.getComponentByName("subjectPublicKeyInfo") if checkCurveSecp256k1(subjectpublickeyinfo): print "Certificate contains EC public key over curve secp256k1 OK!" else: return public_pair = subjectPublicKeyInfoToPublicPair(subjectpublickeyinfo) if not public_pair: print "Problem with getting public pair from certificate :-(" return print "Public Key is point (%d %d) on curve secp256k1" % public_pair print "--- Check 2.2 Comparing coin address in SAN to public key in cert ----" extentions = tbs.getComponentByName('extensions') ## the tx should be in the extensions somewhere. ## we also work out the netcode for pycoin here ## currently the last SAN with a coin address will win ## as multiple URI for cointype is not supported at this stage txid = None netcode = None for extension in extentions: oid = extension.getComponentByName('extnID') if (oid != OID_san): continue value = decoder.decode(extension.getComponentByName('extnValue'), asn1Spec=univ.OctetString())[0] sans = decoder.decode(value, asn1Spec=rfc2459.SubjectAltName())[0] for san in sans: santype = san.getName() if santype == 'dNSName': print "Cert is for DNS name: %s" % san.getComponent() elif santype == 'uniformResourceIdentifier': sanuri = san.getComponent().asOctets() if sanuri.startswith('bitcoin:'): netcode = 'BTC' coinuri = sanuri[8:].split('?') coinaddress = coinuri[0] coinparams = coinuri[1].split('&') for coinparam in coinparams: if coinparam.startswith("transaction="): txid = coinparam.split('=')[1] elif sanuri.startswith('dogecoin:'): netcode = 'DOGE' coinuri = sanuri[9:].split('?') coinaddress = coinuri[0] coinparams = coinuri[1].split('&') for coinparam in coinparams: if coinparam.startswith("transaction="): txid = coinparam.split('=')[1] elif sanuri.startswith('litecoin:'): netcode = 'LTC' coinuri = sanuri[9:].split('?') coinaddress = coinuri[0] coinparams = coinuri[1].split('&') for coinparam in coinparams: if coinparam.startswith("transaction="): txid = coinparam.split('=')[1] elif sanuri.startswith('blackcoin:'): netcode = 'BLK' coinuri = sanuri[10:].split('?') coinaddress = coinuri[0] coinparams = coinuri[1].split('&') for coinparam in coinparams: if coinparam.startswith("transaction="): txid = coinparam.split('=')[1] if not txid: print "No Coin address or TX found in SubjectAltName. Cannot be a UTXOC :-(" return print "Found Coin Address: %s and Transaction: %s" % (coinaddress, txid) pycoin_key = pycoin.key.Key(public_pair=public_pair, netcode=netcode) if not (coinaddress == pycoin_key.address() ): print "Coin address does not match public key on certificate! :-(" return print "Address matches subject public key. OK!" ############################### ## Is our transaction unspent? Using blockchain.info API and chain.so data API to validate Unspent. print "-- Check 3: Is the transaction unspent? --" utxo_valid = False ## dont forget to check the txid just in case its something malicious smuggled in through the cert and passed to requests ## its a SHA-256 hash so its 64 chracters if not (len(txid) == 64): print "TXID wrong size, is not a SHA-256 Hash" return ## try convert from hex, catches bad characters try: int(txid, 16) except: print "illegal characters, cannot be a SHA-256 Hash" return ## use blockchain.info for BTC ## use chain.so for DOGE ## use ??? for BLK ## use ??? for NMC if (netcode=='BTC'): if checkBlockchainInfoUTXO(coinaddress, txid) and checkChainSoUTXO(netcode, coinaddress, txid): utxo_valid = True elif (netcode=='DOGE') or (netcode=='LTC'): utxo_valid = checkChainSoUTXO(netcode, coinaddress, txid) else: print "Unsupported coin %s for 3rd party chain lookup" % netcode return # did we get a valid utxo confirmed by 3rd party? if utxo_valid: print "UTXO valid! OK!" else: print "UTXO invalid! :-(" return ############################### ## Check 4 - Signature: If self-signed or issuer signed? Check that signature against trusted signer list print "-- Check 4: Signature --" ## check the issuer attrib certissuer = tbs.getComponentByName('issuer') print "UTXOC is Signed by: %s" % subjectDnToString(certissuer) certsubject = tbs.getComponentByName('subject') if (certissuer == certsubject): print "Self Signed Cert: CA or 'bond' style UTXOC" signer_public_pair = public_pair else: build_trusted_signer_list() signer_public_pair = trusted_signer_list[subjectDnToString(certissuer)] if not signer_public_pair: print "Signing Cert not found in trusted signer store" return certsigalgo = parsedcert.getComponentByName('signatureAlgorithm') certsigvalue = parsedcert.getComponentByName('signatureValue') rawsig = bitStringtoOctetString(certsigvalue) sigder = "" for hchar in rawsig: sigder+=hchar sig_pair = decoder.decode(sigder)[0] if not (certsigalgo[0] == OID_ecdsaWithSHA256): print "Certificate not signed with ecdsa-SHA256" return r = long(sig_pair[0]) s = long(sig_pair[1]) # generate digest of the tbs tbsder = encoder.encode(tbs) hashvalue = SHA256.new(tbsder) hexdgst = hashvalue.hexdigest() dgst = hashvalue.digest() dgstaslong = encoding.to_long(256, encoding.byte_to_int, dgst)[0] # if pycoin.ecdsa.verify(generator_secp256k1, signer_public_pair, dgstaslong, (r,s)): print "Signature validated. OK!" else: print "Signature validation failed!" return print "------------------------------ ALL TESTS PASSED ------------------------------" return