【Java初心者】Google Tinkを使った電子署名

# 電子署名とは

Wikipediaには
```
電子署名とは、電磁的記録に付与する、電子的な徴証であり、
紙文書における印章やサインに相当する役割をはたすものである。
主に本人確認や、改竄検出符号と組み合わせて偽造・改竄の防止のために用いられる。 
電子署名を実現する仕組みとしては、公開鍵暗号方式に基づくデジタル署名が有力である。
```
とあります。

自分の秘密鍵でファイルに署名し、それを予め配布しておいた公開鍵で検証することで、送信者が「正しい人」であることがわかります。

# Javaで書いてみました

Tinkは、バージョンアップのたびに、APIが変わり、以前書いたコードが非推奨となることが多いため、その都度ドキュメントを見ながら修正しています。

以下に示すコードはtink-1.12.0.jarを用いています。依存ライブラリーとして protobuf-java と gson が必要です。

```java
package sample2024;

import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.JsonKeysetReader;
import com.google.crypto.tink.KeyTemplates;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PublicKeySign;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
import com.google.crypto.tink.signature.SignatureConfig;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Base64;

public class SignatureTest {

    public static void main(String[] args) {
        try {
            String plainText = "こんにちは"; // 署名したいテキスト
            System.out.println("署名したいテキスト:" + plainText);
            SignatureConfig.register();
            System.out.println("電子署名秘密鍵を生成");
            KeysetHandle privateKeysetHandle
                    = KeysetHandle.generateNew(KeyTemplates.get("ECDSA_P256"));

            // 秘密鍵をファイルに保存する場合(セキュリティー上推奨されていません)
            // String SignaturePrivKey = "signature_priv.key";
            // Path sigPrivFile = Paths.get(SignaturePrivKey);
            // System.out.println("Hybrid秘密鍵を保存");
            // String serializedmacKeyset = TinkJsonProtoKeysetFormat.serializeKeyset(
            //        privateKeysetHandle, InsecureSecretKeyAccess.get());
            // Files.write(sigPrivFile, serializedmacKeyset.getBytes(UTF_8));
            // 秘密鍵をファイルから読み込む(セキュリティー上推奨されていません)
            // serializedmacKeyset = Files.readString(sigPrivFile, UTF_8);
            // 読み取ったテキストから鍵を抽出する
            //KeysetHandle keysetHandle2 = CleartextKeysetHandle.read(
            //        JsonKeysetReader.withString(serializedmacKeyset));
            // 秘密鍵から公開鍵を作成
            KeysetHandle publicKeysetHandle = privateKeysetHandle.getPublicKeysetHandle();
            System.out.println("電子署名公開鍵(相手に渡すもの):" + toJson(publicKeysetHandle));

            // 電子署名をするためには秘密鍵と もとのテキストが必要
            PublicKeySign signer = privateKeysetHandle.getPrimitive(PublicKeySign.class);
            byte[] signature = signer.sign(plainText.getBytes());
            String sign_data = new String(Base64.getEncoder().encode(signature));
            System.out.println("電子署名:" + sign_data);

            PublicKeyVerify verifier = publicKeysetHandle.getPrimitive(PublicKeyVerify.class);
            // 署名を検証するためには電子署名と もとのテキストが必要
            verifier.verify(signature, plainText.getBytes());
            System.out.println("電子署名が検証されました");

        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    private static String toJson(KeysetHandle key) throws GeneralSecurityException {
        return TinkJsonProtoKeysetFormat.serializeKeyset(key, InsecureSecretKeyAccess.get());
    }
}

```

# 実行結果

```
署名したいテキスト:こんにちは
電子署名秘密鍵を生成
電子署名公開鍵(相手に渡すもの):{"primaryKeyId":3084469744,"key":[{"keyData":{"typeUrl":"type.googleapis.com/google.crypto.tink.EcdsaPublicKey","value":"EgYIAxACGAIaIQDS8szXsh4hV1Rxp5XFfA5CP3wbPvGgChsIsPTEkfkpxCIhAK5aeSDBZNKvHH4PYZNIuee5gykLtDk6AQQPgvKWrBzt","keyMaterialType":"ASYMMETRIC_PUBLIC"},"status":"ENABLED","keyId":3084469744,"outputPrefixType":"TINK"}]}

電子署名:AbfZRfAwRgIhAJcqUHB/JlzDrPsJyFkqizlLCCnbG8CqkdX7ECUb9YWaAiEA11bfaG1Dn9Uvw0gV3ILZhZ3zIKYMyYPwhOo6UfZ6YBU=
電子署名が検証されました
```