eToken PKI Client: скачать бесплатно для Windows

eToken PKI Client: скачать бесплатно для Windows Электронная цифровая подпись

«почему всем можно, а мне нельзя?» или реверсим api и получаем данные с etoken

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

Идея показалась неплохой, но как это реализовать? Тут я вспомнил, как однажды в бухгалтерии не работал банк-клиент, ругаясь на отсутствие библиотеки с говорящим именем etsdk.dll, меня охватило любопытство и я полез ее ковырять.

Вообще, компания-разработчик на своем сайте распространяет SDK, но для этого надо пройти регистрацию как компания-разработчик ПО, а это явно не я. На просторах интернета документацию найти не удалось, но любопытство одержало верх и я решил разобраться во всём сам. Библиотека – вот она, время есть, кто меня остановит?

Первым делом я запустил DLL Export Viewer от NirSoft, который показал мне приличный список функций, экспортируемых библиотекой. Список выглядит неплохо, прослеживается логика и последовательность действий при работе с токенами. Однако одного списка мало, нужно понять какие параметры, в каком порядке передавать и как получать результаты.

Тут-то и пришла пора вспомнить молодость и запустить OllyDbg версии 2.01, загрузить в него библиотеку ccom.dll криптосистемы Крипто-Ком, используемой банк-клиентом и использующей ту самую библиотеку etsdk.dll, и начать разбираться как именно они это делают.

Поскольку исполняемого файла нет, библиотека загрузится с помощью loaddll.exe из комплекта Olly, поэтому о полноценной отладке мы можем и не мечтать. По сути мы будем использовать отладчик как дизассемблер (да, есть IDA, но с ней я никогда не работал и вообще она платная).

Вызываем контекстное меню и выбираем Search for > All intermodular calls, упорядочиваем результат по имени и ищем функции, начинающиеся на ET*, и не находим. Это значит, что библиотека подключается динамически, поэтому в том же списке мы ищем вызовы GetProcAddress, просматриваем их и с определенной попытки натыкаемся на попытку узнать адрес функции ETReadersEnumOpen, а присмотревшись чуть дальше видим загрузку в память адресов всех функций из библиотеки etsdk.dll.

Неплохо. Полученные адреса функций сохраняются в память командами типа MOV DWORD PTR DS:[10062870],EAX, выделяем каждую такую команду, вызываем контекстное меню и выбираем Find references to > Address constant. В открывшемся окне будут показаны текущая команда и все места вызова функции. Пройдемся по ним и проставим комментарий с именем вызываемой функции – этим мы облегчим себе дальнейшую жизнь.

Пришло время выяснить, как правильно вызывать эти функции. Начнем с начала и изучим получение информации о считывателях. Переходим к месту вызова функции ETReadersEnumOpen и, благодаря оставленным комментариям, видим, что ETReadersEnumOpen, оба ETReadersEnumNext и ETReadersEnumClose сосредоточились в одной функции – очевидно, она, среди прочего, занимается получением списка считывателей.

Все функции используют соглашение о вызове cdecl. Это значит, что результат будет возвращаться в регистре EAX, а параметры передаваться через стек справа-налево. Кроме того, это значит, что все параметры имеют размерность двойного слова, а если не имеют – расширяются до него, что упростит нам жизнь.

Посмотрим окрестности вызова ETReadersEnumOpen:

Передается один параметр, представляющий собой указатель на некую локальную переменную, а после вызова, если результат не равен 0, управление передается на некий явно отладочный код, а если равен – идем дальше (команда JGE передает управление если флаги ZF и OF равны, а флаг OF команда TEST всегда сбрасывает в 0). Таким образом, я заключаю следующий порядок: в функцию передается переменная по ссылке, в которую вернется некий идентификатор перечисления, а как результат функция возвращает код ошибки или 0 если ошибки нет.

Переходим к ETReadersEnumNext:

В нее передаются два параметра: значение переменной, полученное с помощью ETReadersEnumOpen (идентификатор перечисления) и указатель на локальную переменную, куда, очевидно, возвращается очередное значение. Причем поскольку параметры передаются в порядке справа-налево, именно первый параметр – идентификатор, а второй – указатель результата. Код ошибки все так же возвращается через EAX, причем, судя по конструкции цикла, он используется не только для сообщения об ошибке, но и для сообщения о том, что больше перечислять нечего.

С ETReadersEnumClose все еще проще: в нее передается идентификатор перечисления, ну а результат никого не волнует.

Пришло время проверить наше представление об этих функциях. Тут я вынужден сделать небольшое лирическое отступление: дело в том, что по профессии я – сисадмин, и поэтому серьезные компилируемые языки программирования – это не совсем мое. По работе мне больше нужен Bash и Python под Linux, ну а если мне надо быстро что-нибудь сваять под Windows, я использую полюбившийся мне AutoIt.

Плюсами для меня являются:

Минусы:

Это отступление было к тому, что примеры использования функций мы будем ваять именно на AutoIt. Вызов функций из внешних библиотек, в связи с неявной типизацией в языке, выглядит несколько коряво, но работает.

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

Dim $ETSdkDll=DllOpen('etsdk.dll')
Dim $buf=DllStructCreate('BYTE[32]')

Func PrintBuf($buf)
	For $i=1 To DllStructGetSize($buf)
		ConsoleWrite(Hex(DllStructGetData($buf,'buf',$i),2)&' ')
	Next
	ConsoleWrite(@CRLF)
EndFunc

ConsoleWrite('Buffer before: ')
PrintBuf($buf)
$result=DllCall($ETSdkDll,'DWORD','ETReadersEnumOpen', _
	'PTR',DllStructGetPtr($buf) _
)
ConsoleWrite('Buffer after:  ')
PrintBuf($buf)
ConsoleWrite('Return value: '&$result[0]&@CRLF)

Выполнив его, получаем вывод типа такого:

Buffer before: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
Buffer after:  44 6F C8 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
Return value: 0

Прогоняем несколько раз и видим, что меняются только первые 4 байта, значит, в качестве идентификатора используется 4-байтовое целое, а значит мы можем немного причесать код вызова этой функции до такого состояния:

Func ETReaderEnumOpen()
	Local $id=DllStructCreate('DWORD')
	Local $result=DllCall($ETSdkDll,'DWORD','ETReadersEnumOpen', _
		'PTR',DllStructGetPtr($id) _
	)
	Return $result[0]?0:DllStructGetData($id,1)
EndFunc

Подобные эксперименты с функцией ETReadersEnumNext показали следующее: первые 260 байт буфера содержат имя считывателя и нули. Последовательный вызов этой функции перечислил мне все считыватели в системе (например, под ruToken их создано заранее три штуки). Считыватели под eToken создаются динамически, в зависимости от числа подключенных токенов и, самое интересное, у них установлен в еденицу 261-й байт буфера, который, судя по всему, указывает на совместимость считывателя с нашей библиотекой. Если вглядеться в дизассемблированный код, то видно, что записи, у которых 261-й байт равен 0, не обрабатываются. Все остальные байты до конца килобайтного буфера у всех считывателей равны 0 и не различаются.

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

ETTokenBind выглядит сложно и непонятно, но, поковырявшись некоторое время, я пришел к выводу, что функции передается два параметра, первый из который – указатель на буфер величиной 328 байт (0x0148), а второй – указатель на строку с именем считывателя. Путем экспериментов было установлено, что в первые четыре байта буфера возвращается идентификатор (далее: идентификатор привязки). Для чего выделяется весь остальной буфер – пока загадка. С какими токенами я бы не экспериментировал, остальные 324 байта буфера оставались заполнены нулями. Указанный идентификатор, что логично, успешно используется как аргумент функций ETTokenUnbind и ETTokenRebind.

Читайте также:  Как получить электронную подпись для юридических лиц - порядок действий

Следующая функция на очереди – ETRootDirOpen. Принимает три параметра: указатель на результат, идентификатор привязки и константу. У функции есть несколько особенностей.

Первое: возвращаемый результат этой функции проверяется не только на равенство нулю (успех), но и на равенство младших двух байт числу 0x6982, и в случае, если результат равен этому числу, управление передается функции, которая впоследствии вызывает ETTokenLogin, а потом еще раз пытается вызвать ETRootDirOpen. Отсюда можно заключить, что 0x6982 – код ошибки, означающий «Требуется авторизация». Забегая вперед скажу, что все остальные функции, работающие с файлами и папками, устроены так же.

Второе: в качестве одного из параметров эта функция принимает константу 0xF007. Вызовов с другими константами в коде нет. Возможно, эта константа как-то характеризует информацию, записанную на токен (множество корневых папок?). Я попробовал пройти брутфорсом по всем значениям двухбайтовой константы и токен откликнулся только на значения 0x0001, 0xF001-0xF00B (авторизацию, кстати, ни разу не попросил). Позже я выяснил, что на свежеинициализированном токене доступны те же папки. Подумав над этим некоторое время, я пришел к выводу, что по замыслу разработчика, разные корневые папки используются для разных целей, и где-то прописано, что 0xF007 – для ключей.

Третье: значение, возвращаемое функцией, на скриншоте не видно, но возвращается в середину того 328-байтного буфера, который выделялся ранее, из чего можно сделать вывод, что тот буфер – структура, хранящая самые разные идентификаторы и данные, касающиеся рассматриваемого токена.

Раз уж пошла попытка авторизации, время разобраться с ней. Функция ETTokenLogin получает два параметра: идентификатор привязки и указатель на буфер. Сначала я думал, что буфер используется для вывода какого-то результата, однако экспериметы показали, что используется следующий алгоритм: если указатель нулевой или указывает на пустую строку, библиотека рисует интерфейсное окно с запросом пароля, если же он указывает на непустую строку – эта строка используется как пароль. ETTokenLogout воспринимает всего один параметр: идентификатор привязки.

Следующая группа функций: ETDirEnumOpen, ETDirEnumNext и ETDirEnumClose. Их можно попробовать распутать, не заглядывая в код. В общем и целом они должны работать так же, как ETReadersEnum*, с той лишь разницей, что в ETDirEnumOpen будет передаваться в качестве параметра еще и идентификатор текущей папки. Проверяем – работает.

Группа функций ETFilesEnumOpen, ETFilesEnumNext и ETFilesEnumClose просто обязаны работать так же, однако проверить это с уверенностью мы пока не можем, т.к. в корневой папке исследуемого токена, судя по всему, файлов нет, а это значит, что пора уходить вглубь дерева папок, функцией ETDirOpen.

В данном API, похоже, нарисовалась традиция, согласно которой, первый параметр используется для возврата результата, поэтому предположим, что это верно и в этот раз. Второй параметр, прежде чем быть переданным функции, проходит видоизменения с помощью команды MOVZX EDI,DI, т.е. слово расширяется до двойного слова. Очевидно, это нужно для того, чтобы двухбайтовое имя папки передать в четырехбайтовом параметре. Ну а третий параметр по логике вещей должен быть идентификатором открытой папки. Пробуем – получилось. ETDirClose угадывается без сюрпризов: 1 параметр – идентификатор папки.

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

Func PrintDir($Id,$Prefix)
	Local $EnumId=ETDirEnumOpen($Id)
	While 1
		Local $dir=ETDirEnumNext($EnumId)
		If @error Then ExitLoop
		ConsoleWrite($Prefix&'(dir)'&Hex($dir,4)&@CRLF)
		Local $DirId=ETDirOpen($dir,$Id)
		PrintDir($DirId,$Prefix&@TAB)
		ETDirClose($DirId)
	WEnd
	ETDirEnumClose($EnumId)
	$EnumId=ETFilesEnumOpen($Id)
	While 1
		Local $file=ETFilesEnumNext($EnumId)
		If @error Then ExitLoop
		ConsoleWrite($Prefix&'(file)'&Hex($file,4)&@CRLF)
	WEnd
	ETFilesEnumClose($EnumId)
EndFunc

Local $EnumId=ETReaderEnumOpen()
If $EnumId Then
	While 1
		Local $reader=ETReaderEnumNext($EnumId)
		If @error Then ExitLoop
		If Not $reader[1] Then ContinueLoop
		Local $BindId=ETTokenBind($reader[0])
		ConsoleWrite($reader[0]&':'&@CRLF)
		ETTokenLogin($BindId,'123456')
		Local $DirId=ETRootDirOpen($BindId)
		PrintDir($DirId,@TAB)
		ETDirClose($DirId)
	WEnd
EndIf
ETReaderEnumClose($EnumId)

Результат в консоли:

Aladdin Token JC 0:
	(dir)1921
		(dir)DDDD
			(file)0002
			(file)0003
			(file)0004
			(file)0001
		(file)A001
		(file)B001
		(file)C001
		(file)AAAA
		(file)D001

Отлично!

Чтож, мы научились открывать и просматривать папки, пора научиться открывать и читать файлы. ETFileOpen принимает 3 параметра, поэтому для начала пробуем сделать так же, как и для ETDirOpen: результат, имя файла, идентификатор папки и обламываемся: разработчики поменяли местами последние два параметра. Ну хоть ETFileClose работает без сюрпризов.

ETFileRead. Самая страшная функция из всех, т.к. воспринимает аж 5 параметров. Куда столько? Попробуем перечислить что нам нужно: откуда читать (файл), куда читать (буфер), сколько читать и начиная откуда читать. Попробуем разобраться что да как:

Как видно, третий параметр, передаваемый в функцию ETFileRead всегда равен 0xFFFF, поэтому я склонен считать, что это – длина считываемого куска данных. Остальные 4 параметра приходят в функцию, названную мной FileReadHere извне в том же порядке. Ниже на рисунке окрестности вызова этой функции. Значение первого параметра берется из памяти по адресу ESI 8. Указатель на этот адрес используется в функции FileOpenHere (названа по тому же принципу) и туда, очевидно, записан идентификатор открытого файла. Второй параметр равен нулю, поэтому его назначаем ответственным за точку начала чтения файла. Третий параметр (четвертый для ETFileRead) какой-то мутный, поэтому его назначим указателем на буфер-результат. Пятый параметр необычен совсем. В него помещается слово из адреса ESI 12, расширяясь до двойного слова – это необычно, т.к. пока что все смещения, которые я видел, были кратны 4 (12 не кратно 4, потому что это 0x12, т.е. 18 в десятичной). Адрес ESI 10 нигде в окрестностях не упоминается, а вот ESI 0C передается в FileGetInfoHere, поэтому придется сначала разобраться с функцией ETFileGetInfo. Она простая, первый параметр – идентификатор файла, второй – указатель на буфер результата. После вызова в буфере меняются 1, 2, 3, 7 и 8 байты. Забегая вперед, скажу, что выяснится, что последние два байта – размер файла. Именно это значение передается в функцию ETFileRead и в функцию, инициализирующую выходной буфер для нее. Первые два байта результата ETFileGetInfo оказались именем файла. Значение третьего я не понял, но он был установлен в 1 только у одного файла на токене. Таким образом, вырисовывается следующий порядок параметров: идентификатор файла, точка начала чтения, максимальное количество считывемых байт, указатель на буфер, размер буфера.

Раз уж мы затронули ETFileGetInfo, надо бы сразу и реализовать ETDirGetInfo: порядок параметров тот же, только участвует идентификатор папки, а не файла. Возвращаемый результат: имя папки по идентификатору.

На этом мы закончили читать с токена, пришло время писать на токен. Начнем с того, чтобы создать папку. Параметры функции ETDirCreate: указатель для результата (очевидно, после создания папка откроется и сюда вернется идентификатор), имя папки, идентификатор родительской папки и 0. Четвертый параметр жестко прописан в коде и я так и не понял, на что он влияет. Папки успешно создаются при любом его значении. ETDirDelete принимает всего 1 параметр, поэтому это, очевидно, идентификатор открытой папки. ETFileCreate воспринимает пять параметров: указатель на результат, аналогично ETDirCreate, идентификатор папки, имя файла, размер файла и пятый параметр. Если пятый параметр установить в ненулевое значение, то при последующем вызове ETFileGetInfo для этого файла, третий байт результата (тот самый, непонятный) будет установлен в 1. Подумав, я провел эксперимент и убедился, что когда атрибут установлен, для доступа к файлу необходимо ввести пароль, если нет, то это не обязательно. Забавно, что на токене, с которым я экспериментировал, такой файл оказался всего один. Надеюсь, что все остальные файлы зашифрованы на ключе из этого. ETFileDelete работает без сюрпризов, аналогично ETDirDelete.

Последняя функция, обращение к которой реализовано в этой библиотеке – ETFileWrite. Принимает 4 аргумента: идентификатор файла, ноль (эксперимент показывает, что это смешение относительно начала файла), указатель на буфер с данными и размер данных. При этом важно помнить, что файл не расширяется. Если сумма смещения и длины файла превышает размер файла, запись не происходит, поэтому если размер файла требуется изменить, файл придется удалять и создавать заново с новым размером.

Читайте также:  Ключ ЭЦП (электронной подписи)

Далее: если вспомнить таблицу экспорта библиотеки, то в ней есть еще 5 функций, однако их вызов не реализован в данной библиотеке, работающей с СКЗИ Крипто-Ком. На наше счастье, тот же банк распространяет также и библиотеку для работы с СКЗИ Message-Pro – mespro2.dll, которая также может работать с токенами и в ней есть немного больше, а именно – вызов ETTokenLabelGet.

На скриншоте видно, что есть два вызова функции, различающиеся тем, что в первом случае второй параметр равен нулю, а во втором – какому-то числу. Третий параметр всегда указатель, поэтому предположим, что это результат, а первый – было бы логично предположить, что идентификатор связки с токеном. Пробуем запустить с нулем в качестве второго параметра – первые 4 байта в буфере изменились на значение 0x0000000A, т.е. 10, а это как раз длина имени «TestToken» с нулевым байтом в конце. Но если по указателю в третий параметр возврачается двойное слово, получается, указатель на буфер нужного размера надо передавать во второй параметр. Посему заключаем такой порядок: первый раз запускаем функцию так, что второй параметр – нулевой указатель, а третий – указатель на двойное слово. Потом инициализируем буфер нужного размера и запускаем функцию второй раз, при этом второй параметр – указатель на буфер.

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

ETTokenIDGet: 3
ETTokenMaxPinGet: 2
ETTokenMinPinGet: 2
ETTokenPinChange: 2

ETTokenIDGet принимает слишком много параметров для возврата какого-то простого значения, поэтому запустим ее так же, как и ETTokenGetLabel – получается с первой попытки и возвращает строку с номером, написанным на боку токена.

ETTokenMaxPinGet и ETTokenMinPinGet, как раз наоборот, имеют количество параметров, идеальное для возврата однго числового значения. Пробуем первый параметр – идентификатор связки, второй – указатель на число. В результате получаем максимальную и минимально возможные длины пароля, заданные в настройках токена.

ETTokenPinChange, исходя из названия, служит для смены пароля на токен, соответственно, должен бы принимать только идентификатор связки и указатель на строку с новым паролем. Пробуем первый раз, получаем код ошибки 0x6982, который, как мы знаем, означает необходимость выполнить логин на токен. Логично. Повторяем с логином и коротким паролем – получаем ошибку 0x6416. Делаем вывод о том, что длина пароля не соответствует политике. Повторяем с длинным паролем – отрабатывает.

Теперь сводим все функции в один модуль и сохраняем его – будем инклудить в другие проекты. Текст модуля получился такой:

etsdk.au3

;Func ETReadersEnumOpen()
;Func ETReadersEnumNext($EnumId)
;Func ETReadersEnumClose($EnumId)
;Func ETTokenBind($ReaderName)
;Func ETTokenRebind($BindId)
;Func ETTokenUnbind($BindId)
;Func ETTokenLogin($BindId,$Pin='')
;Func ETTokenPinChange($BindId,$Pin)
;Func ETTokenLogout($BindId)
;Func ETRootDirOpen($BindId,$Dir=0xF007)
;Func ETDirOpen($Dir,$DirId)
;Func ETDirCreate($Dir,$DirId)
;Func ETDirGetInfo($DirId)
;Func ETDirClose($DirId)
;Func ETDirDelete($DirId)
;Func ETDirEnumOpen($DirId)
;Func ETDirEnumNext($EnumId)
;Func ETDirEnumClose($EnumId)
;Func ETFileOpen($File,$DirId)
;Func ETFileCreate($File,$DirId,$Size,$Private=0)
;Func ETFileGetInfo($FileId)
;Func ETFileRead($FileId)
;Func ETFileWrite($FileId,$Data,$Pos=0)
;Func ETFileClose($FileId)
;Func ETFileDelete($FileId)
;Func ETFilesEnumOpen($DirId)
;Func ETFilesEnumNext($EnumId)
;Func ETFilesEnumClose($EnumId)
;Func ETTokenLabelGet($BindId)
;Func ETTokenIDGet($BindId)
;Func ETTokenMaxPinGet($BindId)
;Func ETTokenMinPinGet($BindId)
Const $ET_READER_NAME=0
Const $ET_READER_ETOKEN=1
Const $ET_FILEINFO_NAME=0
Const $ET_FILEINFO_PRIVATE=1
Const $ET_FILEINFO_SIZE=2
Dim $ETSdkDll=DllOpen('etsdk.dll')
Func ETReadersEnumOpen()
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETReadersEnumOpen', _
'PTR',DllStructGetPtr($Out) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETReadersEnumNext($EnumId)
Local $Reader=DllStructCreate('CHAR name[260]; BYTE etoken;')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETReadersEnumNext', _
'DWORD',$EnumId, _
'PTR',DllStructGetPtr($Reader) _
)
Local $Result[2]=[	DllStructGetData($reader,'name'), _
DllStructGetData($reader,'etoken')]
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:$Result
EndFunc
Func ETReadersEnumClose($EnumId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETReadersEnumClose', _
'DWORD',$EnumId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETTokenBind($ReaderName)
Local $In=DllStructCreate('BYTE['&(StringLen($ReaderName) 1)&']')
Local $Out=DllStructCreate('DWORD')
DllStructSetData($In,1,$ReaderName)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenBind', _
'PTR',DllStructGetPtr($Out), _
'PTR',DllStructGetPtr($In) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETTokenRebind($BindId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenRebind', _
'DWORD',$BindId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETTokenUnbind($BindId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenUnbind', _
'DWORD',$BindId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETTokenLogin($BindId,$Pin='')
Local $In=DllStructCreate('BYTE['&(StringLen($Pin) 1)&']')
DllStructSetData($In,1,$Pin)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLogin', _
'DWORD',$BindId, _
'PTR',DllStructGetPtr($In) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETTokenPinChange($BindId,$Pin)
Local $In=DllStructCreate('CHAR['&(StringLen($Pin) 1)&']')
DllStructSetData($In,1,$Pin)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenPinChange', _
'DWORD',$BindId, _
'PTR',DllStructGetPtr($In) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETTokenLogout($BindId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLogout', _
'DWORD',$BindId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETRootDirOpen($BindId,$Dir=0xF007)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETRootDirOpen', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$BindId, _
'DWORD',$Dir _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETDirOpen($Dir,$DirId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirOpen', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$Dir, _
'DWORD',$DirId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETDirCreate($Dir,$DirId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirCreate', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$Dir, _
'DWORD',$DirId, _
'DWORD',0 _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETDirGetInfo($DirId)
Local $Out=DllStructCreate('BYTE[8]')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirGetInfo', _
'DWORD',$DirId, _
'PTR',DllStructGetPtr($Out) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETDirClose($DirId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirClose', _
'DWORD',$DirId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETDirDelete($DirId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirDelete', _
'DWORD',$DirId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETDirEnumOpen($DirId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirEnumOpen', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$DirId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETDirEnumNext($EnumId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirEnumNext', _
'DWORD',$EnumId, _
'PTR',DllStructGetPtr($Out) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETDirEnumClose($EnumId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETDirEnumClose', _
'DWORD',$EnumId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETFileOpen($File,$DirId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileOpen', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$DirId, _
'DWORD',$File _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETFileCreate($File,$DirId,$Size,$Private=0)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileCreate', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$DirId, _
'DWORD',$File, _
'DWORD',$Size, _
'DWORD',$Private _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETFileGetInfo($FileId)
Local $Out=DllStructCreate('WORD name;WORD private;WORD;WORD size')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileGetInfo', _
'DWORD',$FileId, _
'PTR',DllStructGetPtr($Out) _
)
Local $Result[3]=[	DllStructGetData($Out,'name'), _
DllStructGetData($Out,'private'), _
DllStructGetData($Out,'size')]
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:$Result
EndFunc
Func ETFileRead($FileId)
Local $FileInfo=ETFileGetInfo($FileId)
If @error Then Return SetError(@error,0,False)
Local $Out=DllStructCreate('BYTE ['&$FileInfo[$ET_FILEINFO_SIZE]&']')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileRead', _
'DWORD',$FileId, _
'DWORD',0, _
'DWORD',0xFFFF, _
'PTR',DllStructGetPtr($Out), _
'DWORD',$FileInfo[$ET_FILEINFO_SIZE] _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETFileWrite($FileId,$Data,$Pos=0)
$Data=Binary($Data)
Local $DataSize=BinaryLen($Data)
Local $In=DllStructCreate('BYTE['&$DataSize&']')
DllStructSetData($In,1,$Data)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileWrite', _
'DWORD',$FileId, _
'DWORD',$Pos, _
'PTR',DllStructGetPtr($In), _
'DWORD',$DataSize _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETFileClose($FileId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileClose', _
'DWORD',$FileId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETFileDelete($FileId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFileDelete', _
'DWORD',$FileId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETFilesEnumOpen($DirId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFilesEnumOpen', _
'PTR',DllStructGetPtr($Out), _
'DWORD',$DirId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETFilesEnumNext($EnumId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFilesEnumNext', _
'DWORD',$EnumId, _
'PTR',DllStructGetPtr($Out) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETFilesEnumClose($EnumId)
Local $CallRes=DllCall($ETSdkDll,'WORD','ETFilesEnumClose', _
'DWORD',$EnumId _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:True
EndFunc
Func ETTokenLabelGet($BindId)
Local $Out1=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenLabelGet', _
'DWORD',$BindId, _
'PTR',0, _
'PTR',DllStructGetPtr($Out1) _
)
If $CallRes[0] Then Return SetError($CallRes[0],0,False)
Local $Out2=DllStructCreate('CHAR['&DllStructGetData($Out1,1)&']')
$CallRes=DllCall($ETSdkDll,'WORD','ETTokenLabelGet', _
'DWORD',$BindId, _
'PTR',DllStructGetPtr($Out2), _
'PTR',DllStructGetPtr($Out1) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out2,1)
EndFunc
Func ETTokenIDGet($BindId)
Local $Out1=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenIDGet', _
'DWORD',$BindId, _
'PTR',0, _
'PTR',DllStructGetPtr($Out1) _
)
If $CallRes[0] Then Return SetError($CallRes[0],0,False)
Local $Out2=DllStructCreate('CHAR['&DllStructGetData($Out1,1)&']')
$CallRes=DllCall($ETSdkDll,'WORD','ETTokenIDGet', _
'DWORD',$BindId, _
'PTR',DllStructGetPtr($Out2), _
'PTR',DllStructGetPtr($Out1) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out2,1)
EndFunc
Func ETTokenMaxPinGet($BindId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenMaxPinGet', _
'DWORD',$BindId, _
'PTR',DllStructGetPtr($Out) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc
Func ETTokenMinPinGet($BindId)
Local $Out=DllStructCreate('DWORD')
Local $CallRes=DllCall($ETSdkDll,'WORD','ETTokenMinPinGet', _
'DWORD',$BindId, _
'PTR',DllStructGetPtr($Out) _
)
Return $CallRes[0] _
?SetError($CallRes[0],0,False) _
:DllStructGetData($Out,1)
EndFunc

Итак, мы можем делать все, что захотим с файловой системой токена. Чтобы продемонстрировать это, я написал простенький скрипт, который будет копировать содержимое с одного токена на другой. Скрипт уровня «Proof-of-concept», т.е. тут не будет уймы проверок, которые должны были бы быть в «правильном» приложении, однако позволит нам получить второй действующий токен.

eTokenCopy.au3
#include <etsdk.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#NoTrayIcon
Opt('MustDeclareVars',1)
Opt('GUIOnEventMode',1)
Opt('GUIDataSeparatorChar',@LF)
Const $Title='eToken Copy'
Const $GUISize[2]=[250,100]
Dim $SrcCtrl,$DstCtrl,$ListTimer
Func TokenCopyDir($SrcId,$DstId)
Local $Name,$SrcSubId,$DstSubId,$SrcInfo,$SrcData
; Проход по папкам с рекурсией
Local $EnumId=ETDirEnumOpen($SrcId)
While 1
$Name=ETDirEnumNext($EnumId)
If @error Then ExitLoop
$SrcSubId=ETDirOpen($Name,$SrcId)
$DstSubId=ETDirOpen($Name,$DstId)
If @error Then
$DstSubId=ETDirCreate($Name,$DstId)
EndIf
TokenCopyDir($SrcSubId,$DstSubId)
ETDirClose($SrcSubId)
ETDirClose($DstSubId)
WEnd
ETDirEnumClose($EnumId)
; Проход по файлам
$EnumId=ETFilesEnumOpen($SrcId)
While 1
$Name=ETFilesEnumNext($EnumId)
If @error Then ExitLoop
$SrcSubId=ETFileOpen($Name,$SrcId)
$SrcInfo=ETFileGetInfo($SrcSubId)
$DstSubId=ETFileOpen($Name,$DstId)
If Not @error Then
ETFileDelete($DstSubId)
EndIf
$DstSubId=ETFileCreate($Name,$DstId,$SrcInfo[$ET_FILEINFO_SIZE],$SrcInfo[$ET_FILEINFO_PRIVATE])
ETFileWrite($DstSubId,ETFileRead($SrcSubId))
ETFileClose($SrcSubId)
ETFileClose($DstSubId)
WEnd
ETFilesEnumClose($EnumId)
EndFunc
Func TokenCopy()
Local $Src=GUICtrlRead($SrcCtrl)
Local $Dst=GUICtrlRead($DstCtrl)
If $Src=='' Or $Dst=='' Then
MsgBox(0x10,$Title,'Не все поля заполнены')
Return False
EndIf
; Из выбранного поля получаем номер токена
$Src=StringMid($Src,StringLen($Src)-8,8)
$Dst=StringMid($Dst,StringLen($Dst)-8,8)
If $Src==$Dst Then
MsgBox(0x10,$Title,'Нельзя выбрать один и тот же токен')
Return False
EndIf
; Подключаемся к токенам
Local $SrcBindId=False,$DstBindId=False
Local $EnumId=ETReadersEnumOpen()
While 1
Local $Reader=ETReadersEnumNext($EnumId)
If @error Then ExitLoop
If Not $Reader[$ET_READER_ETOKEN] Then ContinueLoop
Local $BindId=ETTokenBind($Reader[$ET_READER_NAME])
If ETTokenIDGet($BindId)==$Src Then
$SrcBindId=$BindId
ElseIf ETTokenIDGet($BindId)==$Dst Then
$DstBindId=$BindId
Else
ETTokenUnbind($BindId)
EndIf
WEnd
ETReadersEnumClose($EnumId)
If Not ETTokenLogin($SrcBindId) Then
MsgBox(0x10,$Title,'Ошибка авторизации на токене-источнике')
Return False
EndIf
If Not ETTokenLogin($DstBindId) Then
MsgBox(0x10,$Title,'Ошибка авторизации на токене-назначении')
Return False
EndIf
; Запуск копирования
TokenCopyDir(ETRootDirOpen($SrcBindId),ETRootDirOpen($DstBindId))
ETTokenUnbind($SrcBindId)
ETTokenUnbind($DstBindId)
MsgBox(0x40,$Title,'Копирование завершено')
EndFunc
Func GetTokenList()
Local $Reader, $BindId, $Result=''
Local $EnumId=ETReadersEnumOpen()
While 1
$Reader=ETReadersEnumNext($EnumId)
If @error Then ExitLoop
If Not $Reader[$ET_READER_ETOKEN] Then ContinueLoop
$BindId=ETTokenBind($Reader[$ET_READER_NAME])
$Result&=@LF&ETTokenLabelGet($BindId)&' ('&ETTokenIDGet($BindId)&')'
ETTokenUnbind($BindId)
WEnd
ETReadersEnumClose($EnumId)
Return $Result
EndFunc
Func UpdateTokenList()
Local $Tokens=GetTokenList()
GUICtrlSetData($SrcCtrl,$Tokens,GUICtrlRead($SrcCtrl))
GUICtrlSetData($DstCtrl,$Tokens,GUICtrlRead($DstCtrl))
EndFunc
Func onClose()
Exit
EndFunc
Func GUIInit()
GUICreate($Title,$GUISize[0],$GUISize[1],(@DesktopWidth-$GUISize[0])/2,(@DesktopHeight-$GUISize[1])/2)
GUISetOnEvent($GUI_EVENT_CLOSE,'onClose')
GUICtrlCreateLabel('Источник:',8,8,64,-1,$SS_RIGHT)
GUICtrlCreateLabel('Назначение:',8,32,64,-1,$SS_RIGHT)
$SrcCtrl=GUICtrlCreateCombo('',76,6,$GUISize[0]-84,-1)
$DstCtrl=GUICtrlCreateCombo('',76,30,$GUISize[0]-84,-1)
GUICtrlCreateButton('Копировать',8,54,$GUISize[0]-16,$GUISize[1]-62)
GUICtrlSetOnEvent(-1,'TokenCopy')
GUISetState(@SW_SHOW)
EndFunc
GUIInit()
UpdateTokenList()
$ListTimer=TimerInit()
While 1
; Обновление списка токенов раз в 3 секунды
If TimerDiff($ListTimer)>3000 Then
UpdateTokenList()
$ListTimer=TimerInit()
EndIf
Sleep(100)
WEnd

Я попробовал все СКЗИ, до которых смог дотянуться: Крипто-Ком, Крипто-Про, Message-Pro, Сигнатура и даже Верба. Все эти ключи успешно прошли копирование и работали.

Но как же так? Разве не должны ключи быть неизвлекаемыми с токена? Ответ кроется в спецификациях eToken: дело в том, что неизвлекаемый ключ действительно есть, но служит он только для криптопреобразований с помощью алгоритма RSA. Ни одно из рассмотренных СКЗИ… нет, вот так: ни одно из СКЗИ, одобренных ФСБ для использования на территории РФ (вроде бы) не использует RSA, а все они используют криптопреобразования на основе ГОСТ-*, поэтому eToken – не более чем флэшка с паролем и замысловатым интерфейсом.

Как изменить пин-код по умолчанию для пользователя рутокен эцп 2.0

В комплект драйверов для Windows входит панель управления, которая позволяет переключаться между USB-токенами (если их несколько), просматривать информацию об устройстве, проходить двухфакторную аутентификацию (введение PIN-кода) и выбирать криптопровайдер для использования по умолчанию.

Читайте также:  Как подписать документ ЭЦП pdf и doc

Наиболее простой способ запустить панель управления Рутокен ЭЦП в ОС Windows — навести курсор мыши на иконку и дважды щелкнуть левой кнопкой.  Для упрощения запуска «разрешите» установщику создать иконку на рабочем столе, поставив соответствующую галочку в процессе установки. Если иконка отсутствует, введите название носителя в поисковой строке меню «Пуск» или напишите «control panel» в диалоговом окне (открывается нажатием комбинации клавиш [Win] [R]).

Для начала работы с устройством необходимо выбрать нужный носитель в выпадающем списке, при необходимости проверить сведения о нем (ID, срок действия сертификата) и ввести PIN-код пользователя или администратора.

Для защиты данных применяется двухфакторная аутентификация. Все операции осуществляются при соблюдении двух условий — присутствии токена в USB-разъеме ПК и введении верного пароля.

Для получения доступа к сертификату и ключевой паре (открытый и закрытый ключи) следует запустить панель управления и ввести PIN-код пользователя, который представляет собой определенную комбинацию символов. По умолчанию используется прямая последовательность цифр от единицы до восьмерки — 12345678.

Для изменения настроек Рутокен ЭЦП 2.0 понадобится пароль администратора, который также вводится через панель управления. Перед аутентификацией необходимо переключиться с «пользователя» на «администратора». Пароль можно узнать у организации (удостоверяющего центра, банка и пр.), выдавшей носитель ЭП. По умолчанию задается обратная последовательность цифр от восьмерки до единицы — 87654321.

Для доступа к функциям Рутокен ЭЦП 2.0 можно использовать пин-код по умолчанию, но этот пароль не обеспечивает защиты данных. В целях безопасности стоит придумать другую комбинацию из 7-10 символов перед первым применением устройства. Запишите новый пароль, чтобы не ошибиться при аутентификации. Учитывайте, что после нескольких ошибочных попыток ввода токен будет заблокирован.

Как сменить PIN-код пользователя:

  1. Подключите токен к USB-разъему ПК и откройте панель управления.
  2. Выберите носитель, с которым будете работать.
  3. Убедитесь, что переключатель стоит напротив «Пользователь».
  4. Нажмите «Ввести PIN-код» и в появившемся поле введите 12345678. Если пароль указан корректно, напротив строки появится кнопка «Выйти», если некорректно — отобразится сообщение «Неудачная аутентификация» с указанием количества оставшихся попыток.
  5. Нажмите кнопку «Изменить» напротив строки «Изменить PIN-коды пользователя и администратора».
  6. Введите и подтвердите новый пароль, предварительно выбрав роль «Пользователь». Цветовой индикатор поможет определить уровень надежности PIN-кода (зеленый — надежный, красный — ненадежный).

Смена пин-кода администратора Рутокен ЭЦП 2.0 осуществляется таким же образом, как и изменение пароля пользователя. Для выполнения этой операции необходимо переставить переключатель на «Администратор» при введении пароля по умолчанию (87654321) и нового PIN-кода.

Администратор может разблокировать или сменить пароль пользователя с помощью соответствующих кнопок в разделе «Управление PIN-кодами». В «Настройках» можно также осуществить кэширование пароля (сохранение в памяти), чтобы вводить его только при первом входе в приложение.

Квест номер 2

Квест номер два оказался сильно проще.

Эти методы в реализации ЕПГУ обернуты функциями, упрощающими доступ к плагину в контексте веб-страницы.

Я честно их скопировал, лишь чуть-чуть подправив.

В итоге получилась вот такая простенькая веб-страничка, позволяющая а) выдернуть сертификат держателя токена (выдается в Base64), и подписать данные. Подпись тоже формируется в виде PKCS#7, завернутого в Base64 кодировку.

Код страницы-примера для работы с eToken ГОСТ через плагин ЕПГУ
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<!-- Для ознакомительного и информационного использования -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Пример использования eToken ГОСТ через плагин ЕПГУ</title>
</head>
<body style="font-size: 11px; font-family: Verdana;">
<!-- объект плагина ЕПГУ -->
<object id="etoken" type="application/x-csuser" width="0" height="0" style="overflow: hidden; float: left;">
<!--<param name="onload" value="pluginLoaded" />-->
</object>    
<script type="text/javascript" language="javascript">
/**
*  Operations with eToken GOST using CSuser plugin
*  
**/
// функция подписывания данных токеном. первый параметр - строка данных, второй - ПИН-код токена
// в поле 1 возвращается ЭЦП (PKCS#7, закодированное в Base64), в поле 5 - код ошибки
window.SignDataByEToken = function(mess, pin) {
var plugin = eTokenPlugin();
if (plugin.valid) {
//возвращаемые данные
var return_array = new Object();
return_array[1] = "";   //cms(base64)
return_array[5] = "";   //errorCode
if (mess == "") {
alert("Нет данных для подписывания");
return return_array;
}
try {
// Вывод CMS
return_array[1] = eTokenPlugin().etgSignData(1, 1, pin, mess, 0);
if (eTokenPlugin().etgErrorCode == 28) 
return_array[1] = eTokenPlugin().etgSignData(1, 99, pin, mess, 0);
} catch (e) {
alert("Невозможно подписать данныеrn"   e.description);
return return_array;
}
try {
// Проверка ошибки подписи
return_array[5] = eTokenPlugin().etgErrorCode;
} catch (e) {
alert(e.description);
return return_array;
}
return return_array;
}
}
// функция доступа к сертификату владельца. первый параметр - ПИН-код токена. Возвращает сертификат в Base64
window.GetCertificateByEToken = function(pin) {
var cert = null;
try {
if (eTokenPlugin().valid) {
cert = eTokenPlugin().etgGetCertificate(1, 1, pin);
if (eTokenPlugin().etgErrorCode == 28) {
cert = eTokenPlugin().etgGetCertificate(1, 99, pin);
}
}
} catch (e) {
alert(e.description);
}
return cert;
}
// глобальный аксессор к плагину
window.eTokenPlugin = function() {
return document.getElementById("etoken");
};
// валидация версии плагина. в данном примере не используется
window.checkPluginVersion = function(version) {
if (!(eTokenPlugin() && eTokenPlugin().valid)) return false;
var plugin_version = eTokenPlugin().version.split('.');
var portal_version = version.split('.');
if (isNaN(parseInt(plugin_version[0]))) return false;
if (isNaN(parseInt(plugin_version[1]))) return false;
if (isNaN(parseInt(plugin_version[2]))) return false;
if (isNaN(parseInt(portal_version[0]))) return false;
if (isNaN(parseInt(portal_version[1]))) return false;
if (isNaN(parseInt(portal_version[2]))) return false;
if (parseInt(plugin_version[0]) > parseInt(portal_version[0])) return true;
if (parseInt(plugin_version[0]) < parseInt(portal_version[0])) return false;
if (parseInt(plugin_version[1]) > parseInt(portal_version[1])) return true;
if (parseInt(plugin_version[1]) < parseInt(portal_version[1])) return false;
if (parseInt(plugin_version[2]) == 11 && parseInt(portal_version[2]) == 9) return false;  //9>11 O_o
if (parseInt(plugin_version[2]) > parseInt(portal_version[2])) return true;
if (parseInt(plugin_version[2]) < parseInt(portal_version[2])) return false;
return true;
}
// собственно, начинка этого документа
function doLogin() {
var PIN = document.getElementById("pin").value;
var rd = document.getElementById("cleartext").value;            
var cert = GetCertificateByEToken(PIN);
var ds = SignDataByEToken(rd, PIN);
var dstext = "";
document.getElementById("cert").value = cert;
for (name in ds) {
dstext = dstext   name   " : "   ds[name]   "rn";
}
document.getElementById("dsig").value = dstext;
}  
</script>
<!-- UI -->
<div>
<a id="btnLogin" onclick="doLogin();" style="border : solid 1px black; width : 140px; height : 40 px;" href="#">Выполнить</a><br/><br/>
<b>PIN-код</b><br/>
<input type="password" id="pin" style="width : 250px; border : solid 1px black;"/><br/><br/>
<b>Данные</b><br/>
<input type="text" id="cleartext" style="width : 250px; border : solid 1px black;"/><br/><br/>        
<b>Сертификат</b><br/>
<input type="text" id="cert" style="width : 250px; border : solid 1px black;"/><br/><br/>
<b>ЭЦП</b><br/>
<textarea id="dsig" style="width : 600px; height : 300px; border : solid 1px black;"></textarea><br/><br/>
</div>
</body>
</html>

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

eToken PKI Client: скачать бесплатно для Windows

Мне нравится современный 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.

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

Adblock
detector