Javaでタイムスタンプを生成する方法

タイムスタンプサービスを利用すればタイムスタンプを取得することは出来るのですが、費用がかかってしまいます。
テスト等で利用したい場合に都合が悪いので、タイムスタンプを自前で生成する方法を調べてみました。

わかったこと

Bouncy Castleのorg.bouncycastle.tsp.TimeStampTokenGeneratorにて生成することが出来る。

TimeStampTokenGenerator (Bouncy Castle Library 1.59 API Specification)

実際に行ったこと

準備

タイムスタンプ生成に利用する秘密鍵と証明書を用意する。
OpenSSLが既にインストールされている場合、openssl.cnfに以下の設定を追加することで、
タイムスタンプ局の秘密鍵と証明書を発行出来ます。

設定

[ v3_tsa ]
extendedKeyUsage = critical,timeStamping

発行コマンド

openssl genrsa -out tsakey.pem 2048 # 秘密鍵を作成
openssl req -new -x509 -key tsakey.pem -out tsacert.pem  -extensions v3_tsa -days 10950 # タイムスタンプ局の証明書を発行

サンプルコード

publicbyte[] getTimeStampToken(byte[] messageImprint) throws IOException {
byte[] retVal = null;
try {
digest.reset();
byte[] hash = digest.digest(messageImprint);

// 32-bit cryptographic nonce
SecureRandom random = new SecureRandom();
int nonce = random.nextInt();

// generate TSA request
TimeStampRequestGenerator tsaGenerator = new TimeStampRequestGenerator();
tsaGenerator.setCertReq(true);
ASN1ObjectIdentifier oid = getHashObjectIdentifier(digest.getAlgorithm());
TimeStampRequest request = tsaGenerator.generate(oid, hash, BigInteger.valueOf(nonce));

// read pem file
PrivateKey privateKey = PEMUtil.pemToPrivateKey("tsakey.pem");
X509Certificate certificate = PEMUtil.pemToCertificate("tsacert.pem");

// generate TimeStampToken
SignerInfoGenerator signerInfoGenerator = new JcaSimpleSignerInfoGeneratorBuilder()
.build("SHA256WithRSAEncryption", privateKey, certificate);
DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder()
.setProvider(new BouncyCastleProvider()).build().get(signerInfoGenerator.getDigestAlgorithm());
TimeStampTokenGenerator tstGen = new TimeStampTokenGenerator(signerInfoGenerator, digestCalculator,
new ASN1ObjectIdentifier(ANY_POLICY));    // ANY_POLICY = "2.5.29.32.0"

List<X509Certificate> certList = new ArrayList<>(); // ここでaddCRLs、addCertificatesで設定しておかないと無効なタイムスタンプになってしまう
certList.add(certificate);
Store certs = new JcaCertStore(certList);
tstGen.addCRLs(certs);
tstGen.addCertificates(certs);
retVal = tstGen.generate(request, BigInteger.ONE, new Date()).getEncoded();    // 第二引数はタイムスタンプ局としてのシーケンシャルな値であるべきだが、ここでは固定とした
} catch (Exception e) {
thrownew IOException(e);
}

return retVal;
}