ما برای اکثر موارد استفاده از رمزگذاری کلید عمومی ، رمزگذاری ترکیبی اولیه با نوع کلید DHKEM_X25519_HKDF_SHA256، HKDF_SHA256، AES_256_GCM را توصیه میکنیم.
رمزگذاری کلید عمومی شامل محافظت از دادهها با دو کلید است: یکی عمومی و دیگری خصوصی. کلید عمومی برای رمزگذاری و کلید خصوصی برای رمزگشایی استفاده میشود. این روش در صورتی که فرستنده نتواند اطلاعات محرمانه را ذخیره کند و نیاز به رمزگذاری دادهها با کلید عمومی داشته باشد، انتخاب خوبی است.
مثالهای زیر به شما کمک میکنند تا استفاده از رمزگذاری ترکیبی (Hybrid Encryption) را شروع کنید:
سی++
// 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 }
جاوا
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() {} }
شیء-سی
پایتون
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 مراجعه کنید.
رمزگذاری ترکیبی دارای ویژگیهای زیر است:
- محرمانگی : هیچ کس نمیتواند هیچ اطلاعاتی در مورد متن رمز شده (به جز طول) به دست آورد، مگر اینکه به کلید خصوصی دسترسی داشته باشد.
- عدم تقارن : رمزگذاری متن رمز شده را میتوان با کلید عمومی انجام داد، اما برای رمزگشایی، کلید خصوصی مورد نیاز است.
- تصادفیسازی : رمزگذاری به صورت تصادفی انجام میشود. دو پیام با متن اصلی یکسان، متن رمز شده یکسانی نخواهند داشت. این امر مانع از آن میشود که مهاجمان بدانند کدام متن رمز شده با یک متن اصلی مشخص مطابقت دارد.
رمزگذاری ترکیبی در 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: دیفی-هلمن روی Curve25519 با HKDF-SHA-256 برای استخراج راز مشترک.
- KDF: HKDF-SHA-256 برای استخراج زمینه فرستنده و گیرنده.
- AEAD: AES-256-GCM با نانسهای ۱۲ بایتی که طبق استاندارد 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
حداقل خواص
- اطلاعات متنی و متنی میتوانند طول دلخواهی داشته باشند (در محدوده ۰ تا ۲ و ۳۲ بایت)
- ایمن در برابر حملات متن رمز شده انتخابی تطبیقی
- امنیت ۱۲۸ بیتی برای طرحهای مبتنی بر منحنی بیضوی