Tink راه حل هایی را برای جلوگیری از مدیریت نامناسب کلید ارائه می دهد که منبع اصلی خطر است.
نمای کلی
پس از انتخاب یک نوع اولیه و کلید برای مورد استفاده خود (در قسمت قبلی I want to... )، کلیدهای خود را با سیستم مدیریت کلید خارجی (KMS) که انتخاب کردید مدیریت کنید:
برای محافظت از کلیدهای خود یک کلید رمزگذاری کلید (KEK) در KMS خود ایجاد کنید .
یک URI کلیدی و اعتبارنامه های کلیدی را از KMS خود برای ارسال به Tink بازیابی کنید.
از APIهای Tink یا Tinkey برای ایجاد یک مجموعه کلید رمزگذاری شده استفاده کنید. بعد از اینکه کلیدهای شما رمزگذاری شدند، می توانید آنها را در هر کجا که می خواهید ذخیره کنید.
کلیدهای خود را بچرخانید تا از استفاده مجدد گسترده از کلیدهای خود جلوگیری کنید و پس از به خطر افتادن کلید بازیابی کنید.
مرحله 1: یک KEK در KMS خارجی ایجاد کنید
یک کلید رمزگذاری کلید (KEK) در KMS خارجی خود ایجاد کنید. KEK از کلیدهای شما با رمزگذاری آنها محافظت می کند و یک لایه امنیتی اضافی اضافه می کند.
برای ایجاد KEK به مستندات خاص KMS مراجعه کنید:
- Google Cloud KMS
- آمازون KMS
- HashiCorp Vault (در حال حاضر فقط به زبان Go موجود است)
مرحله 2: یک URI کلیدی و اعتبارنامه دریافت کنید
می توانید هم یک URI کلیدی و هم اعتبارنامه های کلیدی را از KMS خود بازیابی کنید.
URI کلید را دریافت کنید
Tink برای کار با کلیدهای KMS به یک شناسه منبع (URI) نیاز دارد.
برای ساخت این URI، از شناسه منحصربهفردی استفاده کنید که KMS هنگام ایجاد کلید به آن اختصاص میدهد. پیشوند خاص KMS مناسب را اضافه کنید و فرمت URI های کلیدی پشتیبانی شده را همانطور که در این جدول توضیح داده شده است دنبال کنید:
KMS | پیشوند شناسه KMS | فرمت کلید URI |
---|---|---|
AWS KMS | aws-kms:// | aws-kms://arn:aws:kms:[region]:[account-id]:key/[key-id] |
GCP KMS | gcp-kms:// | gcp-kms://projects/*/locations/*/keyRings/*/cryptoKeys/* |
HashiCorp Vault | hcvault:// | hcvault://[key-id] |
مدارک کلیدی را دریافت کنید
اعتبارنامه های لازم را آماده کنید تا Tink بتواند به KMS خارجی احراز هویت کند.
فرم دقیق اعتبارنامه مختص KMS است:
- Google Cloud KMS – Tink به اعتبار حسابهای سرویس نیاز دارد. اینها یک فایل JSON هستند که میتوان آن را از Google Cloud Console ایجاد و دانلود کرد .
- AWS KMS – Tink به یک فایل اعتبار نیاز دارد که حاوی آن باشد
- شناسه کلید دسترسی در ویژگی
accessKey
و - کلید دسترسی مخفی در ویژگی
secretKey
.
- شناسه کلید دسترسی در ویژگی
- HashiCorp Vault – Tink به توکنهای سرویسی نیاز دارد که میتوان آنها را با دستور ایجاد vault token ایجاد کرد.
اگر اعتبارنامه را ارائه نکنید، Tink سعی می کند اعتبارنامه های پیش فرض را بارگیری کند. برای اطلاعات بیشتر، به مستندات خاص KMS مراجعه کنید:
مرحله 3: یک مجموعه کلید رمزگذاری شده ایجاد و ذخیره کنید
از APIهای Tink (برای Google Cloud KMS، AWS KMS، یا HashiCorp Vault) یا Tinkey برای ایجاد مجموعه کلید، رمزگذاری آن با استفاده از KMS خارجی و ذخیره آن در جایی استفاده کنید.
تینکی
tinkey create-keyset --key-template AES128_GCM \
--out-format json --out encrypted_aead_keyset.json \
--master-key-uri gcp-kms://projects/tink-examples/locations/global/keyRings/foo/cryptoKeys/bar \
--credential gcp_credentials.json
جاوا
برای این مثال به پسوند Google Cloud KMS tink-java-gcpkms
نیاز دارید.
package encryptedkeyset; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.crypto.tink.Aead; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.TinkJsonProtoKeysetFormat; import com.google.crypto.tink.aead.AeadConfig; import com.google.crypto.tink.aead.PredefinedAeadParameters; import com.google.crypto.tink.integration.gcpkms.GcpKmsClient; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * A command-line utility for working with encrypted keysets. * * <p>It requires the following arguments: * * <ul> * <li>mode: Can be "generate", "encrypt" or "decrypt". If mode is "generate", it will generate a * keyset, encrypt it and store it in the key-file argument. If mode is "encrypt" or * "decrypt", it will read and decrypt an keyset from the key-file argument, and use it to * encrypt or decrypt the input-file argument. * <li>kek-uri: Use this Cloud KMS' key as the key-encrypting-key for envelope encryption. * <li>gcp-credential-file: Use this JSON credential file to connect to Cloud KMS. * <li>input-file: If mode is "encrypt" or "decrypt", read the input from this file. * <li>output-file: If mode is "encrypt" or "decrypt", write the result to this file. */ public final class EncryptedKeysetExample { private static final String MODE_ENCRYPT = "encrypt"; private static final String MODE_DECRYPT = "decrypt"; private static final String MODE_GENERATE = "generate"; private static final byte[] EMPTY_ASSOCIATED_DATA = new byte[0]; public static void main(String[] args) throws Exception { if (args.length != 4 && args.length != 6) { System.err.printf("Expected 4 or 6 parameters, got %d\n", args.length); System.err.println( "Usage: java EncryptedKeysetExample generate/encrypt/decrypt key-file kek-uri" + " gcp-credential-file input-file output-file"); System.exit(1); } String mode = args[0]; if (!mode.equals(MODE_ENCRYPT) && !mode.equals(MODE_DECRYPT) && !mode.equals(MODE_GENERATE)) { System.err.print("The first argument should be either encrypt, decrypt or generate"); System.exit(1); } Path keyFile = Paths.get(args[1]); String kekUri = args[2]; String gcpCredentialFilename = args[3]; // Initialise Tink: register all AEAD key types with the Tink runtime AeadConfig.register(); // From the key-encryption key (KEK) URI, create a remote AEAD primitive for encrypting Tink // keysets. Aead kekAead = new GcpKmsClient().withCredentials(gcpCredentialFilename).getAead(kekUri); if (mode.equals(MODE_GENERATE)) { KeysetHandle handle = KeysetHandle.generateNew(PredefinedAeadParameters.AES128_GCM); String serializedEncryptedKeyset = TinkJsonProtoKeysetFormat.serializeEncryptedKeyset( handle, kekAead, EMPTY_ASSOCIATED_DATA); Files.write(keyFile, serializedEncryptedKeyset.getBytes(UTF_8)); return; } // Use the primitive to encrypt/decrypt files // Read the encrypted keyset KeysetHandle handle = TinkJsonProtoKeysetFormat.parseEncryptedKeyset( new String(Files.readAllBytes(keyFile), UTF_8), kekAead, EMPTY_ASSOCIATED_DATA); // Get the primitive Aead aead = handle.getPrimitive(Aead.class); Path inputFile = Paths.get(args[4]); Path outputFile = Paths.get(args[5]); if (mode.equals(MODE_ENCRYPT)) { byte[] plaintext = Files.readAllBytes(inputFile); byte[] ciphertext = aead.encrypt(plaintext, EMPTY_ASSOCIATED_DATA); Files.write(outputFile, ciphertext); } else if (mode.equals(MODE_DECRYPT)) { byte[] ciphertext = Files.readAllBytes(inputFile); byte[] plaintext = aead.decrypt(ciphertext, EMPTY_ASSOCIATED_DATA); Files.write(outputFile, plaintext); } } private EncryptedKeysetExample() {} }
برو
import ( "bytes" "fmt" "log" "github.com/tink-crypto/tink-go/v2/aead" "github.com/tink-crypto/tink-go/v2/keyset" "github.com/tink-crypto/tink-go/v2/testing/fakekms" ) // The fake KMS should only be used in tests. It is not secure. const keyURI = "fake-kms://CM2b3_MDElQKSAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EhIaEIK75t5L-adlUwVhWvRuWUwYARABGM2b3_MDIAE" func Example_encryptedKeyset() { // Get a KEK (key encryption key) AEAD. This is usually a remote AEAD to a KMS. In this example, // we use a fake KMS to avoid making RPCs. client, err := fakekms.NewClient(keyURI) if err != nil { log.Fatal(err) } kekAEAD, err := client.GetAEAD(keyURI) if err != nil { log.Fatal(err) } // Generate a new keyset handle for the primitive we want to use. newHandle, err := keyset.NewHandle(aead.AES256GCMKeyTemplate()) if err != nil { log.Fatal(err) } // Choose some associated data. This is the context in which the keyset will be used. keysetAssociatedData := []byte("keyset encryption example") // Encrypt the keyset with the KEK AEAD and the associated data. buf := new(bytes.Buffer) writer := keyset.NewBinaryWriter(buf) err = newHandle.WriteWithAssociatedData(writer, kekAEAD, keysetAssociatedData) if err != nil { log.Fatal(err) } encryptedKeyset := buf.Bytes() // The encrypted keyset can now be stored. // To use the primitive, we first need to decrypt the keyset. We use the same // KEK AEAD and the same associated data that we used to encrypt it. reader := keyset.NewBinaryReader(bytes.NewReader(encryptedKeyset)) handle, err := keyset.ReadWithAssociatedData(reader, kekAEAD, keysetAssociatedData) if err != nil { log.Fatal(err) } // Get the primitive. primitive, err := aead.New(handle) if err != nil { log.Fatal(err) } // Use the primitive. plaintext := []byte("message") associatedData := []byte("example encryption") ciphertext, err := primitive.Encrypt(plaintext, associatedData) if err != nil { log.Fatal(err) } decrypted, err := primitive.Decrypt(ciphertext, associatedData) if err != nil { log.Fatal(err) } fmt.Println(string(decrypted)) // Output: message }
پایتون
"""A command-line utility for generating, encrypting and storing keysets.""" from absl import app from absl import flags from absl import logging import tink from tink import aead from tink.integration import gcpkms FLAGS = flags.FLAGS flags.DEFINE_enum('mode', None, ['generate', 'encrypt', 'decrypt'], 'The operation to perform.') flags.DEFINE_string('keyset_path', None, 'Path to the keyset used for encryption.') flags.DEFINE_string('kek_uri', None, 'The Cloud KMS URI of the key encryption key.') flags.DEFINE_string('gcp_credential_path', None, 'Path to the GCP credentials JSON file.') flags.DEFINE_string('input_path', None, 'Path to the input file.') flags.DEFINE_string('output_path', None, 'Path to the output file.') flags.DEFINE_string('associated_data', None, 'Optional associated data to use with the ' 'encryption operation.') def main(argv): del argv # Unused. associated_data = b'' if not FLAGS.associated_data else bytes( FLAGS.associated_data, 'utf-8') # Initialise Tink aead.register() try: # Read the GCP credentials and setup client client = gcpkms.GcpKmsClient(FLAGS.kek_uri, FLAGS.gcp_credential_path) except tink.TinkError as e: logging.exception('Error creating GCP KMS client: %s', e) return 1 # Create envelope AEAD primitive using AES256 GCM for encrypting the data try: remote_aead = client.get_aead(FLAGS.kek_uri) except tink.TinkError as e: logging.exception('Error creating primitive: %s', e) return 1 if FLAGS.mode == 'generate': # Generate a new keyset try: key_template = aead.aead_key_templates.AES128_GCM keyset_handle = tink.new_keyset_handle(key_template) except tink.TinkError as e: logging.exception('Error creating primitive: %s', e) return 1 # Encrypt the keyset_handle with the remote key-encryption key (KEK) with open(FLAGS.keyset_path, 'wt') as keyset_file: try: keyset_encryption_associated_data = 'encrypted keyset example' serialized_encrypted_keyset = ( tink.json_proto_keyset_format.serialize_encrypted( keyset_handle, remote_aead, keyset_encryption_associated_data ) ) keyset_file.write(serialized_encrypted_keyset) except tink.TinkError as e: logging.exception('Error writing key: %s', e) return 1 return 0 # Use the keyset to encrypt/decrypt data # Read the encrypted keyset into a keyset_handle with open(FLAGS.keyset_path, 'rt') as keyset_file: try: serialized_encrypted_keyset = keyset_file.read() keyset_encryption_associated_data = 'encrypted keyset example' keyset_handle = tink.json_proto_keyset_format.parse_encrypted( serialized_encrypted_keyset, remote_aead, keyset_encryption_associated_data, ) except tink.TinkError as e: logging.exception('Error reading key: %s', e) return 1 # Get the primitive try: cipher = keyset_handle.primitive(aead.Aead) except tink.TinkError as e: logging.exception('Error creating primitive: %s', e) return 1 with open(FLAGS.input_path, 'rb') as input_file: input_data = input_file.read() if FLAGS.mode == 'decrypt': output_data = cipher.decrypt(input_data, associated_data) elif FLAGS.mode == 'encrypt': output_data = cipher.encrypt(input_data, associated_data) else: logging.error( 'Unsupported mode %s. Please choose "encrypt" or "decrypt".', FLAGS.mode, ) return 1 with open(FLAGS.output_path, 'wb') as output_file: output_file.write(output_data) if __name__ == '__main__': flags.mark_flags_as_required([ 'mode', 'keyset_path', 'kek_uri', 'gcp_credential_path']) app.run(main)
مرحله 4: کلیدها را بچرخانید
برای اطمینان از امنیت سیستم خود، باید کلیدها را بچرخانید.
- چرخش خودکار کلید را در KMS خود فعال کنید.
فرکانس مناسب برای چرخاندن کلیدها را تعیین کنید. این بستگی به حساسیت دادههای شما، تعداد پیامهایی که باید رمزگذاری کنید، و اینکه آیا باید چرخش را با شرکای خارجی هماهنگ کنید، بستگی دارد.
- برای رمزگذاری متقارن ، از کلیدهای 30 تا 90 روزه استفاده کنید.
- برای رمزگذاری نامتقارن ، فرکانس چرخش می تواند کمتر باشد، اما فقط در صورتی که بتوانید کلیدها را به طور ایمن لغو کنید.
درباره چرخش کلید در اسناد خاص KMS بیشتر بدانید: