Русский
Русский
English
Статистика
Реклама

Гост 34.11-2012

Поддержка токенов PKCS11 с ГОСТ-криптографией в Python. Часть II Объекты класса Token

18.03.2021 18:13:59 | Автор: admin
imageВ предыдущей статье был представлен модуль pyp11, написанный на языке Си и обеспечивающий поддержку токенов PKCS#11 с российской криптографией. В этой статье будет рассмотрен класс Token, который позволит упростить использование функционала модуля pyp11 в скриптах, написанных на Python-е. Отметим, что в качестве прототипа этого класса был взят класс token, написанный на TclOO и который используется в утилите cryptoarmpkcs:


Прототип класса Token
oo::class create token {  variable libp11  variable handle  variable infotok  variable pintok  variable nodet#Конструктор  constructor {handlelp11 labtoken slottoken} {    global pass    global yespas    set handle $handlelp11    set slots [pki::pkcs11::listslots $handle]    array set infotok []    foreach slotinfo $slots {      set slotflags [lindex $slotinfo 2]      if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} {        if {[string first $labtoken [lindex $slotinfo 1]] != -1} {          set infotok(slotlabel) [lindex $slotinfo 1]          set infotok(slotid) [lindex $slotinfo 0]          set infotok(slotflags) [lindex $slotinfo 2]          set infotok(token) [lindex $slotinfo 3]          #Берется наш токен          break        }      }    }    #Список найденных токенов в слотах    if {[llength [array names infotok]] == 0 } {      error "Constructor: Token not present for library   : $handle"    }    #Объект какого токена    set nodet [dict create pkcs11_handle $handle]    dict set nodet pkcs11_slotid $infotok(slotid)    set tit "Введите PIN-код для токене $infotok(slotlabel)"    set xa [my read_password $tit]    if {$xa == "no"} {      error "Вы передумали вводить PIN для токена $infotok(slotlabel)"    }    set pintok $pass    set pass ""    set rr [my login ]    if { $rr == 0 } {      unset pintok      error "Проверьте PIN-код токена $infotok(slotlabel)."    } elseif {$rr == -1} {      unset pintok      error "Отсутствует токен."    }    my logout  }#Методы класса  method infoslot {} {    return [array get infotok]  }  method listcerts {} {    array set lcerts []    set certsder [pki::pkcs11::listcertsder $handle $infotok(slotid)]    #Перебираем сертификаты    foreach lc $certsder {      array set derc $lc      set lcerts($derc(pkcs11_label)) [list $derc(cert_der) $derc(pkcs11_id)]      #parray derc    }    return [array get lcerts]  }  method read_password {tit} {    global yespas    global pass    set tit_orig "$::labpas"    if {$tit != ""} {      set ::labpas "$tit"    }    tk busy hold ".st.fr1"    tk busy hold ".st.fr3"    #place .topPinPw -in .st.fr1.fr2_certs.labCert  -relx 1.0 -rely 3.0 -relwidth 3.5    place .topPinPw -in .st.labMain  -relx 0.35 -rely 5.0 -relwidth 0.30    set yespas ""    focus .topPinPw.labFrPw.entryPw    vwait yespas    catch {tk busy forget ".st.fr1"}    catch {tk busy forget ".st.fr3"}    if {$tit != ""} {      set ::labpas "$tit_orig"    }    place forget .topPinPw    return $yespas  }  unexport read_password  method rename {type ckaid newlab} {    if {$type != "cert" && $type != "key" && $type != "all"} {      error "Bad type for rename "    }    set uu $nodet    lappend uu "pkcs11_id"    lappend uu $ckaid    lappend uu "pkcs11_label"    lappend uu $newlab    if { [my login ] == 0 } {      unset uu      return 0    }    pki::pkcs11::rename $type $uu    my logout    return 1  }  method changeid {type ckaid newid} {    if {$type != "cert" && $type != "key" && $type != "all"} {      error "Bad type for changeid "    }    set uu $nodet    lappend uu "pkcs11_id"    lappend uu $ckaid    lappend uu "pkcs11_id_new"    lappend uu $newid    if { [my login ] == 0 } {      unset uu      return 0    }    pki::pkcs11::rename $type $uu    my logout    return 1  }  method delete {type ckaid} {    if {$type != "cert" && $type != "key" && $type != "all" && $type != "obj"} {      error "Bad type for delete"    }    set uu $nodet    lappend uu "pkcs11_id"    lappend uu $ckaid    my login    ::pki::pkcs11::delete $type $uu    my logout    return 1  }  method deleteobj {hobj} {    set uu $nodet    lappend uu "hobj"    lappend uu $hobj#tk_messageBox -title "class deleteobj" -icon info -message "hobj: $hobj\n" -detail "$uu"    return [::pki::pkcs11::delete obj $uu ]  }  method listmechs {} {    set llmech [pki::pkcs11::listmechs $handle $infotok(slotid)]    return $llmech  }  method pubkeyinfo {cert_der_hex} {    array set linfopk [pki::pkcs11::pubkeyinfo $cert_der_hex $nodet]    return [array get linfopk]  }  method listobjects {type} {    if {$type != "cert" && $type != "pubkey" && $type != "privkey" && $type != "all" && $type != "data"} {      error "Bad type for listobjects "    }    set allobjs [::pki::pkcs11::listobjects $handle $infotok(slotid) $type]    return $allobjs  }  method importcert {cert_der_hex cka_label} {    set uu $nodet    dict set uu pkcs11_label $cka_label    if {[catch {set pkcs11id [pki::pkcs11::importcert $cert_der_hex $uu]} res] } {      error "Cannot import this certificate:$res"      #          return 0    }    return $pkcs11id  }  method login {} {    set wh 1    set rl -1    while {$wh == 1} {      if {[catch {set rl [pki::pkcs11::login $handle $infotok(slotid) $pintok]} res]} {        if {[string first "SESSION_HANDLE_INVALID" $res] != -1} {          pki::pkcs11::closesession $handle          continue        } elseif {[string first "TOKEN_NOT_PRESENT" $res] != -1} {          set wh 0          continue        }      }      break    }    if {$wh == 0} {      return -1    }    return $rl  }  method logout {} {    return [pki::pkcs11::logout $handle $infotok(slotid)]  }  method keypair {typegost parkey} {    my login    set skey [pki::pkcs11::keypair $typegost $parkey $nodet]    my logout    return $skey  }  method digest {typehash source} {    return [pki::pkcs11::digest $typehash $source $nodet]  }  method signkey {ckm digest hobj_priv} {    set uu $nodet    dict set uu hobj_privkey $hobj_priv    my login    set ss [pki::pkcs11::sign $ckm $digest $uu]    my logout    return $ss  }  method signcert {ckm digest pkcs11_id} {    set uu $nodet    dict set uu pkcs11_id $pkcs11_id    my login    set ss  [pki::pkcs11::sign $ckm $digest $uu]    my logout    return $ss  }  method verify {digest signature asn1pubkey} {    set uu $nodet    dict set uu pubkeyinfo $asn1pubkey    return [pki::pkcs11::verify $digest $signature $uu]  }  method tokenpresent {} {    set slots [pki::pkcs11::listslots $handle]    foreach slotinfo $slots {      set slotid [lindex $slotinfo 0]      set slotlabel [lindex $slotinfo 1]      set slotflags [lindex $slotinfo 2]      if {[lsearch -exact $slotflags TOKEN_PRESENT] != -1} {        if {infotok(slotlabel) == $slotlabel && $slotid == $infotok(slotid)} {          return 1        }      }    }    return 0  }  method setpin {type tpin newpin} {    if {$type != "user" && $type != "so"} {      return 0    }    if {$type == "user"} {      if {$tpin != $pintok} {        return 0      }    }    set ret [::pki::pkcs11::setpin  $handle $infotok(slotid) $type $tpin $newpin]    if {$type == "user"} {      if {$ret} {        set pitok $newpin      }    }    return $ret  }  method inituserpin {sopin upin} {    set ret [::pki::pkcs11::inituserpin $handle $infotok(slotid) $sopin $upin]    return $ret  }  method importkey {uukey} {    set uu $nodet    append uu " $uukey"    my login    if {[catch {set impkey [pki::pkcs11::importkey $uu ]} res] } {        set impkey 0    }    my logout    return $impkey  }#Деструктор  destructor {    variable handle    if {[info exists pintok]} {      my login    }    #    ::pki::pkcs11::unloadmodule  $handle  }}

Класс Token, как и любой класс в Python, включает конструктор, методы и деструктор. Конструктор и деструктор это те же методы, только с предопределёнными именами. Конструктор имеет имя __init__, а деструктор имя __del__. Объявление конструктора и деструктора можно опускать. И в классе Token мы опустим объявление деструктора, а вот конструктор будет необходим. Конструктор будет создавать экземпляр класса Token для конкретного токена с конкретными атрибутами.

I. Конструктор класса Token


Итак, конструктор класса Token выглядит следующим образом:
import sysimport pyp11class Token:  def __init__ (self, handlelp11, slottoken, serialnum):    flags = ''    self.pyver = sys.version[0]    if (self.pyver == '2'):        print ('Только для python3')        quit()#Сохраняем handle библиотеки PKCS#11    self.handle = handlelp11#Сохраняем номер слота с токеном    self.slotid = slottoken#Сохраняем серийный номер токена    self.sn = serialnum#Проверяем наличие в указанном слоте токена с заданным серийным номером    ret, stat = self.tokinfo()#Проверяем код возврата    if (stat != ''):#Возвращаем информацию об ошибке        self.returncode = stat        return#Экземпляр класса (объект) успешно создан


Параметрами конструктора (метода __init__) являются (помимо обязательного self) handle библиотеки токена (handlelp11), номер слота (slottoken), в котором должен находиться токен, и серийный номер токена (serialnum).
Для получения handle библиотеки pkcs#11, номеров слотов и информации о находящихся в них токенах можно использовать следующий скрипт:
#!/usr/bin/python3import sysimport pyp11from Token import Tokendef listslots (handle):    slots = pyp11.listslots(aa)    i = 0    lslots = []    for v in slots:        for f in v[2]:        if (f == 'TOKEN_PRESENT'):                i = 1                lslots.append(v)                break    i += 1    return (lslots)#Библиотеки для Linux#Программный токенlib = '/usr/local/lib64/libls11sw2016.so'#Облачный токен#lib = '/usr/local/lib64/libls11cloud.so'#Аппаратный токен#lib = '/usr/local/lib64/librtpkcs11ecp_2.0.so'#Библиотеки для Windows#lib='C:\Temp\ls11sw2016.dll'try:#Вызываем команду загрузки библиотеки и получаем её handle (дескриптор библиотеки)    aa = pyp11.loadmodule(lib)    print('Handle библиотеки ' + lib + ': ' + aa)except:    print('Except load lib: ')    e = sys.exc_info()[1]    e1 = e.args[0]#Печать ошибки    print (e1)    quit()#Список слотовslots = listslots(aa)i = 0for v in slots:    for f in v[2]:        if (f == 'TOKEN_PRESENT'):        if (i == 0):            print ('\nИнформация о токенах в слотах\n')        it = v[3]        print ('slotid=' + str(v[0]))        print ('\tFlags=' + str(v[2]))        print ('\tLabel="' + it[0].strip() + '"')        print ('\tManufacturer="' + it[1].strip() + '"')        print ('\tModel="' + it[2].strip() + '"')        print ('\tSerialNumber="' + it[3].strip() + '"')        i = 1        break    i += 1pyp11.unloadmodule(aa)if (i == 0):    print ('Нет ни одного подключенного токена. Вставьте токен и повторите операцию')quit()

Если с библиотекой и слотом всё ясно, то с серийным номером токена может возникнуть вопрос а зачем этот параметр нужен и почему именно он, а, например, не метка токена. Сразу оговоримся, что это принципиально для извлекаемых токенов, когда злоумышленником (или случайно) один токен в слоте будет заменён другим токеном. Более того, различные экземпляры токена могут иметь одинаковые метки. И наконец, токен может быть еще не проинициализирован или владелец будет его переинициализировать, в частности, сменит метку токена. Теоретически даже серийный номер не гарантирует его идентичность, оптимально учитывать всю информацию о токене (серийный номер, модель, производитель). В задачи конструктора и входит сохранение в переменных создаваемого экземпляра класса аргументов объекта токен:
...#Сохраняем handle библиотеки PKCS#11    self.handle = handlelp11#Сохраняем номер слота с токеном    self.slotid = slottoken#Сохраняем серийный номер токена    self.sn = serialnum...

Проверкой наличия указанного токена в указанном слоте занимается метод tokinfo(), определенный в данном классе.
Метод tokinfo возвращает два значения (см. выше в конструкторе):
#Проверяем наличие в указанном слоте токена с заданным серийным номером    ret, stat = self.tokinfo()

В первой переменной (ret) содержится результат выполнения метода, а во второй (stat) информация о том, как завершилось выполнение метода. Если вторая переменная пуста, то метод tokinfo успешно выполнился. Если вторая переменная не пуста, то выполнение метода завершилось с ошибкой. Информация об ошибке будет находиться в этой переменной. При обнаружении ошибки выполнения метода self.tokinfo конструктор записывает её в переменную returncode:
#Проверяем наличие в указанном слоте токена с заданным серийным номером    ret, stat = self.tokinfo()#Проверяем код возврата    if (stat != ''):#Возвращаем информацию об ошибке в переменной returncode        self.returncode = stat        return

После создания объекта (экземпляра класса) необходимо проверить значение переменной returncode, чтобы быть уверенным в том, что объект для указанного токена создан:
#!/usr/bin/python3import sysimport pyp11from Token import Token#Выбираем библиотеку#Аппаратный токенlib = '/usr/local/lib64/librtpkcs11ecp_2.0.so'try:    aa = pyp11.loadmodule(lib)except:    e = sys.exc_info()[1]    e1 = e.args[0]    print (e1)    quit()#Серийный номер токенаsn = '9999999999999999'slot = 110#Создаем объект токенаt1 = Token(aa, slot, sn)#Проверка переменной returncodeif (t1.returncode != ''):#Объект создан с ошибкой    print (t1.returncode)#Уничтожение объекта    del t1#Завершение скрипта    quit()#объект успешно создан. . .

Если обнаружена ошибка при создании объекта, то целесообразно этот объект уничтожить:
del <идентификатор объекта>

II. Архитектура методов в классе Token


Главным принципом при написании методов было то, чтобы обработка исключений была внутри методов, а информация об исключениях (ошибках) возвращалась в текстовом виде. Исходя из этого все методы возвращают два значения: собственно результат выполнения и информацию об ошибке. Если ошибок нет, то второе значение пустое. Мы это уже видели на примере использования метода tokinfo в конструкторе. А вот и сам код метода tokinfo:
  def tokinfo(self):    status = ''#Получаем список слотов    try:        slots = pyp11.listslots(self.handle)    except:#Проблемы с библиотекой токена        e = sys.exc_info()[1]        e1 = e.args[0]        dd = ''        status = e1        return (dd, status)    status = ''#Ищем заданный слот с указанным  токеном#Перебираем слоты    for v in slots:#Ищем заданный слот            if (v[0] != self.slotid):                status = "Ошибочный слот"                continue            self.returncode = ''#Список флагов текущего слота            self.flags = v[2]#Проверяем наличие в стоке токена            if (self.flags.count('TOKEN_PRESENT') !=0):#Проверяем серийный номер токена                tokinf = v[3]                sn = tokinf[3].strip()                if (self.sn != sn):                    status = 'Серийный номер токена=\"' + sn + '\" не совпадает с заданным \"' + self.sn + '\"'                    dd = ''                    return (dd, status)                status = ''                break            else:                dd = ''                status = "В слоте нет токена"                return (dd, status)    tt = tokinf    dd = dict(Label=tt[0].strip())    dd.update(Manufacturer=tt[1].strip())    dd.update(Model=tt[2].strip())    dd.update(SerialNumber=tt[3].strip())    self.infotok = dd#Возвращаемые значения    return (dd, status)

Полное описание класса Token находится здесь.
import sysimport pyp11class Token:  def __init__ (self, handlelp11, slottoken, serialnum):    flags = ''    self.pyver = sys.version[0]    if (self.pyver == '2'):        print ('Только для python3')        quit()#Сохраняем handle библиотеки PKCS#11    self.handle = handlelp11#Сохраняем номер слота с токеном    self.slotid = slottoken#Сохраняем серийный номер токена    self.sn = serialnum#Проверяем наличие в указанном слоте с токена с заданным серийным номером    ret, stat = self.tokinfo()#Проверяем код возврата    if (stat != ''):#Возвращаем информацию об ошибке        self.returncode = stat        return#Экземпляр класса (объект) успешно создан  def tokinfo(self):    status = ''#Получаем список слотов    try:        slots = pyp11.listslots(self.handle)    except:#Проблемы с библиотекой токена        e = sys.exc_info()[1]        e1 = e.args[0]        dd = ''        status = e1        return (dd, status)    status = ''#Ищем заданный слот с указанным  токеном#Перебираем слоты    for v in slots:#Ищем заданный слот            if (v[0] != self.slotid):                status = "Ошибочный слот"                continue            self.returncode = ''#Список флагов текущего слота            self.flags = v[2]#Проверяем наличие в стоке токена            if (self.flags.count('TOKEN_PRESENT') !=0):#Проверяем серийный номер токена                tokinf = v[3]                sn = tokinf[3].strip()                if (self.sn != sn):                    status = 'Серийный номер токена=\"' + sn + '\" не совпадает с заданным \"' + self.sn + '\"'                    dd = ''                    return (dd, status)                status = ''                break            else:                dd = ''                status = "В слоте нет токена"                return (dd, status)    tt = tokinf    dd = dict(Label=tt[0].strip())    dd.update(Manufacturer=tt[1].strip())    dd.update(Model=tt[2].strip())    dd.update(SerialNumber=tt[3].strip())    self.infotok = dd    return (dd, status)  def listcerts(self):    try:        status = ''        lcerts = pyp11.listcerts(self.handle, self.slotid)    except:#Проблемы с библиотекой токена        e = sys.exc_info()[1]        e1 = e.args[0]        lcerts = ''        status = e1    return (lcerts, status)  def listobjects(self, type1, value = '' ):    try:        status = ''        if (value == ''):        lobjs = pyp11.listobjects(self.handle, self.slotid, type1)        else:        lobjs = pyp11.listobjects(self.handle, self.slotid, type1, value)    except:#Проблемы с библиотекой токена        e = sys.exc_info()[1]        e1 = e.args[0]        lobjs = ''        status = e1    return (lobjs, status)  def rename(self, type, pkcs11id, label):    try:        status = ''        dd = dict(pkcs11_id=pkcs11id, pkcs11_label=label)        ret = pyp11.rename(self.handle, self.slotid, type, dd)    except:#Проблемы с библиотекой токена        e = sys.exc_info()[1]        e1 = e.args[0]        ret = ''        status = e1    return (ret, status)  def changeckaid(self, type, pkcs11id, pkcs11idnew):    try:        status = ''        dd = dict(pkcs11_id=pkcs11id, pkcs11_id_new=pkcs11idnew)        ret = pyp11.rename(self.handle, self.slotid, type, dd)    except:#Проблемы с библиотекой токена        e = sys.exc_info()[1]        e1 = e.args[0]        ret = ''        status = e1    return (ret, status)  def login(self, userpin):    try:        status = ''        bb = pyp11.login (self.handle, self.slotid, userpin)    except:        e = sys.exc_info()[1]        e1 = e.args[0]        bb = 0        status = e1    return (bb, status)  def logout(self):    try:        status = ''        bb = pyp11.logout (self.handle, self.slotid)    except:        e = sys.exc_info()[1]        e1 = e.args[0]        bb = 0        status = e1    return (bb, status)  def keypair(self, typek, paramk, labkey):#Параметры для ключей    gost2012_512 = ['1.2.643.7.1.2.1.2.1', '1.2.643.7.1.2.1.2.2', '1.2.643.7.1.2.1.2.3']    gost2012_256 = ['1.2.643.2.2.35.1', '1.2.643.2.2.35.2',  '1.2.643.2.2.35.3',  '1.2.643.2.2.36.0', '1.2.643.2.2.36.1', '1.2.643.7.1.2.1.1.1', '1.2.643.7.1.2.1.1.2', '1.2.643.7.1.2.1.1.3', '1.2.643.7.1.2.1.1.4']    gost2001 = ['1.2.643.2.2.35.1', '1.2.643.2.2.35.2',  '1.2.643.2.2.35.3',  '1.2.643.2.2.36.0', '1.2.643.2.2.36.1']#Тип ключа    typekey = ['g12_256', 'g12_512', 'gost2001']    genkey = ''    if (typek == typekey[0]):    gost = gost2012_256    elif (typek == typekey[1]):    gost = gost2012_512    elif (typek == typekey[2]):    gost = gost2001    else:    status = 'Неподдерживаемый тип ключа'    return (genkey, status)    if (gost.count(paramk) == 0) :    status = 'Неподдерживаемые параметры ключа'    return (genkey, status)    try:#Ошибок нет, есть ключевая пара    status = ''    genkey = pyp11.keypair(self.handle, self.slotid, typek, paramk, labkey)    except:#Не удалось создать ключевую пару    e = sys.exc_info()[1]    e1 = e.args[0]    print (e1)#Возвращаеи текст ошибки в словаре    status = e1    return (genkey, status)     def digest(self, typehash, source):#Считаем хэш    try:        status = ''        digest_hex = pyp11.digest (self.handle, self.slotid, typehash, source)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в словаре    status = e1    digest_hex = ''    return (digest_hex, status)#Формирование подписи  def sign(self, ckmpair, digest_hex, idorhandle):#Для подписи можно использовать CKA_ID или handle закрытого ключа    try:        status = ''        sign_hex = pyp11.sign(self.handle, self.slotid, ckmpair, digest_hex, idorhandle)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в словаре    status = e1    sign_hex = ''    return (sign_hex, status)#Проверка подписи  def verify(self, digest_hex, sign_hex, pubkeyinfo):#Для подписи можно использовать CKA_ID или handle закрытого ключа    try:        status = ''        verify = pyp11.verify(self.handle, self.slotid, digest_hex, sign_hex, pubkeyinfo)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    verify = 0    status = e1    return (verify, status)#Инициализировать токен  def inittoken(self, sopin, labtoken):    try:        status = ''        dd = pyp11.inittoken (self.handle, self.slotid, sopin, labtoken)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = 0    status = e1    return (dd, status)#Инициализировать пользовательский PIN-код  def inituserpin(self, sopin, userpin):    try:        status = ''        dd = pyp11.inituserpin (self.handle, self.slotid, sopin, userpin)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = 0    status = e1    return (dd, status)#Сменить пользовательский PIN-код  def changeuserpin(self, oldpin, newpin):    try:        status = ''        dd = pyp11.setpin (self.handle, self.slotid, 'user', oldpin, newpin)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = 0    status = e1    self.closesession ()    return (dd, status)  def closesession(self):    try:        status = ''        dd = pyp11.closesession (self.handle)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = 0    status = e1    return (dd, status)  def parsecert(self, cert_der_hex):    try:        status = ''        dd = pyp11.parsecert (self.handle, self.slotid, cert_der_hex)    except:#Не удалось разобрать сертификат    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = ''    status = e1    return (dd, status)  def importcert(self, cert_der_hex, labcert):    try:        status = ''        dd = pyp11.importcert (self.handle, self.slotid, cert_der_hex, labcert)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = ''    status = e1    return (dd, status)  def delobject(self, hobject):    try:        status = ''        hobjc = dict(hobj=hobject)        dd = pyp11.delete(self.handle, self.slotid, 'obj', hobjc)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = ''    status = e1    return (dd, status)  def delete(self, type, pkcs11id):    if (type == 'obj'):        dd = ''        status = 'delete for type obj use nethod delobject'        return (dd, status)    try:        status = ''        idobj = dict(pkcs11_id=pkcs11id)        dd = pyp11.delete(self.handle, self.slotid, type, idobj)    except:    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = ''    status = e1    return (dd, status)  def listmechs(self):    try:        status = ''        dd = pyp11.listmechs (self.handle, self.slotid)    except:#Не удалось получить список механизмов токена    e = sys.exc_info()[1]    e1 = e.args[0]#Возвращаеи текст ошибки в status    dd = ''    status = e1    return (dd, status)


Рассмотрим импользование функционала модуля pyp11 и аналогичных операторов с использованием класса Token.
В последнем случае необходимо будет создать и объект токена:
<дескриптор объекта> = Token(<дескриптор библиоткети>, <номер слота>, <серийный номер>)if (<дескриптор объекта>.returncode != ''):   print('Ошибка при создании объекта:')#Печать ошибки   print(<дескриптор объекта>.returncode)#Уничтожение объекта   del <дескриптор объекта>   quit()

Начнем с инициализации токена:
try:    ret = pyp11.inittoken (<дескриптор библиоткети>, <номер слота>, <SO-PIN>, <метка токена>)except:#Не удалось проинициализировать токен    e = sys.exc_info()[1]    e1 = e.args[0]    print (e1)    quit()

Аналогичный код при использовании класса Token выглядит так (идентификатор объекта t1):
ret, stat = t1.inittoken(<SO-PIN>, <метка токена>)#Проверка корретности инициализацииif (stat != ''):   print('Ошибка при инициализации токена:')#Печать ошибки   print(stat)   quit()  

Далее мы просто дадим соответствие основных операторов модуля pyp11 и методов класса Token без обработки исключений и ошибок:
<handle> := <дескриптор библиотеки pkcs11><slot> := <дескриптор слота с токеном><error> := <переменная с текстом ошибки><ret> := <результат выполнения оператора><cert_der_hex> := <сертификат в DER-формате в HEX-кодировке>=================================================#Инициализация пользовательского PIN-кода<ret> = pyp11.inituserpin (<handle>, <slot>, <SO-PIN>, <USER-PIN>)<ret>, <error> = <идентификатор объекта>.inituserpin (<SO-PIN>, <USER-PIN>)#Смена USER-PIN кода<ret> = pyp11.setpin (<handle>, <slot>, 'user', <USER-PIN старый>, <USER-PIN новый>)<ret>, <error> = t1.changeuserpin (<USER-PIN старый>, <USER-PIN новый>)#Смена SO-PIN кода<ret> = pyp11.setpin (<handle>, <slot>, 'so', <SO-PIN старый>, <SO-PIN новый>)<ret>, <error> = t1.changesopin (<SO-PIN старый>, <SO-PIN новый>)#Login<ret> = pyp11.login (<handle>, <slot>, <USER-PIN>)<ret>, <error> = t1.login (<USER-PIN>)#Logout<ret> = pyp11.logout (<handle>, <slot>)<ret>, <error> = t1.logout ()#Закрытие сессии<ret> = pyp11.closesession (<handle>)<ret>, <error> = t1.closesession ()#Список сертификатов на токене<ret> = pyp11.listcerts (<handle>, <slot>)<ret>, <error> = t1.listcerts ()#Список объектов на токене<ret> = pyp11.listobjects (<handle>, <slot>, <'cert' | 'pubkey' | 'privkey' | 'data' | 'all'> [, 'value'])<ret>, <error> = t1.listobjects (<'cert' | 'pubkey' | 'privkey' | 'data' | 'all'> [, 'value'])#Разбор сертификата<ret> = pyp11.parsecert (<handle>, <slot>, <cert_der_hex>)<ret>, <error> = t1.parsecert(<cert_der_hex>)#Импорт сертификата<ret> = pyp11.importcert (<handle>, <slot>, <cert_der_hex>, <Метка сертификата>)<ret>, <error> = t1.importcert(<cert_der_hex>, <Метка сертификата>)#Вычисление хэша<ret> = pyp11.digest (<handle>, <slot>, <тип алгоритма>, <контент>)<ret>, <error> = t1.digest(<тип алгоритма>, <контент>)#Вычисление электронной подписи<ret> = pyp11.digest (<handle>, <slot>, <механизм подписи>, <хэш от контента>, <CKA_ID | handle закрытого ключа>)<ret>, <error> = t1.digest(<механизм подписи>, <хэш от контента>, <CKA_ID | handle закрытого ключа>)#Проверка электронной подписи<ret> = pyp11.verify (<handle>, <slot>, <хэш от контента>, <подпись>, <asn1-структура subjectpublickeyinfo в hex>)<ret>, <error> = t1.verify(<хэш от контента>, <подпись>, <asn1-структура subjectpublickeyinfo в hex>)#Генерация ключевой пары<ret> = pyp11.keypair (<handle>, <slot>, <тип ключа>, <OID криптопараметра>, <CKA_LABEL>)<ret>, <error> = t1.keypair(<тип ключа>, <OID криптопараметра>, <CKA_LABEL>)

III. Сборка и установка модуля pyp11 с классом Token


Сборка и установка модуля pyp11 с классом Token ничем не отличается от описанной в первой части.
Итак, скачиваем архив и распаковываем его. Заходим в папку PythonPKCS11 и выполняем команду установки:
python3 setup.py install

После установки модуля переходим в папку tests и запускаем тесты для модуля pyp11.
Для тестирования класса Token переходим в папку test/classtoken.
Для подключения модуля pyp11 и класса Token в скрипты достаточно добавить следующие операторы:
import pyp11from Token import Token


IV. Заключение


В ближайшее время должна появиться и третья часть статьи, в которой будет рассказано, как добавить поддержку российской криптографии в проект PyKCS11.

P.S. Хочу сказать спасибо svyatikov за то, что помог протестировать проект на платформе Windows.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru