نقترح استخدام الطريقة الأساسية للتشفير المختلط مع مفتاح 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/check.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" #include "tink/util/status.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; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; constexpr absl::string_view kEncrypt = "encrypt"; constexpr absl::string_view kDecrypt = "decrypt"; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { 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) { 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. StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = ReadJsonCleartextKeyset(keyset_filename); if (!keyset_handle.ok()) return keyset_handle.status(); // Read the input. 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. 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. 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. 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. 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'; 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.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(HybridEncrypt.class); // Use the primitive to encrypt data. byte[] ciphertext = encryptor.encrypt(input, contextInfo); Files.write(outputFile, ciphertext); } else { HybridDecrypt decryptor = handle.getPrimitive(HybridDecrypt.class); // Use the primitive to decrypt data. byte[] plaintext = decryptor.decrypt(input, contextInfo); Files.write(outputFile, plaintext); } } private HybridExample() {} }
Obj-C
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 للاطلاع على تفاصيل حول كيفية تخزين أو نقل النص المشفر مع تغليف المفتاح.
للتشفير المختلط السمات التالية:
- Secrecy: لا يمكن لأحد الحصول على أي معلومات عن الملفات المشفّرة نص عادي (باستثناء الطول)، ما لم يكن لديهم حق الوصول إلى المفتاح الخاص.
- عدم التماثل: يمكن تشفير النص المُشفر باستخدام المفتاح العام، ولكن لفك التشفير، يجب استخدام المفتاح الخاص.
- التوزيع العشوائي: يتم ترتيب التشفير عشوائيًا. رسالتان لهما نفس لن يقدم النص العادي نفس النص المُشفر. وهذا يمنع المهاجمين من معرفة النص المشفر الذي يتوافق مع نص عادي معين.
يتم تمثيل التشفير المختلط في Tink كزوج من الأساسيات:
- HybridEncrypt للتشفير
- HybridDecrypt لفك التشفير
مَعلمة معلومات السياق
وبالإضافة إلى النص العادي، تقبل طريقة التشفير المختلط معاملاً إضافيًا،
context_info
، والتي عادةً ما تكون بيانات عامة ضمنية من السياق، ولكن
بالنص المُشفر الناتج. وهذا يعني أن النص المشفر
بتأكيد تكامل معلومات السياق، ولكن لا
يضمن سريته أو مصداقيته. يمكن أن تكون معلومات السياق الفعلية فارغة.
خالية، ولكن لضمان فك التشفير الصحيح للنص المُشفر الناتج،
يجب توفير قيمة معلومات السياق نفسها لفك التشفير.
يمكن أن يؤدي التنفيذ الملموس للتشفير المختلط إلى ربط معلومات السياق النص المُشفر بطرق مختلفة، على سبيل المثال:
- استخدام
context_info
كإدخال بيانات مرتبط للتشفير المتماثل AEAD (راجع RFC 5116). - استخدام
context_info
كـ "CtxInfo" مدخلات لإطار هونغ كونغ (HKDF) (إذا كان التنفيذ يستخدم HKDF كدالة اشتقاق رئيسية، cf. 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: خوارزمية Diffie–Hellman over Curve25519 باستخدام 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
راجع RFC 9180 للحصول على المزيد من التفاصيل حول خيارات الخوارزميات لكل من KEM وKDF وAEAD.
على الرغم من أنّ 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.232 بايت)
- توفير الحماية ضد هجمات النص المُشفر التي تم اختيارها بشكل تكيُّفي
- أمان 128 بت للمخططات المستندة إلى منحنى القطع الناقص