Bạn nên sử dụng loại khoá AES128_GCM cho hầu hết các trường hợp sử dụng mã hoá dữ liệu với hàm gốc AEAD.
Mã hoá đã xác thực với dữ liệu liên kết (AEAD) là phương thức 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 đảm bảo tính bảo mật và tính xác thực, đồng thời đảm bảo rằng các thông điệp luôn có văn bản đã mã hoá khác nhau (đầu ra đã mã hoá) ngay cả khi văn bản thô (dữ liệu đầu vào để mã hoá) giống nhau. Phương thức này là đố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 sẽ giúp bạn bắt đầu sử dụng nguyên hàm 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 "tink/config/global_registry.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 << "'." << '\n'; std::clog << "The resulting output will be written to " << output_filename << '\n'; 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
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
Phương thức gốc Mã hoá đã xác thực với dữ liệu liên kết (AEAD) là phương thức gốc 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:
- Secrecy: Không biết gì về văn bản thô, ngoại trừ độ dài.
- Tính xác thực: Không thể thay đổi văn bản thô đã mã hoá cơ bản của 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 thô và giải mã văn bản đã mã hoá được thực hiện bằng cùng một khoá.
- Sắp xếp ngẫu nhiên: Phương thức mã hoá được sắp xếp ngẫu nhiên. Hai thông điệp có cùng văn bản thô sẽ tạo ra các văn bản đã mã hoá khác nhau. Kẻ tấn công không thể biết văn bản đã mã hoá nào tương ứng với một văn bản thô nhất định. Nếu muốn tránh điều này, hãy sử dụng AEAD có tính xác định.
Dữ liệu liên quan
Bạn có thể sử dụng AEAD để 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-id
và encrypted-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 giúp ngăn kẻ tấn công chuyển hồ sơ y tế 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 sử dụng AES128_GCM cho hầu hết các trường hợp sử dụng, nhưng có nhiều loại khoá cho các nhu cầu khác nhau (để bảo mật 256 bit, hãy thay thế AES128 bằng AES256 ở bên dưới). Nhìn chung:
- AES128_CTR_HMAC_SHA256 với Vectơ khởi chạy (IV) 16 byte là chế độ bảo thủ nhất với các giới hạn tốt.
- AES128_EAX ít bảo thủ 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, với cá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 này về văn bản thô và chiều dài dữ liệu liên kết (dưới đây), AES128_GCM sẽ không thành công và rò rỉ tài liệu khoá.
- AES128_GCM_SIV gần như nhanh như AES128_GCM. Phương thức này có các giới hạn giống như AES128_GCM về số lượng và kích thước thông báo, nhưng khi vượt quá các giới hạn này, phương thức này sẽ không thành công theo cách ít nghiêm trọng hơn: phương thức này có thể chỉ rò rỉ thực tế là hai thông báo bằng nhau. Điều này giúp bạn sử dụng an toàn hơn so với AES128_GCM, nhưng phương thức này ít được sử dụng rộng rãi hơn trong thực tế. Để sử dụng tính năng này trong Java, bạn phải cài đặt Conscrypt.
- XChaCha20Poly1305 có giới hạn lớn hơn nhiều về số lượng tin nhắn và kích thước tin nhắn so với AES128_GCM, nhưng khi không thành công (rất khó xảy ra), phương thức này cũng rò rỉ tài liệu khoá. Chế độ này không được tăng tốc phần cứng, vì vậy, có thể chậm hơn các chế độ AES trong trường hợp có thể tăng tốc phần cứng.
Cam kết bảo mật
Các phương thức triển khai AEAD cung cấp:
- Bảo mật CCA2.
- Độ mạnh của chế độ xác thực tối thiểu là 80 bit.
- Khả năng mã hoá ít nhất 232 thư với tổng số 250 byte. Không có cuộc tấn công nào với tối đa 232 văn bản thuần tuý hoặc văn bản đã mã hoá được chọn có xác suất thành công lớn hơn 2-32.