Пример работы с SDK "Kalkan"
Так как меня не покидала уверенность в том что пример такой работы
нужен остальным программистам которые используют SDK "Kalkan",
я решил потратить часть своего времени на его создание. В дальнейшим по тексту
вместо слова "пример" я буду использовать название проекта "Ldw-Example-Kalkan"
чтобы избежать любых неоднозначностей. Собственно сам исходный код
можно скачать здесь.
Кода я писал "Ldw-Example-Kalkan" то
держал в голове такие цели:
1) Приложение после получения его с гит-хранилища
должно запускаться сразу, без какой либо дополнительной конфигурации.
2) Клиент запустив открыв
"Ldw-Example-Kalkan" в браузере должен иметь возможность подписать
произвольные данные с помощью апплета от НУЦ РК и отправить их на
сервер для проверки.
3) Должна быть реализована на сервере "правильная
проверка" подписи данных которые пришли с браузера клиента. Что же это
такое "правильная проверка"? Это соответствие между моей реализацией
программного кода и той бумажкой с требованиями которые прислал НУЦ РК.
4) Помимо "правильной проверки" есть
дополнительные требования к проверке на сервере. Это возможность одновременной
работы с ЭЦП разных версий и проверка БИНа или ИННа в ЭЦП.
В дальнейшем статья состоит из того как мне удалось
или не удалось достичь эти цели.
Цель 1. Конфигурация.
Приложение после получения его с гит-хранилища должно
запускаться сразу, без какой либо дополнительной конфигурации.
Не получилось. В проекте используются некоторые
либы и сертификаты из SDK "Kalkan" . Я не могу их включить в "Ldw-Example-Kalkan"
так как НУЦ РК ограничивает их распространение.
Так что немножко придется потрудиться, коллега. У Вас
должен быть SDK "Kalkan" на компе, чтобы запустить "Ldw-Example-Kalkan"
в работу.
Путь к SDK "Kalkan" обозначим как "\PATH_SDK_Kalkan". Путь к папке где находиться проект "Ldw-Example-Kalkan"
обозначим "\PATH_EXAMPLE_Kalkan";
Для конфигурации "Ldw-Example-Kalkan"
необходимо выполнить нижеследующие шаги как
а) В локальный мавен-репозиторий необходимо
добавить jar из SDK "Kalkan" : knca_provider_jce_kalkan.jar, commons-logging-1.1.1.jar, xmlsec-1.4.4.jar
Перейдите в папку "\PATH_SDK_Kalkan\Java(Kalkan)\libs\" и выполните команды:
mvn
install:install-file -Dfile=knca_provider_jce_kalkan-16.jar
-DgroupId=kz.gov.pki
-DartifactId=knca_provider_jce_kalkan
-Dversion=1.1
-Dpackaging=jar
mvn
install:install-file -Dfile=commons-logging-1.1.1.jar
-DgroupId=kz.gov.pki.libs
-DartifactId=commons-logging
-Dversion=1.1.1
-Dpackaging=jar
mvn
install:install-file -Dfile=xmlsec-1.4.4.jar
-DgroupId=kz.gov.pki.libs
-DartifactId=xmlsec
-Dversion=1.4.4
-Dpackaging=jar
б) Скопируете файлы "new_NCA_GOST.cer" и "new_NCA_RSA.cer" из папки "\PATH_SDK_Kalkan\Keys and Certs\Текущий НУЦ
(old oids)\CA Root Certs\" в папку "\PATH_EXAMPLE_Kalkan\src\main\resources\kz\pki\ca\nuc1\"
в) Скопируете файлы "rca_gost.crt" и "rca_rsa.crt" из папки "\PATH _SDK_Kalkan\ Keys and Certs\Нового НУЦ (new oids)\ca\КУЦ\" в папку "\PATH_EXAMPLE_Kalkan\src\main\resources\kz\pki\ca\ kuc\"
г) Скопируете файлы "knca_root.crt" и "knca_rsa.crt" из папки "\PATH _SDK_Kalkan\ Keys and Certs\Нового НУЦ (new oids)\ ca\НУЦ 2.0\" в папку "\PATH_EXAMPLE_Kalkan\src\main\resources\kz\pki\ca\ kuc\"
д) ВАЖНО! Так как хост на котором я
писал пример находиться за прокси, то в коде я мне пришлось учитывать при
получении списка отозванных ЭЦП.
Если ваш хост работает БЕЗ прокси, то в классе com.lastdaywaiting.example.kalkan.service.SecureManager
исправите константу DEFAUL_USE_PROXY на false.
Если ваш хост работает ЗА прокси, то в классе com.lastdaywaiting.example.kalkan.service.SecureManager
проставьте константам DEFAUL_PROXY_ADDRESS
и DEFAUL_PROXY_PORT нужные значения.
Другой вариант изменения этих настроек, это через
свойства объекта. Так например:
SecureManager secure = new
SecureManager(bin, name, respCode);
secure.setUseProxy( false );
secure.setProxyAddress( "192.168.1.1" );
secure.setProxyPort( 11111 );
Цель 2.
"Ldw-Example-Kalkan" в браузере.
Приложение "Ldw-Example-Kalkan" стандартное веб-приложение на платформе Spring framework. В нем всего одна страница на которой пользователь
может последовательно выполнить ниже следующие шаги:
а) Ввести сообщение которое нужно подписать.
б) Загрузить апплет от НУЦ РК. В данный момент не все
браузеры это могут сделать. Я тестировал все в IE. Я знаю только косвенные способы определения того что
апплет не загрузился, чтобы не усложнять пример я их не стал вставлять в
javascript.
в) Выбрать нужное ЭЦП в формате файла p12 . Другие виды источников ЭЦП в примере не доступны, также с целью
упрощения кода. Если Вам интересно работа сними, то смотрите пример в SDK "Kalkan", там
все другие источники представлены (Казтокен , Личное Удостоверение, EToken Java
72k, AK JaCarta) .
г) Подписать сообщение выбранным ЭЦП. Не забудьте ввести
пароль.
д) Отправить сообщение на проверку. Сообщение в
формате JSON, которое
будет отправлено на сервер для проверки, Вы можете изменить на той же страничке
для целей тестирования.
Каждый раз когда вы нажимаете любую кнопку на странице
в верху страницы показывается результат выполненного действия: Успешно
или описание ошибки.
Цель 3. Проверка подписи на сервере.
Проверка подписи на сервере должна
соответствовать тем требованиям которые предъявляет к нам НУЦ РК. Опять
наверное покажусь занудой, но мне не понравилось то как составили свои требования.
Слишком кратко и непонятно что делать на мой взгляд. Все требования которые хотел
от меня НУЦ РК реализованны в классе com.lastdaywaiting.example.kalkan.service.SecureManager . Для проверки того хорошая это
подпись или нет, надо вызвать метод isGoodSignature. А всю остальную работу с JCE и т.д. пользователю метода знать не
обязательно.
Если все-таки Вы хотите взглянуть на реализацию более
подробнее, то ниже приведены требования от НУЦ РК
и методы которые их реализуют. Некоторые требования я посчитал не достаточно
важными чтобы их реализовывать. Но лучше всего для анализа конечно
смотреть сам исходный код.
1) "Замена криптопровайдера в ИС"
Самый ясный пункт из всех. На сервере приложений надо
использовать их новый криптопровайдер kz.gov.pki.kalkan.jce.provider.KalkanProvider.
В примере этот пункт реализован в секций static #1
класса SecureManager.
2) "Проверка электронной цифровой
подписи"
В этом пункте я посчитал что
требуется убедиться в том, что данные присланные на сервер - подпись от этих данных и
сертификат которым подписывали данные совпадают.
В примере этот пункт реализован в методе reCheckClientSignature класса SecureManager.
3) " Проверка построения корректной цепочки от
проверяемого регистрационного свидетельства до доверенного корневого
регистрационного свидетельства удостоверяющего центра, с учетом промежуточных
регистрационных свидетельств удостоверяющих центров;"
Тут есть не ясность в цепочке подписанных сертификатов
"КУЦ->НУЦ2->ЭЦП респа". Зачем проверять то
что НУЦ2 был подписан сертификатом КУЦ? Оба эти сертификата находятся в "Ldw-Example-Kalkan".
Я знаю что они правильные. Зачем еще одна проверка? Я сделал эту
проверку, но глубинный смысл её необходимости остался для меня сокрытым.
В примере этот пункт реализован в методах checkNucOneCertificateType для ЭЦП НУЦ
1 и checkNucTwoCertificateType для ЭЦП НУЦ
2 класса SecureManager.
4) " Проверка срока действия регистрационного
свидетельства. Проверка сроков действия от проверяемого регистрационного
свидетельства до доверенного корневого регистрационного свидетельства
удостоверяющего центра, с учетом промежуточных регистрационных свидетельств
удостоверяющих центров;"
Код проверки размазан по методам reCheckClientSignature для
проверки ЭЦП респа и методам checkNucOneCertificateType для
сертификата НУЦ 1 и checkNucTwoCertificateType для
сертификата НУЦ 2 .
5) Проверка регистрационного свидетельства на
аннулирование. Проверка регистрационного свидетельства на аннулирование
осуществляется одним из методов:
на основе СОРС НУЦ РК. Данный метод проверки
подтверждает, аннулировано ли проверяемое регистрационное свидетельство на
момент начала срока действия СОРС НУЦ РК;
онлайн проверка регистрационного
свидетельства на аннулирование, основанная на протоколе OCSP
(On-lineCertificateStatusProtocol). Данный метод проверки подтверждает,
аннулировано ли проверяемое регистрационное свидетельство на момент отправки
запроса (текущее время);
на основе дополнительного СОРС. Данный сервис
необходимо использовать совместно с сервисом СОРС, что позволяет получить более
актуальную информацию, чем в сервисе СОРС. Данный метод проверки подтверждает,
аннулировано ли проверяемое регистрационное свидетельство на момент начала
срока действия дополнительного СОРС НУЦ РК
Здесь на выбор предоставляют три возможности для
проверки отозваности. Я выбрал первый. Это загрузка CRL файлов из инета и
проверка по ним. Загрузка CRL- файлов происходит не всегда а через
определенный период (константа HOURS_OF_RELOAD) . После загрузки CRL-файлов , они
сидят в кэше.
В примере этот пункт реализован в методах isNotRevokedCertNucOne для ЭЦП от
НУЦ 1 и isNotRevokedCertNucTwo для ЭЦП от
НУЦ 2.
6) Проверка области использования ключа.
Проверка заключается в проверке значения поля регистрационного свидетельства
«использование ключа» (KeyUsage). Если поле «использование ключа» содержит значения
«Цифровая подпись» и «Неотрекаемость», то это регистрационное свидетельство
используется для ЭЦП. А если поле «использование ключа» содержит значения
«Цифровая подпись» и «Шифрование ключей», то это регистрационное
свидетельство используется для аутентификации;
В примере этот пункт реализован в методе isBadKeyUsage. В нашем примере нужна только
подпись. Так что проверяем наличие свойства «Неотрекаемость».
7) Проверка номера политики
регистрационного свидетельства и разрешенных способах его использования. Если
политика проверяемого регистрационного свидетельства предусматривает
ограничение его использования (только в одной системе), то данное
регистрационное свидетельство и соответствующий закрытый ключ не использоваться
в других системах;
Не реализовано.
8) Проверка метки времени.
Доказательством подписания документа в указанный момент времени является
квитанция метки времени, полученная в НУЦ РК и содержащая время подписания
документа. Данная проверка производится для электронных документов долговременного
хранения и формируется в момент подписания документа;
Не реализовано.
9) Проверка полномочий лица
подписавшего документ. Механизмы проверки полномочий возлагаются на Систему.
Не реализовано.
Цель 4. Дополнительные проверки подписи на сервере.
Тут все относительно просто. Эти требования
которые я сам себе выдумал.
1) Хотя в требованиях от НУЦ РК этого не
сказано, но нам придется работать еще какое-то время с сертификатами
старого (НУЦ 1) и нового образца (НУЦ 2) .
Поэтому в коде вначале сертификат проверяют на
соответствие НУЦ 1 в методе checkNucOneCertificateType, а затем на
соответствие НУЦ 2 в методе checkNucTwoCertificateType.
2) В реальной системе у меня пользователь
заходит на сайт по протоколу HTTPS используя
свой сертификат для аутентификаций на сайте. При подписаний используется другой
сертификат и я хочу быть уверенным то что пользователь не ошибся в выборе
сертификата. Это значит я знаю на момент проверки подписи какой БИН или ИИН
должен быть сертификате подписавшем данные. Этот БИН или ИИН я передаю в
конструктор SecureManager.
Сама проверка проходит в методе isBadBinOrIin.
PS1: К сожалению мне удавалось выделять
достаточно свободного времени на этот проект. По причине почти полного
отсутствия оного. Поэтому все затянулось.
PS2: Пока писал этот пример нашелся еще
один человек который взял эту миссию на себя. Смотрите здесь. У
нас разные подходы, он писал либу для SDK "Kalkan", я пример реализаций их
требований. И возможно его подход будет для вас более удобным и понятным для
использования SDK "Kalkan". Я не настаиваю.
PS3: Я тестил "Ldw-Example-Kalkan"
только на сертификатах юридических и физических лиц. Другие типы сертификатов
(казначейство и нотариус) работать скорее всего не будут.
PS4: Вполне возможно что Вы найдете
ошибки у меня. Как коде, так в этой статье. Просто я их еще не вижу. Если Вы их
заметили , то дайте мне знать, плиз.