В данной статье описывается процесс создания ЭЦП c помощью библиотеки CryptoPro.
Для создания подписи необходим приватный ключ и сертификат. Рассмотрим гипотетическую ситуацию, когда приватный ключ прислан по почте, а не сгенерирован самостоятельно - порочная, но широко распространенная практика.
Предположим, что файл клиентского сертификата называется client.cer, сертификата УЦ - CA.cer, а приватный ключ находится в директории 999996.000
При установке CSP пакет cprocsp-rdr-gui скорее всего не установится, поскольку использует древние версии Motif, но он не нужен для работы, так что установка данного пакета не является обязательной.
Также нужно убедиться, что в системе есть libcurl.so (эта библиотека используется для получения CRL)
После установки необходимо проверить, что в файле /etc/opt/cprocsp/config.ini прописан правильный путь к libcurl.so, по умолчанию он ведет на /usr/local/lib/64/libcurl.so что является ошибкой.
После этого устанавливаем CryptoPro JCP (jcp_plus_jtls_1.0.53.jar)
После успешной установки нужно запустить ControlPane.sh из-под рута и поменять путь к хранилищу ключей на /var/opt/cprocsp/keys/{$user.name}
Для создания подписи необходим приватный ключ и сертификат. Рассмотрим гипотетическую ситуацию, когда приватный ключ прислан по почте, а не сгенерирован самостоятельно - порочная, но широко распространенная практика.
Предположим, что файл клиентского сертификата называется client.cer, сертификата УЦ - CA.cer, а приватный ключ находится в директории 999996.000
1. Установка CryptoPro CSP и CryptoPro JCP
Добываем каким-либо образом дистрибутивы, самый простой способ - скачать их с сайта CryptoPro, предварительно там зарегистрировавшись. Триальная версия полнофункциональна и работает 30 дней.При установке CSP пакет cprocsp-rdr-gui скорее всего не установится, поскольку использует древние версии Motif, но он не нужен для работы, так что установка данного пакета не является обязательной.
Также нужно убедиться, что в системе есть libcurl.so (эта библиотека используется для получения CRL)
После установки необходимо проверить, что в файле /etc/opt/cprocsp/config.ini прописан правильный путь к libcurl.so, по умолчанию он ведет на /usr/local/lib/64/libcurl.so что является ошибкой.
После этого устанавливаем CryptoPro JCP (jcp_plus_jtls_1.0.53.jar)
После успешной установки нужно запустить ControlPane.sh из-под рута и поменять путь к хранилищу ключей на /var/opt/cprocsp/keys/{$user.name}
2. Установка приватного ключа и сертификатов
CryptoPro имеет свой собственный формат приватного ключа и свои собственные контейнеры для хранения ключей и сертификатов. Чтобы установить ключ и сертификаты в контейнеры, нужно проделать следующие действия:- Скопировать в корень дискеты или флэшки сертификат и приватный ключ. Под приватным ключом понимается директория 999996.000 и ее содержимое, файлы header.key, masks2.key, masks.key, name.key, primary2.key, primary.key
$ cp -R /path/to/key/999996.000 /media/flashdrive/ $ cp /path/to/cert/client.cer /media/flashdrive/
- Выполнить команду по копированию ключа с флэшки на диск. Ключ попадет в пользовательское хранилище 'My'. Выполнять команду нужно под пользователем, который будет использовать данный контейнер для подписи. 999996 - название (alias) контейнера. gate@example.com - то, что прописано в поле E сертификата ( можно посмотреть командой keytool --printcert -file /path/to/cert/client.cer )
$ csptest -keycopy -src '\\.\FLASH\gate@example.com' -dest '\\.\HDIMAGE\999996'
Проверить, что все скопировалось, можно командой$ ls -al /var/opt/cprocsp/keys/<username>
-
Альтернативный путь, если нет дискеты или csptest выдает ошибку
Error number 0x8009000f (2148073487). Object already exists.
Руками скопировать приватный ключ в хранилище командой$ cp -R /path/to/key/999996.000 /var/opt/cprocsp/keys/<username>/
- Ассоциировать сертификат с контейнером. Сертификат попадет в пользовательское хранилище 'My'
$ certmgr -inst -file /path/to/file/client.cer -cont '\\.\HDIMAGE\999996'
- Установить сертификат УЦ из-под пользователя root командой
# certmgr -inst -store root -file /path/to/file/CA.cer
$ certmgr --list Certmgr 0.9 prerelease (c) "CryptoPro", 2007-2010. program for managing certificate(CRL) and stores ============================================================================= 1------- Issuer : DC=ru, DC=issuer, CN=EXAMPLE Subject : C=RU, S=RUSSIA, L=MOSCOW, O=ORGANIZATION, OU=IT, CN=GATE_DEMO, E=gate@example.com Serial : 0x2225000000007DF78065 PrivateKey Link: Yes. Container: HDIMAGE\\999996.000\D7BB ============================================================================= [ErrorCode: 0x00000000]Обратите внимание на строку "PrivateKey Link: Yes. Container: HDIMAGE\\999996.000\D7BB". Она показывает наличие связи сертификата и приватного ключа, если выводится "PrivateKey Link: No" это означает, что связь не установлена и использовать такой контейнер для подписи не удастся.
$ certmgr --list -store root Certmgr 0.9 prerelease (c) "CryptoPro", 2007-2010. program for managing certificate(CRL) and stores ============================================================================= 1------- Issuer : DC=ru, DC=issuer, CN=EXAMPLE Subject : DC=ru, DC=issuer, CN=EXAMPLE Serial : 0xE44263EF7B42044F9E20FFF14C6F1327 PrivateKey Link: No ============================================================================= [ErrorCode: 0x00000000]
3. Генерация ЭЦП
Под ЭЦП обычно понимается отсоединенная (detached) подпись в формате pkcs#7. Т.е помимо самой подписи, в сообщение внедряется вся цепочка сертификатов. CryptoPro не предоставляет отдельного пакета CMS для легкой генерации криптографических сообщений, но в принципе в пакете JCP есть все необходимое, чтобы сформировать корректное сообщение самостоятельно. Код по большей части взят из примеров, которые идут в JCP.private static byte[] signWithCryptoProJcp(byte[] data) throws Exception{ String alias = "999996"; String caFile = "/path/to/CA.cer"; String certFile = "/path/to/client.cer"; //load keys for sign final PrivateKey[] keys = new PrivateKey[1]; keys[0] = CMStools.loadKey(alias, null); //load certificates chain final Certificate[] certs = new Certificate[2]; // функция CMStools.loadCertificate() почему-то не работает, хотя сертификат есть в хранилище // пришлось читать сертификат из файла certs[0] = CMStools.readCertificate(certFile); certs[1] = CMStools.readCertificate(caFile); return createCMS(data, keys, certs, true); } private static byte[] createCMS(byte[] data, PrivateKey[] keys, Certificate[] certs, boolean detached) throws Exception { //create CMS // Array.writeFile("/home/grigory/test.msg", data); final ContentInfo all = new ContentInfo(); all.contentType = new Asn1ObjectIdentifier(new OID(CMStools.STR_CMS_OID_SIGNED).value); final SignedData cms = new SignedData(); all.content = cms; cms.version = new CMSVersion(1); // digest cms.digestAlgorithms = new DigestAlgorithmIdentifiers(1); final DigestAlgorithmIdentifier a = new DigestAlgorithmIdentifier( new OID(CMStools.DIGEST_OID).value); a.parameters = new Asn1Null(); cms.digestAlgorithms.elements[0] = a; if (detached) cms.encapContentInfo = new EncapsulatedContentInfo( new Asn1ObjectIdentifier( new OID(CMStools.STR_CMS_OID_DATA).value), null); else cms.encapContentInfo = new EncapsulatedContentInfo(new Asn1ObjectIdentifier( new OID(CMStools.STR_CMS_OID_DATA).value), new Asn1OctetString(data)); // certificates final int ncerts = certs.length; cms.certificates = new CertificateSet(ncerts); cms.certificates.elements = new CertificateChoices[ncerts]; for (int i = 0; i < cms.certificates.elements.length; i++) { final ru.CryptoPro.JCP.ASN.PKIX1Explicit88.Certificate certificate = new ru.CryptoPro.JCP.ASN.PKIX1Explicit88.Certificate(); final Asn1BerDecodeBuffer decodeBuffer = new Asn1BerDecodeBuffer(certs[i].getEncoded()); certificate.decode(decodeBuffer); cms.certificates.elements[i] = new CertificateChoices(); cms.certificates.elements[i].set_certificate(certificate); } // Signature.getInstance final Signature signature = Signature.getInstance(JCP.GOST_EL_SIGN_NAME); byte[] sign; // signer infos final int nsign = keys.length; cms.signerInfos = new SignerInfos(nsign); for (int i = 0; i < cms.signerInfos.elements.length; i++) { signature.initSign(keys[i]); signature.update(data); sign = signature.sign(); cms.signerInfos.elements[i] = new ru.CryptoPro.JCP.ASN.CryptographicMessageSyntax.SignerInfo(); cms.signerInfos.elements[i].version = new CMSVersion(1); cms.signerInfos.elements[i].sid = new SignerIdentifier(); final byte[] encodedName = ((X509Certificate) certs[i]).getIssuerX500Principal().getEncoded(); final Asn1BerDecodeBuffer nameBuf = new Asn1BerDecodeBuffer(encodedName); final Name name = new Name(); name.decode(nameBuf); final CertificateSerialNumber num = new CertificateSerialNumber( ((X509Certificate) certs[i]).getSerialNumber()); cms.signerInfos.elements[i].sid.set_issuerAndSerialNumber( new IssuerAndSerialNumber(name, num)); cms.signerInfos.elements[i].digestAlgorithm = new DigestAlgorithmIdentifier(new OID(CMStools.DIGEST_OID).value); cms.signerInfos.elements[i].digestAlgorithm.parameters = new Asn1Null(); cms.signerInfos.elements[i].signatureAlgorithm = new SignatureAlgorithmIdentifier(new OID(CMStools.SIGN_OID).value); cms.signerInfos.elements[i].signatureAlgorithm.parameters = new Asn1Null(); cms.signerInfos.elements[i].signature = new SignatureValue(sign); } // encode final Asn1BerEncodeBuffer asnBuf = new Asn1BerEncodeBuffer(); all.encode(asnBuf, true); // Array.writeFile("/home/grigory/test.signature", asnBuf.getMsgCopy()); return asnBuf.getMsgCopy(); }У CryptoPro есть замечательный сервис проверки ЭЦП, где можно проверить, что сгенерированная подпись верна.
Комментариев нет:
Отправить комментарий