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

Description

Rutoken DemoShift is a demo application which shows use cases of Rutoken ECP Series
security tokens and smart cards (including wireless devices) for a workgroup with a single mobile device.
It also contains some useful classes for device detection and signing docs.

Github – aktivco/rutoken-vpn-community-edition-server: рутокен vpn community edition – opensource версия продукта компании "актив" рутокен vpn

Рутокен VPN Community Edition (Рутокен VPN CE) основан на решении Рутокен VPN, разработанном компанией “Актив”, которое, в свою очередь, базируется на программном продукте OpenVPN. Open VPN реализует технологию VPN для создания зашифрованных каналов.

При создании продукта преследовались следующие цели:

  • упростить настройку сервера и клиента, что позволяет быстро подготовить инфраструктуру для защищенного подключения к сети компании;
  • внедрить двухфакторную аутентификацию, где в качестве фактора владения используются криптографические токены линейки Рутокен ЭЦП 2.0;
  • обеспечить удобство пользователей: пользователи могут самостоятельно выполнять операции по настройке клиента с помощью портала самообслуживания, а для подключения к VPN им потребуется только запустить клиент, подключить один из совместимых устройств Рутокен и ввести PIN-код.

В интерфейсе управления представлены следующие основные функции:

  • Настройка сети;
  • Создание центра сертификации;
  • Настройка роутинга;
  • Обновление сертификата VPN-сервера;
  • Настройка интеграции с ActiveDirectory.

При этом большая часть возможностей по-прежнему доступна, но для их настройки используются конфигурационные файлы, которые можно менять непосредственно на сервере, а не в web-интерфейсе Рутокен VPN CE.

Существует коммерческая версия продукта – Рутокен VPN Enterprise.

Рутокен VPN Enterprise поставляется в виде виртуальной машины для распространённых систем виртуализации. В этом случае количество одновременно подключенных пользователей зависит только от вычислительной мощности предоставленной виртуальной машине.

Рутокен VPN Community Edition отличается от коммерческих версий тем, что:

  • Не представляется сервер обновлений;
  • Не предоставляется возможность использования шифрования с использованием криптографических алгоритмов ГОСТ Р 34.10-2022 и ГОСТ Р 34.12-2022.

Сервис предназначен для работы в Ubuntu 20.04. Для работы сервиса требуется Python 3.6.0. При развертке под другие операционные системы или отличные версии может потребоваться адаптация продукта и/или механизма настройки окружения.

Настройка окружения для развертки сервиса осуществляется посредством запуска скрипта install.sh, а так же смотрите описание в файле INSTALL для более подробной информации


Клиент для работы с сервисом можно скачать с сайта компании “Актив” – https://www.rutoken.ru/support/download/rutoken-vpn/

Также в GitHub доступен исходный код Рутокен VPN Клиент Community Edition


Руководство для разработчика вы можете найти в файле “Developer Guide.pdf”

How to build

Before building the project:

  • copy librtpkcs11ecp.so and librtpcsc.so libraries to <project_root>/app/src/main/jniLibs/<arch>/, where <arch> is library architecture;
  • copy rtserviceconnection*.aar, pkcs11jna*.jar and pkcs11wrapper*.jar to <project_root>/app/libs/.

Use Android Studio to build the project.

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/libls11sw2022.so'
#Облачный токен
#lib = '/usr/local/lib64/libls11cloud.so'
#Аппаратный токен
#lib = '/usr/local/lib64/librtpkcs11ecp_2.0.so'
#Библиотеки для Windows
#lib='C:Templs11sw2022.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 успешно выполнился. Если вторая переменная не пуста, то выполнение метода завершилось с ошибкой.

#Проверяем наличие в указанном слоте токена с заданным серийным номером
    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. архитектура методов в классе token

Главным принципом при написании методов было то, чтобы обработка исключений была внутри методов, а информация об исключениях (ошибках) возвращалась в текстовом виде. Исходя из этого все методы возвращают два значения: собственно результат выполнения и информацию об ошибке.

  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):
#Параметры для ключей
    gost2022_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']
    gost2022_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 = gost2022_256
    elif (typek == typekey[1]):
    	gost = gost2022_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.

В последнем случае необходимо будет создать и объект токена:


Начнем с инициализации токена:

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 без обработки исключений и ошибок:

Iii. сборка и установка модуля pyp11 с классом token


Сборка и

с классом Token ничем не отличается от описанной в первой части.

Итак, скачиваем архив и распаковываем его. Заходим в папку PythonPKCS11 и выполняем команду установки:

python3 setup.py install


После установки модуля переходим в папку tests и запускаем тесты для модуля pyp11.

Для тестирования класса Token переходим в папку test/classtoken.

Для подключения модуля pyp11 и класса Token в скрипты достаточно добавить следующие операторы:

import pyp11
from Token import Token

License

The project source code is distributed under the Simplified BSD License, unless otherwise specified in the source file.

Preliminary actions

To create a key pair and a certificate on Rutoken ECP Series devices follow these steps:

Requirements

Rutoken DemoShift should be built using:

  • Android Studio 4.0 or newer;
  • Android SDK Platform 29 or newer.

External dependencies are located in Rutoken SDK.

Required libraries:

  • librtpkcs11ecp.so (for following architectures: armeabi-v7a, arm64-v8a);
  • librtpcsc.so (for following architectures: armeabi-v7a, arm64-v8a);
  • rtserviceconnection*.aar, where * is library version (current version is ‘-1.1.0’);
  • pkcs11jna*.jar, where * is library version (current version is ‘-1.1.4’);
  • pkcs11wrapper*.jar, where * is library version (current version is ‘-2.1.0’).
Читайте также:  Безопасный ключ к «облаку»

Restriction

  • Rutoken DemoShift can only be run on physical devices, not on emulators.

Опыт применения технологии рутокен для регистрации и авторизации пользователей в системе (часть 2)

Добрый день! Продолжим разбираться с данной темой (

с предыдущей частью можно ознакомиться по ссылке

).

Сегодня перейдем к практической части. Начнем с настройки своего удостоверяющего центра на основе полноценной криптографической библиотеки с открытым исходным кодом openSSL. Данный алгоритм был проверен с использованием windows 7.

Установив openSSL, мы можем выполнять различные криптографические операции (например, создание ключей и сертификатов) через командную строку.

Алгоритм действий следующий:

  1. Скачиваем установочный дистрибутив openssl-1.1.1g.
    У openSSL имеются различные версии. В документации к Рутокену было сказано, что необходима версия openSSL версии 1.1.0 или новее. Я использовал версию openssl-1.1.1g. Можно скачать openSSL с официального сайта, но для более простой установки необходимо найти установочный файл для windows в сети. Я это сделал за вас: slproweb.com/products/Win32OpenSSL.html
    Необходимо пролистать вниз страницы и скачать Win64 OpenSSL v1.1.1g EXE 63MB Installer.
  2. Устанавливаем openssl-1.1.1g на компьютер.
    Установку необходимо провести по стандартному пути, который указывается автоматически в папку C:Program Files. Программа установится в папку OpenSSL-Win64.
  3. Для того, чтобы настроить openSSL так как вам нужно, существует файл openssl.cfg. Этот файл расположен в пути C:Program FilesOpenSSL-Win64bin если вы установили openSSL так, как было сказано в предыдущем пункте. Переходим в папку, где хранится openssl.cfg и открываем этот файл с использованием, например, Notepad .
  4. Вы наверно догадались, что настройка удостоверяющего центра будет производиться как-то изменением содержимого файла openssl.cfg, и вы абсолютно правы. Для этого требуется настроить команду [ ca ]. В файле openssl.cfg начало текста, куда мы будем вносить изменения, можно найти как: [ ca ].
  5. Теперь я приведу пример настройки с его описанием:
    [ ca ]
    default_ca	= CA_default		
    
     [ CA_default ]
    dir		= /Users/username/bin/openSSLca/demoCA		 
    certs		= $dir/certs		
    crl_dir		= $dir/crl		
    database	= $dir/index.txt	
    new_certs_dir	= $dir/newcerts	
    certificate	= $dir/ca.crt 	
    serial		= $dir/private/serial 		
    crlnumber	= $dir/crlnumber	
    					
    crl		= $dir/crl.pem 		
    private_key	= $dir/private/ca.key
    x509_extensions	= usr_cert
    

    Сейчас необходимо создать каталог demoCA и подкаталоги, как показано в примере выше. И расположить в этот каталог по пути, что указан в dir (у меня /Users/username/bin/openSSLca/demoCA).

    Очень важно правильно прописать dir – это путь к директории, где будет находиться наш удостоверяющий центр. Эта директория должна обязательно находиться в /Users (то есть в учетной записи какого-нибудь пользователя). Если расположить эту директорию например в C:Program Files, система не увидит файл с настройками openssl.cfg (по крайней мере у меня было так).

    $dir — сюда подставляется путь, который указан в dir.

    Еще важный момент создать пустой файл index.txt, без этого файла команды «openSSL ca …» не будут работать.

    Также необходимо иметь файл serial, корневой приватный ключ (ca.key), корневой сертификат (ca.crt). Процесс получения этих файлов будет описан далее.

  6. Подключаем алгоритмы шифрования предоставляемые Рутокеном.
    Это подключение происходит в файле openssl.cfg.
    • Прежде всего требуется скачать необходимые алгоритмы Рутокена. Это файлы rtengine.dll, rtpkcs11ecp.dll.
      Для этого скачиваем Рутокен SDK: www.rutoken.ru/developers/sdk.

      Рутокен SDK – это все, что есть для разработчиков, которые хотят опробовать Рутокен. Там есть как отдельные примеры для работы с Рутокеном на разных языках программирования, так и представлены некоторые библиотеки. Наши библиотеки rtengine.dll и rtpkcs11ecp.dll находятся в Рутокен sdk соответственно по расположению:

      sdk/openssl/rtengine/bin/windows-x86_64/lib/rtengine.dll
      sdk/pkcs11/lib/windows-x86_64/rtpkcs11ecp.dll

      Очень важный момент. Библиотеки rtengine.dll, rtpkcs11ecp.dll не работают без установленного драйвера для Рутокена. Также Рутокен обязательно должен быть подключен к компьютеру. (про установку всего необходимого для Рутокен смотри в предыдущей части статьи ecpexpert.ru/ru/post/506450)

    • Библиотеки rtengine.dll и rtpkcs11ecp.dll можно держать в любом месте учетной записи пользователя.
    • Прописываем пути к этим библиотекам в openssl.cfg. Для этого открываем файл openssl.cfg, в начало этого файла необходимо поместить строчку:
      openssl_conf = openssl_def

      В конец файла необходимо добавить:

      [ openssl_def ]
      engines = engine_section
      [ engine_section ]
      rtengine = gost_section
      [ gost_section ]
      dynamic_path = /Users/username/bin/sdk-rutoken/openssl/rtengine/bin/windows-x86_64/lib/rtengine.dll
      MODULE_PATH = /Users/username/bin/sdk-rutoken/pkcs11/lib/windows-x86_64/rtpkcs11ecp.dll
      RAND_TOKEN = pkcs11:manufacturer=Aktiv Co.;model=Rutoken ECP
      default_algorithms = CIPHERS, DIGEST, PKEY, RAND
      

      dynamic_path – необходимо прописать свой путь к библиотеке rtengine.dll.
      MODULE_PATH — необходимо прописать свой путь к библиотеке rtpkcs11ecp.dll.

  7. Добавляем переменные среды.

    Обязательно нужно добавить переменную среды, которая указывает путь к файлу конфигурации openssl.cfg. В моем случае была создана переменная OPENSSL_CONF с путем C:Program FilesOpenSSL-Win64binopenssl.cfg.

    В переменную path необходимо указать путь до папки, в которой находится openssl.exe, в моем случае это: C:Program FilesOpenSSL-Win64bin.

  8. Теперь можно вернуться к пункту 5 и создать недостающие файлы для каталога demoCA.
    1. Первый важный файл без которого ничего не будет работать — serial. Это файл без расширения, значение которого должно быть 01. Можно создать этот файл самостоятельно и прописать внутрь 01. Также можно скачать его из Рутокен SDK по пути sdk/openssl/rtengine/samples/tool/demoCA/.
      В каталоге demoCA лежит файл serial, который нам как раз и нужен.
    2. Создаем корневой приватный ключ.
      Для этого воспользуемся командой библиотеки openSSL, которую необходимо запустить прямо в командной строке:
      openssl genpkey -algorithm gost2022_256 -pkeyopt paramset:A -out ca.key

    3. Создаем корневой сертификат.
      Для этого воспользуемся следующей командой библиотеки openSSL:
      openssl req -utf8 -x509 -key ca.key -out ca.crt

      Обратите внимание, что для создания корневого сертификата требуется корневой приватный ключ, который создавался на предыдущем шаге. Поэтому командная строка должна быть запущена в одной и той же директории.

    Все теперь имеются все недостающие файлы для полной конфигурации каталога demoCA. Поместите созданные файлы в те каталоги, которые указаны в пункте 5.

Будем считать, что после выполнения всех 8 пунктов наш удостоверяющий центр полностью настроен.

В следующей части я расскажу, каким образом мы будем работать с удостоверяющим центром, чтобы выполнить то, что было описано в предыдущей части статьи.

Рутокен эцп и open source

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

Мне нравится современный Open Source. Нравится не как идея, а с вполне прагматической точки зрения. Фактом является то, что компания любого масштаба может использовать Open Source приложения в своей информационной системе и получить в результате кроссплатформенные, удобные и бесплатные решения практически любых проблем.

Мы прилагаем много усилий, чтобы «подружить» Рутокен ЭЦП и различные приложения Open Source. Для этой цели мы добавили поддержку российских криптоалгоритмов (ГОСТ 28147-89, ГОСТ Р 34-11.94 и ГОСТ Р 34-10.2001) и устройств Рутокен и Рутокен ЭЦП в проект OpenSC, а также разработали свою собственную кроссплатформенную библиотеку PKCS#11, работающую на операционных системах Microsoft Windows, GNU/Linux, Mac OS X, FreeBSD и др.

Стандарт PKCS#11 поддерживается большинством Open Source приложений для подключения криптографических USB-токенов. Основная проблема, с которой мы столкнулись — популярные Open Source приложения прекрасно работают с аппаратной реализацией алгоритма RSA «на борту» Рутокен ЭЦП и не умеют использовать ГОСТы, также реализованные «на борту» Рутокен ЭЦП. Пришлось «обучать» их этому. На сегодняшний день удалось решить эту задачу для OpenSC, OpenSSL и sTunnel, что, если разобраться, не так уж и мало :-).

На выходе мы получили интеграцию Рутокен ЭЦП с различными приложениями Open Source через библиотеку PKCS#11 и через другие механизмы. Ниже приводится сводная таблица, в которой можно найти ссылки на подробные инструкции по интеграции. Список приложений, представленных в таблице, будет постоянно расширяться.

Хочется также отметить, что Форум проекта Рутокен — это, наверное, одно из самых информационно наполненных мест в русскоязычном интернете, где можно получить ответы на вопросы, связанные с использованием криптографических токенов в Linux и приложениях Open Source.

Iv. заключение


В ближайшее время должна появиться и

, в которой будет рассказано, как добавить поддержку российской криптографии в

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

Оцените статью
ЭЦП Эксперт
Добавить комментарий