Javaで無料のタイムスタンプサービスに接続する

## タイムスタンプサービス

無料でタイムスタンプトークンを発行するサービス、[Free TSA Project](http://eswg.jnsa.org/sandbox/freetsa/) に接続します。タイムスタンプリクエスト、タイムスタンプレスポンス、タイムスタンプトークンの生成、解析には[Bouncy Castle Crypto APIs](https://www.bouncycastle.org/java.html)を使用しました。

## タイムスタンププロトコル

タイムスタンプに関わる仕様は、[RFC3161](https://datatracker.ietf.org/doc/html/rfc3161)、[RFC5816](https://datatracker.ietf.org/doc/html/rfc5816) で 定義されています。

HTTP通信で行う際の仕様が [RFC3161 の 3.4 Time-Stamp Protocol via HTTP](https://datatracker.ietf.org/doc/html/rfc3161#section-3.4) に記載されています。

## Bouncy Castle Crypto APIs のダウンロード

[LATEST JAVA RELEASES](https://www.bouncycastle.org/latest_releases.html)から最新版をダウンロードします。

必要なjarファイルは、Provider, ASN.1 Utility Classes, PKIX/CMS/EAC/PKCS/OCSP/TSP/OPENSSL の jar ファイルになります。


## タイムスタンプサービスへの接続

今回は、[HttpClientクラス](https://docs.oracle.com/javase/jp/17/docs/api/java.net.http/java/net/http/HttpClient.html)を使用して、タイムスタンプサービスへ接続しました。

Content-Type に application/timestamp-query を指定し、ボディ句にDERエンコードした タイムスタンプリクエストを設定し、Postメソッドでタイムスタンプサービスに送ります。

## サンプルプログラム

以下は、タイムスタンプサービスに接続し、タイムスタンプレスポンス、タイムスタンプトークンを出力するサンプルプログラムです。

```java
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;

public class TimeStampHttpClient {

	public static void main(String[] args) {
		
		try {
			//ハッシュ値の生成 SHA-256
			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
			byte[] sha256Byte = sha256.digest("Hello World".getBytes());
		
			//タイムスタンプリクエストの作成
			TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
			reqGen.setCertReq(true);
			TimeStampRequest timeStampReq = reqGen.generate(TSPAlgorithms.SHA256, sha256Byte);
			
			FileOutputStream fos1 = new FileOutputStream("HelloWorld.req");
			fos1.write(timeStampReq.getEncoded());
			fos1.close();
			
			
			//タイムスタンプサービスに接続
		    HttpClient client = HttpClient.newBuilder().version(Version.HTTP_1_1).build();
		    
		    HttpRequest req = HttpRequest.newBuilder()
		      .uri(URI.create("http://eswg.jnsa.org/freetsa")) 
		      .header("Content-Type","application/timestamp-query")
		      .POST(HttpRequest.BodyPublishers.ofByteArray(timeStampReq.getEncoded()))
		      .build();
		    
		    var res = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
		    
		    if ( res.statusCode() != 200 ) {
		    	System.out.println("Error");
		    	System.out.println(res.statusCode());
		    	System.exit(-1);
		    }
		    
		    //タイムスタンプレスポンスの出力
		    FileOutputStream fos2 = new FileOutputStream("HelloWorld.tsr");
		    byte[] resArray = res.body();
		    fos2.write(resArray);
			fos2.close();
			
			TimeStampResponse resp = new TimeStampResponse(resArray);
			TimeStampToken token = resp.getTimeStampToken();
			
			//タイムスタンプトークンの出力
		    FileOutputStream fos3 = new FileOutputStream("HelloWorld.tst");
		    fos3.write(token.getEncoded());
			fos3.close();
			
		}
		catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		catch (Throwable e) {
			e.printStackTrace();
		}
	}
}
```