Tink, önemli bir risk kaynağı olan hatalı anahtar yönetimini önlemek için çözümler sunar.
Anahtar oluşturma ve döndürme
Kullanım alanınız için bir ilkel ve anahtar türü seçtikten sonra (önceki İstediğim... bölümünde), anahtarlarınızı seçtiğiniz harici anahtar yönetim sistemiyle yönetin:
Anahtarlarınızı korumak için KMS'nizde anahtar şifreleme anahtarı (KEK) oluşturun.
Tink'e aktarmak için KMS'nizden bir anahtar URI'si ve anahtar kimlik bilgileri alın.
Şifrelenmiş bir anahtar kümesi oluşturmak için Tink'in API'lerini veya Tinkey'i kullanın. Anahtarlarınız şifrelendikten sonra istediğiniz yerde saklayabilirsiniz.
Anahtarlarınızı yoğun şekilde yeniden kullanmaktan kaçınmak ve anahtar güvenliğinin ihlal edilmesinden sonra anahtarınızı kurtarmak için anahtarlarınızı döndürün.
Anahtarları dışa aktarmanız gerekiyorsa güvenli bir şekilde yapmayla ilgili ayrıntılar için Anahtar Materyalini Programatik Olarak Dışa Aktarma bölümüne bakın.
1. adım: Harici KMS'de bir KEK oluşturun
Harici KMS'nizde bir anahtar şifreleme anahtarı (KEK) oluşturun. KEK, anahtarlarınızı şifreleyerek korur ve fazladan bir güvenlik katmanı ekler.
KEK oluşturmak için KMS'ye özgü dokümanlara bakın:
- Google Cloud KMS
- Amazon KMS
- HashiCorp Vault (şu anda yalnızca Go dilinde kullanılabilir)
2. Adım: Anahtar URI'si ve kimlik bilgileri alın
KMS'nizden hem anahtar URI'sini hem de anahtar kimlik bilgilerini alabilirsiniz.
Anahtar URI'sını alma
Tink'in KMS anahtarlarıyla çalışabilmesi için bir Tek Tip Kaynak Tanımlayıcısı (URI) gerekir.
Bu URI'yi oluşturmak için KMS'nin anahtar oluşturulduğunda anahtara atadığı benzersiz tanımlayıcıyı kullanın. Uygun KMS'ye özgü öneki ekleyin ve bu tabloda açıklanan desteklenen anahtar URI'lerinin biçimini kullanın:
KMS | KMS tanımlayıcısı ön eki | Anahtar URI biçimi |
---|---|---|
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] |
Anahtar kimlik bilgilerini alma
Tink'in harici KMS'de kimlik doğrulaması yapabilmesi için gerekli kimlik bilgilerini hazırlayın.
Kimlik bilgisinin tam biçimi KMS'ye özeldir:
- Google Cloud KMS: Tink, hizmet hesabı kimlik bilgilerini gerektirir. Bunlar, Google Cloud Console'da oluşturulup indirilebilen bir JSON dosyasıdır.
- AWS KMS: Tink, aşağıdaki bilgileri içeren bir kimlik bilgileri dosyası gerektirir:
accessKey
özelliğindeki erişim anahtarı kimliğini vesecretKey
mülkünde gizli erişim anahtarını
- HashiCorp Vault: Tink, vault token create komutuyla oluşturulabilen hizmet jetonları gerektirir.
Kimlik bilgileri sağlamazsanız Tink varsayılan kimlik bilgilerini yüklemeye çalışır. Daha fazla bilgi için KMS'ye özel dokümanlara bakın:
3. Adım: Şifrelenmiş bir anahtar grubu oluşturun ve saklayın
Anahtar grubu oluşturmak, harici KMS'yi kullanarak şifrelemek ve bir yere depolamak için Tink API'lerini (Google Cloud KMS, AWS KMS veya HashiCorp Vault için) ya da Tinkey'i kullanın.
Tinkey
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
Java
Bu örnekte, Google Cloud KMS uzantısına tink-java-gcpkms
ihtiyacınız vardır.
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() {} }
Go
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 }
Python
"""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. adım: Anahtarları döndürün
Sisteminizin güvenliğini sağlamak için anahtarları döndürmeniz gerekir.
- KMS'nizde otomatik anahtar rotasyonunu etkinleştirin.
Anahtarları döndürmek için uygun bir sıklık belirleyin. Bu, verilerinizin ne kadar hassas olduğuna, şifrelemeniz gereken ileti sayısına ve rotasyonu harici iş ortaklarıyla koordine etmeniz gerekip gerekmediğine bağlıdır.
- Simetrik şifreleme için 30 ila 90 günlük anahtarlar kullanın.
- Asimetrik şifreleme için rotasyon sıklığı daha düşük olabilir ancak bu yalnızca anahtarları güvenli bir şekilde iptal edebildiğinizde geçerlidir.
Anahtar rotasyonu hakkında daha fazla bilgiyi KMS'ye özel belgelerde bulabilirsiniz: