既存の PDF 文書に長期署名を付与する方法を示します。
通常の署名の有効期限は、使用するデジタル ID の有効期限によって決まります。これは一般的に1~3年間と短期間で、またデジタル ID の発行日からの期間であるため、後から署名する文書ほど残り期間が短くなっていきます。
一方長期署名ではタイムスタンプを組み合わせることで、タイムスタンプの有効期限である7~10年間という長期間の保護を実現します。またタイムスタンプの有効期限は通常タイムスタンプ局によって一定期間が保証されているため、どの文書でも一定の有効期限を確保することができます。
特に電子文書を一定期間保護する必要がある場合、長期署名が適しています。
PLOP DS では PAdES という長期署名フォーマットに対応しています。PAdES は PDF 2.0 より PDF の仕様の一部になりました。
長期署名では タイムスタンプ付き署名の付与 のように署名にタイムスタンプを付与し (JIS の長期署名プロファイルの ES-T 相当)、そこに署名の検証に必要な情報を加えてアーカイブタイムスタンプで保護します (同 ES-A 相当)。
このサンプルを実行すると、PAdES 署名レベル B-LTA に準拠した PDF が出力されます。
長期署名では、検証時に必要となるルート CA 証明書や証明書失効リストを、署名時に用意する必要があります。特にルート CA 証明書はデジタル ID とタイムスタンプの両方のルート CA 証明書が必要ですが、PLOP DS ではこれらの証明書を1ファイルにまとめる必要があります。
このサンプルでは PLOP DS に同梱されている検証用デジタル ID のルート CA 証明書 (PDFlibDemoCA_G3.pem) と、弊社で用意している 検証用タイムスタンプ局のルート CA 証明書 を1つにまとめた cacert.pem を用意しました。このファイルをサンプルコードと同じフォルダに配置してください。
なお証明書失効リストについては証明書内に取得先 URI を埋め込むことができるため、通常は PLOP DS が自動的に取得します。このサンプルでも、証明書失効リストの取得先を含まないデジタル ID のルート CA の証明書失効リストのみを明示的に渡しています。
このサンプルで使用した電子署名は信頼された認証局で発行されたものではないため、Adobe Acorbat 等で開くと「少なくとも 1 つの署名に問題があります。」と表示されますが、信頼された認証局で発行された電子署名を使うと「署名済みであり、すべての署名が有効です。」と表示されます。
このサンプルで使用したタイムスタンプ局は PDFlib 製品の検証に使用してもらうことを目的に弊社が公開しているもので、国税関係書類のような公的な書類には使用できません。詳細は 検証用タイムスタンプサーバーについて をご確認ください。
必要な製品:PDFlib PLOP DS
処理の流れ
①既存の PDF 文書を開く
open_document() 関数を使って、長期署名を付与したい既存の PDF ファイルを開きます。
②長期署名を作成する
prepare_signature() 関数で、オプションで指定した長期署名を作成します。
③長期署名を PDF に付与して出力する
create_document() 関数で、② で作成した長期署名を ① で開いた PDF に付与し、PDF を出力します。
④PDF文書を閉じる
close_document() 関数で、PDF 文書を閉じれば、長期署名の付与は完了します。
ソースコードと出力結果
/*
* 既存 PDF に長期署名を付与:
* 既存の PDF 文書に長期署名を行うサンプルです。
*
* 必要な製品 : PDFlib PLOP DS 5
* 必要なデータ : demo_signer_rsa_2048.p12
* demo_signer_rsa_2048.p12 は data ディレクトリにあります。
* demo_signer_rsa_2048.p12 のパスワードは demo です。
* PDFlibDemoCA_G3.crl (証明書失効リスト)
* demo_signer_rsa_2048.p12 のルート証明書の失効リストです。
* これも data ディレクトリにあります。
* RFC3161 に対応したタイムスタンプサーバー
* このサンプルでは https://www.infotek.co.jp/tsa を使用します。
* cacert.pem (CA 証明書)
* demo_signer_rsa_2048.p12 の CA 証明書 (PDFlibDemoCA_G3.pem) と
* タイムスタンプサーバーの CA 証明書 を1ファイルにしたものです。
*/
import java.io.*;
import com.pdflib.plop;
import com.pdflib.PLOPException;
public class ltv {
public static void main (String argv[]) {
plop plop = null;
/* 文書タイムスタンプのための署名オプション */
String sign_opts =
"engine=builtin " +
"digitalid={filename={demo_signer_rsa_2048.p12}} " +
"password={demo} " +
"timestamp={" +
" critical " +
" source={ " +
" url={https://www.infotek.co.jp/tsa} " +
" sslverifypeer=false " +
" } " +
"} " +
"ltv=full " +
"rootcertfile={./cacert.pem} " +
"crlfile={../data/PDFlibDemoCA_G3.crl} " +
"doctimestamp={source={ " +
" url={https://www.infotek.co.jp/tsa} " +
" sslverifypeer=false " +
"}} ";
try {
/* 既存 PDF に関する変数を用意する */
String optlist;
String searchpath = "../data";
int doc;
/* 実行ファイルに渡す引数(取込む既存PDF名・出力するPDF名)のチェックを行う */
if (argv.length < 2) {
throw new Exception("usage: ltv <filename> <outfilename>");
}
/* PLOP オブジェクトを作成する */
plop = new plop();
/* 読み込みたいファイルの入ったディレクトリを指定する */
optlist = "searchpath {" + searchpath + "} ";
plop.set_option(optlist);
/* ①既存の PDF 文書を開く */
if ((doc = plop.open_document(argv[0], "")) == -1) {
throw new Exception("Error: " + plop.get_apiname()
+ ": " + plop.get_errmsg());
}
/* ②長期署名を作成する */
if (plop.prepare_signature(sign_opts) == -1) {
throw new Exception("Error: " + plop.get_apiname() + ": " + plop.get_errmsg());
}
/* ③長期署名を PDF に付与して出力する */
if (plop.create_document(argv[1], "input=" + doc) == -1) {
throw new Exception("Error: " + plop.get_apiname() + ": " + plop.get_errmsg());
}
/* ④PDF文書を閉じる */
plop.close_document(doc, "");
} catch (PLOPException e) {
System.err.println("PLOP exception occurred in ltv sample:");
System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() + ": " + e.get_errmsg());
} catch (Exception e) {
System.err.println(e);
} finally {
/* PLOP オブジェクトを削除する */
if (plop != null) plop.delete();
}
}
}