AES256_SIV 키 유형과 함께 확정적 AEAD 기본 요소를 사용하는 것이 좋습니다.
연관 데이터로 인증된 확정적 암호화 (확정적 AEAD) 기본 요소는 안정적인 암호문을 생성합니다. 지정된 일반 텍스트를 암호화하면 항상 동일한 암호문이 반환됩니다. 대칭형이므로 암호화와 복호화 모두에 단일 키를 사용합니다.
다음 예시를 통해 확정적 AEAD 기본 요소를 사용하는 방법을 시작할 수 있습니다.
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 }
자바
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
관련 데이터와 함께 확정적 인증된 암호화 (확정적 AEAD) 프리미티브는 확정적 속성을 사용하여 암호화를 제공합니다. 동일한 데이터를 암호화하면 항상 동일한 암호문이 생성됩니다. 이 유형의 암호화는 키 래핑이나 암호화된 데이터 검색을 위한 일부 스킴에 유용합니다 (자세한 내용은 RFC 5297, 섹션 1.3 참고). 이 기본 요소의 결정적 속성으로 인해 공격자가 특정 메시지의 암호문을 알아내기만 하면 해당 메시지의 다른 인스턴스를 식별할 수 있으므로 이 기본 요소의 구현은 비밀 손실로 이어질 수 있습니다.
결정적 AEAD에는 다음과 같은 속성이 있습니다.
- Secrecy: 길이와 반복되는 일반 텍스트의 동일성을 제외하고 일반 텍스트에 관한 정보는 알려져 있지 않습니다.
- 진위성: 암호화된 암호문의 기본 일반 텍스트를 감지되지 않고 변경하는 것은 불가능합니다.
- 대칭: 일반 텍스트를 암호화하고 암호문을 복호화하는 데 동일한 키가 사용됩니다.
- 결정적: 기본 키가 변경되지 않는 한 동일한 매개변수로 일반 텍스트를 두 번 암호화하면 동일한 암호문이 생성됩니다.
관련 데이터
결정적 AEAD는 암호문을 특정 연관 데이터에 연결하는 데 사용할 수 있습니다. user-id 및 encrypted-medical-history 필드가 있는 데이터베이스가 있다고 가정해 보겠습니다. 이 시나리오에서는 encrypted-medical-history을 암호화할 때 user-id을 연결된 데이터로 사용할 수 있습니다. 이렇게 하면 공격자가 한 사용자의 의료 기록을 다른 사용자로 이동할 수 없습니다.
연결된 데이터는 선택사항입니다. 지정된 경우 동일한 연결된 데이터가 암호화 및 복호화 호출 모두에 전달되어야만 복호화가 성공합니다.
키 유형 선택
모든 사용 사례에 AES256_SIV 키 유형을 사용하는 것이 좋습니다.
보안 보장
- 80비트 이상의 인증 강도
- 일반 텍스트와 연결된 데이터는 임의의 길이를 가질 수 있습니다 (0~232바이트 범위 내).
- 키 복구 공격에 대한 128비트 보안 수준과 최대 232개의 키를 사용하는 다중 사용자 공격에서도 마찬가지입니다. 즉, 공격자가 232개의 키로 암호화된 동일한 메시지의 232개의 암호문을 획득하는 경우 단일 키를 획득하려면 2128개의 계산을 실행해야 합니다.
- 각 메시지의 길이가 1MB 미만인 경우 238개의 메시지를 안전하게 암호화할 수 있습니다.