أريد تبادل البيانات

ننصحك باستخدام العنصر الأساسي التشفير المختلط مع نوع المفتاح DHKEM_X25519_HKDF_SHA256 وHKDF_SHA256 وAES_256_GCM لمعظم حالات استخدام التشفير بالمفتاح العام.

يتضمّن التشفير باستخدام المفتاح العام حماية البيانات باستخدام مفتاحَين: أحدهما عام والآخر خاص. يُستخدم المفتاح العام للتشفير، بينما يُستخدم المفتاح الخاص لفك التشفير. يعدّ هذا الخيار مناسبًا إذا كان المرسِل لا يمكنه تخزين الأسرار ويحتاج إلى تشفير البيانات باستخدام مفتاح عام.

تساعدك الأمثلة التالية في البدء باستخدام أداة Hybrid Encryption الأساسية:

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;
}

Go

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() {}
}

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 لمعرفة تفاصيل حول كيفية تخزين أو نقل النص المشفّر مع تغليف المفتاح.

تتضمّن ميزة "التشفير المختلط" الخصائص التالية:

  • Secrecy: لا يمكن لأي شخص الحصول على أي معلومات حول النص العادي المشفّر (باستثناء طوله)، ما لم يكن لديه إذن الوصول إلى المفتاح الخاص.
  • عدم التماثل: يمكن تشفير النص المشفّر باستخدام المفتاح العام، ولكن لفك التشفير، يجب استخدام المفتاح الخاص.
  • التوزيع العشوائي: يتم توزيع التشفير بشكل عشوائي. لن تؤدي رسالتان تتضمّنان النص العادي نفسه إلى إنشاء النص المشفّر نفسه. يمنع ذلك المهاجمين من معرفة النص المشفّر الذي يتوافق مع نص عادي معيّن.

يتم تمثيل التشفير المختلط في 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: Diffie–Hellman over Curve25519 with HKDF-SHA-256 to derive the shared secret.
  • دالة اشتقاق المفاتيح (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 الخاص بـ &quot;فيكتور شوب&quot;. في ما يلي بعض أنواع مفاتيح 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 بت للمخططات المستندة إلى المنحنى الإهليلجي