Chcę wymieniać dane

Na potrzeby większości przypadków użycia szyfrowania klucza publicznego zalecamy użycie elementu podstawowego Szyfrowanie hybrydowe z typem klucza DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, AES_256_GCM.

Szyfrowanie klucza publicznego obejmuje ochronę danych za pomocą 2 kluczy: publicznego i prywatnego. Do szyfrowania używany jest klucz publiczny, a klucz prywatny do odszyfrowywania. To dobry wybór, jeśli nadawca nie może przechowywać obiektów tajnych i musi zaszyfrować dane przy użyciu klucza publicznego.

Z podanych niżej przykładów dowiesz się, jak zacząć korzystać z podstawowego szyfrowania hybrydowego:

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 "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 << "'." << std::endl;
  std::clog << "The resulting output will be written to " << output_filename
            << std::endl;

  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

INSTRUKCJE

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

Szyfrowanie hybrydowe

Podstawowe szyfrowanie hybrydowe łączy wydajność szyfrowania symetrycznego z wygodą kryptografii klucza publicznego (asymetrycznego). Każdy może zaszyfrować dane za pomocą klucza publicznego, ale tylko użytkownicy mający klucz prywatny mogą odszyfrować dane.

W przypadku szyfrowania hybrydowego nadawca generuje nowy klucz symetryczny do szyfrowania tekstu jawnego każdej wiadomości w celu wygenerowania tekstu szyfrowania. Ten klucz symetryczny jest zasłonięty kluczem publicznym odbiorcy. W przypadku odszyfrowywania hybrydowego klucz symetryczny jest odblokowywany przez odbiorcę, a następnie używany do odszyfrowania tekstu szyfrowania w celu odzyskania pierwotnego tekstu jawnego. Szczegółowe informacje o tym, jak przechowywać lub przesyłać tekst szyfrowany razem z herbatą klucza, znajdziesz w sekcji Format przewodu hybrydowego szyfrowania Tink.

Szyfrowanie hybrydowe ma następujące właściwości:

  • Tajność: nikt nie będzie w stanie uzyskać żadnych informacji o zaszyfrowanym tekście (z wyjątkiem jego długości), chyba że ma dostęp do klucza prywatnego.
  • Asymetria: tekst szyfrowania można szyfrować za pomocą klucza publicznego, ale do odszyfrowania wymagany jest klucz prywatny.
  • Kolejność losowa: szyfrowanie jest losowe. 2 wiadomości z tym samym tekstem jawnym nie będą generowały tego samego tekstu szyfrowanego. Dzięki temu atakujący nie wiedzą, który tekst szyfrowany odpowiada danemu tekstowi jawnemu.

Szyfrowanie hybrydowe jest przedstawiane w Tink jako para elementów podstawowych:

  • HybridEncrypt – szyfrowanie
  • HybridDecrypt do odszyfrowywania.

Parametr informacji o kontekście

Oprócz tekstu jawnego szyfrowanie hybrydowe akceptuje też dodatkowy parametr context_info, który zwykle jest danymi publicznymi wynikającymi z kontekstu, ale powinien być powiązany z wynikowym tekstem szyfrowanym. Oznacza to, że tekst szyfrowania umożliwia potwierdzenie integralności informacji kontekstowych, ale nie gwarantujemy ich tajności ani autentyczności. Rzeczywiste informacje kontekstowe mogą być puste lub mieć wartość null, ale aby zagwarantować poprawne odszyfrowanie wynikowego tekstu szyfrowanego, podczas odszyfrowywania należy podać tę samą wartość informacji kontekstowych.

Konkretna implementacja szyfrowania hybrydowego może wiązać informacje kontekstowe z tekstem szyfrowanym na różne sposoby, na przykład:

  • Użyj context_info jako powiązanego źródła danych do szyfrowania symetrycznego AEAD (por. RFC 5116).
  • Użyj context_info jako danych wejściowych „CtxInfo” dla HKDF (jeśli implementacja wykorzystuje HKDF jako funkcję derywacji klucza, patrz RFC 5869).

Wybierz typ klucza

W większości przypadków zalecamy używanie typu klucza DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM. Ten typ klucza implementuje standard hybrydowego szyfrowania klucza publicznego (HPKE) zgodnie z opisem w RFC 9180. HPKE składa się z mechanizmu pokrywania kluczy (KEM), funkcji derywacji klucza (KDF) oraz uwierzytelnionego szyfrowania z użyciem algorytmu powiązanych danych (AEAD).

Firma DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM zatrudnia przede wszystkim:

  • KEM: Diffie–Hellman nad Curve25519 i HKDF-SHA-256, aby uzyskać udostępniony klucz.
  • KDF: HKDF-SHA-256 do uzyskania kontekstu nadawcy i odbiorcy.
  • AEAD: AES-256-GCM z 12-bajtowymi liczbami jednorazowymi wygenerowanymi zgodnie ze standardem HPKE.

Inne obsługiwane typy kluczy HPKE to m.in.:

  • 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

Więcej informacji o algorytmach KEM, KDF i AEAD znajdziesz w RFC 9180.

Chociaż nie jest już to zalecane, Tink obsługuje też niektóre odmiany ECIES, zgodnie z opisem w normie ISO 18033-2 firmy Victor Shoup. Poniżej znajdziesz kilka obsługiwanych typów kluczy 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

Minimalne właściwości

  • Zwykły tekst i informacje kontekstowe mogą mieć dowolną długość (w zakresie 0,.232 bajtów)
  • Ochrona przed adaptacyjnymi atakami wybranymi przez tekst szyfrowany
  • 128-bitowe zabezpieczenia dla schematów opartych na krzywych eliptycznych