PDFのタイムスタンプを正しく設定し、トラブルを回避する完全ガイド
(検索者の疑問:PDFにデジタル署名を付ける際、正確な時刻を証明したいけど手順がわからない。設定後に失敗するケースも多い。どうすれば安全にタイムスタンプを付与し、エラー時に対処できるか?)
まず知っておきたいタイムスタンプとは
PDFにデジタル署名を追加すると、署名者本人が署名した時点の内容が保証されますが、署名が作成されたその瞬間に署名済み PDF が有効であることまでを証明するには「タイムスタンプ」が必要です。
タイムスタンプは TSR(Time Stamp Revocation) という信頼できる第三者(TSA)によって発行され、署名の「タイムスタンピング」プロセスに組み込まれます。
-
タイムスタンプの役割
- 署名の日付・時刻が正確に記録される
- 今後ファイルが改ざんされても「署名時点の内容は安全」かを検証できる
- 期限切れの証明書があっても、タイムスタムが付与されていれば有効性が維持される
-
標準
- CAdES / XAdES / PAdES など、XML ベース、バイナリベース、PDF 専用のデジタル署名規格。PAdES が PDF で最も広く用いられます。
- RFC 3161 が TSAP(Time-Stamping Protocol)の仕様。多くの TSA がこのプロトコルを採用しています。
PDFタイムスタンプを設定する4つの主な方法
1. Adobe Acrobat DC(GUI)でタイムスタンプを付与
-
署名設定
- ファイルを開いたら「ツール」→「署名」→「署名のプロパティ」
- 「署名の詳細設定」で「署名をタイムスタンプ」チェックをオン
-
タイムスタンプサーバーを指定
- 「タイムスタンプサーバー」欄に TSA の URL (例:
http://timestamp.digicert.comなど) を入力 - デフォルトは「認識済みのタイムスタンプサーバー」から選択可能
- 「タイムスタンプサーバー」欄に TSA の URL (例:
-
署名
- 署名フィールドをクリックし、証明書を選択
- 「署名」→「タイムスタンプ付きで署名」
-
確認
- 署名パネルにタイムスタンプの情報が表示されれば成功
注意点
- Acrobat が「署名で使用」できる証明書を検出できない場合、エラーが出ます。
- TSA がオープンにアクセスできない(IP制限やHTTPSの非対応)場合は、別サーバーへ切り替えましょう。
2. PDFtk Pro(コマンドライン)でタイムスタンプを付与
手順
-
PDFtk Pro のインストール
- Windows / macOS / Linux で公式サイトからインストールしてください。
-
タイムスタンプサーバーを指定したコマンド例
pdftk input.pdf output signed.pdf pdftk signed.pdf stamp cert_file.pfx pdftk signed.pdf output final.pdf pdtimestamp -tsa https://freetsa.org/tsr -in final.pdf -out timestampped.pdf-
pdtimestampは PDFtk Pro に含まれるタイムスタンプユーティリティ。-tsaで TSA URL を指定します。
-
-
確認
-
timestampped.pdfに署名とタイムスタンプが正しく埋め込まれているか、Adobe Reader で確認します。
-
よくあるエラー
-
cannot find server:TLS 証明書が検証できない場合。-skipverifyオプションで回避できるが、セキュリティ上注意要。 -
Certificate chain error:PFX ファイルに中間証明書が不足。OpenSSL でopenssl pkcs12 -in cert.pfx -nodes -out cert.pemして中間を組み合わせてください。
3. Java + Apache PDFBox + BouncyCastle(プログラムで制御)
実装概要
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.util.*;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.*;
import org.bouncycastle.cert.jcajce.*;
import org.bouncycastle.cms.*;
import org.bouncycastle.tsp.*;
import org.bouncycastle.operator.*;
import org.bouncycastle.operator.jcajce.*;
public class PdfTimestampExample {
public static void main(String[] args) throws Exception {
// 1. PDFロード
PDDocument doc = PDDocument.load(new File("input.pdf"));
// 2. 署名フィールド取得・作成
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PKCS7);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("John Doe");
signature.setLocation("Tokyo");
signature.setReason("Approval");
doc.addSignature(signature);
// 3. 証明書ロード
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("cert.pfx"), "password".toCharArray());
PrivateKey pk = (PrivateKey) ks.getKey(ks.aliases().nextElement(), "password".toCharArray());
Certificate[] chain = ks.getCertificateChain(ks.aliases().nextElement());
// 4. CMS署名作成
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(pk);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha256Signer, (X509CertificateHolder) new JcaX509CertificateConverter()
.setProvider("BC").getCertificateHolder(chain[0])));
for (int i = 0; i < chain.length; i++) {
gen.addCertificate(new JcaX509CertificateConverter().setProvider("BC").getCertificate(chain[i]));
}
// 5. タイムスタンプ取得
TimeStampTokenGenerator tsGen = new TimeStampTokenGenerator(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
// TSA へリクエスト
TSAClient tsa = new TSAClientBouncyCastle("http://freetsa.org/tsr", pk, chain[0]);
ByteArrayInputStream pdfStream = new ByteArrayInputStream(doc.toByteArray());
CMSSignedData signed = new CMSSignedDataGenerator().
generate(new CMSProcessableInputStream(pdfStream), false);
// タイムスタンプを追加
TimeStampToken tst = tsa.getTimeStampToken(signed.getEncoded());
// 6. PDFに署名+TSを埋め込む
// ...
doc.save("signed_with_ts.pdf");
doc.close();
}
}
ポイント
TSAClientBouncyCastleは BouncyCastle のサンプル実装。TimeStampTokenを取得して署名に添付します。- 署名データとタイムスタンプはともに PDF に格納されるため、最終 PDF は検証可能です。
4. 企業向け S/MIME サーバー(例:DocuSign, Adobe Sign)を利用
- これらは GUI か API で手軽に「署名+タイムスタンプ」を自動付与します。
-
メリット
- 証明書管理や TSA の構成を自前で管理する手間がほぼゼロ。
- 署名の有効性検証レポートが自動生成される。
-
デメリット
- コストがかかる。
- 外部サービスへの依存。
タイムスタンプでよく起きるトラブルと対処法
| トラブル | 原因 | 対処法 |
|---|---|---|
| 「タイムスタンプエラー」 | TSA が応答できない、または TLS 証明書が無効 | TSA の URL が正しいか確認。必要なら別の TSA へ切替え。 |
| 「証明書が失効している」 | 署名時に使用した証明書が失効している | 失効前に必ずタイムスタンプを取得する。 |
| 「時計ズレ」 | コンピュータのシステムクロックがずれている | NTP で正確な時間設定。 |
| 「中間証明書が不足」 | 署名証明書チェーンに中間証明書が入っていない | PKCS#12 に中間証明書を追加、もしくは PEM で結合。 |
| 「PDF が壊れる」 | 署名パラメータが PDF と合わない | バージョン互換性を確認。PDF バージョンが古い場合は PDF/A で保存。 |
| 「タイムスタンプ形式が不適合」 | RFC 3161 ではなく独自フォーマットが使われた | PAdES‑B 等の標準に従った形式を使用。 |
タイムスタンプ設定のベストプラクティス
-
証明書の管理
- 署名に使用する証明書は 失効前 にタイムスタンプを取得。
- 中間証明書を必ず一緒に渡す。
-
TSA の選定
- 信頼できる 第三者(国際的に認知された TSA)を選ぶ。
- 高可用性 を持つサービス(例: DigiCert, Sectigo, Comodo)を使用。
-
PDFバージョン
- Acrobat では PDF 1.7 以降が推奨。
-
Create PDF/A選択で長期保存向け設定も併せて行うと良い。
-
検証手順
- 署名後に Adobe Reader で「プロパティ」→「署名」タブを確認。
- 「署名情報」→「タイムスタンプ」を展開し、正確な時刻と TSA の署名があるか検証。
-
自動化
- 企業環境では CI/CD パイプラインに署名タスクを組み込み、毎回同じ手順で署名+タイムスタンプを付与する。
- 例:GitHub Actions +
pdtimestampスクリプト。
よくある質問(Q&A)
Q1. タイムスタンプを取りたくない場合はどうする?
A1. 署名を付与するアプリ側で「タイムスタンプ付き」オプションをオフにすれば OK。 ただし、署名後に証明書失効になると有効性が失われるリスクがあります。
Q2. タイムスタンプはいつ付与すればいい?
A2. 署名完了直後にタイムスタンプを取得するのが最も安全です。
Q3. タイムスタンプのデータを自前でサーバーに保存していない?
A3. PDF 内に TimeStampToken が埋め込まれているので、外部サーバーに保管する必要はありません。
Q4. いくつかのPDFで「署名が不正」表示になる。何が問題?
A4. 署名パラメータが PDF の構造と一致していない、もしくは署名時のアルゴリズムが古い(SHA‑1 など)場合に発生します。PDF バージョンを更新し、SHA‑256 以降のアルゴリズムを使用しましょう。
まとめ
- タイムスタンプはデジタル署名の信頼性を大幅に高める 重要な要素です。
- Adobe Acrobat を使えば直感的に、PDFtk や PDFBox でスクリプト化も可能。
- TSA の設定や証明書チェーンの整備を怠らないことが、後々のトラブル回避につながります。
- 署名後は必ず PDF Reader で「署名情報」を確認し、タイムスタンプが正しく埋め込まれているかをチェックしましょう。
これで PDF へのタイムスタンプ設定とトラブル対処法を網羅できました。次回のドキュメント署名が成功、かつ長期的に証明力を保てるよう、ぜひ実践してみてください。


コメント