Tôi muốn mã hóa dữ liệu theo cách xác định

Bạn nên sử dụng nguyên tắc cơ bản AEAD xác định với loại khoá AES256_SIV.

Phương thức gốc Mã hoá đã xác thực có tính xác định với dữ liệu liên kết (Deterministic AEAD) tạo ra các văn bản mã hoá ổn định: việc mã hoá một văn bản thuần tuý nhất định luôn trả về cùng một văn bản mã hoá. Đây là thuật toán đối xứng, tức là thuật toán này sử dụng một khoá duy nhất cho cả quá trình mã hoá và giải mã.

Các ví dụ sau đây sẽ giúp bạn bắt đầu sử dụng nguyên thuỷ AEAD có tính xác định:

C++

// A command-line utility for testing Tink Deterministic AEAD.
#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 "tink/daead/deterministic_aead_config.h"
#include "tink/deterministic_aead.h"
#include "util/util.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, "", "Filename to operate on");
ABSL_FLAG(std::string, output_filename, "", "Output file name");
ABSL_FLAG(std::string, associated_data, "",
          "Associated data for Deterministic AEAD (default: empty");

namespace {

using ::crypto::tink::DeterministicAead;
using ::crypto::tink::DeterministicAeadConfig;
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 {

// Deterministic AEAD example CLI implementation.
Status DeterministicAeadCli(absl::string_view mode,
                            const std::string& keyset_filename,
                            const std::string& input_filename,
                            const std::string& output_filename,
                            absl::string_view associated_data) {
  Status result = DeterministicAeadConfig::Register();
  if (!result.ok()) return result;

  // Read keyset from file.
  StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
      ReadJsonCleartextKeyset(keyset_filename);
  if (!keyset_handle.ok()) return keyset_handle.status();

  // Get the primitive.
  StatusOr<std::unique_ptr<DeterministicAead>> daead =
      (*keyset_handle)
          ->GetPrimitive<crypto::tink::DeterministicAead>(
              crypto::tink::ConfigGlobalRegistry());
  if (!daead.ok()) return daead.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) {
    StatusOr<std::string> result = (*daead)->EncryptDeterministically(
        *input_file_content, associated_data);
    if (!result.ok()) return result.status();
    output = *result;
  } else if (mode == kDecrypt) {
    StatusOr<std::string> result = (*daead)->DecryptDeterministically(
        *input_file_content, associated_data);
    if (!result.ok()) return result.status();
    output = *result;
  }

  // Write output to 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 associated_data = absl::GetFlag(FLAGS_associated_data);

  std::clog << "Using keyset from file " << keyset_filename
            << " to Deterministic AEAD-" << mode << " file " << input_filename
            << " with associated data '" << associated_data << "'."
            << std::endl;
  std::clog << "The resulting output will be written to " << output_filename
            << "." << std::endl;

  CHECK_OK(tink_cc_examples::DeterministicAeadCli(
      mode, keyset_filename, input_filename, output_filename, associated_data));
  return 0;
}

Go

import (
	"bytes"
	"fmt"
	"log"

	"github.com/tink-crypto/tink-go/v2/daead"
	"github.com/tink-crypto/tink-go/v2/insecurecleartextkeyset"
	"github.com/tink-crypto/tink-go/v2/keyset"
)

func Example() {
	// A keyset created with "tinkey create-keyset --key-template=AES256_SIV". Note
	// that this keyset has the secret key information in cleartext.
	jsonKeyset := `{
			"key": [{
				"keyData": {
						"keyMaterialType":
								"SYMMETRIC",
						"typeUrl":
								"type.googleapis.com/google.crypto.tink.AesSivKey",
						"value":
								"EkAl9HCMmKTN1p3V186uhZpJQ+tivyc4IKyE+opg6SsEbWQ/WesWHzwCRrlgRuxdaggvgMzwWhjPnkk9gptBnGLK"
				},
				"keyId": 1919301694,
				"outputPrefixType": "TINK",
				"status": "ENABLED"
		}],
		"primaryKeyId": 1919301694
	}`

	// Create a keyset handle from the cleartext keyset in the previous
	// step. 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 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.
	keysetHandle, err := insecurecleartextkeyset.Read(
		keyset.NewJSONReader(bytes.NewBufferString(jsonKeyset)))
	if err != nil {
		log.Fatal(err)
	}

	// Retrieve the DAEAD primitive we want to use from the keyset handle.
	primitive, err := daead.New(keysetHandle)
	if err != nil {
		log.Fatal(err)
	}

	// Use the 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).
	plaintext := []byte("message")
	associatedData := []byte("associated data")
	ciphertext, err := primitive.EncryptDeterministically(plaintext, associatedData)
	if err != nil {
		log.Fatal(err)
	}

	// Use the 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 returns an error.
	decrypted, err := primitive.DecryptDeterministically(ciphertext, associatedData)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(ciphertext)
	fmt.Println(string(decrypted))
	// Output:
	// [1 114 102 56 62 150 98 146 84 99 211 36 127 214 229 231 157 56 143 192 250 132 32 153 124 244 238 112]
	// message
}

Java

package deterministicaead;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.crypto.tink.DeterministicAead;
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.daead.DeterministicAeadConfig;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * A command-line utility for encrypting small files with Deterministic AEAD.
 *
 * <p>It loads cleartext keys from disk - this is not recommended!
 *
 * <p>It requires the following arguments:
 *
 * <ul>
 *   <li>mode: Can be "encrypt" or "decrypt" to encrypt/decrypt the input to the output.
 *   <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] associated-data: Associated data used for the encryption or decryption.
 */
public final class DeterministicAeadExample {
  private static final String MODE_ENCRYPT = "encrypt";
  private static final String MODE_DECRYPT = "decrypt";

  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 DeterministicAeadExample encrypt/decrypt key-file input-file output-file"
              + " [associated-data]");
      System.exit(1);
    }
    String mode = args[0];
    Path keyFile = Paths.get(args[1]);
    Path inputFile = Paths.get(args[2]);
    Path outputFile = Paths.get(args[3]);
    byte[] associatedData = new byte[0];
    if (args.length == 5) {
      associatedData = args[4].getBytes(UTF_8);
    }

    // Initialise Tink: register all Deterministic AEAD key types with the Tink runtime
    DeterministicAeadConfig.register();

    // Read the keyset into a KeysetHandle
    KeysetHandle handle =
        TinkJsonProtoKeysetFormat.parseKeyset(
            new String(Files.readAllBytes(keyFile), UTF_8), InsecureSecretKeyAccess.get());

    // Get the primitive
    DeterministicAead daead =
        handle.getPrimitive(RegistryConfiguration.get(), DeterministicAead.class);

    // Use the primitive to encrypt/decrypt files.
    if (MODE_ENCRYPT.equals(mode)) {
      byte[] plaintext = Files.readAllBytes(inputFile);
      byte[] ciphertext = daead.encryptDeterministically(plaintext, associatedData);
      Files.write(outputFile, ciphertext);
    } else if (MODE_DECRYPT.equals(mode)) {
      byte[] ciphertext = Files.readAllBytes(inputFile);
      byte[] plaintext = daead.decryptDeterministically(ciphertext, associatedData);
      Files.write(outputFile, plaintext);
    } else {
      System.err.println("The first argument must be either encrypt or decrypt, got: " + mode);
      System.exit(1);
    }

    System.exit(0);
  }

  private DeterministicAeadExample() {}
}

Python

import tink
from tink import daead
from tink import secret_key_access


def example():
  """Encrypt and decrypt using deterministic AEAD."""
  # Register the deterministic AEAD key manager. This is needed to create a
  # DeterministicAead primitive later.
  daead.register()

  # A keyset created with "tinkey create-keyset --key-template=AES256_SIV". Note
  # that this keyset has the secret key information in cleartext.
  keyset = r"""{
      "key": [{
          "keyData": {
              "keyMaterialType":
                  "SYMMETRIC",
              "typeUrl":
                  "type.googleapis.com/google.crypto.tink.AesSivKey",
              "value":
                  "EkAl9HCMmKTN1p3V186uhZpJQ+tivyc4IKyE+opg6SsEbWQ/WesWHzwCRrlgRuxdaggvgMzwWhjPnkk9gptBnGLK"
          },
          "keyId": 1919301694,
          "outputPrefixType": "TINK",
          "status": "ENABLED"
      }],
      "primaryKeyId": 1919301694
  }"""

  # Create a keyset handle from the cleartext keyset in the previous
  # step. 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 cleartext_keyset_handle, as it implies
  # that your key material is passed in cleartext which is a security risk.
  keyset_handle = tink.json_proto_keyset_format.parse(
      keyset, secret_key_access.TOKEN
  )

  # Retrieve the DeterministicAead primitive we want to use from the keyset
  # handle.
  primitive = keyset_handle.primitive(daead.DeterministicAead)

  # Use the 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 = primitive.encrypt_deterministically(b'msg', b'associated_data')

  # Use the 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.
  output = primitive.decrypt_deterministically(ciphertext, b'associated_data')

AEAD xác định

Nguyên tắc Mã hoá đã xác thực có tính xác định với dữ liệu liên kết (Deterministic AEAD) cung cấp hoạt động mã hoá có thuộc tính xác định: việc mã hoá cùng một dữ liệu luôn tạo ra cùng một văn bản mã hoá. Loại mã hoá này rất hữu ích cho việc bao bọc khoá hoặc cho một số lược đồ tìm kiếm trên dữ liệu đã mã hoá (xem RFC 5297, Phần 1.3 để biết thêm thông tin). Do thuộc tính xác định của nó, việc triển khai nguyên tắc cơ bản này có thể dẫn đến việc mất tính bí mật vì kẻ tấn công chỉ cần tìm ra văn bản mã hoá cho một thông báo nhất định để xác định các phiên bản khác của thông báo đó.

AEAD xác định có các thuộc tính sau:

  • Secrecy: Không có thông tin nào về văn bản thuần tuý được biết, ngoại trừ độ dài và tính bình đẳng của các văn bản thuần tuý lặp lại.
  • Tính xác thực: Không thể thay đổi văn bản thuần tuý được mã hoá làm cơ sở cho văn bản mã hoá mà không bị phát hiện.
  • Đối xứng: Quá trình mã hoá văn bản thuần tuý và giải mã văn bản mã hoá được thực hiện bằng cùng một khoá.
  • Xác định: Miễn là khoá chính không thay đổi, việc mã hoá văn bản thuần tuý hai lần bằng cùng một tham số sẽ cho ra cùng một văn bản mã hoá.

Dữ liệu liên quan

Bạn có thể dùng AEAD xác định để liên kết văn bản mã hoá với dữ liệu liên kết cụ thể. Giả sử bạn có một cơ sở dữ liệu với các trường user-idencrypted-medical-history. Trong trường hợp này, user-id có thể được dùng làm dữ liệu được liên kết khi mã hoá encrypted-medical-history. Điều này ngăn chặn kẻ tấn công di chuyển nhật ký y tế từ người dùng này sang người dùng khác.

Bạn không bắt buộc phải cung cấp dữ liệu liên kết. Nếu được chỉ định, quá trình giải mã chỉ thành công nếu cùng một dữ liệu được liên kết được truyền đến cả lệnh gọi mã hoá và giải mã.

Chọn loại khoá

Bạn nên sử dụng loại khoá AES256_SIV cho mọi trường hợp sử dụng.

Đảm bảo về bảo mật

  • Độ mạnh của chế độ xác thực ít nhất là 80 bit.
  • Văn bản thuần tuý và dữ liệu liên kết có thể có độ dài tuỳ ý (trong phạm vi từ 0 đến 232 byte).
  • Mức độ bảo mật 128 bit chống lại các cuộc tấn công khôi phục khoá, cũng như trong các cuộc tấn công nhiều người dùng với tối đa 232 khoá – tức là nếu đối phương thu được 232 văn bản mã hoá của cùng một thông báo được mã hoá theo 232 khoá, thì họ cần thực hiện 2128 phép tính để thu được một khoá duy nhất.
  • Khả năng mã hoá an toàn 238 thư, miễn là mỗi thư có độ dài dưới 1 MB.