Для большинства случаев использования шифрования с открытым ключом мы рекомендуем примитив гибридного шифрования с типом ключа DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, AES_256_GCM .
Шифрование с открытым ключом предполагает защиту данных с помощью двух ключей: одного открытого и одного закрытого. Открытый ключ используется для шифрования, а закрытый — для расшифровки. Это хороший выбор, если отправитель не может хранить секреты и нуждается в шифровании данных с помощью открытого ключа.
Следующие примеры помогут вам начать использовать примитив гибридного шифрования:
C++
// A command-line utility for testing Tink Hybrid Encryption. #include <iostream> #include <memory> #include <ostream> #include <string> #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/absl_check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "tink/config/global_registry.h" #include "util/util.h" #ifndef TINK_EXAMPLES_EXCLUDE_HPKE #include "tink/hybrid/hpke_config.h" #endif #include "tink/hybrid/hybrid_config.h" #include "tink/hybrid_decrypt.h" #include "tink/hybrid_encrypt.h" #include "tink/keyset_handle.h" ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); ABSL_FLAG(std::string, mode, "", "Mode of operation {encrypt|decrypt}"); ABSL_FLAG(std::string, input_filename, "", "Input file name"); ABSL_FLAG(std::string, output_filename, "", "Output file name"); ABSL_FLAG(std::string, context_info, "", "Context info for Hybrid Encryption/Decryption"); namespace { using ::crypto::tink::HybridDecrypt; using ::crypto::tink::HybridEncrypt; using ::crypto::tink::KeysetHandle; constexpr absl::string_view kEncrypt = "encrypt"; constexpr absl::string_view kDecrypt = "decrypt"; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { absl::Status HybridCli(absl::string_view mode, const std::string& keyset_filename, const std::string& input_filename, const std::string& output_filename, absl::string_view context_info) { absl::Status result = crypto::tink::HybridConfig::Register(); if (!result.ok()) return result; #ifndef TINK_EXAMPLES_EXCLUDE_HPKE // HPKE isn't supported when using OpenSSL as a backend. result = crypto::tink::RegisterHpke(); if (!result.ok()) return result; #endif // Read the keyset from file. absl::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = ReadJsonCleartextKeyset(keyset_filename); if (!keyset_handle.ok()) return keyset_handle.status(); // Read the input. absl::StatusOr<std::string> input_file_content = ReadFile(input_filename); if (!input_file_content.ok()) return input_file_content.status(); // Compute the output. std::string output; if (mode == kEncrypt) { // Get the hybrid encryption primitive. absl::StatusOr<std::unique_ptr<HybridEncrypt>> hybrid_encrypt_primitive = (*keyset_handle) ->GetPrimitive<crypto::tink::HybridEncrypt>( crypto::tink::ConfigGlobalRegistry()); if (!hybrid_encrypt_primitive.ok()) { return hybrid_encrypt_primitive.status(); } // Generate the ciphertext. absl::StatusOr<std::string> encrypt_result = (*hybrid_encrypt_primitive)->Encrypt(*input_file_content, context_info); if (!encrypt_result.ok()) return encrypt_result.status(); output = encrypt_result.value(); } else { // operation == kDecrypt. // Get the hybrid decryption primitive. absl::StatusOr<std::unique_ptr<HybridDecrypt>> hybrid_decrypt_primitive = (*keyset_handle) ->GetPrimitive<crypto::tink::HybridDecrypt>( crypto::tink::ConfigGlobalRegistry()); if (!hybrid_decrypt_primitive.ok()) { return hybrid_decrypt_primitive.status(); } // Recover the plaintext. absl::StatusOr<std::string> decrypt_result = (*hybrid_decrypt_primitive)->Decrypt(*input_file_content, context_info); if (!decrypt_result.ok()) return decrypt_result.status(); output = decrypt_result.value(); } // Write the output to the output file. return WriteToFile(output, output_filename); } } // namespace tink_cc_examples int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv); ValidateParams(); std::string mode = absl::GetFlag(FLAGS_mode); std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename); std::string input_filename = absl::GetFlag(FLAGS_input_filename); std::string output_filename = absl::GetFlag(FLAGS_output_filename); std::string context_info = absl::GetFlag(FLAGS_context_info); std::clog << "Using keyset from file " << keyset_filename << " to hybrid " << mode << " file " << input_filename << " with context info '" << context_info << "'." << '\n'; std::clog << "The resulting output will be written to " << output_filename << '\n'; ABSL_CHECK_OK(tink_cc_examples::HybridCli( mode, keyset_filename, input_filename, output_filename, context_info)); return 0; }
Идти
import ( "bytes" "fmt" "log" "github.com/tink-crypto/tink-go/v2/hybrid" "github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset" "github.com/tink-crypto/tink-go/v2/keyset" ) func Example() { // A private keyset created with // "tinkey create-keyset --key-template=DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM --out private_keyset.cfg". // Note that this keyset has the secret key information in cleartext. privateJSONKeyset := `{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PRIVATE", "typeUrl": "type.googleapis.com/google.crypto.tink.HpkePrivateKey", "value": "EioSBggBEAEYAhogVWQpmQoz74jcAp5WOD36KiBQ71MVCpn2iWfOzWLtKV4aINfn8qlMbyijNJcCzrafjsgJ493ZZGN256KTfKw0WN+p" }, "keyId": 958452012, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 958452012 }` // The corresponding public keyset created with // "tinkey create-public-keyset --in private_keyset.cfg". publicJSONKeyset := `{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PUBLIC", "typeUrl": "type.googleapis.com/google.crypto.tink.HpkePublicKey", "value": "EgYIARABGAIaIFVkKZkKM++I3AKeVjg9+iogUO9TFQqZ9olnzs1i7Sle" }, "keyId": 958452012, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 958452012 }` // Create a keyset handle from the keyset containing the public key. Because the // public keyset does not contain any secrets, we can use [keyset.ReadWithNoSecrets]. publicKeysetHandle, err := keyset.ReadWithNoSecrets( keyset.NewJSONReader(bytes.NewBufferString(publicJSONKeyset))) if err != nil { log.Fatal(err) } // Retrieve the HybridEncrypt primitive from publicKeysetHandle. encPrimitive, err := hybrid.NewHybridEncrypt(publicKeysetHandle) if err != nil { log.Fatal(err) } plaintext := []byte("message") encryptionContext := []byte("encryption context") ciphertext, err := encPrimitive.Encrypt(plaintext, encryptionContext) if err != nil { log.Fatal(err) } // Create a keyset handle from the cleartext private keyset in the previous // step. The keyset handle provides abstract access to the underlying keyset to // limit the access of the raw key material. WARNING: In practice, // it is unlikely you will want to use a insecurecleartextkeyset, as it implies // that your key material is passed in cleartext, which is a security risk. // Consider encrypting it with a remote key in Cloud KMS, AWS KMS or HashiCorp Vault. // See https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#storing-and-loading-existing-keysets. privateKeysetHandle, err := insecurecleartextkeyset.Read( keyset.NewJSONReader(bytes.NewBufferString(privateJSONKeyset))) if err != nil { log.Fatal(err) } // Retrieve the HybridDecrypt primitive from privateKeysetHandle. decPrimitive, err := hybrid.NewHybridDecrypt(privateKeysetHandle) if err != nil { log.Fatal(err) } decrypted, err := decPrimitive.Decrypt(ciphertext, encryptionContext) if err != nil { log.Fatal(err) } fmt.Println(string(decrypted)) // Output: message }
Java
package hybrid; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.crypto.tink.HybridDecrypt; import com.google.crypto.tink.HybridEncrypt; import com.google.crypto.tink.InsecureSecretKeyAccess; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.RegistryConfiguration; import com.google.crypto.tink.TinkJsonProtoKeysetFormat; import com.google.crypto.tink.hybrid.HybridConfig; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * A command-line utility for hybrid encryption. * * <p>It loads cleartext keys from disk - this is not recommended! * * <p>It requires the following arguments: * * <ul> * <li>mode: either 'encrypt' or 'decrypt'. * <li>key-file: Read the key material from this file. * <li>input-file: Read the input from this file. * <li>output-file: Write the result to this file. * <li>[optional] contex-info: Bind the encryption to this context info. */ public final class HybridExample { public static void main(String[] args) throws Exception { if (args.length != 4 && args.length != 5) { System.err.printf("Expected 4 or 5 parameters, got %d\n", args.length); System.err.println( "Usage: java HybridExample encrypt/decrypt key-file input-file output-file context-info"); System.exit(1); } String mode = args[0]; if (!mode.equals("encrypt") && !mode.equals("decrypt")) { System.err.println("Incorrect mode. Please select encrypt or decrypt."); System.exit(1); } Path keyFile = Paths.get(args[1]); Path inputFile = Paths.get(args[2]); byte[] input = Files.readAllBytes(inputFile); Path outputFile = Paths.get(args[3]); byte[] contextInfo = new byte[0]; if (args.length == 5) { contextInfo = args[4].getBytes(UTF_8); } // Register all hybrid encryption key types with the Tink runtime. HybridConfig.register(); // Read the keyset into a KeysetHandle. KeysetHandle handle = TinkJsonProtoKeysetFormat.parseKeyset( new String(Files.readAllBytes(keyFile), UTF_8), InsecureSecretKeyAccess.get()); if (mode.equals("encrypt")) { // Get the primitive. HybridEncrypt encryptor = handle.getPrimitive(RegistryConfiguration.get(), HybridEncrypt.class); // Use the primitive to encrypt data. byte[] ciphertext = encryptor.encrypt(input, contextInfo); Files.write(outputFile, ciphertext); } else { HybridDecrypt decryptor = handle.getPrimitive(RegistryConfiguration.get(), HybridDecrypt.class); // Use the primitive to decrypt data. byte[] plaintext = decryptor.decrypt(input, contextInfo); Files.write(outputFile, plaintext); } } private HybridExample() {} }
Объект-С
Python
import tink from tink import hybrid from tink import secret_key_access def example(): """Encrypt and decrypt using hybrid encryption.""" # Register the hybrid encryption key managers. This is needed to create # HybridEncrypt and HybridDecrypt primitives later. hybrid.register() # A private keyset created with # tinkey create-keyset \ # --key-template=DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM \ # --out private_keyset.cfg # Note that this keyset has the secret key information in cleartext. private_keyset = r"""{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PRIVATE", "typeUrl": "type.googleapis.com/google.crypto.tink.HpkePrivateKey", "value": "EioSBggBEAEYAhogVWQpmQoz74jcAp5WOD36KiBQ71MVCpn2iWfOzWLtKV4aINfn8qlMbyijNJcCzrafjsgJ493ZZGN256KTfKw0WN+p" }, "keyId": 958452012, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 958452012 }""" # The corresponding public keyset created with # "tinkey create-public-keyset --in private_keyset.cfg" public_keyset = r"""{ "key": [{ "keyData": { "keyMaterialType": "ASYMMETRIC_PUBLIC", "typeUrl": "type.googleapis.com/google.crypto.tink.HpkePublicKey", "value": "EgYIARABGAIaIFVkKZkKM++I3AKeVjg9+iogUO9TFQqZ9olnzs1i7Sle" }, "keyId": 958452012, "outputPrefixType": "TINK", "status": "ENABLED" }], "primaryKeyId": 958452012 }""" # Create a keyset handle from the keyset containing the public key. Because # this keyset does not contain any secrets, we can use # `parse_without_secret`. public_keyset_handle = tink.json_proto_keyset_format.parse_without_secret( public_keyset ) # Retrieve the HybridEncrypt primitive from the keyset handle. enc_primitive = public_keyset_handle.primitive(hybrid.HybridEncrypt) # Use enc_primitive to encrypt a message. In this case the primary key of the # keyset will be used (which is also the only key in this example). ciphertext = enc_primitive.encrypt(b'message', b'context_info') # Create a keyset handle from the private keyset. The keyset handle provides # abstract access to the underlying keyset to limit the exposure of accessing # the raw key material. WARNING: In practice, it is unlikely you will want to # use a tink.json_proto_keyset_format.parse, as it implies that your key # material is passed in cleartext which is a security risk. private_keyset_handle = tink.json_proto_keyset_format.parse( private_keyset, secret_key_access.TOKEN ) # Retrieve the HybridDecrypt primitive from the private keyset handle. dec_primitive = private_keyset_handle.primitive(hybrid.HybridDecrypt) # Use dec_primitive to decrypt the message. Decrypt finds the correct key in # the keyset and decrypts the ciphertext. If no key is found or decryption # fails, it raises an error. decrypted = dec_primitive.decrypt(ciphertext, b'context_info')
Гибридное шифрование
Примитив гибридного шифрования сочетает в себе эффективность симметричного шифрования с удобством асимметричной криптографии с открытым ключом. Любой может зашифровать данные, используя открытый ключ, но расшифровать их смогут только пользователи, имеющие закрытый ключ.
При гибридном шифровании отправитель генерирует новый симметричный ключ для шифрования открытого текста каждого сообщения, получая таким образом зашифрованный текст. Этот симметричный ключ инкапсулируется вместе с открытым ключом получателя. При гибридном расшифровании симметричный ключ декапсулируется получателем, а затем используется для расшифровки зашифрованного текста с целью восстановления исходного открытого текста. Подробную информацию о том, как хранить или передавать зашифрованный текст вместе с инкапсуляцией ключа, см. в разделе «Формат передачи данных Tink Hybrid Encryption».
Гибридное шифрование обладает следующими свойствами:
- Конфиденциальность : Никто не может получить никакой информации о зашифрованном открытом тексте (кроме его длины), если у него нет доступа к закрытому ключу.
- Асимметрия : Шифрование зашифрованного текста может быть выполнено с помощью открытого ключа, но для расшифровки требуется закрытый ключ.
- Рандомизация : Шифрование выполняется случайным образом. Два сообщения с одинаковым открытым текстом не дадут одинаковый зашифрованный текст. Это предотвращает возможность злоумышленникам определить, какой зашифрованный текст соответствует данному открытому тексту.
В Tink гибридное шифрование представлено парой примитивов:
- HybridEncrypt для шифрования
- HybridDecrypt для расшифровки
Параметр контекстной информации
В дополнение к открытому тексту, гибридное шифрование принимает дополнительный параметр context_info , который обычно представляет собой общедоступные данные, неявно содержащиеся в контексте, но должны быть связаны с результирующим зашифрованным текстом. Это означает, что зашифрованный текст позволяет подтвердить целостность информации о контексте, но не гарантирует его секретность или подлинность. Фактическая информация о контексте может быть пустой или нулевой, но для обеспечения правильного расшифрования результирующего зашифрованного текста необходимо предоставить то же значение информации о контексте для расшифровки.
В конкретной реализации гибридного шифрования контекстная информация может быть связана с зашифрованным текстом различными способами, например:
- Используйте
context_infoв качестве связанных входных данных для симметричного шифрования AEAD (см. RFC 5116 ). - Используйте
context_infoв качестве входных данных “CtxInfo” для HKDF (если реализация использует HKDF в качестве функции вывода ключа, см. RFC 5869 ).
Выберите тип ключа
Для большинства случаев использования мы рекомендуем использовать тип ключа DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM . Этот тип ключа реализует стандарт гибридного шифрования с открытым ключом (HPKE), как указано в RFC 9180. HPKE состоит из механизма инкапсуляции ключа (KEM), функции вывода ключа (KDF) и алгоритма аутентифицированного шифрования с ассоциированными данными (AEAD).
В частности, DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM использует:
- KEM: алгоритм Диффи-Хеллмана над кривой 25519 с использованием HKDF-SHA-256 для получения общего секрета.
- KDF: HKDF-SHA-256 для определения контекста отправителя и получателя.
- AEAD: AES-256-GCM с 12-байтовыми одноразовыми числами, сгенерированными в соответствии со стандартом HPKE.
К числу поддерживаемых типов ключей HPKE относятся, помимо прочих, следующие:
-
DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_128_GCM -
DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20_POLY1305 -
DHKEM_P256_HKDF_SHA256_HKDF_SHA256_AES_128_GCM -
DHKEM_P521_HKDF_SHA512_HKDF_SHA512_AES_256_GCM
Более подробную информацию о выборе алгоритмов для KEM, KDF и AEAD см. в RFC 9180.
Хотя это больше не рекомендуется, Tink также поддерживает некоторые варианты ECIES, описанные в стандарте ISO 18033-2 Виктора Шоупа . Ниже перечислены некоторые поддерживаемые типы ключей ECIES:
-
ECIES_P256_HKDF_HMAC_SHA256_AES128_GCM -
ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_GCM -
ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256 -
ECIES_P256_COMPRESSED_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256
Минимальные свойства
- Открытый текст и контекстная информация могут иметь произвольную длину (в диапазоне от 0 до 2,52 байт).
- Защита от адаптивных атак с использованием выбранного зашифрованного текста.
- 128-битная защита для схем на основе эллиптических кривых.