Guide to Google Tink – 谷歌Tink指南

最后修改: 2019年 4月 23日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

Nowadays, many developers use cryptographic techniques to protect user data.

现在,许多开发者使用加密技术来保护用户数据。

In cryptography, small implementation errors can have serious consequences, and understanding how to implement cryptography correctly is a complex and time-consuming task.

在密码学中,小的执行错误会产生严重的后果,了解如何正确执行密码学是一项复杂而耗时的任务。

In this tutorial, we’re going to describe Tink – a multi-language, cross-platform cryptographic library that can help us to implement secure, cryptographic code.

在本教程中,我们将介绍Tink – 一个多语言、跨平台的加密库,可以帮助我们实现安全的加密代码。

2. Dependencies

2.依赖性

We can use Maven or Gradle to import Tink.

我们可以使用Maven或Gradle来导入Tink。

For our tutorial, we’ll just add Tink’s Maven dependency:

在我们的教程中,我们只需添加Tink的Maven依赖项

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

Though we could have used Gradle instead:

虽然我们可以用Gradle代替。

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

3. Initialization

3.初始化

Before using any of Tink APIs we need to initialize them.

在使用任何Tink APIs之前,我们需要初始化它们。

If we need to use all implementations of all primitives in Tink, we can use the TinkConfig.register() method:

如果我们需要使用Tink中所有基元的所有实现,我们可以使用TinkConfig.register()方法。

TinkConfig.register();

While, for example, if we only need AEAD primitive, we can use AeadConfig.register() method:

而比如说,如果我们只需要AEAD原语,我们可以使用AeadConfig.register()方法。

AeadConfig.register();

A customizable initialization is provided for each implementation, too.

也为每个实现提供了一个可定制的初始化。

4. Tink Primitives

4.Tink Primitives

The main objects the library uses are called primitives which, depending on the type, contains different cryptographic functionality.

该库使用的主要对象被称为基元,根据不同的类型,包含不同的加密功能。

A primitive can have multiple implementations:

一个基元可以有多个实现。

Primitive Implementations
AEAD AES-EAX, AES-GCM, AES-CTR-HMAC, KMS Envelope, CHACHA20-POLY1305
Streaming AEAD AES-GCM-HKDF-STREAMING, AES-CTR-HMAC-STREAMING
Deterministic AEAD AEAD: AES-SIV
MAC HMAC-SHA2
Digital Signature ECDSA over NIST curves, ED25519
Hybrid Encryption ECIES with AEAD and HKDF, (NaCl CryptoBox)

We can obtain a primitive by calling the method getPrimitive() of the corresponding factory class passing it a KeysetHandle:

我们可以通过调用相应工厂类的方法 getPrimitive(),传递给它一个KeysetHandle来获得一个基元。

Aead aead = AeadFactory.getPrimitive(keysetHandle);

4.1. KeysetHandle

4.1.键集手柄

In order to provide cryptographic functionality, each primitive needs a key structure that contains all the key material and parameters.

为了提供加密功能,每个基元需要一个包含所有密钥材料和参数的密钥结构

Tink provides an object – KeysetHandle – which wraps a keyset with some additional parameters and metadata.

Tink提供了一个对象–KeysetHandle–,它用一些额外的参数和元数据来包装一个键集。

So, before instantiating a primitive, we need to create a KeysetHandle object:

因此,在实例化一个基元之前,我们需要创建一个KeysetHandle对象。

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

And after generating a key, we might want to persist it:

而在生成一个密钥后,我们可能想把它持久化。

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

Then, we can subsequently load it:

然后,我们可以随后加载它。

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

5. Encryption

5.加密

Tink provides multiple ways of applying the AEAD algorithm. Let’s take a look.

Tink提供了多种应用AEAD算法的方法。让我们来看看。

5.1. AEAD

5.1.AEAD

AEAD provides Authenticated Encryption with Associated Data which means that we can encrypt plaintext and, optionally, provide associated data that should be authenticated but not encrypted.

AEAD提供了带有关联数据的认证加密,这意味着我们可以加密明文,并且可以选择提供应该被认证但不被加密的关联数据

Note that this algorithm ensures the authenticity and integrity of the associated data but not its secrecy.

请注意,这种算法能确保相关数据的真实性和完整性,但不能确保其保密性。

To encrypt data with one of the AEAD implementations, as we previously saw, we need to initialize the library and create a keysetHandle:

要用AEAD实现之一来加密数据,正如我们之前看到的,我们需要初始化库并创建一个keysetHandle:

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

Once we’ve done that, we can get the primitive and encrypt the desired data:

一旦我们完成了这一工作,我们就可以获得原始数据并对所需的数据进行加密。

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

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

Next, we can decrypt the ciphertext using the decrypt() method:

接下来,我们可以使用decrypt()方法对ciphertext进行解密。

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

5.2. Streaming AEAD

5.2.流动的AEAD

Similarly, when the data to be encrypted is too large to be processed in a single step, we can use the streaming AEAD primitive:

同样地,当要加密的数据太大,无法在单一步骤中处理时,我们可以使用流式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();

Basically, we needed WriteableByteChannel to achieve this.

基本上,我们需要WriteableByteChannel来实现这一目标。

So, to decrypt the cipherTextFile, we’d want to use a ReadableByteChannel:

因此,为了解密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. Hybrid Encryption

6.混合加密

In addition to symmetric encryption, Tink implements a couple of primitives for hybrid encryption.

除了对称加密之外,Tink还实现了几个混合加密的基元。

With Hybrid Encryption we can get the efficiency of symmetric keys and the convenience of asymmetric keys.

通过混合加密,我们可以获得对称密钥的效率和非对称密钥的便利。

Simply put, we’ll use a symmetric key to encrypt the plaintext and a public key to encrypt the symmetric key only.

简单地说,我们将使用对称密钥对明文进行加密,使用公钥只对对称密钥进行加密

Notice that it provides secrecy only, not identity authenticity of the sender.

请注意,它只提供保密性,而不是发件人的身份真实性。

So, let’s see how to use HybridEncrypt and HybridDecrypt:

因此,让我们看看如何使用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());

The contextInfo is implicit public data from the context that can be null or empty or used as “associated data” input for the AEAD encryption or as “CtxInfo” input for HKDF.

contextInfo是来自上下文的隐性公共数据,它可以是null或空的,或者用作AEAD加密的 “关联数据 “输入或HKDF的 “CtxInfo “输入。

The ciphertext allows for checking the integrity of contextInfo but not its secrecy or authenticity.

密码文允许检查contextInfo的完整性,但不允许检查其保密性或真实性。

7. Message Authentication Code

7.信息验证码

Tink also supports Message Authentication Codes or MACs.

Tink还支持消息验证码或MAC。

A MAC is a block of a few bytes that we can use to authenticate a message.

MAC是一个由几个字节组成的块,我们可以用它来验证一个信息。

Let’s see how we can create a MAC and then verify its authenticity:

让我们看看我们如何创建一个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());

In the event that the data isn’t authentic, the method verifyMac() throws a GeneralSecurityException.

如果数据不真实,方法verifyMac()会抛出一个GeneralSecurityException.

8. Digital Signature

8.数字签名

As well as encryption APIs, Tink supports digital signatures.

除了加密API之外,Tink还支持数字签名。

To implement digital signature, the library uses the PublicKeySign primitive for the signing of data, and PublickeyVerify for verification:

为了实现数字签名,库使用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());

Similar to the previous encryption method, when the signature is invalid, we’ll get a GeneralSecurityException.

与之前的加密方法类似,当签名无效时,我们会得到一个GeneralSecurityException.

9. Conclusion

9.结论

In this article, we introduced the Google Tink library using its Java implementation.

在这篇文章中,我们使用Google Tink库的Java实现来介绍它。

We’ve seen how to use to encrypt and decrypt data and how to protect its integrity and authenticity. Moreover, we’ve seen how to sign data using digital signature APIs.

我们已经看到如何使用加密和解密数据以及如何保护其完整性和真实性。此外,我们还看到了如何使用数字签名API来签署数据。

As always, the sample code is available over on GitHub.

像往常一样,样本代码可在GitHub上获得