1. 序章

現在、多くの開発者は暗号化技術を使用してユーザーデータを保護しています。

暗号化では、小さな実装エラーが深刻な結果をもたらす可能性があり、暗号化を正しく実装する方法を理解することは、複雑で時間のかかる作業です。

このチュートリアルでは、 Tink について説明します。これは、安全な暗号化コードの実装に役立つ、多言語のクロスプラットフォーム暗号化ライブラリです。

2. 依存関係

MavenまたはGradleを使用してTinkをインポートできます。

このチュートリアルでは、TinkのMaven依存関係を追加するだけです。

<dependency>
    <groupId>com.google.crypto.tink</groupId>
    <artifactId>tink</artifactId>
    <version>1.2.2</version>
</dependency>

代わりにGradleを使用することもできますが、

dependencies {
  compile 'com.google.crypto.tink:tink:latest'
}

3. 初期化

Tink APIを使用する前に、それらを初期化する必要があります。

Tinkのすべてのプリミティブのすべての実装を使用する必要がある場合は、 TinkConfig.register()メソッドを使用できます。

TinkConfig.register();

たとえば、AEADプリミティブのみが必要な場合は、 AeadConfig.register()メソッドを使用できます。

AeadConfig.register();

実装ごとにカスタマイズ可能な初期化も提供されます。

4. ティンクプリミティブ

ライブラリが使用する主なオブジェクトはプリミティブと呼ばれ、タイプに応じて、さまざまな暗号化機能が含まれています。

プリミティブは複数の実装を持つことができます:

原生的 実装
AEAD AES-EAX、AES-GCM、AES-CTR-HMAC、KMSエンベロープ、CHACHA20-POLY1305
ストリーミングAEAD AES-GCM-HKDF-STREAMING、AES-CTR-HMAC-STREAMING
決定論的AEAD AEAD:AES-SIV
マック HMAC-SHA2
デジタル署名 NIST曲線上のECDSA、ED25519
ハイブリッド暗号化 AEADおよびHKDFを使用したECIES、(NaCl CryptoBox)

対応するファクトリクラスのメソッドgetPrimitive()を呼び出して KeysetHandle を渡すことにより、プリミティブを取得できます。

Aead aead = AeadFactory.getPrimitive(keysetHandle);

4.1. KeysetHandle

が暗号化機能を提供するために、各プリミティブには、すべてのキーマテリアルとパラメーターを含むキー構造が必要です。

Tinkは、いくつかの追加のパラメーターとメタデータでキーセットをラップするオブジェクト– KeysetHandle –を提供します。

したがって、プリミティブをインスタンス化する前に、KeysetHandleオブジェクトを作成する必要があります。

KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM);

そして、キーを生成した後、それを永続化することができます。

String keysetFilename = "keyset.json";
CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withFile(new File(keysetFilename)));

その後、それをロードできます。

String keysetFilename = "keyset.json";
KeysetHandle keysetHandle = CleartextKeysetHandle.read(JsonKeysetReader.withFile(new File(keysetFilename)));

5. 暗号化

Tinkは、AEADアルゴリズムを適用する複数の方法を提供します。 見てみましょう。

5.1. AEAD

AEADは、関連データを使用した認証付き暗号化を提供します。つまり、プレーンテキストを暗号化でき、オプションで、認証されるべきであるが暗号化されてはならない関連データを提供できます

このアルゴリズムは、関連するデータの信頼性と整合性を保証しますが、その機密性は保証しないことに注意してください。

以前に見たように、AEAD実装の1つでデータを暗号化するには、ライブラリを初期化し、 keysetHandle:を作成する必要があります。

AeadConfig.register();
KeysetHandle keysetHandle = KeysetHandle.generateNew(
  AeadKeyTemplates.AES256_GCM);

それが済んだら、プリミティブを取得して目的のデータを暗号化できます。

String plaintext = "baeldung";
String associatedData = "Tink";

Aead aead = AeadFactory.getPrimitive(keysetHandle); 
byte[] ciphertext = aead.encrypt(plaintext.getBytes(), associatedData.getBytes());

次に、 decode()メソッドを使用して暗号文を復号化できます。

String decrypted = new String(aead.decrypt(ciphertext, associatedData.getBytes()));

5.2. ストリーミングAEAD

同様に、暗号化するデータが大きすぎて単一のステップで処理できない場合は、ストリーミングAEADプリミティブを使用できます。

AeadConfig.register();
KeysetHandle keysetHandle = KeysetHandle.generateNew(
  StreamingAeadKeyTemplates.AES128_CTR_HMAC_SHA256_4KB);
StreamingAead streamingAead = StreamingAeadFactory.getPrimitive(keysetHandle);

FileChannel cipherTextDestination = new FileOutputStream("cipherTextFile").getChannel();
WritableByteChannel encryptingChannel =
  streamingAead.newEncryptingChannel(cipherTextDestination, associatedData.getBytes());

ByteBuffer buffer = ByteBuffer.allocate(CHUNK_SIZE);
InputStream in = new FileInputStream("plainTextFile");

while (in.available() > 0) {
    in.read(buffer.array());
    encryptingChannel.write(buffer);
}

encryptingChannel.close();
in.close();

基本的に、これを実現するにはWriteableByteChannelが必要でした。

したがって、 cipherTextFileを復号化するには、 ReadableByteChannelを使用します。

FileChannel cipherTextSource = new FileInputStream("cipherTextFile").getChannel();
ReadableByteChannel decryptingChannel =
  streamingAead.newDecryptingChannel(cipherTextSource, associatedData.getBytes());

OutputStream out = new FileOutputStream("plainTextFile");
int cnt = 1;
do {
    buffer.clear();
    cnt = decryptingChannel.read(buffer);
    out.write(buffer.array());
} while (cnt>0);

decryptingChannel.close();
out.close();

6. ハイブリッド暗号化

対称暗号化に加えて、Tinkはハイブリッド暗号化用のいくつかのプリミティブを実装しています。

ハイブリッド暗号化を使用すると、対称鍵の効率と非対称鍵の利便性を得ることができます。

簡単に言うと、対称鍵を使用して平文を暗号化し、公開鍵を使用して対称鍵のみを暗号化します。

送信者のIDの信頼性ではなく、機密性のみを提供することに注意してください。

それでは、 HybridEncryptHybridDecrypt:の使い方を見てみましょう。

TinkConfig.register();

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(
  HybridKeyTemplates.ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256);
KeysetHandle publicKeysetHandle = privateKeysetHandle.getPublicKeysetHandle();

String plaintext = "baeldung";
String contextInfo = "Tink";

HybridEncrypt hybridEncrypt = HybridEncryptFactory.getPrimitive(publicKeysetHandle);
HybridDecrypt hybridDecrypt = HybridDecryptFactory.getPrimitive(privateKeysetHandle);

byte[] ciphertext = hybridEncrypt.encrypt(plaintext.getBytes(), contextInfo.getBytes());
byte[] plaintextDecrypted = hybridDecrypt.decrypt(ciphertext, contextInfo.getBytes());

contextInfo は、 null または空であるか、AEAD暗号化の「関連データ」入力またはHKDFの「CtxInfo」入力として使用できるコンテキストからの暗黙のパブリックデータです。

暗号文では、 contextInfo の整合性をチェックできますが、その機密性や信頼性はチェックできません。

7. メッセージ認証コード

Tinkは、メッセージ認証コードまたはMACもサポートしています。

MACは、メッセージの認証に使用できる数バイトのブロックです。

MACを作成して、その信頼性を検証する方法を見てみましょう。

TinkConfig.register();

KeysetHandle keysetHandle = KeysetHandle.generateNew(
  MacKeyTemplates.HMAC_SHA256_128BITTAG);

String data = "baeldung";

Mac mac = MacFactory.getPrimitive(keysetHandle);

byte[] tag = mac.computeMac(data.getBytes());
mac.verifyMac(tag, data.getBytes());

データが本物でない場合、メソッド verifyMac()GeneralSecurityExceptionをスローします。

8. デジタル署名

Tinkは、暗号化APIだけでなく、デジタル署名もサポートしています。

デジタル署名を実装するために、ライブラリはデータの署名に PublicKeySign プリミティブを使用し、検証にPublickeyVerifyを使用します。

TinkConfig.register();

KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);
KeysetHandle publicKeysetHandle = privateKeysetHandle.getPublicKeysetHandle();

String data = "baeldung";

PublicKeySign signer = PublicKeySignFactory.getPrimitive(privateKeysetHandle);
PublicKeyVerify verifier = PublicKeyVerifyFactory.getPrimitive(publicKeysetHandle);

byte[] signature = signer.sign(data.getBytes()); 
verifier.verify(signature, data.getBytes());

前の暗号化方式と同様に、署名が無効な場合、GeneralSecurityException。が発生します。

9. 結論

この記事では、Java実装を使用したGoogleTinkライブラリを紹介しました。

データの暗号化と復号化に使用する方法と、データの整合性と信頼性を保護する方法を見てきました。 さらに、デジタル署名APIを使用してデータに署名する方法を見てきました。

いつものように、サンプルコードはGitHubから入手できます。