Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
    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
        )
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
    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))
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
 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)
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
 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)
Ejemplo n.º 9
0
 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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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