JavaでCryptoProを使用してPDFドキュメントに署名する方法





こんにちは 私はアルファバンクの従業員であり、暗号化情報保護ツールが組み込まれたソフトウェアの開発に従事しています。



この記事では、次のことについてお話したいと思います。





現代の情報の世界では、電子署名とそれが提供する基本サービス(データの整合性、著者の異議のないこと)の重要性を過大評価することは困難です。 国家機関、個人、および法人は、相互間の情報交換フローで電子署名を広く使用しています。



法律によれば、電子署名は、自分の手で署名され、許可された人の印で封印された紙媒体と同等の重要性を持つ文書に法的効力を与えることができます。



PDFの利点



人に優しいプレゼンテーションにさまざまな情報を保存するためのコンテナとして、PDF形式は当然の人気を得ています。 どのOSでも開くことができます。 PDFドキュメントには、テキストと表形式のデータだけでなく、オーディオとビデオの記録、エンジニアリンググラフィックス、3次元モデルも含めることができます。 PDFドキュメントの追加処理のために、この形式には、イベントの発生時またはユーザーアクションの発生時にフォームおよびフィールドのコンテンツを変更するJavaScriptサポートなどの強力なツールが含まれています。



Adobe Systemsツール(PDF開発者)は、電子署名の使用をサポートしています。 PKCS#7標準の拡張電子署名およびその拡張CAdESが添付されたメッセージとは異なり、署名付きPDFドキュメントを表示するために追加の特別なソフトウェアは必要ありません。 すべての場合に必要な暗号化プロバイダーに加えて。



すなわち Adobeツールを使用すると、文書内の電子署名を視覚化できます。



PDFドキュメントに署名するとき、視覚化パラメーターを設定し、ページ番号、署名付きブロックのサイズ、それを配置するフィールドの名前を指定し、会社のロゴなどのグラフィックイメージを追加し、ディレクターの手書き署名のスキャンと組織のシールを追加できます。



このようなドキュメントを表示する場合、AcrobatおよびAdobe Readerは、ドキュメント本文の視覚化に加えて、関連情報を含む署名タブを表示します。署名の検証のステータスを示すアイコン、ドキュメントが変更されたかどうか、公開鍵証明書の検証結果、最後の検証の時刻、ページおよび電子署名を含むフィールド。



署名ツール



Javaバージョンが使用されました: "1.8.0_111" HotSpot(TM)64-Bit Server VM(build 25.111-b14)。



ライセンスを取得した開発者から情報を保護するための認定された手段として、暗号化プロバイダーCryptoPro CSP v4.0およびCryptoPro JCP-v.2.0を使用し、CryptoPro Java CSP v.4.0モジュールをインストールします。



CryptoPro JCP-v.2.0にCryptoPro Java CSP v.4.0モジュールが含まれているのはなぜですか?



プロバイダーのCryptoPro JCPは、長い間非認証期間を経て、2018年12月31日まで規制当局からコンプライアンスの証明書を受け取ったため、開発者からの情報によると、認証に再び不確実性が生じる可能性があります。 CryptoPro Java CSP v.4.0モジュールは、それ自体では暗号変換を実行せず、本質的にCryptoPro CSPプロバイダーに対するAPIであり、次の認定は疑問の余地がありません。 ここで、暗号プロバイダーが内部目的専用に使用されている場合、暗号情報保護証明書の有効な証明書は必要ないと言わなければなりません。



Java Cryptography Architecture(JCA)仕様によると、私のアプリケーションでは、暗号化プロバイダーの機能であるJCSPを指定して使用しています。 CryptoProのインストール後、このプロバイダーは../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ダウンロード

default_local.policyおよびdefault_US_export.policyに含まれる:



grant {

// There is no restriction to any algorithms.

permission javax.crypto.CryptoAllPermission;

};







JavaでPDFドキュメントを操作するためのiText itextpdf.comライブラリの使用は、プロバイダーCryptoProによって決定されました。



CryptoPro JCPには、iTextpdfライブラリバージョン5.1.3用のGitパッチファイルが付属しています-

jcp-2.0.xxxxx \ Doc \ itextpdf \ itextpdf_5.1.3.gost.user.patch







このパッチは、プロバイダーCryptoProと連携するようにitextpdfを適合させます。 ライブラリバージョン5.1.3のソースコードをダウンロードし、Gitバージョン管理システムのBashコマンドラインコマンドを使用してパッチを適用する必要があります。git apply --stat itextpdf_5.1.3.gost.user.patch







次に、更新されたソースコードから結果のライブラリを収集し、アプリケーションに接続する必要があります。



CryptoPro JCPのsamples-sources.jarファイルには多くの例が含まれています。 特に、PDFドキュメントの電子ドキュメントの署名と検証の例があります。 (\ PDF \ SignVerifyPDFExample.java)。



アセンブリの問題



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] \github\iTextpdf_5.1.3_patched_cryptopro_bc1.50\src\main\java\com\itextpdf\text\pdf\PdfPKCS7.java:[138,23] error: package ru.CryptoPro.JCP does not exist

[ERROR] \github\iTextpdf_5.1.3_patched_cryptopro_bc1.50\src\main\java\com\itextpdf\text\pdf\PdfPKCS7.java:[139,31] error: package ru.CryptoPro.reprov.x509 does not exist







CryptoPro 2.0からJCP.jarおよびJCPRevTools.jarファイルを取得し、Mavenが使用するJREディレクトリに配置する必要があります。Java\ jdk1.8.0_111 \ jre \ lib \ ext。 もちろん、それらはclassPathアプリケーションにあるべきです。



そのため、ライブラリはアセンブルされ、アプリケーションに接続されます。 そして、ここで主な問題が発生します。 iTextpdf_5.1.3には、Bouncy Castleバージョン1.46(暗号化プロバイダーを実装し、ASN.1構造をサポートするオープンソースライブラリ)への依存関係が含まれています。



  <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bctsp-jdk15</artifactId> <version>1.46</version> <type>jar</type> <scope>compile</scope> <optional>true</optional> </dependency>
      
      





CryptoPro JCP 2.0の供給は、Bouncy Castleバージョン1.50 bcpkix-jdk15on-1.50およびbcprov-jdk15on-1.5にそれぞれ依存し、CryptoProのインストール時にjre / lib / extに配置されます。



その結果、アプリケーションとPDF署名メソッドを起動すると、エラーが発生します。



Exception in thread "main" java.lang.NoClassDefFoundError: org/bouncycastle/asn1/DEREncodable

at 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.DEREncodable

at 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開発者はメインスレッドでこのような例外に遭遇し、問題の分析と修正方法に多くの時間を費やすことができることを知っています。 NoClassDefFoundError例外の本質は次のとおりです-実行時にJava仮想マシンがコンパイル段階で利用可能な特定のクラスを見つけることができない場合にスローされます。



判明したこと-iTextpdf_5.1.3ライブラリは古いプロバイダーのBouncy Castleに依存しており、iTextpdfの新しいバージョンにはCryptoProからのパッチはありません。



具体的には、CryptoPro JCP 2.0には、Bouncy Castleの新しいバージョンに応じてCAdES.jarライブラリが付属しています。 このライブラリをJREから削除するか、CryptoPro JCP 2.0のインストール時にCAdES署名の形成のサポートを完全に拒否すると、問題は解決します。



しかし、CAdESサポートが維持されるとしたらどうでしょうか。



ライブラリの競合を取り除くには、次の手順を実行する必要があります。





その結果、プロジェクトiTextpdf_5.1.3_patched_cryptopro_bc1.50がアセンブルを開始します。 競合は解決され、CryptoProとitextpdfはorg.bouncycastle 1.50の同じバージョンを参照します。



iTextpdf_5.1.3_patched_cryptopro_bc1.50のソースコードはGitHubで入手できます: iTextpdf_5.1.3_patched_cryptopro_bc1.50



サンプルコード、複数のPDF署名



CryptoProと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); //'\0' 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形式のバージョン番号を含むコンテナー名のリストを受け入れます。 指定された名前に従って、キーと証明書がキ​​ャッシュから取得されます。 画像は、署名の視覚化のためにロードされます。



ルート証明書を持つオブジェクトが作成され、証明書パスが形成されます。これは、クラスcom.itextpdf.text.pdf.PdfSignatureAppearanceのsetCryptoメソッドに必要です。



ループでは、PDFドキュメントのページは、名前がメソッドに渡されたキーで署名されます。



デモンストレーションでは、有効な証明書と無効な証明書の2つの署名が作成されました。











[署名]タブは次のとおりです。











PDFドキュメントの電子署名の使用



私の意見では、PDF形式で直接署名することは特別な場合です。 ファイル自体のこのようなスタンプは、目で確認することを目的とした電子署名の視覚化を目的としています。 これは、自動化が不要で、オペレーターが検査員の役割を果たす少数の文書に関しては便利で効果的です。



オペレータは、大量の電子文書を交換する集中的なプロセスに対処できません。 この場合、情報システムは自動化を実行し、美しい視覚化の必要性はなくなります。 したがって、大きなPDFファイルの転送とスタンプ画像の処理に、情報システムと電子チャネルのリソースを浪費する必要はありません。



電子署名を個別のファイルとして作成するか、ファイルと署名をPKCS#7標準(CAdES)の1つのコンテナーにラップする方がより実用的です。 添付または分離署名付きのこの標準は、情報システム間の大規模なワークフローに最適です。



同じPDFドキュメントは、切断された署名を使用してCAdES標準に従って署名できます。その結果、PDF自体と署名付きコンテナの2つのファイルが作成されます。



Javaにサインアップするときに発生した問題を思い出してください。 結論-Javaのアプリケーションプログラミングインターフェースに関して、CryptoProによるPDFのサポートは不十分です。 既存のitextpdfライブラリは、独自に編集する必要がありました。



暗号化情報保護ツールが組み込まれた商用ソフトウェアの開発に関連する活動の分野では、このアプローチは受け入れられないため、ライセンシー組織がパッチ、更新、改善を実施する必要があります。 したがって、投稿で説明されているPDF文書に署名する方法を使用して、可能性を実証し、内部で使用することができます。



便利なリンク



  1. Java Cryptography Extension(JCE)無制限の強度の管轄ポリシーファイル8
  2. CryptoPro Webサイト
  3. 開発者iTextpdf
  4. Bouncy Castle「以前のBCリリースから1.47以降への移植」
  5. GitHubのiTextpdf_5.1.3_patched_cryptopro_bc1.50ライブラリのソースコード



All Articles