PDF帳票に電子署名・タイムスタンプを付与する

製品開発担当の大です。こんにちは。

先週エイチ・オー・エスでは「シーオーリポーツ Ver.12」「シーオーリポーツ for Java Ver.4」と2つの新製品をリリースしました。詳しくはリンク先をごらんください。今回は「シーオーリポーツ for Java Ver.4」に追加された機能のひとつ、PDF帳票に電子署名とタイムスタンプを付与する例をご紹介したいと思います。

今回の例は体験版でも動作させることができます。ぜひダウンロードして実際に試してみてください。

体験版ダウンロード:
https://www.hos.co.jp/download/

電子署名とタイムスタンプが付与されたPDF

PDF帳票の準備

ここでは簡単のためシーオーリポーツ for Java V4付属のサンプルからフォームファイルと画像ファイルを持ってきて使用します。「デベロッパーズマニュアル>サンプル>実践的な帳票出力処理(2)連票出力」を開き、「前準備」にある Invoice.cfx と stamp_suzuki.png を適当な場所に保存します。

プログラムは以下のように記述します(今回関係のないフィールド等には値は設定しません)。

public class 電子署名サンプル {
   
    public static void main(String[] args) {
        new 電子署名サンプル().createPDF();
    }

    public void createPDF() {
        var job = new CrFileOutJob(CorDocumentType.PDF, "電子署名サンプル.pdf");
        var draw = new CrDraw();
        try (var form = CrForm.open(draw, "Invoice.cfx")) {
            draw.startJob(job);
            form.printOut();
            draw.endJob();
        } catch (CrException cex) {
            draw.abortJob();
            throw cex;
        } finally {
            draw.deleteInstance();
        }
    }
}

いったん実行してPDF帳票が出力されることを確認します。(サンプルのフォームファイルには体験版のコピーライトが表示されます。製品をお持ちの方は、フォームファイルをいったん製品版のフォームエディタで開いて保存し直していただくとコピーライト表示が消えます。)

PDF帳票

電子証明書の準備

次に、電子署名用の証明書・秘密鍵の入ったPKCS#12形式のキーストア(拡張子 .pfx または .p12)を用意します(すでに自前の証明書をお持ちのかたは、この項は飛ばしていただいて構いません)。ここでは手っ取り早く、JDK付属のkeytoolを使用して自己証明書を作成して使用します。

>"c:\Program Files\Java\jdk-21\bin\keytool.exe" -genkeypair -keyalg RSA -storetype PKCS12 -keystore test.pfx
キーストアのパスワードを入力してください:
新規パスワードを再入力してください:
識別名を入力します。サブコンポーネントを空のままにする場合はドット(.)を1つ入力し、中カッコ内のデフォルト値を使用する場合は[ENTER]を押します。
姓名は何ですか。
[Unknown]:  hos test
組織単位名は何ですか。
[Unknown]:
組織名は何ですか。
[Unknown]:  HOS Co.,Ltd.
都市名または地域名は何ですか。
[Unknown]:  Nagoya
都道府県名または州名は何ですか。
[Unknown]:  Aichi
この単位に該当する2文字の国コードは何ですか。
[Unknown]:  JP
CN=hos test, OU=Unknown, O="HOS Co.,Ltd.", L=Nagoya, ST=Aichi, C=JPでよろしいですか。
[いいえ]:  y

90日間有効な3,072ビットのRSAのキー・ペアと自己署名型証明書(SHA384withRSA)を生成しています
ディレクトリ名: CN=hos test, OU=Unknown, O="HOS Co.,Ltd.", L=Nagoya, ST=Aichi, C=JP

作成したtest.pfxの中身を確認します。

>"c:\Program Files\Java\jdk-21\bin\keytool.exe" -v -list -keystore test.pfx
キーストアのパスワードを入力してください:
キーストアのタイプ: PKCS12
キーストア・プロバイダ: SUN

キーストアには1エントリが含まれます

別名: mykey
作成日: 2024/01/19
エントリ・タイプ: PrivateKeyEntry
証明書チェーンの長さ: 1
証明書[1]:
所有者: CN=hos test, OU=Unknown, O="HOS Co.,Ltd.", L=Nagoya, ST=Aichi, C=JP
発行者: CN=hos test, OU=Unknown, O="HOS Co.,Ltd.", L=Nagoya, ST=Aichi, C=JP
シリアル番号: d7bc5c369a09376
有効期間の開始日: Fri Jan 19 08:47:41 JST 2024終了日: Thu Apr 18 08:47:41 JST 2024
証明書のフィンガプリント:
SHA1: BE:68:AC:FB:40:E6:49:FD:A9:B2:67:BB:FA:DF:FC:68:CB:0D:8C:AE
SHA256: EC:26:61:D3:79:71:91:80:3F:E8:CC:99:1B:51:FA:7F:1D:8D:0B:76:AD:3B:E9:09:8C:8A:DE:3F:01:06:74:2C
署名アルゴリズム名: SHA384withRSA
サブジェクト公開キー・アルゴリズム: 3072ビットRSAキー
バージョン: 3

拡張:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A7 26 82 95 F8 01 62 02   53 5D B4 09 79 92 58 87  .&....b.S]..y.X.
0010: 62 DE 30 6D                                        b.0m
]
]

*******************************************
*******************************************

「別名」に書かれている値(ここではmykey)が後で必要になるのでメモしておきます。

BouncyCastleのインストール

シーオーリポーツ for Java V4で電子署名・タイムスタンプを扱うためには、BouncyCastleが必要です。こちらから以下の3つのjarをダウンロードし、参照ライブラリに追加してください。

ASN.1 Utility Classesbcutil-jdk18on-XXX.jar
PKIX/CMS/EAC/PKCS OCSP/TSP/OPENSSLbcpkix-jdk18on-XXX.jar
ASN.1 Utility Classesbcprov-jdk18on-XXX.jar

PDF帳票に電子署名する

それではPDF帳票に電子署名していきます。先ほど作成したcreatePDF()を編集して、以下のようにします。

public void createPDF() {
    var job = new CrFileOutJob(CorDocumentType.PDF, "電子署名サンプル.pdf");
    var doc = (CrPdfDocument) job.getDocument();
    try  {
        var keyStore = Files.readAllBytes(Paths.get("test.pfx"));
        var password = "hogehoge"; // キーストア作成時に指定したパスワード
        var alias = "mykey";
        var sign = new CrDigitalSignature(keyStore, password, alias);            
        doc.setDigitalSignature(sign);
    } catch (IOException ioex) {
        ioex.printStackTrace();
    }
    var draw = new CrDraw();
    try (var form = CrForm.open(draw, "Invoice.cfx")) {
        draw.startJob(job);
        form.printOut();
        draw.endJob();
    } catch (CrException cex) {
        draw.abortJob();
        throw cex;
    } finally {
        draw.deleteInstance();
    }
}

再実行すると、電子署名されたPDFが作成されました!
(test.pfxは正規の認証局によって認証されたものではないため、Acrobat Readerで読み込むと警告が発生します。)

電子署名されたPDF

PDF帳票にタイムスタンプを付与する

先ほどの修正した部分に続いて以下の記述を挿入します。

public void createPDF() {
    var job = new CrFileOutJob(CorDocumentType.PDF, "電子署名サンプル.pdf");
    var doc = (CrPdfDocument) job.getDocument();
    try  {
        var keyStore = Files.readAllBytes(Paths.get("test.pfx"));
        var password = "hogehoge"; // キーストア作成時に指定したパスワード
        var alias = "mykey";
        var sign = new CrDigitalSignature(keyStore, password, alias);            
        doc.setDigitalSignature(sign);
    } catch (IOException ioex) {
        ioex.printStackTrace();
    }
    doc.setTimeStamp(new CrTimeStamp("http://ts.ssl.com"));
    var draw = new CrDraw();
    try (var form = CrForm.open(draw, "Invoice.cfx")) {
        draw.startJob(job);
        form.printOut();
        draw.endJob();
    } catch (CrException cex) {
        draw.abortJob();
        throw cex;
    } finally {
        draw.deleteInstance();
    }
}

再実行すると、こんどは署名に加えてタイムスタンプが付与されました!
この例ではSSL.comのタイムスタンプを使用しています。他のタイムスタンプサーバを使用する場合はURLを適宜変更してください。

タイムスタンプつきのPDF

署名フィールドを用意する

さて、ここまで作成した署名済みPDFは、Acrobat Readerで開いてみると署名パネルに「不可視署名」と表示されていたと思います。今度は署名フィールドを用意して、これを帳票上で確認できるようにしたいと思います。

public void createPDF() {
    var job = new CrFileOutJob(CorDocumentType.PDF, "電子署名サンプル.pdf");
    var doc = (CrPdfDocument) job.getDocument();
    try  {
        var keyStore = Files.readAllBytes(Paths.get("test.pfx"));
        var password = "hogehoge"; // キーストア作成時に指定したパスワード
        var alias = "mykey";
        var sign = new CrDigitalSignature(keyStore, password, alias);            
        doc.setDigitalSignature(sign);
    } catch (IOException ioex) {
        ioex.printStackTrace();
    }
    doc.setTimeStamp(new CrTimeStamp("http://ts.ssl.com"));
    var draw = new CrDraw();
    try (var form = CrForm.open(draw, "Invoice.cfx")) {
        var image = (CrImageField) form.getCrObject("納品_検印3");
        var field = new CrPdfDocument.SignatureField(image);
        doc.setSignatureField("印鑑", field);
        draw.startJob(job);
        image.loadDataFile("stamp_suzuki.png");
        form.printOut();
        draw.endJob();
    } catch (CrException cex) {
        draw.abortJob();
        throw cex;
    } finally {
        draw.deleteInstance();
    }
}

フォーム上のオブジェクト(ここではイメージフィールド)をフィールドの外観として使用しています。フィールドの外観として使用するオブジェクトは、フォーム上にあらかじめ配置されているものでも良いですし、newしたオブジェクトでも構いません。

署名フィールドのあるPDF

最後に

シーオーリポーツ for Java Ver.4を使用してPDF帳票に電子署名とタイムスタンプを付与する例をご紹介させていただきました。いかがでしたでしょうか。そのほかの新機能もまた別の記事でご紹介していきたいと思います。お楽しみに!