ฉันต้องการแลกเปลี่ยนข้อมูล

เราขอแนะนำแบบพื้นฐานการเข้ารหัสแบบผสมที่มี คีย์ DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, AES_256_GCM ประเภทสำหรับ Use Case การเข้ารหัสคีย์สาธารณะส่วนใหญ่

การเข้ารหัสคีย์สาธารณะเกี่ยวข้องกับการปกป้องข้อมูลด้วยคีย์ 2 คีย์ ได้แก่ คีย์สาธารณะและคีย์หนึ่ง ส่วนตัว ระบบจะใช้คีย์สาธารณะในการเข้ารหัส และใช้คีย์ส่วนตัว สำหรับการถอดรหัส นี่คือตัวเลือกที่ดีหากผู้ส่งไม่สามารถจัดเก็บข้อมูลลับและ จำเป็นต้องเข้ารหัสข้อมูลด้วยคีย์สาธารณะ

ตัวอย่างต่อไปนี้จะช่วยคุณเริ่มต้นใช้งานการเข้ารหัสแบบผสมแบบไฮบริด

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

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
}

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')

การเข้ารหัสแบบผสม

รูปแบบพื้นฐานของการเข้ารหัสแบบไฮบริดจะรวมประสิทธิภาพของการเข้ารหัสแบบสมมาตรเข้าด้วยกัน ด้วยความสะดวกของวิทยาการเข้ารหัสคีย์สาธารณะ (อสมมาตร) ทุกคนสามารถเข้ารหัส โดยใช้คีย์สาธารณะ แต่มีเพียงผู้ใช้ที่มีคีย์ส่วนตัวเท่านั้นที่สามารถถอดรหัส

สำหรับการเข้ารหัสแบบผสม ผู้ส่งจะสร้างคีย์สมมาตรใหม่เพื่อเข้ารหัส ข้อความธรรมดาของแต่ละข้อความเพื่อสร้างข้อความเข้ารหัส คีย์แบบสมมาตรนั้น ห่อหุ้มด้วยคีย์สาธารณะของผู้รับ สำหรับการถอดรหัสแบบไฮบริด ผู้รับจะถอดรหัสคีย์แบบสมมาตรแล้วจึงใช้เพื่อถอดรหัส Ciphertext เพื่อกู้คืนข้อความธรรมดาต้นฉบับ โปรดดูสายการเข้ารหัสแบบไฮบริดของ Pink รูปแบบเพื่อดูรายละเอียดเกี่ยวกับวิธีจัดเก็บหรือ ส่งข้อความการเข้ารหัสพร้อมกับการห่อหุ้มคีย์

การเข้ารหัสแบบไฮบริดมีคุณสมบัติต่อไปนี้

  • 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 ผ่าน Curve25519 กับ HKDF-SHA-256 เพื่อดึงข้อมูลการแชร์ ข้อมูลลับ
  • KDF: HKDF-SHA-256 เพื่อสร้างบริบทของผู้ส่งและผู้รับ
  • AEAD: AES-256-GCM ที่มี nonces 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 ของ Victor Shoup มาตรฐาน คีย์ 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 บิตสำหรับรูปแบบที่ใช้กราฟวงรี