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

—

от автора

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 sys import pyp11 class 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/python3 import sys import pyp11 from Token import Token def 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 = 0 for 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 += 1 pyp11.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/python3 import sys import pyp11 from 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) #Проверка переменной returncode if (t1.returncode != ''): #Объект создан с ошибкой     print (t1.returncode) #Уничтожение объекта     del t1 #Завершение скрипта     quit() #объект успешно создан . . .

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

del <идентификатор объекта>

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

Главным принципом при написании методов было то, чтобы обработка исключений была внутри методов, а информация об исключениях (ошибках), возвращалась в текстовом виде. Исходя из этого все методы возвращают два значения: собственно результат выполнения и информацию об ошибке. Если ошибок нет, то второе значение пустое. Мы это уже видели на примере использования метода 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 sys import pyp11 class 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 pyp11 from Token import Token

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

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

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

ссылка на оригинал статьи https://habr.com/ru/post/547770/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *