127018, Москва, ул.Сущевский Вал д.16 стр.5 Тел./факс: +7 (495) 780 4820 +7 (495) 660 2330 сайт: http://www.cryptopro.ru e-mail: info@cryptopro.ru |
|
|
Java Cryptography Architecture Описание стандартного интерфейса JCA. Руководство администратора безопасности Общее описание средства криптографической защиты информации (СКЗИ) КриптоПро JCP 1.0 Связь с разработчиком. |
Настоящее руководство содержит описание основной функциональности криптопровайдера КриптоПро JCP и примеры его использования (основной класс провайдера ru.CryptoPro.JCP.JCP).
Криптопровайдер КриптоПро JCP является средством криптографической защиты информации (СКЗИ КриптоПро JCP 1.0), реализующим российские криптографические алгоритмы и функционирующим под управлением виртуальной машины Java 2 Runtime Environment версии 1.4.2 и выше, соответствующей спецификации Sun Java 2 TM Virtual Machine.
Криптопровайдер КриптоПро JCP должен использоваться с сертифицированными SUN Java-машинами, соответствующим требованиям безопасности SUN. Защищенность криптографических объектов, создаваемых и обрабатываемых криптопровайдером, зависит от степени защищенности и корректности Java-машины, и может быть снижена при использовании виртуальных машин, не имеющих сертификата SUN. Список сертифицированных Java-машин находится на сайте SUN по адресу: http://java.sun.com/j2se/licensees/index.html
Криптопровайдер КриптоПро JCP реализует стандартный интерфейс Java Cryptography Architecture (JCA) в соответствии с российскими криптографическими алгоритмами и в соответствии с этим интерфейсом обеспечивает выполнение следующих операций:
Помимо перечисленных операций, осуществляемых в соответствии со стандартным интерфейсом JCA, криптопровайдер КриптоПро JCP предоставляет дополнительные возможности работы с сертификатами:
Основные технические данные и характеристики СКЗИ, а также информацию о совместимости с другими продуктами КриптоПро см. в Руководстве администратора безопасности
Криптопровайдер КриптоПро JCP осуществляет генерирование ключевой пары ЭЦП, соответствующей алгоритму ГОСТ Р 34.10-2001, через стандартный интерфейс JCA при помощи класса KeyPairGenerator. Генерирование ключей любого другого алгоритма при помощи криптопровайдера КриптоПро JCP запрещается.
Объект генерирования ключевой пары ЭЦП (далее генератор) создается посредством вызова метода getInstance() класса KeyPairGenerator. Этот метод является статическим и возвращает ссылку на класс KeyPairGenerator, который обеспечивает выполнение требуемой операции.
Для создания генератора ключевой пары ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-2001 методу getInstance() необходимо в качестве параметра передать имя, идентифицирующее данный алгоритм ("GOST3410" или JCP.GOST_DEGREE_NAME). При таком вызове метода getInstance() совместно с определением требуемого алгоритма генерирования ключевой пары осуществляется также определение требуемого типа криптопровайдера (КриптоПро JCP). Также стандартный интерфейс JCA позволяет в качестве параметра функции getInstance() класса KeyPairGenerator вместе с именем алгоритма передавать имя криптопровайдера, используемого для выполнения требуемой операции. Таким образом, создание генератора ключевой пары ЭЦП осуществляется одним из следующих способов:
KeyPairGenerator kg = KeyPairGenerator.getInstance("GOST3410"); KeyPairGenerator kg = KeyPairGenerator.getInstance("GOST3410", "JCP"); KeyPairGenerator kg = KeyPairGenerator.getInstance(JCP.GOST_DEGREE_NAME, JCP.PROVIDER_NAME);
Генерирование ключевых пар ЭЦП при помощи такого генератора kg
будет осуществляться в соответствии
с алгоритмом ГОСТ Р 34.10-2001 и с установленными в контрольной панели параметрами (параметрами по умолчанию).
Если существует необходимость использования другого набора параметров (отличного от параметров по умолчанию),
то следует установить требуемый набор параметров созданному генератору.
Следует помнить, что допустимым набором устанавливаемых параметров для генерирование ключевой пары ЭЦП
является набор, у которого параметры подписи соответствуют алгоритму ГОСТ Р 34.10-2001.
После того, как генератор ключевой пары был создан, может возникнуть
необходимость установить некий набор параметров генерирования ключевой пары ЭЦП, отличный от параметров,
установленных в контрольной панели. Операция изменения существующего набора параметров допустима
только в том случае, если параметры подписи устанавливаемого набора параметров соответствуют алгоритму ГОСТ Р 34.10-2001,
и осуществляется при помощи метода initialize() класса KeyPairGenerator.
Этому методу в качестве параметра передается объект AlgIdInterface
, представляющий собой
интерфейс набора устанавливаемых параметров (создание объектов такого типа описывается ниже).
Тогда изменение набора параметров генератора ключевой пары производится следующим образом:
AlgIdInterface keyParams; // интерфейс набора параметров ключа kg.initialize(keyParams); // установка параметров, определенных интерфейсом keyParams
Следует помнить о том, что изменение параметров генерирования ключевой пары имеет смысл только до выполнения непосредственно генерирования пары.
Стандартный интерфейс JCA допускает вызовы метода initialize() класса KeyPairGenerator и с другими параметрами (например, длина ключа), но при использовании криптопровайдера КриптоПро JCP такие вызовы не имеют смысла, поскольку они не изменяют набора параметров, установленного ранее генератору.
Генерирование ключевой пары ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-2001 осуществляется только после создания генератора и, если это необходимо, определения его параметров. Вызов метода generateKeyPair() класса KeyPairGenerator возвращает новую ключевую пару ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-2001 и установленным набором параметров (или с параметрами по умолчанию):
KeyPair pair = kg.generateKeyPair();
Пример генерирования ключевой пары см. samples/samples_src.jar/userSamples/KeyPairGen.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Криптопровайдер КриптоПро JCP осуществляет хранение закрытых ключей и соответствующих им сертификатов открытых ключей на ключевых носителях через стандартный интерфейс JCA при помощи класса KeyStore. Следует заметить, что использование интерфейса этого класса является общим как для работы с ключевыми носителями, так и для работы с хранилищем сертификатов. Однако, существуют и некоторые особенности, описанные ниже.
Поскольку генерирование закрытых и открытых ключей подписи разрешено только для алгоритма ГОСТ Р 34.10-2001, то и запись закрытых ключей на ключевые носители разрешена только для ключей этого алгоритма. Чтение закрытых ключей с носителей допустимо для ключей, соответствующих алгоритмам ГОСТ Р 34.10-94 и ГОСТ Р 34.10-2001. Запись и чтение сертификатов открытых ключей допустимы для обоих алгоритмов и удовлетворяет следующим правилам:
Также следует обратить внимание на одну важную особенность реализации интерфейса KeyStore в криптопровайдере КриптоПро JCP: хранилище ключей (ключевой носитель), используемое через данный интерфейс, по умолчанию разграничено по пользователям.
После того, как закрытый ключ подписи, соответствующий алгоритму ГОСТ Р 34.10-2001, был создан, криптопровайдер КриптоПро JCP позволяет записать его на один из перечисленных ключевых носителей. Также как и генерирование, сохранение на ключевые носители разрешается только для закрытых ключей, соответствующих алгоритму ГОСТ Р 34.10-2001.
Осуществление операций с ключевыми носителями (в том числе и запись закрытого ключа) производится через стандартный интерфейс хранилища закрытых ключей JCA (интерфейс класса KeyStore) посредством выполнения следующих действий:
Определение типа используемого ключевого носителя осуществляется посредством вызова метода getInstance() класса KeyStore. Этот метод является статическим и возвращает ссылку на класс KeyStore, который обеспечивает выполнение требуемой операции.
Для определения конкретного типа ключевого носителя методу getInstance() необходимо в качестве параметра передать имя, идентифицирующее необходимый тип. В криптопровайдере КриптоПро JCP реализовано несколько типов носителей:
При таком вызове метода getInstance() совместно с определением требуемого типа ключевого носителя осуществляется также определение требуемого типа криптопровайдера (КриптоПро JCP). Также стандартный интерфейс JCA позволяет в качестве параметра функции getInstance() класса KeyStore вместе с типом носителя указывать имя криптопровайдера, используемого для выполнения требуемой операции. Таким образом, определение типа используемого ключевого носителя осуществляется одним из следующих способов:
KeyStore ks = KeyStore.getInstance("HDImageStore"); KeyStore ks = KeyStore.getInstance("HDImageStore", "JCP"); KeyStore ks = KeyStore.getInstance("FloppyStore"); KeyStore ks = KeyStore.getInstance("FloppyStore", "JCP"); KeyStore ks = KeyStore.getInstance("OCFStore"); KeyStore ks = KeyStore.getInstance("OCFStore", "JCP"); KeyStore ks = KeyStore.getInstance("J6CFStore"); KeyStore ks = KeyStore.getInstance("J6CFStore", "JCP");
Определение типа используемого ключевого носителя представляет собой инициализацию стандартного ключевого хранилища JCA, поэтому операции записи на ключевой носитель или чтения с него следует осуществлять в соответствии с интерфейсом JCA, а именно, требуется предварительная загрузка содержимого носителя и последующее после выполнения операции сохранение содержимого.
Согласно интерфейсу стандартного ключевого хранилища JCA перед началом выполнения каких либо операций требуется загрузка всего содержимого хранилища, следовательно, перед выполнением операций с ключевым носителем следует загрузить его содержимое. Загрузка содержимого стандартного хранилища JCA осуществляется посредством вызова метода load() класса KeyStore. Согласно интерфейсу JCA функции load() следует передавать два параметра: поток, из которого осуществляется чтение содержимого ключевого хранилища, и пароль на хранилище.
Поскольку работа с ключевыми носителями (чтение/запись закрытых ключей и соответствующих им сертификатов) и с хранилищем сертификатов (чтение/запись доверенных сертификатов) в криптопровайдере КриптоПро JCP реализована согласно общему интерфейсу JCA класса KeyStore, то в некоторых случаях вызов функции load() осуществляет как загрузку содержимого ключевого носителя, так и содержимого хранилища сертификатов, проинициализированного именем данного носителя. Ввиду этого возникают особенности использования параметров функции load():
null
.
Если проинициализированное именем носителя стандартное хранилище KeyStore
используется как для работы с данным ключевым носителем, так и для работы с хранилищем сертификатов,
то в качестве данного параметра следует указывать поток содержимого хранилища
сертификатов. Тогда загруженное содержимое потока будет использоваться только при работе
с хранилищем сертификатов, а при работе с носителями будет игнорироваться. Если проинициализированного
именем данного носителя хранилища сертификатов на момент вызова функции load() не существует,
то в качестве этого параметра следует указывать null
.null
.Таким образом, перед началом выполнения операции с ключевым носителем следует выполнить загрузку содержимого этого носителя (и, если это требуется, загрузку проинициализированного именем носителя хранилища сертификатов) следующим образом:
ks.load(null, null); // не существует хранилища сертификатов, // проинициализированного именем данного // ключевого носителя char[] passwd; ks.load(null, passwd); // хранилище сертификатов существует, // на него установлен пароль passwd, // но последующие операции будут производиться // только с носителем InputStream stream; ks.load(stream, passwd);// хранилище сертификатов существует, // на него установлен пароль passwd, // последующие операции будут производиться // как с носителем, так и с хранилищем // сертификатов. Содержимое хранилища // записано в stream.
После того, как содержимое носителя (и, если это требуется, содержимое проинициализированного именем носителя хранилища сертификатов) было загружено, осуществляется собственно запись закрытого ключа на носитель. Данная операция реализуется при помощи вызова метода setKeyEntry() класса KeyStore. Согласно стандартному интерфейсу JCA существует два способа вызова данного метода с различными наборами параметров. Криптопровайдер КриптоПро JCP допускает только один набор параметров:
String alias; // идентификатор (уникальное имя) ключа и // соответствующего ему сертификата // открытого ключа PrivateKey key; // закрытый ключ char[] password; // пароль на ключ (в общем случае отличается // от пароля на хранилище, используемого при загрузке) Certificate[] chain;// цепочка сертификатов, начиная с корневого и // и заканчивая сертификатом открытого ключа, // соответствующего закрытому ks.setKeyEntry(alias, key, password, chain);
Следует отметить некоторые особенности вызова функции setKeyEntry():
alias
является уникальным именем записываемого
закрытого ключа и соответствующей ему цепочки сертификатов. Если
на носителе уже существует закрытый ключ и, возможно, соответствующая
ему цепочка сертификатов, то при попытке записи на этот носитель
нового ключа с тем же самым alias
произойдет
полное очищение области носителя, относящейся к существующему ключу,
и на ее место запишется новый ключ и новая цепочка. Кроме того,
если в хранилище доверенных сертификатов содержится сертификат
с заданным alias
, то этот сертификат будет удален из хранилища
доверенных сертификатов;key
должен
соответствовать типу ГОСТ Р 34.10-2001. Запись ключа
любого другого алгоритма запрещается;password
может выступать
любой символьный массив, допустимо также отсутствие пароля, т.е. null
.
Таким образом, этот пароль является исключительно паролем на записываемый
ключ и не имеет ничего общего с паролем на носитель (если таковой имеется,
см. выше). Особенность использования
данного параметра возникает лишь в случае использования карточки в
качестве носителя (при инициализации ключевого хранилища JCA именем
"OCFStore" или "J6CFStore"). В этом случае пароль на хранимый на носителе ключ должен
совпадать с паролем доступа к контейнеру. Этот пароль в свою очередь не обязан совпадать
с паролем доступа к хранилищу (к носителю), необходимым при загрузке
носителя;chain
выступает цепочка сертификатов, состоящая либо
из единственного сертификата, открытый ключ которого соответствует
записываемому закрытому ключу, либо из собственно цепочки сертификатов, в которой сертификат соответствующий ключу находится
в нулевом элементе массива. Сертификатом, соответсвующим ключу, может быть как самоподписанный
сертификат, так и сертификат, заверенный доверенным центром сертификации.
Если при этом передаваемый сертификат не соответствует закрытому ключу key
,
то метод setKeyEntry() вызовет исключение.Согласно стандартному интерфейсу класса KeyStore
после любой операции, изменяющей содержимое стандартного хранилища JCA, его следует перезаписать.
Операция сохранения осуществляется вызовом функции store() класса
KeyStore.
Если изменения касались только содержимого ключевого носителя, то вызов данной функции не является
обязательным (записываемые закрытые ключи сохраняются на носителе автоматически, однако
в этом случае также можно воспользоваться функцией store(null, null)).
Если же изменения касались содержимого хранилища сертификатов,
проинициализированного именем данного носителя, то функцию store() следует вызывать
с двумя параметрами: выходной поток, в который записывается новое содержимое хранилища сертификатов
и пароль на это хранилище (этот пароль в последствии будет использоваться для
доступа к носителю, именем которого проинициализировано хранилище сертификатов, а также
для доступа к самому хранилищу). Передаваемый в качестве параметра пароль на это хранилище
не должен быть null
.
Перезапись содержимого хранилища доверенных сертификатов осуществляется следующим образом:
OutputStream stream; // поток, в который записывается // измененное содержимое хранилища char[] password; // пароль для последующего доступа // к данному хранилищу ks.store(stream, password);Пример записи закрытого ключа на носитель см. samples/samples_src.jar/userSamples/KeyPairGen.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
В отличии от записи закрытых ключей подписи на ключевые носители, чтение закрытых ключей с этих носителей разрешена как для алгоритма ГОСТ Р 34.10-2001, так и для ключей алгоритма ГОСТ Р 34.10-94. Возможность чтения ключей, соответствующих алгоритму ГОСТ Р 34.10-94, реализована из соображений совместимости с криптопровайдером КриптоПро CSP, но несмотря на это для таких ключей запрещены операции создания подписи и генерирования запроса на сертификат.
Чтение закрытых ключей подписи производится через стандартный интерфейс хранилища закрытых ключей JCA (через интерфейс класса KeyStore) посредством выполнения следующих действий:
После того, как содержимое носителя (и, если это требуется, содержимое проинициализированного именем носителя хранилища сертификатов) было загружено, осуществляется собственно чтение закрытого ключа с носителя. Данная операция реализуется при помощи вызова метода getKey() класса KeyStore, возвращающего требуемый закрытый ключ, следующим образом:
String alias; // идентификатор (уникальное имя) получаемого закрытого ключа, // установленный при записи ключа на носитель char[] password;// пароль на ключ, установленный при записи ключа на носитель PrivateKey key = (PrivateKey)ks.getKey(alias, password);
Следует отметить некоторые особенности вызова функции getKey():
alias
является уникальным именем получаемого
закрытого ключа, которое было установлено
при записи ключа на носитель. Если
на заданном носителе не существует закрытого ключа с передаваемым
именем alias
, то вызов getKey() вернет null
;password
представляет собой пароль на
запрашиваемый закрытый ключ, который был установлен при записи
этого ключа в контейнер. Как отмечалось выше, для карточек (при
инициализации ключевого хранилища JCA именем "OCFStore" или "J6CFStore")
пароли на все ключи, хранимые на карточке, совпадают с паролем, прошитым в карточке
при ее создании.Криптопровайдер КриптоПро JCP позволяет осуществлять запись на ключевые носители сертификатов открытых ключей, соответствующих хранящимся на носителе закрытым ключам. Таким образом, операция записи сертификатов открытых ключей допустима для ключей, соответствующих алгоритмам ГОСТ Р 34.10-94 и ГОСТ Р 34.10-2001, и приводит к следующим результатам:
При этом осуществляется проверка соответствия открытого ключа записываемого сертификата закрытому ключу.
Операция записи сертификата открытого ключа подписи производится через стандартный интерфейс хранилища закрытых ключей JCA (через интерфейс класса KeyStore) посредством выполнения следующих действий:
После того, как содержимое носителя (и, если это требуется, содержимое проинициализированного именем носителя хранилища сертификатов) было загружено, осуществляется собственно запись сертификата открытого ключа на носитель. Данная операция реализуется при помощи вызова метода setCertificateEntry() класса KeyStore следующим образом:
String alias; // идентификатор (уникальное имя) закрытого ключа, // которому соответствует открытый ключ сертификата Certificate cert; // записываемый сертификат ks.setCertificateEntry(alias, cert);
Следует отметить некоторые особенности вызова функции setCertificateEntry():
alias
является уникальным именем
закрытого ключа, которому соответствует открытый ключ
записываемого сертификата. Если ключу с заданным
именем alias
уже соответствует некоторый
сертификат на носителе, то этот сертификат будет перезаписан
новым. В противном случае передаваемый сертификат будет просто
добавлен на носитель.
При этом после записи сертификату будет присвоено имя соответствующего ему закрытого
ключа - передаваемый методу setCertificateEntry() в качестве параметра
alias
. Если же на носителе не существует закрытого
ключа с заданным alias
, то передаваемый сертификат
будет добавлен в хранилище доверенных сертификатов;cert
представляет собой записываемый на носитель сертификат.
Открытый ключ этого сертификата должен соответствовать закрытому ключу
с именем alias
, если он существует, в противном случае метод setCertificateEntry()
сгенерирует исключение.Криптопровайдер КриптоПро JCP позволяет осуществлять чтение с ключевых носителей сертификатов открытых ключей, соответствующих алгоритмам ГОСТ Р 34.10-94 и ГОСТ Р 34.10-2001 через стандартный интерфейс хранилища закрытых ключей JCA (интерфейс класса KeyStore) посредством выполнения следующих действий:
После того, как содержимое носителя (и, если это требуется, содержимое проинициализированного именем носителя хранилища сертификатов) было загружено, осуществляется собственно чтение сертификата открытого ключа с носителя. Данная операция реализуется при помощи вызова метода getCertificate() класса KeyStore, возвращающего запрашиваемый сертификат, следующим образом:
String alias; // идентификатор (уникальное имя) сертификата, // установленный при записи сертификата на носитель Certificate cert = ks.getCertificate(alias);
Следует отметить некоторые особенности вызова функции getCertificate() с передаваемым
параметром alias
, являющимся уникальным именем запрашиваемого сертификата:
alias
,
то метод getCertificate() вернет сертификат с носителя;alias
,
но в хранилище сертификатов есть сертификат с таким именем, то
метод getCertificate() вернет сертификат из хранилища сертификатов;alias
не существует ни на носителе,
ни в хранилище сертификатов, то метод getCertificate() вернет null
.Удаление секретного ключа с ключевого носителя осуществляется вызовом функции deleteEntry
с передаваемым параметром alias
, являющимся уникальным именем ключа.
Для носителей требующих пароля для удаления контейнера (например, смарт-карт Оскар), в качестве
имени ключа необходимо передать строку, состоящую из имени ключа, 4 символов двоеточия ("::::")
и пароля доступа к ключу.
Изменить пути к хранилищам "FloppyStore" и "HDImageStore" можно из контрольной панели КриптоПро JCP или программно:
//Установка нового пути FloppyStore.setDir(String pathFloppy) HDImageStore.setDir(String pathHD) //Получение текущего пути String dirFloppy = FloppyStore.getDir() String dirHD = HDImageStore.getDir()
Криптопровайдер КриптоПро JCP осуществляет хранение доверенных сертификатов в определяемым пользователем хранилище сертификатов через стандартный интерфейс JCA (класс KeyStore). Следует заметить, что использование интерфейса этого класса является общим как для работы хранилищем сертификатов, так и для работы с ключевыми носителями. Особенности работы с хранилищем сертификатов описаны ниже.
Криптопровайдер КриптоПро JCP позволяет осуществлять запись доверенных сертификатов в определяемое пользователем хранилище доверенных сертификатов, соответствующее стандартному интерфейсу хранилища JCA (класс KeyStore). Для этого необходимо выполнить последовательность действий, аналогичную последовательности при работе с ключевыми носителями:
Аналогично определению типа используемого ключевого носителя.
Для удобства пользователя был также создан тип хранилища "CertStore". В хранилище данного типа могут храниться только сертификаты.
Инициализация такого хранилища может осуществляться одним из следующих способов:
KeyStore ks = KeyStore.getInstance("CertStore"); KeyStore ks = KeyStore.getInstance("CertStore", "JCP");
Инициализация хранилища доверенных сертификатов представляет собой инициализацию стандартного хранилища JCA, поэтому для операций записи сертификатов в хранилище или чтения из него требуется предварительная загрузка содержимого хранилища и последующее сохранение содержимого.
Согласно интерфейсу стандартного хранилища JCA перед началом выполнения каких либо операций требуется загрузка всего содержимого хранилища. Загрузка содержимого стандартного хранилища JCA осуществляется посредством вызова метода load() класса KeyStore. Согласно интерфейсу JCA функции load() следует передавать два параметра: поток, из которого осуществляется чтение содержимого ключевого хранилища, и пароль на хранилище.
Особенности вызова метода load() ввиду общего интерфейса работы с ключевыми носителями и с хранилищем сертификатов подробно описаны выше.
После того, как содержимое хранилища сертификатов было загружено, осуществляется собственно запись доверенного сертификата. Данная операция реализуется при помощи вызова метода setCertificateEntry() класса KeyStore следующим образом:
String alias; // идентификатор (уникальное имя) устанавливаемого // в хранилище сертификата Certificate cert; // записываемый сертификат ks.setCertificateEntry(alias, cert);
Следует отметить некоторые особенности вызова функции setCertificateEntry().
с передачей ему параметра alias
, являющегося уникальным именем
записываемого сертификата.
alias
, то он будет перезаписан
передаваемым сертификатом cert
;alias
, но
на носителе, чьим именем было проинициализировано хранилище
сертификатов, существует закрытый ключ (и, возможно, сертификат открытого ключа, соответствующего
закрытому) с заданным alias
, то передаваемый сертификат
будет добавлен на этот носитель. При этом будет осуществлена проверка соответствия
передаваемого сертификата cert
закрытому ключу, который хранится
на носителе с именем alias
(подробнее см. выше);alias
, то передаваемый сертификат будет просто добавлен в
хранилище доверенных сертификатов с именем alias
.Производится аналогично сохранению содержимого ключевого носителя.
Пример записи сертификата в хранилище доверенных сертификатов см. samples/samples_src.jar/userSamples/Certificates.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Криптопровайдер КриптоПро JCP позволяет осуществлять чтение доверенных корневых сертификатов из хранилища, определенного пользователем через стандартный интерфейс JCA класса KeyStore, посредством выполнения следующих действий:
После того, как содержимое хранилища сертификатов было загружено, осуществляется собственно чтение сертификата из этого хранилища. Данная операция реализуется при помощи вызова метода getCertificate() класса KeyStore, возвращающего запрашиваемый сертификат, следующим образом:
String alias; // идентификатор (уникальное имя) сертификата, // установленный при записи сертификата в хранилище Certificate cert = ks.getCertificate(alias);
Следует отметить некоторые особенности вызова функции getCertificate() с передаваемым
параметром alias
, являющимся уникальным именем запрашиваемого сертификата.
alias
, то
метод getCertificate() вернет сертификат из хранилища сертификатов;alias
, но
такой сертификат есть на носителе, чьим именем было проинициализировано хранилище
сертификатов,
то метод getCertificate() вернет сертификат с носителя;alias
не существует
ни в хранилище сертификатов, ни на носителе, то метод getCertificate() вернет null
.Пример чтения сертификата из хранилища доверенных сертификатов см. samples/samples_src.jar/userSamples/Certificates.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Примечание: Согласно интерфейсу JCA существует возможность загрузки хранилища сертификатов без пароля ks.load(stream, null)
.
При этом провайдер КриптоПро JCP позволяет осуществлять все операции связанные с чтением и запрещает операции связанные с изменением хранилища
(изменять хранилище можно только при загрузке его с паролем).
Криптопровайдер КриптоПро JCP позволяет осуществлять генерирование случайных чисел на основе алгоритма ГОСТ 28147-89, через стандартный интерфейс JCA при помощи класса SecureRandom.
Генератор случайных чисел создается посредством вызова метода getInstance() класса SecureRandom. Этот метод является статическим и возвращает ссылку на созданный объект класса SecureRandom.
Для создания генератора методу getInstance() необходимо в качестве параметра передать имя, идентифицирующее алгоритм ("CPRandom" или JCP.CP_RANDOM). При таком вызове метода getInstance() совместно с определением требуемого алгоритма осуществляется также определение требуемого типа криптопровайдера (КриптоПро JCP). Стандартный интерфейс JCA позволяет в качестве параметра функции getInstance() класса SecureRandom вместе с именем алгоритма указывать имя криптопровайдера, используемого для выполнения операции. Таким образом, создание генератора осуществляется одним из следующих способов:
SecureRandom rnd = SecureRandom.getInstance("CPRandom"); SecureRandom rnd = SecureRandom.getInstance("CPRandom", "JCP"); SecureRandom rnd = SecureRandom.getInstance(JCP.CP_RANDOM, JCP.PROVIDER_NAME);
Некоторые функции JCA предусматривают возможность установки необходимого SecureRandom для выполнения конкретных операций. Например, можно установить конкретный генератор случайных чисел в класс Signature при создании цифровой подписи с помощью функции
void initSign(PrivateKey privateKey, SecureRandom random).При генерировании ключевой пары в класс KeyPairGenerator можно установить конкретный генератор функцией
void initialize(AlgorithmParameterSpec params, SecureRandom random).Однако, чтобы обеспечить необходимое качество случайных последовательностей, КриптоПро JCP игнорирует генераторы, переданные таким способом в качестве параметров. Поэтому для увеличения производительности не стоит создавать новые генераторы только для того, чтобы проинициализировать ими другие классы JCA/JCE.
Для других целей, после того, как генератор случайных чисел был создан, можно получить случайную последовательность функцией void nextBytes(byte[] bytes)
В любой момент времени созданный генератор можно доинициализировать с помощью функции public byte[] generateSeed(int numBytes) любой последовательностью. Это довольно долгая операция и не стоит ей злоупотреблять в приложениях, требующих высокой производительности.
В процессе работы генератор случайных чисел КриптоПро JCP контролирует качество выходной последовательности и проводит периодический контроль целостности. В случае обнаружения нарушения целостности, генератор возбуждает исключение ru.CryptoPro.JCP.Random.RandomRefuseException. Возникновение этой ошибки возможно в любом месте, где используется генератор. Например, при генерировании ключа, при подписи. Использование криптопровайдера КриптоПро JCP в этом случае не допускается.
При генерировании ключевой пары с помощью класса KeyPairGenerator для гарантии качества выходной последовательности (и, следовательно, секретного ключа) генератор случайных чисел КриптоПро JCP необходимо доинициализировать. При генерировании ключа пользователю по умолчанию предлагается использовать оконный интерфейс. Для доинициализации будет использовано время между пользовательскими событиями: нажатиями клавиш, кнопок мыши, движениями мыши.
Для переключения между типами датчика (консольный, графический) необходимо запустить соответствующий класс:
При запуске класса прописывается соответствующая настройка, поэтому достаточно запустить его один раз.
Пример использования генератора случайных чисел с использованием класса SecureRandom см. samples/samples_src.jar/userSamples/Random.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Криптопровайдер КриптоПро JCP осуществляет хеширование данных в соответствии с алгоритмом ГОСТ Р 34.11-94, через стандартный интерфейс JCA при помощи класса MessageDigest.
Объект хеширования данных в соответствии с алгоритмом ГОСТ Р 34.11-94 создается посредством вызова метода getInstance() класса MessageDigest. Этот метод является статическим и возвращает ссылку на класс MessageDigest, который обеспечивает выполнение требуемой операции.
Для создания объекта хеширования в соответствии с алгоритмом ГОСТ Р 34.11-94 методу getInstance() необходимо в качестве параметра передать имя, идентифицирующее данный алгоритм ("GOST3411" или JCP.GOST_DIGEST_NAME). При таком вызове метода getInstance() совместно с определением требуемого алгоритма хеширования данных осуществляется также определение требуемого типа криптопровайдера (КриптоПро JCP). Также стандартный интерфейс JCA позволяет в качестве параметра функции getInstance() класса MessageDigest вместе с именем алгоритма указывать имя криптопровайдера, используемого для выполнения требуемой операции. Таким образом, создание генератора ключевой пары осуществляется одним из следующих способов:
MessageDigest digest = MessageDigest.getInstance("GOST3411"); MessageDigest digest = MessageDigest.getInstance("GOST3411", "JCP"); MessageDigest digest = MessageDigest.getInstance(JCP.GOST_DIGEST_NAME, JCP.PROVIDER_NAME);
Хеширование данных при помощи созданного таким образом объекта digest
будет осуществляться в соответствии
с алгоритмом ГОСТ Р 34.11-94 и с установленными в контрольной панели параметрами (параметрами по умолчанию).
Стандартный интерфейс JCA класса MessageDigest
не позволяет изменять параметры созданного объекта хеширования, но
если существует такая необходимость, то при помощи дополнительных возможностей криптопровайдера КриптоПро JCP
можно установить требуемые параметры хеширования (отличные от параметров,
установленных в контрольной панели).
После того, как объект хеширования данных был создан, может возникнуть
необходимость изменить параметры хеширования, установленные ранее в контрольной панели.
Операция изменения существующего набора параметров не может быть осуществлена при помощи
стандартного интерфейса JCA класса MessageDigest,
поэтому для ее реализации следует привести созданный объект хеширования к типу
GostDigest
и уже для объекта этого класса воспользоваться методом reset(), передавая
данному методу идентификатор устанавливаемых параметров (OID
):
// ВНИМАНИЕ! для совместимости с другими продуктами КриптоПро // допустимо использовать только параметры по умолчанию: // "1.2.643.2.2.30.1" OID digestOid = new OID("1.2.643.2.2.30.1"); /* преобразование к типу GostDigest */ GostDigest gostDigest = (GostDigest)digest; /* установка требуемых параметров */ gostDigest.reset(digestOid);
Метод reset() (без параметров) стандартного интерфейса JCA класса MessageDigest изменяет установленные параметры хеширования на параметры по умолчанию.
Использование метода изменения параметров хеширования см. samples/samples_src.jar/userSamples/Digest.java (входит в комплект поставки программного обеспечения КриптоПро JCP). Данная операция имеет смысл только до начала выполнения непосредственной операции создания хеша данных.
В некоторых случаях требуется создать копию уже существующего объекта хеширования данных, например, когда требуется осуществить хеширование как и части данных, так и всего исходного массива данных. В этом случае после того, как была обработана требуемая часть данных, необходимо сохранить (при помощи копирования) объект хеширования, и продолжить обработку оставшейся части (в результате чего будут обработаны все исходные данные). Уже после выполняется подсчет значения хеша для обоих объектов (исходного - соответствующего всем данным и скопированного - соответствующего части данных).
Для этих целей используется метод clone() класса MessageDigest, который возвращает точную копию существующего объекта хеширования. Этот метод может быть вызван на любом этапе выполнения операции хеширования после того, как объект хеширования был проинициализирован и до того, как операция хеширования была завершена. Cм. samples/samples_src.jar/userSamples/Digest.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
После того, как объект хеширования был создан, вычисление хеша данных в соответствии с алгоритмом ГОСТ Р 34.11-94 производится в два этапа: обработка данных и последующее завершение операции хеширования.
Обработка хешируемых данных может быть осуществлена двумя способами:
Для обработки любым из этих способов хешируемые данные должны быть представлены в виде байтового массива.
Метод update() класса MessageDigest осуществляет обработку хешируемых данных, представленных в виде байтового массива и подаваемых ему в качестве параметра. Существует 3 варианта обработки байтового массива данных при помощи этого метода:
byte[] data; for(int i = 0; i < data.length; i++) digest.update(data[i]);
byte[] data; int BLOC_LEN = 1024; // если длина исходных данных меньше длины блока if(data.length/BLOC_LEN == 0) digest.update[data]; else { // цикл по блокам for (int i = 0; i < data.length/BLOC_LEN; i++) { byte[] bloc = new byte[BLOC_LEN]; for(int j = 0; j < BLOC_LEN; j++) bloc[j] = data[j + i * BLOC_LEN]; digest.update(bloc); } // обработка остатка byte[] endBloc = new byte[data.length % BLOC_LEN]; for(int j = 0; j < data.length % BLOC_LEN; j++) bloc[j] = data[j + data.length - data.length % BLOC_LEN - 1]; digest.update(bloc); }
byte[] data; digest.update(data);
Допускается комбинирование первого и второго варианта, обработка блоками различной длины, а также использование метода update(byte[]data, int offset, int len) - обработка массива данных со смещением. Но в любом случае следует помнить, что для корректного подсчета хеша на этапе завершения операции хеширования необходимо обработать все байты массива данных.
Помимо использования метода update() класса MessageDigest обработка хешируемых данных может быть осуществлена посредством метода read() класса DigestInputStream. Фактически, этот метод в зависимости от способа обработки данных (см. ниже) вызывает соответствующий вариант обработки при помощи метода update(). Для осуществления обработки данных из исходного байтового массива данных необходимо создать новый объект типа ByteArrayInputStream, а затем из него и созданного ранее объекта хеширования данных получить новый объект типа DigestInputStream:
byte[] data; ByteArrayInputStream stream = new ByteArrayInputStream(data); DigestInputStream digestStream = new DigestInputStream(stream, digest);
После того, как объект типа DigestInputStream создан, обработка хешируемых данных осуществляется при помощи метода read() класса DigestInputStream. При этом, как и в случае метода update(), существует 3 варианта использования метода read():
while (digestStream.available() != 0) digestStream.read();
int BLOC_LEN = 1024; int DATA_LEN = digestStream.available(); // если длина исходных данных меньше длины блока if(DATA_LEN/BLOC_LEN == 0) { byte[] data = new byte[DATA_LEN]; digestStream.read(data, 0, DATA_LEN); } else { // цикл по блокам for (int i = 0; i < DATA_LEN/BLOC_LEN; i++) { byte[] bloc = new byte[BLOC_LEN]; digestStream.read(bloc, 0, BLOC_LEN); } // обработка остатка byte[] endBloc = new byte[DATA_LEN % BLOC_LEN]; digestStream.read(endBloc, 0, DATA_LEN % BLOC_LEN); }
byte[] data = new byte[digestStream.available()]; digestStream.read(data, 0, digestStream.available());
Допускается комбинирование первого и второго варианта, обработка блоками различной длины, а также использование метода read(byte[]data, int offset, int len) - запись считанных данных в массив со смещением. Но в любом случае следует помнить, что для корректного подсчета хеша на этапе завершения операции хеширования необходимо обработать все байты массива данных.
После того, как все данные были обработаны, следует завершить операцию хеширования. Завершение осуществляется при помощи метода digest() класса MessageDigest. В результате выполнения этой функции подсчитывается значение хеша. Получить это значение можно двумя способами:
Примеры хеширования данных в соответствии с алгоритмом ГОСТ Р 34.11-94 см. samples/samples_src.jar/userSamples/Digest.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Криптопровайдер КриптоПро JCP осуществляет формирование электронной цифровой подписи (ЭЦП) данных, соответствующей алгоритму ГОСТ Р 34.10-2001, через стандартный интерфейс JCA при помощи класса Signature. Формирование ЭЦП для любого другого алгоритма при помощи криптопровайдера КриптоПро JCP запрещается.
Объект формирования ЭЦП данных создается посредством вызова метода getInstance() класса Signature. Этот метод является статическим и возвращает ссылку на класс Signature, который обеспечивает выполнение требуемой операции.
Для создания объекта формирования ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-2001 (а точнее, алгоритм подписи ГОСТ Р 34.10-2001 с алгоритмом хеширования ГОСТ Р 34.11-94) методу getInstance() необходимо в качестве параметра передать имя, идентифицирующее данный алгоритм:
При таком вызове метода getInstance() совместно с определением требуемого алгоритма формирования ЭЦП осуществляется также определение требуемого типа криптопровайдера (КриптоПро JCP). Также стандартный интерфейс JCA позволяет в качестве параметра функции getInstance() класса Signature вместе с именем алгоритма передавать имя криптопровайдера, используемого для выполнения требуемой операции.
Таким образом, создание объекта формирования ЭЦП осуществляется одним из следующих способов:
Signature sig = Signature.getInstance("GOST3411withGOST3410EL"); Signature sig = Signature.getInstance("GOST3411withGOST3410EL", "JCP"); Signature sig = Signature.getInstance(JCP.GOST_EL_SIGN_NAME, JCP.PROVIDER_NAME); //для совместимости с КриптоПро CSP (подпись имеет обратный порядок байт) Signature sig = Signature.getInstance("CryptoProSignature"); Signature sig = Signature.getInstance("CryptoProSignature", "JCP"); Signature sig = Signature.getInstance(JCP.CRYPTOPRO_SIGN_NAME, JCP.PROVIDER_NAME);
После того, как объект формирования ЭЦП был создан, необходимо определить набор параметров алгоритма ГОСТ Р 34.10-2001, в соответствии с которыми будет осуществляться операция формирования ЭЦП. Определение параметров ЭЦП осуществляется во время инициализации операции создания подписи методом initSign() класса Signature. в соответствии с параметрами закрытого ключа подписи, передаваемыми данному методу. Этот ключ не только определяет параметры формирования ЭЦП, но и используется в процессе ее формирования.
Необходимо помнить, что закрытые ключи, подаваемые на инициализацию объекта формирования ЭЦП, созданного описанным выше способом, должны соответствовать алгоритму ГОСТ Р 34.10-2001. Способ генерирования таких ключей описан выше. Создание подписи для любых других ключей запрещено.
Таким образом, инициализация операции формирования ЭЦП, во время которой происходит определение параметров подписи, осуществляется следующим образом:
PrivateKey privateKey; // обязательно ключ с алгоритмом "GOST3410" // (соответствующий алгоритму ГОСТ Р 34.10-2001) sig.initSign(privateKey);
После инициализации объекта формирования ЭЦП закрытым ключом
может возникнуть необходимость изменить параметры формирования ЭЦП (установить параметры, отличные
от параметров закрытого ключа). Изменять разрешается
только параметры хеширования, используемые в процессе формирования ЭЦП, причем изменение
этих параметров допустимо только до начала операции формирования ЭЦП.
Изменение параметров хеширования осуществляется при помощи метода setParameter() класса
Signature.
Этому методу в качестве параметра передается объект ParamsInterface
,
являющийся интерфейсом устанавливаемых параметров хеширования (создание объектов
такого типа описывается ниже).
Тогда изменение параметров хеширования для формирования ЭЦП осуществляется
следующим образом:
ParamsInterface digestParams; // интерфейс параметров хеширования sig.setParameter(digestParams); // установка параметров, определенных интерфейсом digestParams
Следует помнить, что использование данного метода имеет смысл только после того, как объект формирования подписи был проинициализирован. Если параметры хеширования были изменены при формировании подписи, то они должны быть соответствующим образом изменены в процессе проверки подписи.
После того, как объект подписи был создан и проинициализирован, операция формирования подписи производится в два этапа: обработка данных и последующее вычисление подписи, завершающее операцию формирования ЭЦП.
Обработка подписываемых данных осуществляется при помощи метода update() класса Signature. Этот метод осуществляет обработку подписываемых данных, представленных в виде байтового массива и подаваемых ему в качестве параметра. Существует 3 варианта обработки байтового массива данных при помощи этого метода:
byte[] data; for(int i = 0; i < data.length; i++) sig.update(data[i]);
byte[] data; int BLOC_LEN = 1024; // если длина исходных данных меньше длины блока if(data.length/BLOC_LEN == 0) sig.update[data]; else { // цикл по блокам for (int i = 0; i < data.length/BLOC_LEN; i++) { byte[] bloc = new byte[BLOC_LEN]; for(int j = 0; j < BLOC_LEN; j++) bloc[j] = data[j + i * BLOC_LEN]; sig.update(bloc); } // обработка остатка byte[] endBloc = new byte[data.length % BLOC_LEN]; for(int j = 0; j < data.length % BLOC_LEN; j++) bloc[j] = data[j + data.length - data.length % BLOC_LEN - 1]; sig.update(bloc); }
byte[] data; sig.update(data);
Допускается комбинирование первого и второго варианта, обработка блоками различной длины, а также использование метода update(byte[]data, int offset, int len) - обработка массива данных со смещением. Но в любом случае следует помнить, что для корректного вычисления подписи на этапе завершения операции создания подписи необходимо обработать все байты массива данных.
После того, как все данные были обработаны, следует завершить операцию формирования ЭЦП. Завершение осуществляется при помощи метода sign() класса Signature. В результате выполнения этой функции вычисляется значение подписи. Получить это значение можно двумя способами:
Криптопровайдер КриптоПро JCP осуществляет проверку ЭЦП данных, соответствующей алгоритму ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001, через стандартный интерфейс JCA при помощи класса Signature.
Объект проверки ЭЦП данных создается посредством вызова метода getInstance() класса Signature. Этот метод является статическим и возвращает ссылку на класс Signature, который обеспечивает выполнение требуемой операции.
Для создания объекта проверки ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001 (а точнее, алгоритм подписи ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001 с алгоритмом хеширования ГОСТ Р 34.11-94) методу getInstance() необходимо в качестве параметра передать имя, идентифицирующее требуемый алгоритм проверки ЭЦП (см. создание объекта формирования ЭЦП; для алгоритма ГОСТ Р 34.10-94 - "GOST3411withGOST3410" или JCP.GOST_SIGN_NAME).
После того, как объект проверки ЭЦП был создан, необходимо определить набор параметров заданного при создании объекта алгоритма (ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001), в соответствии с которыми будет осуществляться операция проверки ЭЦП. Определение параметров ЭЦП осуществляется во время инициализации операции проверки подписи методом initVerify() класса Signature. в соответствии с параметрами открытого ключа проверки, передаваемыми данному методу. Этот ключ не только определяет параметры проверки ЭЦП, но и используется в самом процессе проверки.
Необходимо помнить, что открытые ключи, подаваемые на инициализацию объекта проверки ЭЦП, созданного описанным выше способом должны соответствовать алгоритму этого объекта (соответственно, алгоритмам ГОСТ Р 34.10-94 и ГОСТ Р 34.10-2001). Способ генерирования ключей для алгоритма ГОСТ Р 34.10-2001 описан выше. Ключи, соответствующие алгоритму ГОСТ Р 34.10-94, могут быть получены, например, из хранилища сертификатов. Помимо этого для корректной проверки ЭЦП требуется, чтобы открытый ключ проверки соответствовал закрытому ключу, на котором осуществлялось формирование подписи.
Таким образом, инициализация операции проверки ЭЦП, во время которой происходит определение параметров ЭЦП, осуществляется следующим образом:
PublicKey publicKey; // алгоритм ГОСТ Р 34.10-94 или ГОСТ Р 34.10-2001 sig.initVerify(publicKey);
После инициализации объекта проверки ЭЦП открытым ключом может возникнуть необходимость изменить параметры проверки ЭЦП (установить параметры, отличные от параметров открытого ключа). Такая необходимость может возникнуть в случае, когда параметры ЭЦП были некоторым образом изменены при ее формировании. Тогда для корректной проверки этой ЭЦП требуется аналогичным образом изменить параметры объекта проверки подписи (установить те же самые параметры). Изменение параметров проверки ЭЦП осуществляется аналогично изменению параметров формирования ЭЦП.
После того, как объект проверки подписи был создан и проинициализирован, операция проверки подписи производится в два этапа: обработка данных и последующая проверка подписи, завершающая текущую операцию.
Обработка подписанных данных осуществляется при помощи метода update() класса Signature и полностью аналогична обработке данных при создании подписи.
После того, как все данные были обработаны, следует завершить операцию проверки ЭЦП. Завершение осуществляется при помощи метода verify() класса Signature. Этой функции передается проверяемое значение подписи и в результате ее работы возвращается логическое значение: true - подпись верна, false - подпись не верна. Значение проверяемой подписи можно передать двумя способами:
Примеры создания и проверки подписи см. samples/samples_src.jar/userSamples/SignAndVerify.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Для осуществления операций, реализуемых криптопровайдером КриптоПро JCP, зачастую требуется использование стандартных методов JCA работы с сертификатами. Такими операциями, например, являются:
Поскольку криптопровайдер КриптоПро JCP не реализует стандартные методы работы с сертификатами, а лишь обеспечивает их поддержку, то в данной документации приводится лишь описание использования этих методов при выполнении перечисленных выше операций.
Генерирование X509-сертификатов осуществляется при помощи метода generateCertificate() класса CertificateFactory следующим образом:
InputStream inStream; CertificateFactory cf = CertificateFactory.getInstance("X509"); //или //CertificateFactory cf = CertificateFactory.getInstance(JCP.CERTIFICATE_FACTORY_NAME); Certificate cert = cf.generateCertificate(inStream);
Метод generateCertificate() получает в качестве параметра входной поток, в который записан закодированный в DER-кодировке сертификат, и возвращает объект класса Certificate. Инициализацию объекта класса CertificateFactory следует производить именем "X509" или JCP.CERTIFICATE_FACTORY_NAME (как показано выше). В этом случае выдаваемый методом generateCertificate() сертификат будет удовлетворять стандарту X.509, а значит являться объектом класса X509Certificate (этот класс является расширением класса Certificate). Криптопровайдер КриптоПро JCP поддерживает только стандарт X.509.
Генерирование X509-сертификатов используется в тех операциях, которые согласно стандартному интерфейсу JCA требуют для своего выполнения объекты класса Certificate. Такими операциями являются, например, запись закрытого ключа на носитель и запись сертификата в хранилище доверенных сертификатов или на носитель.
Закодированный в DER-кодировке сертификат, передаваемый функции generateCertificate() во входном
потоке может быть получен при помощи методов класса GostCertificateRequest
(см.
дополнительные возможности работы с сертификатами). Получение закодированного сертификата
при помощи класса GostCertificateRequest
используется в тех случаях, когда
требуется соответствие открытого ключа сертификата только
что созданному закрытому ключу (например, при осуществлении записи закрытого ключа на носитель).
Если же закрытый ключ не известен (например, при проверке ЭЦП), либо закрытый ключ, которому
соответствует открытый ключ сертификата был создан ранее (например, при записи сертификата
на носитель, на котором уже существует закрытый ключ), то в этом случае закодированный
сертификат, передаваемый функции generateCertificate(), может быть прочитан из файла.
Кодирование существующего сертификата (объекта класса Certificate) осуществляется при помощи метода getEncoded() класса Certificate следующим образом:
Certificate cert; byte[] encoded = cert.getEncoded();
Закодированный в DER-кодировке методом getEncoded() сертификат возвращается в виде байтового
массива. Сертификат cert
, для которого осуществляется кодирование,
может быть получен различными методами: генерированием X509-сертификата, чтением сертификата
открытого ключа с носителя, либо чтением доверенного сертификата из хранилища (все эти методы
возвращают объект класса
Certificate).
Операция кодирования сертификата используется в случае, когда требуется сохранить в файл только что сгенерированный или прочитанный с носителя (или из хранилища) сертификат. Пример записи сертификата в файл см. samples/samples_src.jar/userSamples/Certificates.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Получение открытого ключа из сертификата (объекта класса Certificate) осуществляется при помощи метода getPublicKey() класса Certificate следующим образом:
Certificate cert; PublicKey publicKey = cert.getPublicKey();
Сертификат cert
, из которого получается открытый ключ publicKey
,
может быть получен различными методами: генерированием X509-сертификата, чтением сертификата
открытого ключа с носителя, либо чтением доверенного сертификата из хранилища (все эти методы
возвращают объект класса
Certificate).
Операция получения открытого ключа из сертификата используется при осуществлении проверки ЭЦП
При выполнении операции записи закрытого ключа на носитель согласно стандартному интерфейсу JCA вместе с закрытым ключом на носитель следует записывать и цепочку сертификатов, начинающуюся с сертификата открытого ключа, соответствующего закрытому и заканчивающуюся доверенным корневым сертификатом. Благодаря этому требованию подпись сертификата открытого ключа, соответствующего записываемому закрытому ключу, всегда заверена цепочкой сертификатов.
Цепочка, как уже говорилось выше, может состоять только из одного сертификата (открытый ключ которого соответствует закрытому ключу). Если сертификат открытого ключа является самоподписанным, то проверка подписи этого сертификата не требуется (сертификат заверен закрытым ключом, которому соответствует открытый ключ этого сертификата). Дело обстоит иначе, когда сертификат подписан на некотором корневом сертификате центра, либо на некотором промежуточном сертификате (который в свою очередь подписан на корневом или на другом промежуточном сертификате). Тогда для обеспечения проверки подписи такого сертификата при его чтении, совместно с записью сертификата открытого ключа на носитель в хранилище доверенных сертификатов следует класть всю цепочку сертификатов, которой заверен этот сертификат.
Для проверки цепочки в режиме совместимости с КриптоПро УЦ в состав КриптоПро JCP входит провайдер RevCheck (JCPRevCheck.jar). Для его вызова в примерах, описанных ниже, следует заменить вызов алгоритма "PKIX" на вызов алгоритма "CPPKIX"
//для построения цепочки CertPathBuilder builder = CertPathBuilder.getInstance("CPPKIX"); и //для проверки цепочки CertPathValidator validator = CertPathValidator.getInstance("CPPKIX");
Пример построения и проверки цепочки сертификатов см. samples/samples_src.jar/userSamples/Certificates.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Начиная с версии java 1.5 появилась возможность проверять цепочку сертификатов используя On-Line Certificate Status Protocol (OCSP). Для проверки используются стандартные средства java-машины.
Пример проверки см. samples/samples_src.jar/userSamples/OCSPValidateCert.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Криптопровайдер КриптоПро JCP дает возможность работы с временным хранилищем ключей и сертификатов.
Данное хранилище существует только в памяти, его нельзя записать на носитель.
Для использования данного хранилища необходимо обеспечить недоступность сторонних лиц к java-машине!
Если есть возможность доступа сторонних лиц к java-машине, то использование данного типа хранилища ЗАПРЕЩЕНО,
т.к. иначе существует возможность доступа к секретным ключам (касается "MemoryStoreX", см. далее).
Осуществление операций с хранилищем производится через стандартный интерфейс хранилища закрытых ключей JCA
(интерфейс класса KeyStore).
Определение типа используемого хранилища осуществляется одним из следующих способов:
KeyStore ks = KeyStore.getInstance("MemoryStore"); KeyStore ks = KeyStore.getInstance("MemoryStore0"); //существует 10 хранилищ данного типа : MemoryStore0-MemoryStore9
При инициализации "MemoryStore" каждый раз возвращается новый объект, а при инициализации типа "MemoryStoreX" возвращается ссылка на ранее созданный объект. Далее работа аналогична работе с ключевыми носителями.
//загрузка хранилища (осуществляется для инициализации стандартного объекта KeyStore) ks.load(null, null); //запись ключа в хранилище String alias; // идентификатор (уникальное имя) ключа PrivateKey key; // закрытый ключ char[] password; // пароль на ключ Certificate[] chain; // цепочка сертификатов ks.setKeyEntry(alias, key, password, chain); //Чтение закрытого ключа из хранилища String alias; // идентификатор (уникальное имя) получаемого закрытого ключа, // установленный при записи ключа char[] password; // пароль на ключ, установленный при записи ключа PrivateKey key = (PrivateKey)ks.getKey(alias, password); //Запись сертификата в хранилище String aliasCert; // идентификатор сертификата Certificate cert; // записываемый сертификат ks.setCertificateEntry(aliasCert, cert); //Чтение сертификата из хранилища String aliasCert; // идентификатор сертификата, установленный при записи сертификата Certificate cert = ks.getCertificate(aliasCert); //Удаление String alias; //идентификатор ключа или сертификата ks.deleteEntry(alias);
Данный тип хранилища представляет собой кэш и может использоваться для ускорения работы с ключами и сертификатами, особенно это актуально при работе с ключами, на которые установлен пароль.
Зачастую при работе с закрытыми ключами подписи возникает необходимость изменить
набор параметров того или иного алгоритма для выполнения требуемой операции. Для этих целей
в криптопровайдере КриптоПро JCP реализован интерфейс ParamsInterface
параметров
алгоритма, являющийся реализацией стандартного класса
AlgorithmParameterSpec.
Объект типа ParamsInterface
, в зависимости от способа его создания, определяет
Во всех случаях, интерфейс параметров/набора параметров ParamsInterface
позволяет:
В процессе генерирования ключевой пары подписи существует возможность изменить набор параметров, в соответствии с которым будет создана ключевая пара (как описывалось выше, по умолчанию создается пара с параметрами, установленными в контрольной панели). В этот набора параметров входят:
Изменение такого набора параметров осуществляется при помощи интерфейса AlgIdInterface
,
являющегося расширением интерфейса ParamsInterface
. Объект типа AlgIdInterface
представляет собой интерфейс устанавливаемого набора параметров. Такой объект может быть создан при
помощи класса AlgIdSpec
, являющегося реализацией этого интерфейса несколькими способами:
// идентификатор набора параметров для ключа подписи, соответствующего алгоритму ГОСТ Р 34.10-2001 // "1.2.643.2.2.19" или JCP.GOST_EL_KEY_OID String keyOIDStr; OID keyOid = new OID(keyOIDStr); // идентификаторы параметров алгоритмов OID signOid; OID digestOid; OID cryptOid; // параметры алгоритмов ParamsInterface signParams; ParamsInterface digestParams; ParamsInterface cryptParams; /* определение набора параметров по умолчанию (установленного в контрольной панели) */ AlgIdSpec keyParams1 = new AlgIdSpec(null); /* определение набора параметров по идентификатору алгоритма генерирования ключевой пары (в этом случае устанавливается набор параметров по умолчанию, соответствующий этому идентификатору). На данный момент единственным допустимым идентификатором набора параметров для генерирования ключа подписи является "1.2.643.2.2.19" (или JCP.GOST_EL_KEY_OID), поэтому такой способ создания идентичен первому способу*/ AlgIdSpec keyParams2 = new AlgIdSpec(keyOid); /* определение набора параметров по идентификатору алгоритма генерирования ключевой пары и заданным идентификаторам параметров. Получение таких идентификаторов описано ниже.*/ AlgIdSpec keyParams3 = new AlgIdSpec(keyOid, signOid, digestOid, cryptOid); /* определение набора параметров по идентификатору алгоритма генерирования ключевой пары и заданным параметрам алгоритмов. Получение таких параметров описано ниже.*/ AlgIdSpec keyParams4 = new AlgIdSpec(keyOid, signParams, digestParams, cryptParams);
Явно изменение параметров алгоритма подписи ГОСТ Р 34.10-2001 не встречается в функциях стандартного интерфейса JCE,
но оно может быть использовано в процессе определения набора параметров для генерирования ключевых пар подписи
(см. выше). Для изменения используемых параметров ЭЦП, необходимо в первую очередь
получить интерфейс параметров алгоритма подписи ГОСТ Р 34.10-2001 по умолчанию (установленных в контрольной панели).
Данная операция может быть выполнена при помощи статического метода getDefaultSignParams() класса
AlgIdSpec
, возвращающего ссылку на интерфейс ParamsInterface
:
ParamsInterface signParams = AlgIdSpec.getDefaultSignParams();
Получение набора параметров ЭЦП и установка параметров по умолчанию (будут использоваться в дальнейшем):
/* получение всех допустимых идентификаторов параметров алгоритма подписи*/ Enumeration signOids = signParams.getOIDs(); /* получение одного из идентификаторов. Он может быть передан в соответствующий конструктор keyParams3 для класса AlgIdSpec*/ OID signOid = (OID)signOids.nextElement(); /* изменение идентификатора параметров ЭЦП по умолчанию. Измененные таким образом параметры ЭЦП могут быть переданы в соответствующий конструктор keyParams4 для класса AlgIdSpec*/ signOids.setDefault(signOid);
Изменение параметров алгоритма хеширования может быть использовано в явном виде
в процессе создания ЭЦП, а
также в неявном виде в процессе определения набора параметров для генерирования ключевых пар подписи
(см. выше). Для изменения используемых параметров хеширования,
необходимо в первую очередь
получить интерфейс параметров алгоритма хеширования ГОСТ Р 34.11-94 по умолчанию (установленных в контрольной панели).
Данная операция может быть выполнена при помощи статического метода getDefaultDigestParams() класса
AlgIdSpec
, возвращающего ссылку на интерфейс ParamsInterface
:
ParamsInterface digestParams = AlgIdSpec.getDefaultDigestParams();
Получение набора параметров хеширования и установка параметров по умолчанию (будут использоваться в дальнейшем):
/* получение всех допустимых идентификаторов параметров алгоритма хеширования*/ Enumeration digestOids = digestParams.getOIDs(); /* получение одного из идентификаторов. Он может быть передан в соответствующий конструктор keyParams3 для класса AlgIdSpec*/ OID digestOid = (OID)digestOids.nextElement(); /* изменение идентификатора параметров хеширования по умолчанию. Измененные таким образом параметры хеширования могут быть переданы в соответствующий конструктор keyParams4 для класса AlgIdSpec. Помимо этого они могут быть использованы для изменения параметров хеширования при создании ЭЦП.*/ digestOids.setDefault(digestOid);
Явно изменение параметров алгоритма шифрования ГОСТ 28147-89 не встречается в функциях стандартного интерфейса JCE,
но оно может быть использовано в процессе определения набора параметров для генерирования ключевых пар подписи
(см. выше). Для изменения используемых параметров ЭЦП, необходимо в первую очередь
получить интерфейс параметров алгоритма шифрования ГОСТ 28147-89 по умолчанию (установленных в контрольной панели).
Данная операция может быть выполнена при помощи статического метода getDefaultCryptParams класса
AlgIdSpec
, возвращаюшего ссылку на интерфейс ParamsInterface
:
ParamsInterface cryptParams = AlgIdSpec.getDefaultCryptParams();
Получение набора параметров шифрования и установка параметров по умолчанию (будут использоваться в дальнейшем):
/* получение всех допустимых идентификаторов параметров алгоритма шифрования*/ Enumeration cryptOids = cryptParams.getOIDs(); /* получение одного из идентификаторов. Он может быть передан в соответствующий конструктор keyParams3 для класса AlgIdSpec*/ OID cryptOid = (OID)cryptOids.nextElement(); /* изменение идентификатора параметров шифрования по умолчанию. Измененные таким образом параметры шифрования могут быть переданы в соответствующий конструктор keyParams4 для класса AlgIdSpec*/ cryptOids.setDefault(cryptOid);
Помимо возможности работы с сертификатами через стандартный интерфейс JCA, в криптопровайдере КриптоПро JCP реализованы некоторые дополнительные функции работы с сертификатами:
Перечисленные операции осуществляются при помощи специального класса GostCertificateRequest
.
Данный класс реализует генерирование запросов и самоподписанных сертификатов в соответствии
с алгоритмом подписи ГОСТ Р 34.10-2001 c алгоритмом хеширования ГОСТ Р 34.11-94.
Ключи, в соответствии с которыми осуществляется генерирование запросов и
самоподписанных сертификатов, также должны соответствовать алгоритму ГОСТ Р 34.10-2001 (способ генерирования
таких ключей описан выше). Возможно также генерирование запросов и сертификатов
для ключей обмена.
Структура запроса имеет следующий вид:
CertificationRequest ::= SEQUENCE { certificationRequestInfo SEQUENCE { version INTEGER, subject Name, subjectPublicKeyInfo SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }, attributes [0] IMPLICIT SET OF Attribute }, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING}
Структура самоподписанного сертификата имеет следующий вид:
Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signature BIT STRING } TBSCertificate ::= SEQUENCE { version [0] Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version shall be v2 or v3 subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, -- If present, version shall be v2 or v3 extensions [3] Extensions OPTIONAL -- If present, version shall be v3 -- } Version ::= INTEGER { v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER Validity ::= SEQUENCE { notBefore Time, notAfter Time } Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime } UniqueIdentifier ::= BIT STRING SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING } Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING }
Когда требуется создать запрос или сертификат сначала надо воспользоваться конструктором:
GostCertificateRequest request = new GostCertificateRequest();
Установить способ использования ключа keyUsage можно методом setKeyUsage()
,
параметром передается int - битовая маска способов использования ключа.
По умолчанию используется комбинация DIGITAL_SIGNATURE
"цифровая подпись"
и NON_REPUDIATION
"неотрекаемость"
или константа SIGN_DEFAULT
, объединяющая два эти значения.
Если Вы создаете запрос для ключа шифрования (т.е. для алгоритма "GOST3410DH")
стоит добавить KEY_ENCIPHERMENT
"шифрование ключей" и
KEY_AGREEMENT
"согласование ключей".
Можно воспользоваться константой CRYPT_DEFAULT
которая объединяет все четыре значения.
int keyUsage = GostCertificateRequest.DIGITAL_SIGNATURE | GostCertificateRequest.NON_REPUDIATION | GostCertificateRequest.KEY_ENCIPHERMENT | GostCertificateRequest.KEY_AGREEMENT; request.setKeyUsage(keyUsage);
Добавить ExtendedKeyUsage "улучшенный ключ" можно методом addExtKeyUsage()
.
Параметр методу addExtKeyUsage()
можно указывать массивом
int[]{1, 3, 6, 1, 5, 5, 7, 3, 4}
или можно строкой "1.3.6.1.5.5.7.3.3"
или объектом типа ru.CryptoPro.JCP.params.OID
. По умолчанию список будет пустым.
request.addExtKeyUsage(GostCertificateRequest.INTS_PKIX_EMAIL_PROTECTION); request.addExtKeyUsage("1.3.6.1.5.5.7.3.2"); // "Проверка подлинности клиента"
Допустимые OIDы для ExtendedKeyUsage и номера битов маски keyUsage описаны в стандарте
В классе GostCertificateRequest
определены следующие константы:
public static final int[] INTS_PKIX_SERVER_AUTH = {1, 3, 6, 1, 5, 5, 7, 3, 1}; public static final int[] INTS_PKIX_CLIENT_AUTH = {1, 3, 6, 1, 5, 5, 7, 3, 2}; public static final int[] INTS_PKIX_CODE_SIGNING = {1, 3, 6, 1, 5, 5, 7, 3, 3}; public static final int[] INTS_PKIX_EMAIL_PROTECTION = {1, 3, 6, 1, 5, 5, 7, 3, 4}; public static final int[] INTS_PKIX_IPSEC_END_SYSTEM = {1, 3, 6, 1, 5, 5, 7, 3, 5}; public static final int[] INTS_PKIX_IPSEC_TUNNEL = {1, 3, 6, 1, 5, 5, 7, 3, 6}; public static final int[] INTS_PKIX_IPSEC_USER = {1, 3, 6, 1, 5, 5, 7, 3, 7}; public static final int[] INTS_PKIX_TIME_STAMPING = {1, 3, 6, 1, 5, 5, 7, 3, 8}; public static final int[] INTS_PKIX_OCSP_SIGNING = {1, 3, 6, 1, 5, 5, 7, 3, 9};
При необходимости можно в запрос добавить собственное расширение, помимо KeyUsage и ExtendedKeyUsage. Пример добавления расширения основные ограничения BasicConstraints в запрос:
Extension ext = new Extension(); int[] extOid = {2, 5, 29, 19}; ext.extnID = new Asn1ObjectIdentifier(extOid); ext.critical = new Asn1Boolean(true); byte[] extValue = {48, 6, 1, 1, -1, 2, 1, 5}; ext.extnValue = new Asn1OctetString(extValue); request.addExtension(ext);
Такое расширение автоматически добавляется в сертификат при генерировании самоподписанного сертификата
(без обращения к центру сертификации) методами класса GostCertificateRequest
Это расширение имеет значения "Тип субъекта = ЦС", "Ограничение на длину пути = 5" и является критическим.
Использовать метод addExtension()
для установки в запрос KeyUsage и ExtendedKeyUsage нельзя,
для этого надо воспользоваться методами setKeyUsage()
и addExtKeyUsage()
Использовавшийся ранее для инициализации объектов типа GostCertificateRequest
метод init
request.init("GOST3410"); // JCP.GOST_DEGREE_NAME - для ключей подписи,и
request.init("GOST3410DH", isServer); // JCP.GOST_DH_NAME - для ключей обмена.начиная с версии 1.0.48 объявлен deprecated и не рекомендуется к использованию. Вызов
init("GOST3410")
эквивалентен вызову
request.setKeyUsage( GostCertificateRequest.SIGN_DEFAULT);
Вызов init("GOST3410DH", isServer)
эквивалентен двум вызовам
request.setKeyUsage( GostCertificateRequest.CRYPT_DEFAULT); request.addExtKeyUsage(GostCertificateRequest.INTS_PKIX_CLIENT_AUTH); request.addExtKeyUsage(GostCertificateRequest.INTS_PKIX_SERVER_AUTH); // только для сервераили трем, если второй параметр метода
init()
установлен в true.
Использовавшийся ранее флаг "Подписывание сертификатов" исключен из списка по умолчанию, теперь его надо указывать явно.
Пример генерирования запроса на сертификат, отправки запроса центру и получения сертификата, соответствующего запросу от центра см. samples/samples_src.jar/userSamples/Certificates.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Для генерирования запроса на сертификат при помощи класса GostCertificateRequest
необходимо выполнить следующую последовательность действий:
После того, как генератор был проинициализирован в соответствии с требуемыми алгоритмом ключа и назначением сертификата, до начала непосредственно генерирования запроса, заключающейся в подписи и кодировании содержимого полей запроса, следует определить параметры и значение открытого ключа субъекта, в соответствии с которым и будет создаваться запрос на сертификат. Эта операция осуществляется при помощи метода setPublicKeyInfo(), которому в качестве параметра передается открытый ключ:
PublicKey publicKey; request.setPublicKeyInfo(publicKey);Открытый ключ
publicKey
должен соответствовать алгоритму, которым был
проинициализирован генератор.
Функция setPublicKeyInfo() позволяет переустанавливать значение и параметры открытого ключа, в соответствии с которым создается запрос на сертификат. Но такие изменения допустимы лишь до тех пор, пока запрос не был подписан. В противном случае этот метод выбросит исключение.
Для осуществления генерирования запроса на сертификат объекту типа GostCertificateRequest
следует передать всю необходимую информацию о субъекте (открытый ключ и имя).
Определение имени субъекта осуществляется
при помощи метода setSubjectInfo(), которому в качестве параметра передается
строковое представление имени в соответствии со стандартом X.500 :
String name = "CN=Ivanov, OU=Security, O=CryptoPro, C=RU"; request.setSubjectInfo(name);
При повторном вызове функции setSubjectInfo() осуществляется замена установленного предыдущим ее вызовом имени на новое. Таким образом, метод setPublicKeyInfo() позволяет переопределять имя субъекта, для которого осуществляется генерирование запроса на сертификат. Но такие изменения допустимы лишь до тех пор, пока запрос не был подписан. В противном случае этот метод сгенерирует исключение.
После того, как все необходимые данные о субъекте внесены (открытый ключ
и имя),
осуществляется непосредственно генерирование запроса, заключающееся в подписи переданных
объекту типа GostCertificateRequest
данных и их кодировании.
Эта операция осуществляется при помощи метода encodeAndSign(), которому
в качестве параметра передается закрытый ключ ЭЦП, используемый для подписи
запроса на сертификат:
PrivateKey privateKey; request.encodeAndSign(privateKey);
Передаваемый закрытый ключ privateKey
должен соответствовать алгоритму, которым был
проинициализирован генератор.
Каждый создаваемый запрос может быть подписан лишь один раз. При попытке вызова
этой функции повторно сгенерируется исключение. В результате вызова функции encodeAndSign()
запрос представляется приобретает описанный выше вид, и в памяти он хранится
в DER-кодировке.
После того, как запрос был подписан и закодирован (другими словами, сгенерирован),
требуется получить его из памяти. Класс GostCertificateRequest
позволяет
получать запрос в трех видах:
PrintStream stream; // выходной поток, в который печатается // сформированный запрос request.printToDER(stream); // записывается в поток в DER-кодировке request.printToBASE64(stream); // записывается в поток в BASE64-кодировке byte[] encoded = request.getEncoded(); // возвращается в виде байтового // массива в DER-кодировке
Таким образом, сформированный запрос может быть получен как в DER-кодировке, так и в BASE64-кодировке. Запрос может быть записан либо в поток, либо в байтовый массив.
Запись в поток удобна в тех случаях, когда запрос требуется сохранить в некоторый файл (метод printToDER() сохраняет запрос в DER-кодировке, а метод printToBASE64() - в BASE64-кодировке). Если же предполагается дальнейшее использование данного запроса (например, отправка его центру сертификации), то удобнее его получать в виде байтового массива при помощи метода getEncoded().
После того, как запрос был создан, его можно отправить
центру сертификации для получения соответствующего запросу сертификата.
Класс GostCertificateRequest
позволяет осуществить эту операцию
различными способами:
После того, как запрос был создан, можно предварительно не сохранять его в массив или поток, а сразу после генерирования отправить центру сертификации для получения запрашиваемого сертификата. Операция отправки запроса центру непосредственно после его генерирования осуществляется при помощи функции getEncodedCert(), которая получает в качестве параметра http-адрес центра сертификации и возвращает закодированный в DER-кодировке сертификат, соответствующий подписанному запросу, в виде байтового массива:
String httpAddress = "http://www.cryptopro.ru/certsrv/"; byte[] encodedCert = request.getEncodedCert(httpAddress);
Полученный таким образом закодированный в DER-кодировке сертификат может в дальнейшем использоваться стандартными средствами JCA (например, функциями класса CertificateFactory).
Стоит заметить, что после того, как сертификат от центра был получен, запрос по-прежнему может быть сохранен в требуемом формате, однако большого в смысла в этом уже нет. Также следует заметить, что отправлен центру сертификации может быть только подписанный запрос. В противном случае метод getEncodedCert() сгенерирует исключение.
Сохраненный в DER-кодировке запрос может быть отправлен центру сертификации для получения запрашиваемого сертификата при помощи статического метода getEncodedCertFromDER() двумя способами:
String httpAddress = "http://www.cryptopro.ru/certsrv/"; InputStrean stream; // входной поток, в который записан // запрос в DER-кодировке byte[] encoded; // DER-закодированный запрос byte[] encodedCert = GostCertificateRequest.getEncodedCertFromDER(httpAddress, stream); byte[] encodedCert = GostCertificateRequest.getEncodedCertFromDER(httpAddress, encoded);
Оба вызова метода getEncodedCertFromDER() получают в качестве одного из параметров http-адрес центра сертификации и возвращают закодированный в DER-кодировке сертификат, соответствующий подписанному запросу, в виде байтового массива.
Полученный таким образом закодированный в DER-кодировке сертификат может в дальнейшем использоваться стандартными средствами JCA (например, функциями класса CertificateFactory).
Разница заключается в том, что первому способу вызова функции getEncodedCertFromDER()
в качестве параметра передается входной поток, в который записан закодированный в DER-кодировке запрос. Такой
поток обычно направлен на файл, содержащий запрос. Запись же запроса
в файл может быть осуществлена при помощи метода printToDER() класса
GostCertificateRequest
(подробнее см. сохранение запроса).
Второму же способу вызова функции getEncodedCertFromDER() в качестве
параметра передается байтовый массив, содержащий в себе DER-закодированный запрос.
Такой массив может быть получен при помощи метода getEncoded() класса
GostCertificateRequest
(подробнее см. сохранение запроса).
Сохраненный в BASE64-кодировке запрос может быть отправлен центру сертификации для получения запрашиваемого сертификата при помощи статического метода getEncodedCertFromDER() следующим образом:
String httpAddress = "http://www.cryptopro.ru/certsrv/"; InputStrean stream; // входной поток, в который записан // запрос в BASE64-кодировке byte[] encodedCert = GostCertificateRequest.getEncodedCertFromBASE64(httpAddress, stream);
Метод getEncodedCertFromBASE64()
получает в качестве параметров http-адрес центра сертификации и входной поток,
в который записан закодированный в BASE64-кодировке запрос. Такой
поток обычно направлен на файл, содержащий запрос. Запись же запроса
в файл может быть осуществлена при помощи метода printToBASE64() класса
GostCertificateRequest
(подробнее см. сохранение запроса).
Метод getEncodedCertFromBASE64() возвращает закодированный в DER-кодировке
сертификат, соответствующий подписанному запросу, в виде байтового массива.
Полученный таким образом закодированный в DER-кодировке сертификат может в дальнейшем использоваться стандартными средствами JCA (например, функциями класса CertificateFactory).
После того, как соответствующий запросу сертификат был получен от центра, зачастую требуется
выполнить построение цепочки сертификатов, начинающейся с корневого сертификата центра, и заканчивающейся
полученным от этого центра сертификатом. Класс GostCertificateRequest
позволяет
получать корневой сертификат центра сертификации при помощи статического метода getEncodedRootCert()
следующим образом:
String httpAddress = "http://www.cryptopro.ru/certsrv/"; byte[] encodedRootCert = GostCertificateRequest.getEncodedRootCert(httpAddress);
Функция getEncodedRootCert() получает в качестве параметра http-адрес центра сертификации и возвращает закодированный в DER-кодировке корневой сертификат центра в виде байтового массива.
Полученный таким образом закодированный в DER-кодировке корневой сертификат encodedRootCert
может в дальнейшем быть обработан функциями класса CertificateFactory,
и после может использоваться, например, для построения цепочек. Обработанный такой сертификат
может быть добавлен в хранилище доверенных сертификатов.
Пример генерирования запроса на сертификат, отправки запроса центру и получения сертификата, соответствующего запросу от центра см. samples/samples_src.jar/userSamples/Certificates.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Для осуществления сохранения закрытого ключа на носитель
совместно с закрытым ключом также требуется сертификат открытого ключа,
соответствующего закрытому. Для генерирования таких сертификатов удобно пользоваться
методом getEncodedSelfCert() класса GostCertificateRequest
.
Эта функция получает в качестве параметра ключевую пару субъекта (он же издатель),
а также имя субъекта (оно же имя издателя). Передаваемая ключая пара должна соответствовать
алгоритму, которым был проинициализирован генератор.
Сертификат возвращается в DER-кодировке в виде байтового массива.
После того, как объект класса GostCertificateRequest
проинициализирован, осуществляется
собственно генерирование сертификата:
KeyPair pair; // ключевая пара субъекта (она же пара издателя) String name; // имя субъекта (оно же имя издателя) byte[] encodedCert = request.getEncodedSelfCert(pair, name);
Полученный таким образом закодированный в DER-кодировке самоподписанный сертификат encodedCert
может в дальнейшем быть обработан функциями класса CertificateFactory,
и после может использоваться, например, для записи закрытого ключа на носитель.
При генерировании самоподписанного сертификата (без обращения к центру сертификации) методами класса
GostCertificateRequest
ему проставляются те же расширения, что и при генерировании запроса, а также
расширение basicConstraints - основные ограничения. Это расширение имеет значения
"Тип субъекта = ЦС", "Ограничение на длину пути = 5" и является критическим.
Необходимо помнить, что генерирование самоподписанных сертификатов имеет смысл только для тестовых целей. Для реальной работы следует пользоваться генерированием запросов для отправки их центрам сертификации.
Пример генерирования самоподписанного сертификата см. samples/samples_src.jar/userSamples/KeyPairGen.java (входит в комплект поставки программного обеспечения КриптоПро JCP).
Криптопровайдер КриптоПро JCP обеспечивает формирование и проверку ЭЦП в соответствии с алгоритмом ГОСТ Р 34.10-2001 для отдельного объекта XML-документа, для всего XML-документа, а также для двух независимых подписей всего XML-документа.
Криптопровайдер КриптоПро JCP позволяет осуществлять формирование и проверку электронной цифровой подписи XML-документа в соответствии с алгоритмом ГОСТ Р 34.10-2001. При этом криптопровайдер КриптоПро JCP использует четыре библиотеки, обеспечивающие работу с электронной цифровой подписью XML-документов:
Основные операции осуществляются при помощи функций следующих классов: org.apache.xml.security.algorithms, org.apache.xml.security.exceptions, org.apache.xml.security.keys, org.apache.xml.security.signature, org.apache.xml.security.transforms, org.apache.xml.security.utils. Поскольку криптопровайдер КриптоПро JCP не реализует методы перечисленных выше пакетов, а лишь обеспечивает их поддержку для алгоритма подписи ГОСТ Р 34.10-2001, то в данной документации подробное описание этих методов не приводится.
Перед началом использования классов из библиотеки XML Security необходимо зарегистрировать ГОСТ алгоритмы. Сделать это можно двумя способами:
Во-первых, вызовом метода ru.CryptoPro.JCPxml.XmlInit.init()
(старый метод
ru.CryptoPro.JCPxml.xmldsig.JCPXMLDSigInit.init()
тоже поддерживается).
Во-вторых, вызовом стандартного инициализатора org.apache.xml.security.Init.init()
,
который обязателен при работе с библиотекий XML Security, но с предварительно установленным свойством
System.setProperty("org.apache.xml.security.resource.config", "resource/jcp.xml")
или указывая это свойство при запуске Java-машины следующим образом:
java -Dorg.apache.xml.security.resource.config=resource/jcp.xml.
Таким образом, регистрация ГОСТ алгоритмов не требует перекомпиляции приложения.
Соответствующие константы определены в файле ru.CryptoPro.JCPxml.Consts
/** * имя Property настройки конфигурации. */ public static final String PROPERTY_NAME = "org.apache.xml.security.resource.config"; /** * Имя ресурса конфигурации. */ public static final String CONFIG = "resource/jcp.xml"; /** * алгоритм подписи (ГОСТ Р 34.10-2001) */ public static final String URI_GOST_SIGN = "http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"; /** * алгоритм хеширования, используемый при подписи (ГОСТ Р 34.11-94) */ public static final String URI_GOST_DIGEST = "http://www.w3.org/2001/04/xmldsig-more#gostr3411"; /** * алгоритм подписи (ГОСТ Р 34.10-2001) по новому стандарту */ public static final String URN_GOST_SIGN = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102001-gostr3411"; /** * алгоритм хеширования, ГОСТ Р 34.11-94 по новому стандарту */ public static final String URN_GOST_DIGEST = "urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr3411";
Для идентификации российских алгоритмов подписи и хеширования внутри XML
в КриптоПро CSP 2.0, 3.0, 3.6 и ранних версиях КриптоПро JCP использовались следущие пространства имен:
Для алгоритма хеширования использовалось пространство имен
http://www.w3.org/2001/04/xmldsig-more#gostr3411
,
а для алгоритма подписи
http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411
.
Эти пространства имен использовать не рекомендуется, хотя работоспособность полностью сохранена для совместимости.
С появлением нового
проекта стандарта
рекомендуется использовать для алгоритма подписи
urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102001-gostr3411
и для алгоритма хеширования
urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr3411
.
На основе методов перечисленных
выше пакетов криптопровайдер КриптоПро JCP позволяет осуществлять формирование подписи
как отдельного объекта XML-документа, так и всего содержимого XML-документа (соответственно,
проверку подписи как объекта, так и всего содержимого документа). Помимо этого
существует возможность формирования и проверки двух независимых подписей одного XML-документа.
Все перечисленные способы создания и проверки электронной цифровой подписи XML-документа для алгоритма ГОСТ Р 34.10-2001
подробно описываются в примерах, которые входят в комплект поставки программного обеспечения КриптоПро JCP
(samples/samples_src.jar/xmlSign/
).
Внимание! Библиотека JCPxml.jar должна находиться вместе с библиотекой xmlsec.jar, так чтобы ClassLoader при загрузке класса, реализующего алгоритм ГОСТ из библиотеки JCPxml.jar, имел доступ к базовому классу, который находится в библиотеке xmlsec.jar. Например, если серверное приложение, которое должно проверять подпись, запущено на сервере J2EE и включает в себя библиотеку xmlsec.jar, а JCPxml.jar установлена в lib/ext, появится конфликт, который сделает невозможной подпись/проверку. Системный ExtClassLoader, который осуществляет загрузку из lib/ext, не будет иметь доступ к базовому классу и не сможет загрузить классы из JCPxml.jar. В свою очередь ClassLoader приложения (WebappClassLoader) не будет иметь доступ к классам из JCPxml.jar. Для устранения конфликта можно переложить библиотеку JCPxml.jar в приложение к библиотеке xmlsec.jar.
Криптопровайдер КриптоПро JCP позволяет осуществить формирование и проверку ЭЦП в соответствии с алгоритмом подписи ГОСТ Р 34.10-2001 и алгоритмом хеширования ГОСТ Р 34.11-94 для сообщений, созданных на основе Cryptographic Message Syntax (CMS).
Примеры создания и подписи сообщений CMS, а также проверки подписи входят в комплект поставки программного обеспечения КриптоПро JCP
(samples/samples_src.jar/CMS_samples/
). В соответствии с Cryptographic Message Syntax подпись может быть 2х видов: подпись на данные
и подпись на подписываемые атрибуты подписи (если они существуют (см.
CMS)), что и реализовано в примерах.
При использовании скрипта samples/samples_src.jar/CMS_samples/CSignData.js
(или аналогичного на VBS из примеров к CSP)
для создания отделенной подписи следует помнить, что скрипт при чтении данных кодирует их в UTF-16LE кодировку.
Поэтому для проверки такой подписи из java (см. примеры) следует данные (content) закодировать в UTF-16LE.
Соответственно для проверки отделенной подписи сгенерированной в java с помощью скрипта необходимо,
чтобы подпись была на закодированные в UTF-16LE кодировку данные (см. samples/samples_src.jar/CMS_samples/CSignDataUse.java
).
При работе криптопровайдером КриптоПро JCP операции
могут осуществляться не только через стандартный интерфейс JCA, но также при помощи утилиты keytool.
При генерировании самоподписанного сертификата при помощи утилиты
keytool
никакие расширения сертификату не проставляются. Для генерирования сертификатов с расширениями следует
воспользоваться методами класса GostCertificateRequest
(см. выше).
Ниже приводятся примеры осуществления перечисленных операций при помощи данной утилиты.
В данном примере осуществляется просмотр содержимого ключевого носителя (жесткий диск) и проинициализированного именем этого носителя хранилища доверенных сертификатов.
Просмотр содержимого осуществляется при помощи команды -list
, которой в качестве параметров
передаются:
-provider ru.CryptoPro.JCP.JCP
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
Таким образом, просмотр содержимого носителя и соответствующего ему хранилища доверенных сертификатов осуществляется:
keytool -list -provider ru.CryptoPro.JCP.JCP -storetype HDImageStore -keystore c:\.keystore -v -storepass 123456
В данном примере осуществляется генерирование закрытого ключа ЭЦП и соответствующего ему самоподписанного сертификата в соответствии с алгоритмом ГОСТ Р 34.10-2001 и запись их на носитель.
Генерирование и запись ключа и сертификата осуществляется при помощи команды -genkey
,
которой в качестве параметров передаются:
-alias myKey
-keysize 512
-provider ru.CryptoPro.JCP.JCP
-keypass 11111111
-storetype HDImageStore
-dname CN=myKey,O=CryptoPro,C=RU
-keystore c:\.keystore
-storepass 123456
-keyalg GOST3410
-sigalg GOST3411withGOST3410EL
Таким образом, генерирование закрытого ключа и соответствующего ему самоподписанного сертификата и запись их на носитель осуществляется:
keytool -genkey -alias myKey -keysize 512 -provider ru.CryptoPro.JCP.JCP -keypass 11111111 -storetype HDImageStore -dname CN=myKey,O=CryptoPro,C=RU -keystore c:\.keystore -storepass 123456 -keyalg GOST3410 -sigalg GOST3411withGOST3410EL
В данном примере осуществляется генерирование запроса на сертификат открытого ключа в соответствии с хранящимся на носителе закрытым ключом и запись запроса в файл.
Генерирование и запись в файл запроса осуществляется при помощи команды -certreq
,
которой в качестве параметров передаются:
-alias myKey
-provider ru.CryptoPro.JCP.JCP
-keypass 11111111
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
-sigalg GOST3411withGOST3410EL
-file c:\request.bin
Таким образом, генерирование запроса на сертификат открытого ключа в соответствии с хранящимся на носителе закрытым ключом и запись запроса в файл осуществляется:
keytool -certreq -alias myKey -provider ru.CryptoPro.JCP.JCP -keypass 11111111 -storetype HDImageStore -keystore c:\.keystore -storepass 123456 -sigalg GOST3411withGOST3410EL -file c:\request.bin
В данном примере осуществляется генерирование самоподписанного сертификата открытого ключа в соответствии с хранящимся на носителе закрытым ключом и запись сертификата на носитель. Если на носителе уже существует сертификат открытого ключа, соответствующий данному закрытому ключу, то он будет перезаписан.
Генерирование и запись на носитель самоподписанного сертификата осуществляется при помощи
команды -selfcert
, которой в качестве параметров передаются:
-alias myKey
-provider ru.CryptoPro.JCP.JCP
-keypass 11111111
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
-sigalg GOST3411withGOST3410EL
-dname CN=myKey,O=CryptoPro,C=RU
keytool -selfcert -alias myKey -provider ru.CryptoPro.JCP.JCP -keypass 11111111 -storetype HDImageStore -keystore c:\.keystore -storepass 123456 -sigalg GOST3411withGOST3410EL -dname CN=myKey,O=CryptoPro,C=RU
В данном примере осуществляется чтение сертификата открытого ключа с носителя и запись сертификата в файл.
Чтение сертификата открытого ключа с носителя и запись его в файл осуществляется
при помощи команды -export
,
которой в качестве параметров передаются:
-alias myKey
-provider ru.CryptoPro.JCP.JCP
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
-file c:\myKeyCert.cer
Таким образом, чтение сертификата открытого ключа с носителя и запись сертификата в файл осуществляется:
keytool -export -alias myKey -provider ru.CryptoPro.JCP.JCP -storetype HDImageStore -keystore c:\.keystore -storepass 123456 -file c:\myKeyCert.cer
В данном примере осуществляется чтение сертификата открытого ключа из файла и запись его на носитель в соответствии с хранящимся на носителе закрытым ключом.
Чтение сертификата открытого ключа из файла и запись его на носитель осуществляется
при помощи команды -import
,
которой в качестве параметров передаются:
-alias myKey
-provider ru.CryptoPro.JCP.JCP
-keypass 11111111
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
-file c:\myKeyCert.cer
Таким образом, чтение сертификата открытого ключа из файла и запись его на носитель в соответствии с хранящимся на носителе закрытым ключом осуществляется:
keytool -import -alias myKey -provider ru.CryptoPro.JCP.JCP -keypass 11111111 -storetype HDImageStore -keystore c:\.keystore -storepass 123456 -file c:\myKeyCert.cer
В данном примере осуществляется чтение доверенного сертификата из хранилища и запись сертификата в файл.
Чтение доверенного сертификата из хранилища и запись его в файл осуществляется
при помощи команды -export
,
которой в качестве параметров передаются:
-alias myCert
-provider ru.CryptoPro.JCP.JCP
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
-file c:\myCert.cer
keytool -export -alias myCert -provider ru.CryptoPro.JCP.JCP -storetype HDImageStore -keystore c:\.keystore -storepass 123456 -file c:\myCert.cer
В данном примере осуществляется чтение доверенного сертификата из файла и запись его в хранилище.
Чтение доверенного сертификата и запись его в хранилище осуществляется
при помощи команды -import
,
которой в качестве параметров передаются:
-alias myCert
-provider ru.CryptoPro.JCP.JCP
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
-file c:\myCert.cer
keytool -import -alias myCert -provider ru.CryptoPro.JCP.JCP -storetype HDImageStore -keystore c:\.keystore -storepass 123456 -file c:\myCert.cer
В данном примере осуществляется удаление закрытого ключа и соответствующего ему самоподписанного сертификата с носителя.
Удаление ключа и соответствующего ему самоподписанного сертификата с носителя осуществляется
при помощи команды -delete
,
которой в качестве параметров передаются:
-alias myKey
-provider ru.CryptoPro.JCP.JCP
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
Таким образом, удаление закрытого ключа и соответствующего ему самоподписанного сертификата с носителя осуществляется:
keytool -delete -alias myKey -provider ru.CryptoPro.JCP.JCP -storetype HDImageStore -keystore c:\.keystore -v -storepass 123456
При удалении ключа с носителя, требующего пароля доступа к ключу на носителе при удалении (например, смарт-карте Оскар) в качестве имени необходимо передать строку состоящую из имени, 4 символов двоеточия, и пароля доступа к ключу. Таким образом, удаление закрытого ключа и соответствующего ему самоподписанного сертификата со смарт-карты Оскар осуществляется:
keytool -delete -alias myKey::::12345678 -provider ru.CryptoPro.JCP.JCP -storetype OCFStore -keystore c:\.keystore -v -storepass 123456
В данном примере осуществляется удаление доверенного сертификата из хранилища.
Удаление доверенного сертификата из хранилища осуществляется
при помощи команды -delete
,
которой в качестве параметров передаются:
-alias myCert
-provider ru.CryptoPro.JCP.JCP
-storetype HDImageStore
-keystore c:\.keystore
-storepass 123456
Таким образом, удаление доверенного сертификата из хранилища осуществляется:
keytool -delete -alias myCert -provider ru.CryptoPro.JCP.JCP -storetype HDImageStore -keystore c:\.keystore -v -storepass 123456
Также можно воспользоваться готовыми классами пакета ComLine из модуля Samples, входящего в состав КриптоПро JCP. Запустите ComLine с вызовом нужного класса либо сам класс, используя следующие параметры командной строки:
java ComLine NameofClass args
или java NameofClass args
например:
java ComLine KeyPairGen -alias name_of_key -dname CN=autor,OU=Security,O=CryptoPro,C=RU -reqCertpath C:/req.txt
или
java KeyPairGen -alias name_of_key -dname CN=autor,OU=Security,O=CryptoPro,C=RU -reqCertpath C:/req.txt
Проверку установки и основных настроек провайдера можно осуществить запуском:
CheckConf (без параметров)
Запуском:
CheckConfFull [-servDir C:/*.*]
можно проверить работоспособность провайдеров.
Выполняются тесты на генерирование ключей, генерирование и проверку подписи, а также тесты на создание ssl-соединения (если установлен КриптоПро JTLS). (Запуск возможен при условии, что КриптоПро JCP был установлен успешно).
Генерирование ключевой пары осуществляется в соответствии с алгоритмами обмена Диффи-Хелмана и подписи ГОСТ Р 34.10-2001.
KeyPairGen -alias name_of_key [-alg GOST3410] [-storetype HDImageStore] [-storepath null] [-storepass null] [-keypass password] [-isServer true] -dname CN=autor,OU=Security,O=CryptoPro,C=RU -reqCertpath C:/*.* -encoding der
Полученные таким образом ключи можно использовать как для генерирования ЭЦП, так и для обмена.
getCert -alias name_of_key [-storetype HDImageStore] [-storepath null] [-storepass null] -http http://www.cryptopro.ru/certsrv/ -certpath C:/*.cer -reqCertpath C:/*.*
Certs -alias name_of_key [-storetype HDImageStore] [-storepath null] [-storepass null] [-keypass password] -certs C:/my.cer,C:/*.cer,...,C:/root.cer
Формирование электронной цифровой подписи осуществляется в соответствии с алгоритмом ГОСТ Р 34.10-2001.
Signature -alias name_of_key [-storetype HDImageStore] [-storepath null] [-storepass null] [-keypass password] -signpath C:/*.* -filepath C:/*.*
Проверка электронной цифровой подписи осуществляется в соответствии с алгоритмами ГОСТ Р 34.10-94 и ГОСТ Р 34.10-2001.
SignatureVerif -alias name_of_key [-storetype HDImageStore] [-storepath null] [-storepass null] -signpath C:/*.* -filepath C:/*.*
Server [-port port] [-auth true] [-keyStoreType HDImageStore] [-trustStoreType HDImageStore] -trustStorePath C:/*.* -trustStorePassword trust_pass -keyStorePassword key_pass
При запросе ресурса shutdown
сервер останавливается, предварительно послав клиенту ответ,
который содержит сообщение об остановке сервера по окончании сессии.
Client [-port port] [-server serverName] [-keyStoreType HDImageStore] [-trustStoreType HDImageStore] -trustStorePath C:/*.* -trustStorePassword trust_pass -keyStorePassword key_pass [-fileget gettingFileName] [-fileout outputFilePath]