POM
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.11.1</version>
</dependency>
<dependency>
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
<version>1.2.2</version>
</dependency>
Java
@Test
public void test() throws GeneralSecurityException {
AeadConfig.register();
try {
KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_GCM);
Aead aead = AeadFactory.getPrimitive(keysetHandle);
String plaintext="napier";
String aad="qwerty123";
System.out.println("Text:"+plaintext);
byte[] ciphertext = aead.encrypt(plaintext.getBytes(), aad.getBytes());
System.out.println("Cipher:"+ciphertext.toString());
byte[] decrypted = aead.decrypt(ciphertext, aad.getBytes());
String s = new String(decrypted);
System.out.println("Text:"+s);
} catch (GeneralSecurityException e) {
System.out.println(e);
System.exit(1);
}
}
其他说明
初始化
在使用任何Tink API之前,我们需要对其进行初始化。
如果需要使用Tink中所有原语的所有实现,则可以使用TinkConfig.register()方法:
1 | TinkConfig.register(); |
---|---|
例如,如果仅需要AEAD原语,则可以使用AeadConfig.register()方法:
1 | AeadConfig.register(); |
---|---|
4. Tink原语
库使用的主要对象称为基元,根据类型,基元包含不同的密码功能。
一个原语可以有多种实现:
原始 | 实作 |
---|---|
AEAD | AES-EAX,AES-GCM,AES-CTR-HMAC,KMS信封,CHACHA20-POLY1305 |
流式AEAD | AES-GCM-HKDF流,AES-CTR-HMAC流 |
确定性AEAD | AEAD:AES-SIV |
苹果电脑 | HMAC SHA2 |
电子签名 | 超过NIST曲线的ECDSA,ED25519 |
混合加密 | 具有AEAD和HKDF的ECIES,(NaCl CryptoBox) |
我们可以通过调用相应工厂类的方法getPrimitive()并将其传递给KeysetHandle来获取原语:
1 | Aead aead = AeadFactory.getPrimitive(keysetHandle); |
---|---|
4.1。键集句柄
为了提供加密功能,每个原语都需要一个包含所有密钥材料和参数的密钥结构。
Tink提供了一个对象KeysetHandle, 该对象包装了带有一些其他参数和元数据的键集。
因此,在实例化原语之前,我们需要创建一个KeysetHandle 对象:
1 | KeysetHandle keysetHandle = KeysetHandle.generateNew(AeadKeyTemplates.AES256_GCM); |
---|---|
在生成密钥之后,我们可能想要保留它:
1 2 |
String keysetFilename = ``"keyset.json"``; CleartextKeysetHandle.write(keysetHandle, JsonKeysetWriter.withFile(``new File(keysetFilename))); |
---|---|
然后,我们可以随后加载它:
1 2 |
String keysetFilename = ``"keyset.json"``; KeysetHandle keysetHandle = CleartextKeysetHandle.read(JsonKeysetReader.withFile(``new File(keysetFilename))); |
---|---|
5.加密
5.1。AEAD
AEAD提供了具有关联数据的身份验证加密,这意味着我们可以加密纯文本,还可以选择提供应进行身份验证但未加密的关联数据。
请注意,此算法可确保关联数据的真实性和完整性,但不能保证其保密性。
如前所述,要使用一种AEAD实现对数据进行加密,我们需要初始化该库并创建一个keysetHandle:
1 2 3 |
AeadConfig.register(); KeysetHandle keysetHandle = KeysetHandle.generateNew( ``AeadKeyTemplates.AES256_GCM); |
---|---|
完成之后,我们可以获取原语并加密所需的数据:
1 2 3 4 5 |
String plaintext = ``"baeldung"``; String associatedData = ``"Tink"``; Aead aead = AeadFactory.getPrimitive(keysetHandle); byte``[] ciphertext = aead.encrypt(plaintext.getBytes(), associatedData.getBytes()); |
---|---|
接下来,我们可以使用crypto()方法解密密文:
1 | String decrypted = ``new String(aead.decrypt(ciphertext, associatedData.getBytes())); |
---|---|
5.2。流式AEAD
同样,当要加密的数据太大而无法在单个步骤中处理时,我们可以使用流式AEAD原语:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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还实现了一些用于混合加密的原语。
使用混合加密,我们可以获得对称密钥的效率和非对称密钥的便利性。
简而言之,我们将使用对称密钥来加密纯文本,并使用公共密钥来仅加密对称密钥。
请注意,它仅提供保密性,不提供发送者的身份真实性。
因此,让我们看看如何使用HybridEncrypt和HybridDecrypt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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是从可以是上下文暗示公共数据 零或空或用作“相关联的数据”输入为AEAD加密或为“CtxInfo”输入为香港民主促进会。
该密文可以用于检查的完整性contextInfo但不是它的保密性或真实性。
7.消息验证码
Tink还支持消息身份验证代码或MAC。
MAC是几个字节的块,可用于验证消息。
让我们看看如何创建一个MAC,然后验证其真实性:
1 2 3 4 5 6 7 8 9 10 11 |
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.数字签名
除加密API外,Tink还支持数字签名。
为了实现数字签名,该库使用PublicKeySign原语进行数据签名,并使用PublickeyVerify进行验证:
1 2 3 4 5 6 7 8 9 10 11 12 |
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实现介绍了Google Tink库。
我们已经看到了如何使用加密和解密数据以及如何保护其完整性和真实性。此外,我们已经看到了如何使用数字签名API对数据进行签名。
与往常一样,示例代码可在GitHub上获得。
其他记录
- 报错:The type com.google.protobuf.GeneratedMessage$Builder cannot be resolved. It is indirectly referenced from required .class files 错误记录
- 解决:记得引入protobuf-java
参考资料
- https://www.baeldung.com/google-tink
- Tink是一个多语言,跨平台的库,提供安全,易于正确使用和难以滥用的加密API。
- https://github.com/google/tink