Перейти к основному содержимому

Руководство по Google Tink

· 5 мин. чтения

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-ПОТОК, AES-CTR-HMAC-ПОТОК |
| Детерминированный AEAD | AEAD: AES-SIV |
| МАК | HMAC-SHA2 |
| Цифровая подпись | ECDSA по кривым NIST, ED25519 |
| Гибридное шифрование | ECIES с AEAD и HKDF (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, как мы видели ранее, нам нужно инициализировать библиотеку и создать keysetHandle:

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

Как только мы это сделали, мы можем получить примитив и зашифровать нужные данные:

String plaintext = "foreach";
String associatedData = "Tink";

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

Далее мы можем расшифровать зашифрованный текст с помощью метода decrypt() :

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 реализует несколько примитивов для гибридного шифрования.

С гибридным шифрованием мы можем получить эффективность симметричных ключей и удобство асимметричных ключей.

Проще говоря, мы будем использовать симметричный ключ для шифрования открытого текста и открытый ключ для шифрования только симметричного ключа.

Обратите внимание, что он обеспечивает только секретность, а не подлинность личности отправителя.

Итак, давайте посмотрим, как использовать HybridEncrypt и HybridDecrypt:

TinkConfig.register();

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

String plaintext = "foreach";
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 для HKDF.

Зашифрованный текст позволяет проверить целостность contextInfo, но не его секретность или подлинность. ``

7. Код аутентификации сообщения

Tink также поддерживает коды аутентификации сообщений или MAC-адреса.

MAC — это блок из нескольких байтов, который мы можем использовать для аутентификации сообщения.

Давайте посмотрим, как мы можем создать MAC, а затем проверить его подлинность:

TinkConfig.register();

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

String data = "foreach";

Mac mac = MacFactory.getPrimitive(keysetHandle);

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

В случае, если данные не являются подлинными, метод verifyMac() генерирует исключение GeneralSecurityException .

8. Цифровая подпись

Помимо API-интерфейсов шифрования, Tink поддерживает цифровые подписи.

Для реализации цифровой подписи библиотека использует примитив PublicKeySign для подписи данных и PublickeyVerify для проверки:

TinkConfig.register();

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

String data = "foreach";

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

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

Как и в предыдущем методе шифрования, когда подпись недействительна, мы получим исключение GeneralSecurityException.

9. Заключение

В этой статье мы представили библиотеку Google Tink, используя ее реализацию Java.

Мы увидели, как использовать для шифрования и расшифровки данных и как защитить их целостность и подлинность. Более того, мы увидели, как подписывать данные с помощью API цифровой подписи.

Как всегда, пример кода доступен на GitHub .