เราขอแนะนําให้ใช้รูปแบบ AEAD กับประเภทคีย์ AES128_GCM สําหรับกรณีการใช้งานการเข้ารหัสข้อมูลส่วนใหญ่
การเข้ารหัสที่มีการรับรองพร้อมข้อมูลที่เกี่ยวข้อง (AEAD) เป็นรูปแบบพื้นฐานที่ง่ายและเหมาะสมที่สุดสําหรับ Use Case ส่วนใหญ่ AEAD ให้ความลับและความถูกต้อง และช่วยให้มั่นใจว่าข้อความจะมีข้อความที่เข้ารหัส (เอาต์พุตที่เข้ารหัส) แตกต่างกันเสมอ แม้ว่าข้อความธรรมดา (อินพุตสําหรับการเข้ารหัส) จะเหมือนกันก็ตาม โดยเป็นการเข้ารหัสแบบสมมาตรที่ใช้คีย์เดียวทั้งสำหรับการเข้ารหัสและถอดรหัส
ตัวอย่างต่อไปนี้จะช่วยคุณเริ่มต้นใช้งานพรอมต์ 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
รูปแบบการเข้ารหัสที่ตรวจสอบสิทธิ์พร้อมข้อมูลที่เกี่ยวข้อง (AEAD) เป็นรูปแบบการเข้ารหัสที่พบบ่อยที่สุดและเหมาะกับความต้องการส่วนใหญ่
AEAD มีพร็อพเพอร์ตี้ต่อไปนี้
- Secrecy: ไม่มีใครทราบข้อมูลใดๆ เกี่ยวกับข้อความธรรมดา ยกเว้นความยาว
- ความถูกต้อง: ไม่สามารถเปลี่ยนแปลงข้อความธรรมดาที่เข้ารหัสซึ่งอยู่เบื้องหลังข้อความที่เข้ารหัสได้โดยที่ไม่มีการตรวจพบ
- แบบสมมาตร: การเข้ารหัสข้อความธรรมดาและการถอดรหัสข้อความที่เข้ารหัสจะใช้คีย์เดียวกัน
- การสุ่ม: การเข้ารหัสเป็นแบบสุ่ม ข้อความ 2 รายการที่มีข้อความธรรมดาเหมือนกันจะให้ข้อความที่เข้ารหัสต่างกัน ผู้โจมตีจะไม่ทราบว่ามีการเข้ารหัสใดที่สอดคล้องกับข้อความธรรมดาที่ระบุ หากต้องการหลีกเลี่ยงปัญหานี้ ให้ใช้ AEAD แบบกำหนดได้แทน
ข้อมูลที่เกี่ยวข้อง
AEAD สามารถใช้เพื่อเชื่อมโยงข้อความที่เข้ารหัสกับข้อมูลที่เชื่อมโยงที่เฉพาะเจาะจง สมมติว่าคุณมีฐานข้อมูลที่มีช่อง user-id
และ encrypted-medical-history
ในกรณีนี้ user-id
สามารถใช้เป็นข้อมูลที่เชื่อมโยงเมื่อเข้ารหัส encrypted-medical-history
ซึ่งจะช่วยป้องกันไม่ให้ผู้โจมตีย้ายประวัติทางการแพทย์จากผู้ใช้รายหนึ่งไปยังอีกรายหนึ่ง
เลือกประเภทคีย์
แม้ว่าเราจะแนะนําให้ใช้ AES128_GCM สําหรับการใช้งานส่วนใหญ่ แต่ก็มีประเภทคีย์ต่างๆ สําหรับความต้องการที่แตกต่างกัน (สําหรับการรักษาความปลอดภัย 256 บิต ให้แทนที่ AES128 ด้วย AES256 ด้านล่าง) โดยทั่วไป
- AES128_CTR_HMAC_SHA256 ที่มีเวกเตอร์การเริ่มต้น (IV) 16 ไบต์เป็นโหมดที่อนุรักษ์นิยมมากที่สุดซึ่งมีขอบเขตที่ดี
- AES128_EAX มีประสิทธิภาพน้อยกว่าและเร็วกว่า AES128_CTR_HMAC_SHA256 เล็กน้อย
- โดยปกติแล้ว AES128_GCM จะเป็นโหมดที่เร็วที่สุด โดยมีขีดจํากัดที่เข้มงวดที่สุดเกี่ยวกับจํานวนข้อความและขนาดข้อความ เมื่อความยาวของข้อความธรรมดาและข้อมูลที่เกี่ยวข้อง (ด้านล่าง) เกินขีดจํากัดเหล่านี้ AES128_GCM จะใช้งานไม่ได้และข้อมูลคีย์จะรั่วไหล
- AES128_GCM_SIV ทำงานได้เร็วเกือบเท่า AES128_GCM ซึ่งมีข้อจำกัดเกี่ยวกับจำนวนข้อความและขนาดข้อความเหมือนกับ AES128_GCM แต่เมื่อมีการส่งข้อความเกินขีดจำกัดเหล่านี้ การเข้ารหัสจะดำเนินการไม่สำเร็จในลักษณะที่ร้ายแรงน้อยกว่า ซึ่งอาจเปิดเผยเฉพาะข้อเท็จจริงที่ว่าข้อความ 2 รายการเท่ากัน ซึ่งทำให้ใช้งานได้ปลอดภัยกว่า AES128_GCM แต่ในทางปฏิบัติแล้วมีการใช้งานน้อยกว่า หากต้องการใช้สิ่งนี้ใน Java คุณต้องติดตั้ง Conscrypt
- XChaCha20Poly1305 จำกัดจำนวนข้อความและขนาดข้อความได้มากกว่า AES128_GCM มาก แต่เมื่อดำเนินการไม่สำเร็จ (ซึ่งมีโอกาสน้อยมาก) ก็จะรั่วไหลข้อมูลคีย์ด้วย ซึ่งไม่มีการเร่งด้วยฮาร์ดแวร์ จึงอาจช้ากว่าโหมด AES ในสถานการณ์ที่การเร่งด้วยฮาร์ดแวร์พร้อมใช้งาน
การรับประกันความปลอดภัย
การใช้งาน AEAD มีข้อดีดังนี้
- การรักษาความปลอดภัย CCA2
- ระดับการตรวจสอบสิทธิ์อย่างน้อย 80 บิต
- ความสามารถในการเข้ารหัสข้อความอย่างน้อย 232 ข้อความที่มีความยาวรวม 250 ไบต์ ไม่มีการโจมตีที่มีข้อความธรรมดาหรือข้อความที่เข้ารหัสซึ่งเลือกไว้ไม่เกิน 232 รายการที่มีความน่าจะเป็นในการประสบความสำเร็จมากกว่า 2-32