Настройка КриптоПро: ЭЦП browser plug in, CSP, браузера, электронной подписи, TLS

Настройка КриптоПро: ЭЦП browser plug in, CSP, браузера, электронной подписи, TLS ЭЦП

Инструменты подписи

Использовалась версия Java: «1.8.0_111» HotSpot(TM) 64-Bit Server VM (build 25.111-b14).

В качестве сертифицированного средства защиты информации от лицензированного разработчика применяем криптографический провайдер КриптоПро CSP v4.0 и КриптоПро JCP – v.2.0, с установкой модуля КриптоПро Java CSP v.4.0

Почему КриптоПро JCP – v.2.0, с модулем КриптоПро Java CSP v.4.0?

Потому, что провайдер КриптоПро JCP после длительного несертифицированного периода получил сертификат соответствия от регулятора до 31.12.2022, а дальше, по информации от разработчика, с сертификацией может вновь возникнуть неопределенность. Модуль КриптоПро Java CSP v.4.

0 не выполняет в себе криптографических преобразований и является по сути API к провайдеру КриптоПро CSP, с очередной сертификацией которого вопросов нет. Здесь нужно сказать, что действующий сертификат на СКЗИ не обязателен при условии использования криптографического провайдера исключительно для внутренних целей.

В соответствии со спецификацией Java Cryptography Architecture (JCA), в своем приложении я указываю и использую функции криптографического провайдера: JCSP. После установки КриптоПро, данный провайдер отображается в списке всех доступных в файле ../java/jdk1.8.x_xxx/jre/lib/security/java.security, где можно настраивать, который из них предпочтительнее для использования по умолчанию, если явно не указано в приложении: ## List of providers and their preference orders (see above):#security.provider.1=ru.CryptoPro.JCSP.JCSP

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

Для этого в ../java/jdk1.8.x_xxx/jre/lib/security необходимо заменить файлы local_policy.jar и US_export_policy.jar на предоставленные по адресу: Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8 Downloadи содержащие в default_local.policy и default_US_export.policy:

Криптопро jcp: инструкция

Благодаря данному модулю обеспечивается криптографическая защита в соответствии с Java Cryptography Architecture. Дистрибутив интегрируется с архитектурой Java, соответственно со спецификацией JCE обеспечивается корректная работа с созданием и шифрованием ключей. Модуль позволяет создавать надежно защищенные приложения, применяя Apache XML Security.

В руководстве по настройке рекомендуется настраивать КриптоПро JCP через панель управления. Для этого в командной строке необходимо набрать следующее сочетание «cd C:jcp.1.0.53lib», а затем выполнить команду «controlpane.bat “С:Program FilesJavajre7”». Более детальная инструкция по настройке КриптоПро JCP можно найти на официальном сайте разработчика.

Полезные ссылки

  1. Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 8
  2. Сайт компании КриптоПро
  3. Разработчик iTextpdf
  4. Bouncy Castle «Porting from earlier BC releases to 1.47 and later»
  5. Исходный код библиотеки iTextpdf_5.1.3_patched_cryptopro_bc1.50 в GitHub

Преимущества pdf

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

Инструменты Adobe Systems (разработчика формата PDF) поддерживают использование электронной подписи. В отличие от сообщений с присоединенной усиленной электронной подписью стандарта PKCS#7 и его усовершенствования CAdES, для просмотра документа PDF с подписью не требуется дополнительное специальное ПО. Кроме криптографического провайдера, который требуется во всех случаях.

Т.е. инструменты Adobe позволяют визуализировать электронную подпись в документе.

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

При просмотре таких документов, кроме визуализации в теле документа, программы Acrobat и Adobe Reader отображают вкладку «Подписи» с сопутствующей информацией: значок, показывающий статус проверки подписи, сведения о том, был ли изменен документ, результат проверки сертификата открытого ключа, время последней проверки, страницу и поле, содержащие электронную подпись.

Применение электронной подписи pdf-документов

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

С интенсивным процессом обмена большим количеством электронных документов, оператор не справится. В этом случае информационная система выполняет автоматизацию, и необходимость в красивой визуализации отпадает. Соответственно излишне тратить ресурсы информационной системы и электронных каналов, на передачу объемных PDF файлов и обработку изображений штампа.

Практичнее формировать электронную подпись в виде отдельного файла или оборачивать файл и подпись в один контейнер стандарта PKCS#7 (CAdES). Этот стандарт с присоединенной или отсоединенной подписью отлично подойдет для большого документооборота между информационными системами.

Тот же документ PDF можно подписать по стандарту CAdES отсоединенной подписью, в итоге будет два файла — сам PDF и контейнер с подписью.

Вспомним проблемы, с которыми пришлось столкнуться при подписании на Java. Вывод — формат PDF сейчас плохо поддерживается КриптоПро в части прикладного программного интерфейса для Java. Существующую библиотеку itextpdf пришлось править самостоятельно.

В сфере деятельности, связанной с разработкой коммерческого программного обеспечения со встроенными СКЗИ, такой подход недопустим, патчи, обновления и доработки должны выполняться организацией-лицензиатом. Поэтому приведенный в посте способ подписи PDF-документа можно применять с целью демонстрации возможностей и для внутреннего использования.

Пример кода, несколько подписей pdf

Пример использования КриптоПро и iTextpdf_5.1.3_patched_cryptopro_bc1.50 выглядит следующим образом:

 /**
     * 
     * @param aliases
     *            - имена контейнеров с ключами ЭП
     * @param data
     *            - массив байтов с документом PDF
     * @param pdfVersion
     *            - номер версии формата PDF
     * @return
     * @throws SignatureProcessorException
     */
    public static byte[] samplePDFSignature(String[] aliases, byte[] data, char pdfVersion) throws SignatureProcessorException {
        ByteArrayOutputStream bais = new ByteArrayOutputStream();
        HashMap<X509Certificate, PrivateKey> currSignAttrMap = new HashMap<X509Certificate, PrivateKey>();
        for (String alias : aliases) {
            X509Certificate certificate = (X509Certificate) signAttributesMap1.get(alias)[0];
            PrivateKey privateKey = (PrivateKey) signAttributesMap1.get(alias)[1];
            
            currSignAttrMap.put(certificate, privateKey);
            if (certificate == null) {
                throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   CERTIFICATE_NOT_FOUND_BY_ALIAS);
            }
            
            if (privateKey == null) {
                throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   PRIVATE_KEY_NOT_FOUND_BY_ALIAS);
            }
        }
        try {
            FileInputStream fis = new FileInputStream(new File(FILE_PATH));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int n = 0;
            while ((n = fis.read(buf, 0, buf.length)) != -1) {
                baos.write(buf, 0, n);
            }
            fis.close();
            byte[] im = baos.toByteArray();
            
            X509Certificate innerCA = obtainCertFromTrustStoreJKS(false, INNER_CA);
            PdfStamper stp = null;
            PdfReader reader = null;
            int pageNumber = 1;
            for (Entry<X509Certificate, PrivateKey> entry : currSignAttrMap.entrySet()) {
                if (bais.toByteArray().length == 0) {
                    reader = new PdfReader(data);
                } else {
                    reader = new PdfReader(bais.toByteArray());
                    bais = new ByteArrayOutputStream();
                }
                stp = PdfStamper.createSignature(reader, bais, pdfVersion); //''
                Certificate[] certPath = new Certificate[] {entry.getKey(), innerCA};
                PdfSignatureAppearance sap = stp.getSignatureAppearance();
                sap.setProvider("JCSP"); //JCP
                sap.setCrypto(entry.getValue(), certPath, null,
                        PdfSignatureAppearance.CRYPTOPRO_SIGNED);
                Image image = Image.getInstance(im);
                sap.setImage(image);
                sap.setVisibleSignature(new Rectangle(150, 150), pageNumber, null);
                pageNumber  ;
                stp.close();
                bais.close();
                reader.close();
            }
        } catch (RuntimeException e) {
            throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   ExceptionUtils.getFullStackTrace(e));
        } catch (IOException e) {
            throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   ExceptionUtils.getFullStackTrace(e));
        } catch (DocumentException e) {
            throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   ExceptionUtils.getFullStackTrace(e));
        } catch (CertificateEncodingException e) {
            throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   ExceptionUtils.getFullStackTrace(e));
        } catch (Exception e) {
            throw new SignatureProcessorException(PDF_SIGNATURE_ERROR   ExceptionUtils.getFullStackTrace(e));
        }
        return bais.toByteArray();
    }

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

Создается объект с корневым сертификатом для формирования пути сертификации, который требуется методу setCrypto класса com.itextpdf.text.pdf.PdfSignatureAppearance.

В цикле выполняется подпись страниц PDF-документа ключами, имена которых были переданы в метод.

Для демонстрации выполнены две подписи — валидная и невалидная с недействительным сертификатом.

Вкладка «Подписи» выглядит следующим образом:

Проблемы и трудности сборки


После успешного обновления исходного кода itextpdf в нем появляются зависимости на пакеты ru.CryptoPro.JCP и ru.CryptoPro.reprov.x509.

Без них проект с исходным кодом itextpdf_5.1.3.gost не соберется.

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project itextpdf: Compilation failure: Compilation failure:[ERROR] githubiTextpdf_5.1.3_patched_cryptopro_bc1.50srcmainjavacomitextpdftextpdfPdfPKCS7.java:

[138,23] error: package ru.CryptoPro.JCP does not exist[ERROR] githubiTextpdf_5.1.3_patched_cryptopro_bc1.50srcmainjavacomitextpdftextpdfPdfPKCS7.java:[139,31] error: package ru.CryptoPro.reprov.x509 does not exist Нужно взять из поставки КриптоПро 2.0 файлы JCP.

Итак, библиотека собрана, подключаем ее в приложение. И тут возникает основная проблема. iTextpdf_5.1.3 содержит зависимость на Bouncy Castle версии 1.46 – библиотеку с открытым кодом, реализующую криптографический провайдер и поддержку ASN.1 структур.

Поставка КриптоПро JCP 2.0 в свою очередь имеет зависимости на Bouncy Castle версии 1.50 bcpkix-jdk15on-1.50 и bcprov-jdk15on-1.5, соответственно, они помещаются в jre/lib/ext при установке КриптоПро.

В итоге при запуске своего приложения и метода подписания PDF мы получаем ошибку:

Exception in thread «main» java.lang.NoClassDefFoundError: org/bouncycastle/asn1/DEREncodableat com.itextpdf.text.pdf.PdfSigGenericPKCS.setSignInfo(PdfSigGenericPKCS.java:97)at com.itextpdf.text.pdf.PdfSignatureAppearance.preClose(PdfSignatureAppearance.java:1003)at com.itextpdf.text.pdf.

PdfSignatureAppearance.preClose(PdfSignatureAppearance.java:904)at com.itextpdf.text.pdf.PdfStamper.close(PdfStamper.java:194)at ru.alfabank.ccjava.trustcore.logic.SignatureProcessor.pdfSignature(SignatureProcessor.java:965)at ru.alfabank.ccjava.trustcore.logic.

SignatureProcessor.main(SignatureProcessor.java:1363)Caused by: java.lang.ClassNotFoundException: org.bouncycastle.asn1.DEREncodableat java.net.URLClassLoader.findClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)at sun.misc.

Launcher$AppClassLoader.loadClass(Unknown Source)at java.lang.ClassLoader.loadClass(Unknown Source)… 6 moreКаждый разработчик Java сталкивался с таким исключением в потоке main и знает, что можно потратить много времени на разбор проблемы, и как все исправить.

Что получается – библиотека iTextpdf_5.1.3 имеет зависимость от более старого провайдера Bouncy Castle, а для новых версий iTextpdf нет патча от КриптоПро.

Конкретно в поставке КриптоПро JCP 2.0 зависимости на новую версию Bouncy Castle имеет библиотека CAdES.jar. Если удалить из JRE эту библиотеку или вовсе отказаться от поддержки формирования CAdES подписей при установке КриптоПро JCP 2.0, то проблема будет решена.

Но что если поддержка CAdES должна остаться?

Чтобы избавиться от конфликта библиотек, необходимо предпринять следующие шаги:


Как итог, проект iTextpdf_5.1.3_patched_cryptopro_bc1.50 начинает собираться. Конфликт разрешен, КриптоПро и itextpdf ссылаются на одну версию org.bouncycastle 1.50.

Исходный код iTextpdf_5.1.3_patched_cryptopro_bc1.50 выложен в GitHub: iTextpdf_5.1.3_patched_cryptopro_bc1.50

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