0
0

【Java初心者】Google Tinkを使ったMAC(メッセージ認証コード)

Last updated at Posted at 2024-02-12

Macとは

Googleのサイトによると
https://cloud.google.com/kms/docs/mac-signatures?hl=ja

MAC 署名は、データの整合性と信頼性の検証に利用される暗号出力です。

とあります。そして

MAC 署名には主に 2 つの目的があります。
* 署名されたデータの整合性を検証します。
* メッセージの信頼性を確認する。

と説明されています。

また、こちらのサイトでは

メッセージ認証コード(MAC)とは、相手から届いたメッセージが途中で
改竄やすり替えに合っていないか検証するための短い符号。
送信者と受信者が共有する秘密のデータ(鍵)とメッセージ本体から生成する。

となっています。

ただ今回のサンプルでは、「共通鍵を渡していない相手のメッセージ」が改ざんされていないかを確認するという目的では使えません。(その場合は別の方法による電子署名を使います)

Javaで書いてみました

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

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

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.Mac;
import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
import com.google.crypto.tink.mac.MacConfig;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Base64;

public class MacTest {

    /**
     * plainTextの内容が変更されていないか認証コードを作り あとで検証する
     *
     * @param args
     */
    public static void main(String[] args) {
        String plainText = "こんにちは"; // もとのテキスト
        try {
            System.out.println("Mac秘密鍵を生成");
            MacConfig.register();
            KeysetHandle privateMacKeysetHandle = KeysetHandle.generateNew(KeyTemplates.get("HMAC_SHA256_128BITTAG"));
            String serializedmacKeyset = TinkJsonProtoKeysetFormat.serializeKeyset(
                    privateMacKeysetHandle, InsecureSecretKeyAccess.get()); // MAC秘密鍵(JSON)

            // MAC秘密鍵をローカルに保存する場合(非推奨)
            //  System.out.println("Mac秘密鍵を保存");
            //  String macKey = "mac_priv.key";
            //  Path macFile = Paths.get(macKey);
            //  Files.write(macFile, serializedmacKeyset.getBytes(UTF_8));
            // MACコードを生成するには 平文、MAC秘密鍵が必要
            String macData = getMac(plainText, serializedmacKeyset);
            System.out.println("MAC-DATA:" + macData);
            // 同じテキストで試します。なお、違う文字列で試すと検証が失敗します
            String plainText1 = plainText; 
            // 検証する場合は 検証対象のデータ、MAC秘密鍵 , MACコードが必要)
            boolean verMac = verMac(plainText1, serializedmacKeyset, macData);
            System.out.println("Mac検証:" + verMac);
        } catch (IOException | GeneralSecurityException e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * Mac(メッセージ認証コード)出力
     *
     * @param plaintext 平文
     * @param macKey 共通鍵
     * @return Macコード(Base64)
     */
    static String getMac(String plaintext, String macKey) throws GeneralSecurityException, IOException {
        KeysetHandle handle = CleartextKeysetHandle.read(JsonKeysetReader.withString(macKey));
        Mac macPrimitive = handle.getPrimitive(Mac.class);
        byte[] macTag = macPrimitive.computeMac(plaintext.getBytes());
        return new String(Base64.getEncoder().encode(macTag));
    }

    /**
     * Mac(メッセージ認証コード)検証
     *
     * @param plaintext 平文
     * @param macKey 共通鍵
     * @param macfile MACコード
     * @return 正しく検証できればtrue
     */
    static boolean verMac(String plaintext, String macKey, String macfile) {
        try {
            KeysetHandle keysetHandle = CleartextKeysetHandle.read(
                    JsonKeysetReader.withString(macKey));
            Mac mac = keysetHandle.getPrimitive(Mac.class);
            mac.verifyMac(Base64.getDecoder().decode(macfile), plaintext.getBytes());
            return true;
        } catch (IOException | GeneralSecurityException e) {
            System.out.println(e.getMessage());
            return false;
        }
    }
}

実行結果

Mac秘密鍵を生成
MAC-DATA:AeBy8Mmaf6Bx1JJHUBeKNvpUCi5T
Mac検証:true

検証できない(もとのデータとは異なっている)場合は以下のようになります

Mac秘密鍵を生成
MAC-DATA:AeBy8Mmaf6Bx1JJHUBeKNvpUCi5T
invalid MAC
Mac検証:false
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0