Как стать автором
Обновить

Разбираем и просматриваем квалифицированные сертификаты средствами Python/Tkinter

Время на прочтение10 мин
Количество просмотров12K
Всего голосов 11: ↑10 и ↓1+9
Комментарии24

Комментарии 24

Код — жуть. На PEP8 и юнит тесты наплевали с высокой колокольни. Не стыдно такое выкладывать?
Нормальный код. Понятный. Мне бы было не стыдно.
Вы слишком перфекционист, не надо так.
Пардон, но нет. Для студента-второкурсника — нормальный. Для профессионала, который в разработке не один год (судя по профилю автора), это позорище. Я понимаю, когда люди пишут для себя, для себя можно гавнякать сколько угодно, но выкладывать такое в пакет для общего пользования лично я бы постеснялся. Ладно PEP, но тесты-то, тем более, что основа для тестов в самом коде как раз присутствует.
PS: И нет, не перфекционизм совершенно, а элементарные стандарты. Вы не знакомы?
На мой личный взгляд, работающий пакет без тестов неизмеримо лучше идеально_прекрасного_покрытого_тестами_и_полностью_соответствующего_пеп_стандартам, но несуществующего.
Зря вы так. Обычно как раз тесты дают до некоторой степени уверенность в работоспособности кода, тем более кода на таких языках, как питон. Без тестов автору можно разве что поверить на слово.
Ну тогда и не надейтесь, что кто-нибудь захочет доработать ваш говнокод. Такой код живёт до тех пор автор заинтересован или жив. Потом это никто в здравом уме поддерживать не станет, проще переписать с нуля.

Да автор вроде и не предлагает дорабатывать. Тем людям с манией величия)

А что такое PEP8? Без него никак код на Python-e из командной строки Linux писать нельзя? То есть у меня как у той обезьяны с "Войной и миром" получилось. По теории вероятности тыкал-тыкал и столько имоций вызвал. А какие юнит тесты для графической утилиты нужно написать? Запускаешь и смотришь то она показывает или нет. Для пакета fsb795 тоже из командной строки его запускаешь и видишь тестовый результат:


$python fsb795.py
...

Но за критику спасибо. Буду знать, что главное одежка.

А что такое PEP8? Без него никак код на Python-e из командной строки Linux писать нельзя?

Т.е., вы кодите на Python, мало того, вы получившийся результат выкладываете на обозрение и при этом специально игнорируете «Style Guide for Python Code»?

Не пойму в чем игнорирование, если и python2 и python3 с удовольствием принимают этот код?

В том, что вы показываете этот код другим людям, а не только интерпретаторам python.

Не понял! Я вам ничего не показывал, вы сами смотрите, это ваш выбор.
А другие спасибо говорят и я на них ориентируюсь.
Хорошо, в следующий раз учту PEP8 по максимуму. Но вы не назвали ни одного пункта из этих РЕКОМЕНДАЦИЙ, который был бы серьезно нарушен.
А задача-то решена или нет? Работает или нет?

Я вам ничего не показывал, вы сами смотрите, это ваш выбор.

Уберите тогда вставки кода из статьи, вы же его не показываете, тогда и смотреть не будем?

А задача-то решена или нет?

Какая?

Работает или нет?

Вам про тесты уже написали.

А другие спасибо говорят и я на них ориентируюсь.

А может вы им зп платите, продолжайте ориентироваться.
Но вы не назвали ни одного пункта из этих РЕКОМЕНДАЦИЙ, который был бы серьезно нарушен.

Отступы, то 2 пробела, то 4, даже не буду это комментировать.

Не вычистили непонятные комментарии из кода, не убрали отладочные принты, спасибо что хоть закомментировали. Вы же в публичный репозиторий выложили, неужели даже какой-то попытки ревью не было?
flake8 my_python_script.py does the job.
Для вашей графической утилиты вам решать какие тесты для нее писать и писать ли вообще. Я говорил про пакет, который вы отправили в репозиторий. Вот для него тесты желательны. Вы действительно не понимаете смысл автоматических тестов? Вы пакет выложили для того, чтобы потешить свое самолюбие, или для пользы всеобщей? Вот представьте, что кто-то (допустим, я) решил воспользоваться вашим пакетом в своей работе. Тестов у вас нет. Как убедиться, что пакет хотя бы отчасти работает правильно? Мамой клянетесь? Это несерьезно.

Я уже ответил:


fsb795.py
#  -*- coding: utf-8 -*-
import os, sys
import pyasn1
import binascii
import six
from pyasn1_modules import rfc2459, pem
from pyasn1.codec.der import decoder
from datetime import datetime, timedelta

class Certificate:
  cert_full = ''
  cert = ''
  pyver = ''
  formatCert = ''
  def __init__ (self,fileorstr):
    if not os.path.exists(fileorstr):
#Сертификат в PEM-кодировке из строки
        strcert = fileorstr.strip('\n')
        if (strcert[0:27] != '-----BEGIN CERTIFICATE-----'):
            return
        idx, substrate = pem.readPemBlocksFromFile(six.StringIO(
        strcert), ('-----BEGIN CERTIFICATE-----',
                    '-----END CERTIFICATE-----')
    )
        self.pyver = sys.version[0]
        try:
            self.cert_full, rest = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())
            self.cert = self.cert_full["tbsCertificate"]
            self.formatCert = 'PEM'
#           print (self.cert_full["signatureAlgorithm"])
#           print (self.cert_full["signatureAlgorithm"]['algorithm'])
#           print (self.cert_full["signatureValue"].prettyPrint())
        except:
            self.pyver = ''
            self.formatCert = ''
        return

    self.pyver = sys.version[0]
    filename = fileorstr
    if (self.pyver == '2'):
        if sys.platform != "win32":
            filename = filename.encode("UTF-8")
        else:
            filename = filename.encode("CP1251")
#Проверяем на DER
    file1 = open(filename, "rb")
    substrate = file1.read()
    if (self.pyver == '2'):
            b0 = ord(substrate[0])
            b1 = ord(substrate[1])
    else:
            b0 = substrate[0]
            b1 = substrate[1]
#Проверка наличия последовательности 0x30, длина сертификата не может быть меньше 127 байт
    if (b0 == 48 and b1 > 128) :
        self.formatCert = 'DER'
    else:
        self.formatCert = 'PEM'
        file1 = open(filename, "r")
        idx, substrate = pem.readPemBlocksFromFile(
            file1, ('-----BEGIN CERTIFICATE-----',
                '-----END CERTIFICATE-----')
        )
    file1.close()
    try:
        self.cert_full, rest = decoder.decode(substrate, asn1Spec=rfc2459.Certificate())
        self.cert = self.cert_full["tbsCertificate"]
    except:
        self.pyver = ''
        self.formatCert = ''

  def notation_OID(self, oidhex_string):
    ''' Input is a hex string and as one byte is 2 charecters i take an 
        empty list and insert 2 characters per element of the list.
        So for a string 'DEADBEEF' it would be ['DE','AD','BE,'EF']. '''
    hex_list = []
    for char in range(0, len(oidhex_string), 2):
        hex_list.append(oidhex_string[char]+oidhex_string[char+1])

    ''' I have deleted the first two element of the list as my hex string
        includes the standard OID tag '06' and the OID length '0D'. 
        These values are not required for the calculation as i've used 
        absolute OID and not using any ASN.1 modules. Can be removed if you
        have only the data part of the OID in hex string. '''
    del hex_list[0]
    del hex_list[0]

    # An empty string to append the value of the OID in standard notation after
    # processing each element of the list.
    OID_str = ''

    # Convert the list with hex data in str format to int format for
    # calculations.
    for element in range(len(hex_list)):
        hex_list[element] = int(hex_list[element], 16)

    # Convert the OID to its standard notation. Sourced from code in other
    # languages and adapted for python.

    # The first two digits of the OID are calculated differently from the rest.
    x = int(hex_list[0] / 40)
    y = int(hex_list[0] % 40)
    if x > 2:
        y += (x-2)*40
        x = 2

    OID_str += str(x)+'.'+str(y)

    val = 0
    for byte in range(1, len(hex_list)):
        val = ((val << 7) | ((hex_list[byte] & 0x7F)))
        if (hex_list[byte] & 0x80) != 0x80:
            OID_str += "."+str(val)
            val = 0

    # print the OID in dot notation.
    return (OID_str)

  def subjectSignTool(self):
    if(self.cert == ''):
        return ('')
    for ext in self.cert["extensions"]:
        #Ищем расширение subjectSignTool
        if(str(ext['extnID']) == "1.2.643.100.111"):
                #Его значение надо возвращать
                if sys.platform != "win32":
                    seek = 4
                else:
                    seek = 4
#Проверка версии python-а
                if (self.pyver == '2'):
#                    print ('V==2')
                    return ext['extnValue'][seek-2:]
                seek = seek - 2
#                print ('V!=2')
                sst = ext['extnValue'][seek:].prettyPrint()
                if (len(sst) > 1 and sst[0] == '0' and sst[1] == 'x'):
                    sst = binascii.unhexlify(sst[2:])
                    sst = sst.decode('utf-8')

                return (sst)
    return ('')

  def issuerSignTool(self):
    if(self.cert == ''):
        return ([])
    for ext in self.cert["extensions"]:
        #Ищем расширение subjectSignTool
        if(str(ext['extnID']) == "1.2.643.100.112"):
                #Его значение надо возвращать
                vv = ext['extnValue']
#                print(vv)
#Проверка версии python-а
                of2 = 1
                if (self.pyver == '2'):
                    of1 = ord(vv[of2])
                else:
                    of1 = vv[of2]

                if (of1 > 128):
                    of2 += (of1 - 128)
                of2 += 1
#Add for 0x30
                of2 += 1            
                if (self.pyver == '2'):
                    of1 = ord(vv[of2])
                else:
                    of1 = vv[of2]

                if (of1 > 128):
                    of2 += (of1 - 128)
                    of2 += 1
# Поля issuerSignTools          
                fsbCA = []
#Длина первого поля
#                print(of2)
                for j in range(0,4):
                    if (self.pyver == '2'):
                        ltek = ord(vv[of2])
                        stek = of2 + 1
                    else:
                        ltek = vv[of2 + 0]
                        stek = of2 + 1
                    fsb = vv[stek: stek+ltek]
                    if (self.pyver == '3'):
                        fsb = vv[stek: stek+ltek].prettyPrint()
                        if (len(fsb) > 1 and fsb[0] == '0' and fsb[1] == 'x'):
                            try:
                                val1 = binascii.unhexlify(fsb[2:])
                                fsb = val1.decode('utf-8')
                            except:
                                fsb = vv[stek: stek+ltek].prettyPrint()

#                            fsb = val1
#                    print(fsb)
                    fsbCA.append(fsb)
                    of2 += (ltek + 2)
#Возврат значений issuerSignTools
                return(fsbCA)
    return ([])

  def classUser(self):
    if(self.cert == ''):
        return ('')
    for ext in self.cert["extensions"]:
        #Ищем расширение subjectSignTool
        if(str(ext['extnID']) == "2.5.29.32"):
                print ('2.5.29.32')
                #Классы защищенности
                #           print (ext['extnValue'])
#Переводит из двоичной системы счисления (2) в hex
                kc = ext['extnValue'].prettyPrint()
#                print(kc)
#Сдвиг на 0x
#Проверка версии python-а
                if (self.pyver == '2'):
                    kc_hex = kc[2:]
                else:
                    kc_hex = kc[2:]
#                print(kc_hex)
# 4 - длина заголовка           
                kc_hex = kc_hex[4:]
#                print(kc_hex)
                i32 = kc_hex.find('300806062a85036471')
                tmp_kc = ''
                while (i32 != -1 ) :
                    #'300806062a85036471' - 10 байт бинарных и 20 hex-овых; 4 - это 3008
#                    print ('НАШЛИ КС i32=' + str(i32))
                    kcc_tek = kc_hex[i32+4: i32 + 20]
#           print (kcc_tek)
                    oid_kc = self.notation_OID(kcc_tek)
#                    print (oid_kc)
                    tmp_kc = tmp_kc + oid_kc +';;'
                    kc_hex = kc_hex[i32 + 20:]
                    i32 = kc_hex.find('300806062a85036471')

#           for ext1 in ext:
#               print (ext1)
#                print ('2.5.29.32 END')
#           print (ext['extnID'])
#           print (ext['extnValue'])
                return (tmp_kc)
    return ('')

  def parse_issuer_subject (self, who):
    if(self.cert == ''):
        return ({})
    infoMap = {
        "1.2.840.113549.1.9.2": "unstructuredName",
        "1.2.643.100.1": "OGRN",
        "1.2.643.100.5": "OGRNIP",
        "1.2.643.3.131.1.1": "INN",
        "1.2.643.100.3": "SNILS",
        "2.5.4.3": "CN",
        "2.5.4.4": "SN",
        "2.5.4.5": "serialNumber",
        "2.5.4.42": "GN",
        "1.2.840.113549.1.9.1": "E",
        "2.5.4.7": "L",
        "2.5.4.8": "ST",
        "2.5.4.9": "street",
        "2.5.4.10": "O",
        "2.5.4.11": "OU",
        "2.5.4.12": "title",
        "2.5.4.6": "Country",
    }
    issuer_or_subject = {}
#Владелец сертификата: 0 - неизвестно 1 - физ.лицо 2 - юр.лицо
    vlad = 0
    vlad_o = 0
    for rdn in self.cert[who][0]:
        if not rdn:
            continue
        oid = str(rdn[0][0])
#            oid = str(rdn[0]['type'])
        value = rdn[0][1]
#            value = rdn[0]['value']
#SNILS
        if(oid == '1.2.643.100.3'):
            vlad = 1
#OGRN
        elif(oid == '1.2.643.100.1'):
            vlad = 2
#O
        elif(oid == '2.5.4.10'):
            vlad_o = 1
        value = value[2:]
#        print(value)
        if (self.pyver == '3'):
#           value = str(value).encode('raw_unicode_escape').decode('utf8')
            val = value.prettyPrint()
#            if (len(val) > 2 and val[1] != '\''):
#            if ((len(val) > 1 and val[1] != '\'') or (len(val) == 2 and (val[0] == '\xD0' or val[0] == '\xD1'))):
            if (len(val) > 1 and val[0] == '0' and val[1] == 'x'):
                try:
                      val1 = binascii.unhexlify(val[2:])
                      value = val1.decode('utf-8')
                except:
                      pass
        try:
            if not infoMap[oid] == "Type":
                issuer_or_subject[infoMap[oid]] = value
            else:
                try:
                    issuer_or_subject[infoMap[oid]] += ", %s" % value
                except KeyError:
                    issuer_or_subject[infoMap[oid]] = value
        except KeyError:
            issuer_or_subject[oid] = value
        if(vlad_o == 1):
            vlad = 2
    return issuer_or_subject, vlad

  def issuerCert(self):
    return (self.parse_issuer_subject ("issuer"))

  def subjectCert(self):
    return (self.parse_issuer_subject ('subject'))

  def signatureCert(self):
    if(self.cert == ''):
        return ({})
    algosign = self.cert_full["signatureAlgorithm"]['algorithm']
    kk = self.cert_full["signatureValue"].prettyPrint()
    if kk[-3:-1] == "'B":
#        print(kk[-3:-1])
        #Избавляемся от "' в начале строки и 'B" и конце строки
        kk = kk[2:-3]
#Переводит из двоичной системы счисления (2) в целое
        kkh=int(kk, 2)
##    else: 
        #В MS из десятичной системы в целое
##        kkh=int(kk, 10)
    else:
       kkh=int(kk, 10)
    sign_hex = hex(kkh)
    sign_hex = sign_hex.rstrip('L')
    return (algosign, sign_hex[2:])

  def publicKey(self):
    if(self.cert == ''):
        return ({})
    pubkey = self.cert['subjectPublicKeyInfo']
    tmp_pk = {}
    ff = pubkey['algorithm']
    algo = ff['algorithm']
    tmp_pk['algo'] = algo
#Проверка на ГОСТ
    if (str(algo).find("1.2.643") == -1):
        print ('НЕ ГОСТ')
        return (tmp_pk)

    param = ff['parameters']
    lh = param.prettyPrint()[2:]
#Со 2-й по 11 позиции, первые два байта тип и длина hex-oid-а
    l1 = int(lh[7:8], 16)
    lh1 = self.notation_OID(lh [4:4+4+l1*2])
#Длина следующего oid-а
    l2 = int(lh[4+4+l1*2 + 3: 4+4+l1*2  + 4], 16)
#oid из hex в точечную форму
    lh2 = self.notation_OID(lh [4+4+l1*2:4+4+l1*2 + 4 + l2*2])

    key_bytes = pubkey['subjectPublicKey']
#Читаем значение открытого ключа как битовую строку
    kk = key_bytes.prettyPrint()
##    if sys.platform != "win32":
    if kk[-3:-1] == "'B":
#        print(kk[-3:-1])
        #Избавляемся от "' в начале строки и 'B" и конце строки
        kk = kk[2:-3]
#Переводит из двоичной системы счисления (2) в целое
        kkh=int(kk, 2)
##    else: 
        #В MS из десятичной системы в целое
##        kkh=int(kk, 10)
    else:
       kkh=int(kk, 10)
#    print (kkh)
#Из целого в HEX
    kk_hex = hex(kkh)
#Значение ключа в hex хранится как 0x440... (длина ключа 512 бит) или 0x48180... (длина ключа 1024 бита)
    if (kk_hex[3] == '4'):
        kk_hex = kk_hex[5:]
    elif (kk_hex[3] == '8'):
        kk_hex = kk_hex[7:]
#Обрезвем концевик
    kk_hex = kk_hex.rstrip('L')

    tmp_pk['curve'] = lh1
    tmp_pk['hash'] = lh2
    tmp_pk['valuepk'] = kk_hex
    return (tmp_pk)

  def prettyPrint(self):
    if(self.cert == ''):
        return ('')
    return (self.cert_full.prettyPrint())

  def serialNumber(self):
    return(self.cert.getComponentByName('serialNumber'))

  def validityCert(self):
    valid_cert = self.cert.getComponentByName('validity')
    validity_cert = {}
    not_before = valid_cert.getComponentByName('notBefore')
    not_before = str(not_before.getComponent())

    not_after = valid_cert.getComponentByName('notAfter')
    not_after = str(not_after.getComponent())
#    if isinstance(not_before, GeneralizedTime):
#        not_before = datetime.strptime(not_before, '%Y%m%d%H%M%SZ')
#    else:
    validity_cert['not_before'] = datetime.strptime(not_before, '%y%m%d%H%M%SZ')

#    if isinstance(not_after, GeneralizedTime):
#        not_after = datetime.strptime(not_after, '%Y%m%d%H%M%SZ')
#    else:
    validity_cert['not_after'] = datetime.strptime(not_after, '%y%m%d%H%M%SZ')

#    print (validity_cert) 
    return validity_cert

  def KeyUsage(self):
    X509V3_KEY_USAGE_BIT_FIELDS = (
    'digitalSignature',
    'nonRepudiation',
    'keyEncipherment',
    'dataEncipherment',
    'keyAgreement',
    'keyCertSign',
    'CRLSign',
    'encipherOnly',
    'decipherOnly',
    )
    if(self.cert == ''):
        return ([])
    ku = []
    for ext in self.cert["extensions"]:
        #Ищем расширение keyUsage
        if(str(ext['extnID']) != "2.5.29.15"):
             continue
        print ('2.5.29.15')
        os16 = ext['extnValue'].prettyPrint()
#        print(os16)
        os16 = '0404' + os16[2:]
#        print(os16)
        os = binascii.unhexlify(os16[0:])
        octet_strings = os
        e, f= decoder.decode(decoder.decode(octet_strings)[0], rfc2459.KeyUsage())
#        print (e)
        n = 0
        while n < len(e):
          if e[n]:
            ku.append(X509V3_KEY_USAGE_BIT_FIELDS[n])
#            print(X509V3_KEY_USAGE_BIT_FIELDS[n])
          n += 1
        return(ku)
    return ([])

if __name__ == "__main__":

#For test
    certpem = """
-----BEGIN CERTIFICATE-----
MIIG3DCCBougAwIBAgIKE8/KkAAAAAAC4zAIBgYqhQMCAgMwggFKMR4wHAYJKoZI
hvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRwwGgYDVQQIDBM3
NyDQsy4g0JzQvtGB0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxPzA9BgNV
BAkMNjEyNTM3NSDQsy4g0JzQvtGB0LrQstCwLCDRg9C7LiDQotCy0LXRgNGB0LrQ
sNGPLCDQtC4gNzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+
0YHRgdC40LgxGDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEB
EgwwMDc3MTA0NzQzNzUxQTA/BgNVBAMMONCT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+
0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMB4XDTE4MDcwOTE1MjYy
NFoXDTI3MDcwOTE1MjYyNFowggFVMR4wHAYJKoZIhvcNAQkBFg9jb250YWN0QGVr
ZXkucnUxITAfBgNVBAMMGNCe0J7QniDCq9CV0LrQtdC5INCj0KbCuzEwMC4GA1UE
Cwwn0KPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMSEwHwYD
VQQKDBjQntCe0J4gwqvQldC60LXQuSDQo9CmwrsxCzAJBgNVBAYTAlJVMRgwFgYD
VQQIDA83NyDQnNC+0YHQutCy0LAxRDBCBgNVBAkMO9Cj0JvQmNCm0JAg0JjQm9Cs
0JjQndCa0JAsINCULjQsINCQ0J3QotCgIDMg0K3Qojsg0J/QntCcLjk0MRgwFgYD
VQQHDA/Qsy7QnNC+0YHQutCy0LAxGDAWBgUqhQNkARINMTE0Nzc0NjcxNDYzMTEa
MBgGCCqFAwOBAwEBEgwwMDc3MTA5NjQzNDgwYzAcBgYqhQMCAhMwEgYHKoUDAgIk
AAYHKoUDAgIeAQNDAARAW3hfhvDdUxa6N8hEDjmOg/LsDDRHj5DanAyARtNB/2b5
BEzQCg4lUwrO/VHmvoUtvsrLqrxV6Ae+jh+GFli9WKOCA0AwggM8MBIGA1UdEwEB
/wQIMAYBAf8CAQAwHQYDVR0OBBYEFMQYnG5GfYRnj2ehEQ5tv8Fso/qBMAsGA1Ud
DwQEAwIBRjAdBgNVHSAEFjAUMAgGBiqFA2RxATAIBgYqhQNkcQIwKAYFKoUDZG8E
Hwwd0KHQmtCX0JggwqvQm9CY0KDQodCh0JstQ1NQwrswggGLBgNVHSMEggGCMIIB
foAUi5g7iRhR6O+cAni46sjUILJVyV2hggFSpIIBTjCCAUoxHjAcBgkqhkiG9w0B
CQEWD2RpdEBtaW5zdnlhei5ydTELMAkGA1UEBhMCUlUxHDAaBgNVBAgMEzc3INCz
LiDQnNC+0YHQutCy0LAxFTATBgNVBAcMDNCc0L7RgdC60LLQsDE/MD0GA1UECQw2
MTI1Mzc1INCzLiDQnNC+0YHQutCy0LAsINGD0LsuINCi0LLQtdGA0YHQutCw0Y8s
INC0LiA3MSwwKgYDVQQKDCPQnNC40L3QutC+0LzRgdCy0Y/Qt9GMINCg0L7RgdGB
0LjQuDEYMBYGBSqFA2QBEg0xMDQ3NzAyMDI2NzAxMRowGAYIKoUDA4EDAQESDDAw
NzcxMDQ3NDM3NTFBMD8GA1UEAww40JPQvtC70L7QstC90L7QuSDRg9C00L7RgdGC
0L7QstC10YDRj9GO0YnQuNC5INGG0LXQvdGC0YCCEDRoHkDLQe8zqaC3yHaSmikw
WQYDVR0fBFIwUDAmoCSgIoYgaHR0cDovL3Jvc3RlbGVjb20ucnUvY2RwL2d1Yy5j
cmwwJqAkoCKGIGh0dHA6Ly9yZWVzdHItcGtpLnJ1L2NkcC9ndWMuY3JsMIHGBgUq
hQNkcASBvDCBuQwj0J/QkNCa0JwgwqvQmtGA0LjQv9GC0L7Qn9GA0L4gSFNNwrsM
INCf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0KPQpsK7DDbQl9Cw0LrQu9GO0YfQ
tdC90LjQtSDihJYgMTQ5LzMvMi8yLTk5OSDQvtGCIDA1LjA3LjIwMTIMONCX0LDQ
utC70Y7Rh9C10L3QuNC1IOKEliAxNDkvNy8xLzQvMi02MDMg0L7RgiAwNi4wNy4y
MDEyMAgGBiqFAwICAwNBALvjFGhdFE9llvlvKeQmZmkI5J+yO2jFWTh8nXPjIpiL
OutUew2hIZv15pJ1QM/VgRO3BTBGDOoIrq8LvgC+3kA=
-----END CERTIFICATE-----
"""

    c1 = Certificate(certpem)
#    c1 = Certificate('cert.der')
#    c1 = Certificate('p10.p10')
    if (c1.pyver == ''):
        print('Context for certificate not create')
        exit(-1)
    print('=================formatCert================================')
    print(c1.formatCert)
    res = c1.subjectSignTool()
    print('=================subjectSignTool================================')
    print (res)
    print('=================issuerSignTool================================')
    res1 = c1.issuerSignTool()
    for ist in range(len(res1)):
        print (str(ist) + '=' + res1[ist])
    print('=================classUser================================')
    res2 = c1.prettyPrint()
#    print(res2)
    res3 = c1.classUser()
    print (res3)
    print('=================issuerCert================================')
    iss, vlad_is = c1.issuerCert()
    print ('vlad_is=' + str(vlad_is))
    for key in iss.keys():
        print (key + '=' + iss[key])
    print('=================subjectCert================================')
    sub, vlad_sub = c1.subjectCert()
    print ('vlad_sub=' + str(vlad_sub))
    for key in sub.keys():
        print (key + '=' + sub[key])
    print('=================publicKey================================')
    key_info = c1.publicKey()
    if(len(key_info) > 0):
        print(key_info['curve'])
        print(key_info['hash'])
        print(key_info['valuepk'])
    print('=================serialNumber================================')
    print(c1.serialNumber())
    print('=================validityCert================================')
    valid = c1.validityCert()
    print(valid['not_after'])
    print(valid['not_before'])
    print('=================signatureCert================================')
    algosign, value = c1.signatureCert()
    print(algosign)
    print(value)
    print('================KeyUsage=================================')
    ku = c1.KeyUsage()
    for key in ku:
        print (key)
#    print(ku)
    print('================END=================================')

Сохраняем в файле fsb795.py и выполняем команду:


$python fsb795

или


$python3 fsb795

Получаем релультат тестирования:

=================formatCert================================
PEM
=================subjectSignTool================================
СКЗИ «ЛИРССЛ-CSP»
=================issuerSignTool================================
0=ПАКМ «КриптоПро HSM»
1=ПАК «Головной УЦ»
2=Заключение № 149/3/2/2-999 от 05.07.2012
3=Заключение № 149/7/1/4/2-603 от 06.07.2012
=================classUser================================
2.5.29.32
1.2.643.100.113.1;;1.2.643.100.113.2;;
=================issuerCert================================
vlad_is=2
E=dit@minsvyaz.ru
CN=Головной удостоверяющий центр
Country=RU
L=Москва
O=Минкомсвязь России
ST=77 г. Москва
INN=007710474375
street=125375 г. Москва, ул. Тверская, д. 7
OGRN=1047702026701
=================subjectCert================================
vlad_sub=2
E=contact@ekey.ru
CN=ООО «Екей УЦ»
Country=RU
L=г.Москва
O=ООО «Екей УЦ»
ST=77 Москва
INN=007710964348
street=УЛИЦА ИЛЬИНКА, Д.4, АНТР 3 ЭТ; ПОМ.94
OGRN=1147746714631
OU=Удостоверяющий центр
=================publicKey================================
1.2.643.2.2.36.0
1.2.643.2.2.30.1
5b785f86f0dd5316ba37c8440e398e83f2ec0c34478f90da9c0c8046d341ff66f9044cd00a0e25530acefd51e6be852dbecacbaabc55e807be8e1f861658bd58
=================serialNumber================================
93558035364173444023011
=================validityCert================================
2027-07-09 15:26:24
2018-07-09 15:26:24
=================signatureCert================================
1.2.643.2.2.3
bbe314685d144f6596f96f29e426666908e49fb23b68c559387c9d73e322988b3aeb547b0da1219bf5e6927540cfd58113b70530460cea08aeaf0bbe00bede40
================KeyUsage=================================
2.5.29.15
nonRepudiation
keyCertSign
CRLSign
================END=================================


Я понимаю о чем вы говорите. И тесты есть, но этот тест мне больше по душе

Я читал ваш код и видел часть, которая генерирует этот результат. Это практически заготовка для нормального теста. Проблема в том, что этот тест понятен только вам, ибо вы знаете, каков должен быть ожидаемый выхлоп. А вот я не знаю, так что для меня, как для человека, которому ваш пакет интересен, результат вашего теста будет нулевым, я же не знаю, правильно вы сертификат распарсили или нет. Плюс не учтены возможные краевые ситуации (если таковые имеются, а они должны быть, судя по обработке except в коде).
Но вашу позицию я понял, так что дальнейшие пререкания считаю бессмысленными.
Я просто уверен, что вы в команде никогда не работали. Представьте, если каждый будет писать код в одном проекте так, как ему вздумается? Будет хаос, и люди начнут ненавидеть друг друга. Для этого и придумали соглашения по стилю написания, в питоне это pep8.
Первое впечатление — «Привет из нулевых».
Сомнительная польза, код «как не надо писать на Python», ну и застывший в веках Tcl/Tk.

Не надо так!
Мы же хотим получить доступ к атрибутам квалифицированного сертификата через Python и дать графическую утилиту для их просмотра.

Нееее, мы хотим больше знать о x509, а не читать документацию к утилите в стиле «Очень полезная кнопка ». )
А уж как получить доступ к атрибутам сертификата как-нибудь сами разберёмся.
Извините, я в Python новичок (как и в программировании в целом) и тема тестирования для меня еще является тёмной. Поэтому задам нубский вопрос. Например, я написал программу, которая парсит сертификаты. Я смотрю вывод программы и смотрю содержание сертификата, например, с помощью mmc в ОС Windows: вывод программы совпадает с тем, что я вижу в mmc. Это ведь не значит, что программа работает неправильно? Нужны ли так тесты в данном случае?
я в Python новичок (как и в программировании в целом)

Дело наживное. Москва, говорят, не сразу строилась.


вывод программы совпадает с тем, что я вижу в ОС.

Не верь глазам своим и тем более mmc. Бери в руки ASN1-парсер, парсь сертификат, смотри соответствие rfc (в нашем случае еще и требованиям ТК-26) и делай выводы. А тесты разработчику всегда нужны и он всегда тестит. О потребитель, как правило, доверяется производителю. Это к слову.

Спасибо, буду разбираться в теме тестирования!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации