Tôi muốn mã hoá dữ liệu

Bạn nên sử dụng phương thức gốc AEAD bằng loại khoá AES128_GCM cho hầu hết các trường hợp sử dụng phương thức mã hoá dữ liệu.

Quy trình mã hoá xác thực bằng dữ liệu được liên kết (AEAD) là phương pháp nguyên gốc đơn giản và phù hợp nhất cho hầu hết các trường hợp sử dụng. AEAD cung cấp tính bảo mật và xác thực, đồng thời đảm bảo rằng thông báo luôn có các bản mật mã khác nhau (đầu ra đã mã hoá) ngay cả khi văn bản thuần tuý (dữ liệu đầu vào cho quá trình mã hoá) giống nhau. Lớp này đối xứng, 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 giúp bạn bắt đầu sử dụng dữ liệu gốc AEAD:

C++

// A command-line utility for testing Tink 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/aead.h"
#include "tink/aead/aead_config.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 AEAD (default: empty");

namespace {

using ::crypto::tink::Aead;
using ::crypto::tink::AeadConfig;
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 {

// AEAD example CLI implementation.
Status AeadCli(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 = AeadConfig::Register();
  if (!result.ok()) return result;

  // Read the 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<Aead>> aead =
      (*keyset_handle)
          ->GetPrimitive<crypto::tink::Aead>(
              crypto::tink::ConfigGlobalRegistry());
  if (!aead.ok()) return aead.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> encrypt_result =
        (*aead)->Encrypt(*input_file_content, associated_data);
    if (!encrypt_result.ok()) return encrypt_result.status();
    output = encrypt_result.value();
  } else {  // operation == kDecrypt.
    StatusOr<std::string> decrypt_result =
        (*aead)->Decrypt(*input_file_content, associated_data);
    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 associated_data = absl::GetFlag(FLAGS_associated_data);

  std::clog << "Using keyset from file " << keyset_filename << " to 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::AeadCli(mode, keyset_filename, input_filename,
                                     output_filename, associated_data));
  return 0;
}

Go


import (
	"bytes"
	"fmt"
	"log"

	"github.com/tink-crypto/tink-go/v2/aead"
	"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_GCM". Note
	// that this keyset has the secret key information in cleartext.
	jsonKeyset := `{
			"key": [{
					"keyData": {
							"keyMaterialType":
									"SYMMETRIC",
							"typeUrl":
									"type.googleapis.com/google.crypto.tink.AesGcmKey",
							"value":
									"GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg=="
					},
					"keyId": 294406504,
					"outputPrefixType": "TINK",
					"status": "ENABLED"
			}],
			"primaryKeyId": 294406504
	}`

	// 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 AEAD primitive we want to use from the keyset handle.
	primitive, err := aead.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.Encrypt(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.Decrypt(ciphertext, associatedData)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(decrypted))
	// Output: message
}

Java

package aead;

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

import com.google.crypto.tink.Aead;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.TinkJsonProtoKeysetFormat;
import com.google.crypto.tink.aead.AeadConfig;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * A command-line utility for encrypting small files with 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 AeadExample {
  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 AeadExample 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);
    }
    // Register all AEAD key types with the Tink runtime.
    AeadConfig.register();

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

    // Get the primitive.
    Aead aead = handle.getPrimitive(Aead.class);

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

  private AeadExample() {}
}

Obj-C

HƯỚNG DẪN

Python

import tink
from tink import aead
from tink import secret_key_access


def example():
  """Encrypt and decrypt using AEAD."""
  # Register the AEAD key managers. This is needed to create an Aead primitive
  # later.
  aead.register()

  # A keyset created with "tinkey create-keyset --key-template=AES256_GCM". Note
  # that this keyset has the secret key information in cleartext.
  keyset = r"""{
      "key": [{
          "keyData": {
              "keyMaterialType":
                  "SYMMETRIC",
              "typeUrl":
                  "type.googleapis.com/google.crypto.tink.AesGcmKey",
              "value":
                  "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg=="
          },
          "keyId": 294406504,
          "outputPrefixType": "TINK",
          "status": "ENABLED"
      }],
      "primaryKeyId": 294406504
  }"""

  # Create a keyset handle from the cleartext keyset in the previous
  # step. The keyset handle provides abstract access to the underlying keyset to
  # limit access of 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 Aead primitive we want to use from the keyset handle.
  primitive = keyset_handle.primitive(aead.Aead)

  # 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(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(ciphertext, b'associated_data')

AEAD

Dữ liệu nguyên gốc Mã hoá xác thực với dữ liệu được liên kết (AEAD) là cơ sở dữ liệu phổ biến nhất để mã hoá dữ liệu và phù hợp với hầu hết các nhu cầu.

AEAD có các thuộc tính sau:

  • Bảo mật: Không có thông tin nào về văn bản thuần tuý được biết, ngoại trừ độ dài của văn bản đó.
  • Tính xác thực: Hệ thống không thể thay đổi văn bản thuần tuý đã mã hoá bên dưới bản mật mã nếu không phát hiện được.
  • Đối xứng: Việc mã hoá văn bản thuần tuý và giải mã bản mật mã được thực hiện bằng cùng một khoá.
  • Sắp xếp ngẫu nhiên: Quá trình mã hoá sẽ diễn ra ngẫu nhiên. Hai thông điệp có cùng văn bản thuần tuý sẽ tạo ra các bản mật mã khác nhau. Kẻ tấn công không thể biết mật mã nào tương ứng với một văn bản thuần tuý nhất định. Nếu bạn muốn tránh việc này, hãy sử dụng Deterministic AEAD.

Dữ liệu liên quan

Bạn có thể sử dụng AEAD để liên kết văn bản mật mã với dữ liệu cụ thể được liên kết. 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 liên kết khi mã hoá encrypted-medical-history. Điều này ngăn kẻ tấn công di chuyển lịch sử bệnh từ người dùng này sang người dùng khác.

Chọn loại khoá

Mặc dù bạn nên dùng AES128_GCM cho hầu hết các trường hợp sử dụng, nhưng vẫn có nhiều loại khoá cho các nhu cầu khác nhau (đối với bảo mật 256 bit, hãy thay AES128 bằng AES256 dưới đây). Thông thường:

  • AES128_CTR_HMAC_SHA256 với Vectơ khởi tạo (IV) 16 byte là chế độ thận trọng nhất với các giới hạn tốt.
  • AES128_EAX ít thận trọng hơn một chút và nhanh hơn một chút so với AES128_CTR_HMAC_SHA256.
  • AES128_GCM thường là chế độ nhanh nhất, có giới hạn nghiêm ngặt nhất về số lượng tin nhắn và kích thước tin nhắn. Khi vượt quá các giới hạn về độ dài dữ liệu được liên kết và văn bản thuần tuý (dưới đây), AES128_GCM sẽ lỗi và rò rỉ nội dung chính.
  • AES128_GCM_SIV có tốc độ gần bằng AES128_GCM, có giới hạn rất tốt cho số lượng tin nhắn lớn, nhưng ít được thiết lập hơn một chút. Để dùng hàm này trong Java, bạn phải cài đặt Conscrypt.
  • XChaCha20Poly1305 có giới hạn về số lượng thông báo và kích thước thông báo lớn hơn nhiều so với AES128_GCM, nhưng khi không thành công (rất hiếm khi) thì nội dung chính cũng sẽ bị rò rỉ. Quá trình này không được tăng tốc phần cứng, nên có thể chậm hơn các chế độ AES trong trường hợp có tính năng tăng tốc phần cứng.

Đảm bảo khả năng bảo mật

Dịch vụ triển khai AEAD cung cấp:

  • Bảo mật CCA2.
  • Độ mạnh xác thực tối thiểu 80 bit.
  • Khả năng mã hoá ít nhất 232 thông báo với tổng cộng 250 byte. Không có cuộc tấn công nào có tối đa 232 văn bản thuần tuý đã chọn hoặc thuật toán mật mã đã chọn có xác suất thành công lớn hơn 2-32.