1. 概述
在现代软件开发中,加密技术被广泛用于保护用户数据安全。
但加密实现中,一个小错误就可能带来严重后果。而正确掌握加密实现方法,对大多数开发者来说,既复杂又耗时。
✅ Google Tink 是一个跨语言、跨平台的加密库,它帮助我们更安全、更高效地实现加密逻辑。
本篇文章将介绍 Tink 的核心概念、使用方法以及常见场景,适用于有经验的 Java 开发者,不会过多讲解加密基础概念。
2. 依赖引入
你可以使用 Maven 或 Gradle 来引入 Tink 的依赖。
Maven 示例:
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
<version>1.2.2</version>
</dependency>
Gradle 示例:
dependencies {
implementation 'com.google.crypto.tink:tink:latest'
}
⚠️ 注意:
latest
不是实际版本号,使用时应替换为具体版本。
3. 初始化
在使用 Tink 的任何 API 之前,必须先注册其服务。
注册所有 Tink 实现:
TinkConfig.register();
如果你只需要使用 AEAD(Authenticated Encryption with Associated Data)功能,可以只注册对应的模块:
AeadConfig.register();
Tink 支持按需注册,你可以根据需要注册特定的加密模块,避免引入不必要的依赖。
4. 核心 Primitives(加密原语)
Tink 的核心是各种加密原语(Primitives),它们封装了不同的加密功能。每种原语可以有多个具体实现。
以下是常见的加密原语及其典型实现:
Primitive | 实现示例 |
---|---|
AEAD | AES-EAX, AES-GCM, AES-CTR-HMAC, CHACHA20-POLY1305 |
Streaming AEAD | AES-GCM-HKDF-STREAMING, AES-CTR-HMAC-STREAMING |
Deterministic AEAD | AES-SIV |
MAC | HMAC-SHA2 |
数字签名 | ECDSA over NIST curves, ED25519 |
混合加密(Hybrid) | ECIES with AEAD and HKDF, NaCl CryptoBox |
获取原语对象的方法如下:
Aead aead = AeadFactory.getPrimitive(keysetHandle);
4.1 KeysetHandle
每个加密原语都需要一个密钥集(Keyset),而 Tink 提供了 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)));
⚠️ 注意:上面使用的是明文存储,适用于本地调试。生产环境应使用加密存储或 KMS(密钥管理系统)。
5. 加密操作
5.1 AEAD 加密
AEAD(带关联数据的身份验证加密)可以加密明文,并选择性地对附加数据进行身份验证。
示例:
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());
// 解密
String decrypted = new String(aead.decrypt(ciphertext, associatedData.getBytes()));
✅ 附加数据(associatedData)不会被加密,但会参与完整性验证。
5.2 流式 AEAD 加密(Streaming 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();
解密示例:
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. 混合加密(Hybrid Encryption)
混合加密结合了对称加密和非对称加密的优点:
- 对称加密速度快,用于加密数据;
- 非对称加密用于加密对称密钥。
示例:
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
是可选的上下文信息,用于增强加密的安全性。
7. 消息认证码(MAC)
Tink 支持使用 MAC(Message Authentication Code)对消息进行完整性验证。
示例:
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 支持数字签名,使用 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());
⚠️ 如果签名无效,
verify()
方法会抛出异常。
9. 总结
本文介绍了 Google Tink 库的基本使用方式,包括:
- 依赖引入
- 初始化
- 密钥管理
- 常见加密原语的使用:AEAD、流式加密、混合加密、MAC、数字签名
Tink 提供了安全、易用的加密接口,适合需要在项目中安全实现加密功能的 Java 开发者。
完整示例代码已上传至 GitHub:https://github.com/eugenp/tutorials/tree/master/libraries-security