diff --git a/kms/snippets/README.rst b/kms/snippets/README.rst index 17e34befaf0c..3acb00f5d913 100644 --- a/kms/snippets/README.rst +++ b/kms/snippets/README.rst @@ -77,20 +77,6 @@ To run this sample: $ python quickstart.py -Snippets -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=kms/api-client/snippets.py,kms/api-client/README.rst - - - - -To run this sample: - -.. code-block:: bash - - $ python snippets.py -.. _Google Cloud SDK: https://cloud.google.com/sdk/ +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/kms/snippets/README.rst.in b/kms/snippets/README.rst.in index f8aef3a243f7..cfd81fc800bd 100644 --- a/kms/snippets/README.rst.in +++ b/kms/snippets/README.rst.in @@ -15,10 +15,5 @@ setup: samples: - name: Quickstart file: quickstart.py -- name: Snippets - file: snippets.py - show_help: True -- name: Asymmetric - file: asymmetric.py folder: kms/api-client diff --git a/kms/snippets/asymmetric.py b/kms/snippets/asymmetric.py deleted file mode 100644 index 9c8abd15cc9b..000000000000 --- a/kms/snippets/asymmetric.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/bin/python -# Copyright 2018 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License.rom googleapiclient import discovery - -import hashlib - -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec, padding, utils - -from google.cloud import kms_v1 -from google.cloud.kms_v1 import enums - - -# [START kms_create_asymmetric_key] -def create_asymmetric_key(project_id, location_id, key_ring_id, crypto_key_id): - """Creates an RSA encrypt/decrypt key pair within a specified KeyRing.""" - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing associated with the CryptoKey. - parent = client.key_ring_path(project_id, location_id, key_ring_id) - - # Create the CryptoKey object template - purpose = enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT - algorithm = enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.\ - RSA_DECRYPT_OAEP_2048_SHA256 - crypto_key = {'purpose': purpose, - 'version_template': {'algorithm': algorithm}} - - # Create a CryptoKey for the given KeyRing. - response = client.create_crypto_key(parent, crypto_key_id, crypto_key) - - print('Created CryptoKey {}.'.format(response.name)) - return response -# [END kms_create_asymmetric_key] - - -# [START kms_get_asymmetric_public] -def get_asymmetric_public_key(key_name): - """ - Retrieves the public key from a saved asymmetric key pair on Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.hazmat.backends.default_backend - cryptography.hazmat.primitives.serialization - """ - - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - - key_txt = response.pem.encode('ascii') - key = serialization.load_pem_public_key(key_txt, default_backend()) - return key -# [END kms_get_asymmetric_public] - - -# [START kms_decrypt_rsa] -def decrypt_rsa(ciphertext, key_name): - """ - Decrypt the input ciphertext (bytes) using an - 'RSA_DECRYPT_OAEP_2048_SHA256' private key stored on Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - """ - - client = kms_v1.KeyManagementServiceClient() - response = client.asymmetric_decrypt(key_name, ciphertext) - return response.plaintext -# [END kms_decrypt_rsa] - - -# [START kms_encrypt_rsa] -def encrypt_rsa(plaintext, key_name): - """ - Encrypt the input plaintext (bytes) locally using an - 'RSA_DECRYPT_OAEP_2048_SHA256' public key retrieved from Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.hazmat.primitives.asymmetric.padding - cryptography.hazmat.primitives.hashes - """ - # get the public key - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - key_txt = response.pem.encode('ascii') - public_key = serialization.load_pem_public_key(key_txt, default_backend()) - - # encrypt plaintext - pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), - algorithm=hashes.SHA256(), - label=None) - return public_key.encrypt(plaintext, pad) -# [END kms_encrypt_rsa] - - -# [START kms_sign_asymmetric] -def sign_asymmetric(message, key_name): - """ - Create a signature for a message using a private key stored on Cloud KMS - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - hashlib - """ - # Note: some key algorithms will require a different hash function - # For example, EC_SIGN_P384_SHA384 requires SHA384 - client = kms_v1.KeyManagementServiceClient() - digest_bytes = hashlib.sha256(message).digest() - - digest_json = {'sha256': digest_bytes} - - response = client.asymmetric_sign(key_name, digest_json) - return response.signature -# [END kms_sign_asymmetric] - - -# [START kms_verify_signature_rsa] -def verify_signature_rsa(signature, message, key_name): - """ - Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the - specified message - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.exceptions.InvalidSignature - cryptography.hazmat.primitives.asymmetric.padding - cryptography.hazmat.primitives.asymmetric.utils - cryptography.hazmat.primitives.hashes - hashlib - """ - # get the public key - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - key_txt = response.pem.encode('ascii') - public_key = serialization.load_pem_public_key(key_txt, default_backend()) - - # get the digest of the message - digest_bytes = hashlib.sha256(message).digest() - - try: - # Attempt verification - public_key.verify(signature, - digest_bytes, - padding.PSS(mgf=padding.MGF1(hashes.SHA256()), - salt_length=32), - utils.Prehashed(hashes.SHA256())) - # No errors were thrown. Verification was successful - return True - except InvalidSignature: - return False -# [END kms_verify_signature_rsa] - - -# [START kms_verify_signature_ec] -def verify_signature_ec(signature, message, key_name): - """ - Verify the validity of an 'EC_SIGN_P256_SHA256' signature - for the specified message - - Example key_name: - "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\ - /KEY_ID/cryptoKeyVersions/1" - - Requires: - cryptography.exceptions.InvalidSignature - cryptography.hazmat.primitives.asymmetric.ec - cryptography.hazmat.primitives.asymmetric.utils - cryptography.hazmat.primitives.hashes - hashlib - """ - # get the public key - client = kms_v1.KeyManagementServiceClient() - response = client.get_public_key(key_name) - key_txt = response.pem.encode('ascii') - public_key = serialization.load_pem_public_key(key_txt, default_backend()) - - # get the digest of the message - digest_bytes = hashlib.sha256(message).digest() - - try: - # Attempt verification - public_key.verify(signature, - digest_bytes, - ec.ECDSA(utils.Prehashed(hashes.SHA256()))) - # No errors were thrown. Verification was successful - return True - except InvalidSignature: - return False -# [END kms_verify_signature_ec] diff --git a/kms/snippets/asymmetric_test.py b/kms/snippets/asymmetric_test.py deleted file mode 100644 index cc621003c475..000000000000 --- a/kms/snippets/asymmetric_test.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/python -# Copyright 2018 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from os import environ -from time import sleep - -import asymmetric - -from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePublicKey -from cryptography.hazmat.backends.openssl.rsa import _RSAPublicKey - -from google.api_core.exceptions import GoogleAPICallError -from google.cloud.kms_v1 import enums - -from snippets import create_key_ring - -from snippets_test import create_key_helper - - -def setup_module(module): - """ - Set up keys in project if needed - """ - t = TestKMSAsymmetric() - try: - # create keyring - create_key_ring(t.project_id, t.location, t.keyring_id) - except GoogleAPICallError: - # keyring already exists - pass - s1 = create_key_helper(t.rsaDecryptId, - enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - RSA_DECRYPT_OAEP_2048_SHA256, - t) - s2 = create_key_helper(t.rsaSignId, - enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - RSA_SIGN_PSS_2048_SHA256, - t) - s3 = create_key_helper(t.ecSignId, - enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - EC_SIGN_P256_SHA256, - t) - - if s1 or s2 or s3: - # leave time for keys to initialize - sleep(20) - - -class TestKMSAsymmetric: - project_id = environ['GCLOUD_PROJECT'] - keyring_id = 'kms-samples' - location = 'global' - parent = 'projects/{}/locations/{}'.format(project_id, location) - - rsaSignId = 'rsa-sign' - rsaDecryptId = 'rsa-decrypt' - ecSignId = 'ec-sign' - - rsaSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring_id, rsaSignId) - rsaDecrypt = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring_id, rsaDecryptId) - ecSign = '{}/keyRings/{}/cryptoKeys/{}/cryptoKeyVersions/1' \ - .format(parent, keyring_id, ecSignId) - - message = 'test message 123' - message_bytes = message.encode('utf-8') - - def test_get_public_key(self): - rsa_key = asymmetric.get_asymmetric_public_key(self.rsaDecrypt) - assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' - rsa_key = asymmetric.get_asymmetric_public_key(self.rsaSign) - assert isinstance(rsa_key, _RSAPublicKey), 'expected RSA key' - ec_key = asymmetric.get_asymmetric_public_key(self.ecSign) - assert isinstance(ec_key, _EllipticCurvePublicKey), 'expected EC key' - - def test_rsa_encrypt_decrypt(self): - ciphertext = asymmetric.encrypt_rsa(self.message_bytes, - self.rsaDecrypt) - # signature should be 256 bytes for RSA 2048 - assert len(ciphertext) == 256, \ - 'ciphertext should be 256 chars; got {}'.format(len(ciphertext)) - plaintext_bytes = asymmetric.decrypt_rsa(ciphertext, - self.rsaDecrypt) - assert plaintext_bytes == self.message_bytes - plaintext = plaintext_bytes.decode('utf-8') - assert plaintext == self.message - - def test_rsa_sign_verify(self): - sig = asymmetric.sign_asymmetric(self.message_bytes, - self.rsaSign) - # signature should be 256 bytes for RSA 2048 - assert len(sig) == 256, \ - 'sig should be 256 chars; got {}'.format(len(sig)) - success = asymmetric.verify_signature_rsa(sig, - self.message_bytes, - self.rsaSign) - assert success is True, 'RSA verification failed' - changed_bytes = self.message_bytes + b'.' - success = asymmetric.verify_signature_rsa(sig, - changed_bytes, - self.rsaSign) - assert success is False, 'verify should fail with modified message' - - def test_ec_sign_verify(self): - sig = asymmetric.sign_asymmetric(self.message_bytes, - self.ecSign) - assert len(sig) > 50 and len(sig) < 300, \ - 'sig outside expected length range' - success = asymmetric.verify_signature_ec(sig, - self.message_bytes, - self.ecSign) - assert success is True, 'EC verification failed' - changed_bytes = self.message_bytes + b'.' - success = asymmetric.verify_signature_ec(sig, - changed_bytes, - self.ecSign) - assert success is False, 'verify should fail with modified message' diff --git a/kms/snippets/create_key_asymmetric_decrypt.py b/kms/snippets/create_key_asymmetric_decrypt.py new file mode 100644 index 000000000000..cac157958ebd --- /dev/null +++ b/kms/snippets/create_key_asymmetric_decrypt.py @@ -0,0 +1,54 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_asymmetric_decrypt] +def create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, id): + """ + Creates a new asymmetric decryption key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-asymmetric-decrypt-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created asymmetric decrypt key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_asymmetric_decrypt] diff --git a/kms/snippets/create_key_asymmetric_sign.py b/kms/snippets/create_key_asymmetric_sign.py new file mode 100644 index 000000000000..9bf18a7a996d --- /dev/null +++ b/kms/snippets/create_key_asymmetric_sign.py @@ -0,0 +1,54 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_asymmetric_sign] +def create_key_asymmetric_sign(project_id, location_id, key_ring_id, id): + """ + Creates a new asymmetric signing key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-asymmetric-signing-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created asymmetric signing key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_asymmetric_sign] diff --git a/kms/snippets/create_key_hsm.py b/kms/snippets/create_key_hsm.py new file mode 100644 index 000000000000..84ba37e5d00e --- /dev/null +++ b/kms/snippets/create_key_hsm.py @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_hsm] +def create_key_hsm(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS backed by Cloud HSM. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-hsm-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + protection_level = kms.enums.ProtectionLevel.HSM + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + 'protection_level': protection_level + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created hsm key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_hsm] diff --git a/kms/snippets/create_key_labels.py b/kms/snippets/create_key_labels.py new file mode 100644 index 000000000000..e64a10cb955f --- /dev/null +++ b/kms/snippets/create_key_labels.py @@ -0,0 +1,58 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_labels] +def create_key_labels(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS with labels. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-labeled-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + }, + 'labels': { + 'team': 'alpha', + 'cost_center': 'cc1234' + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created labeled key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_labels] diff --git a/kms/snippets/create_key_ring.py b/kms/snippets/create_key_ring.py new file mode 100644 index 000000000000..c01e8490516b --- /dev/null +++ b/kms/snippets/create_key_ring.py @@ -0,0 +1,46 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_ring] +def create_key_ring(project_id, location_id, id): + """ + Creates a new key ring in Cloud KMS + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + id (string): ID of the key ring to create (e.g. 'my-key-ring'). + + Returns: + KeyRing: Cloud KMS key ring. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent location name. + location_name = client.location_path(project_id, location_id) + + # Build the key ring. + key_ring = {} + + # Call the API. + created_key_ring = client.create_key_ring(location_name, id, key_ring) + print('Created key ring: {}'.format(created_key_ring.name)) + return created_key_ring +# [END kms_create_key_ring] diff --git a/kms/snippets/create_key_rotation_schedule.py b/kms/snippets/create_key_rotation_schedule.py new file mode 100644 index 000000000000..e6bbdb62d361 --- /dev/null +++ b/kms/snippets/create_key_rotation_schedule.py @@ -0,0 +1,67 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_rotation_schedule] +def create_key_rotation_schedule(project_id, location_id, key_ring_id, id): + """ + Creates a new key in Cloud KMS that automatically rotates. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-rotating-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Import time for getting the current time. + import time + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + }, + + # Rotate the key every 30 days. + 'rotation_period': { + 'seconds': 60*60*24*30 + }, + + # Start the first rotation in 24 hours. + 'next_rotation_time': { + 'seconds': int(time.time()) + 60*60*24 + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created labeled key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_rotation_schedule] diff --git a/kms/snippets/create_key_symmetric_encrypt_decrypt.py b/kms/snippets/create_key_symmetric_encrypt_decrypt.py new file mode 100644 index 000000000000..54b9c5f40981 --- /dev/null +++ b/kms/snippets/create_key_symmetric_encrypt_decrypt.py @@ -0,0 +1,54 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_symmetric_encrypt_decrypt] +def create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id, id): + """ + Creates a new symmetric encryption/decryption key in Cloud KMS. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + id (string): ID of the key to create (e.g. 'my-symmetric-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key ring name. + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + + # Build the key. + purpose = kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + algorithm = kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + key = { + 'purpose': purpose, + 'version_template': { + 'algorithm': algorithm, + } + } + + # Call the API. + created_key = client.create_crypto_key(key_ring_name, id, key) + print('Created symmetric key: {}'.format(created_key.name)) + return created_key +# [END kms_create_key_symmetric_encrypt_decrypt] diff --git a/kms/snippets/create_key_version.py b/kms/snippets/create_key_version.py new file mode 100644 index 000000000000..9c84f808a943 --- /dev/null +++ b/kms/snippets/create_key_version.py @@ -0,0 +1,47 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_create_key_version] +def create_key_version(project_id, location_id, key_ring_id, key_id): + """ + Creates a new version of the given key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key for which to create a new version (e.g. 'my-key'). + + Returns: + CryptoKeyVersion: Cloud KMS key version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key version. + version = {} + + # Call the API. + created_version = client.create_crypto_key_version(key_name, version) + print('Created key version: {}'.format(created_version.name)) + return created_version +# [END kms_create_key_version] diff --git a/kms/snippets/decrypt_asymmetric.py b/kms/snippets/decrypt_asymmetric.py new file mode 100644 index 000000000000..7b040cdd4203 --- /dev/null +++ b/kms/snippets/decrypt_asymmetric.py @@ -0,0 +1,46 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_decrypt_asymmetric] +def decrypt_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, ciphertext): + """ + Decrypt the ciphertext using an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to use (e.g. '1'). + ciphertext (bytes): Encrypted bytes to decrypt. + + Returns: + DecryptResponse: Response including plaintext. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + decrypt_response = client.asymmetric_decrypt(key_version_name, ciphertext) + print('Plaintext: {}'.format(decrypt_response.plaintext)) + return decrypt_response +# [END kms_decrypt_asymmetric] diff --git a/kms/snippets/decrypt_symmetric.py b/kms/snippets/decrypt_symmetric.py new file mode 100644 index 000000000000..a5cbe714279b --- /dev/null +++ b/kms/snippets/decrypt_symmetric.py @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_decrypt_symmetric] +def decrypt_symmetric(project_id, location_id, key_ring_id, key_id, ciphertext): + """ + Decrypt the ciphertext using the symmetric key + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + ciphertext (bytes): Encrypted bytes to decrypt. + + Returns: + DecryptResponse: Response including plaintext. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + decrypt_response = client.decrypt(key_name, ciphertext) + print('Plaintext: {}'.format(decrypt_response.plaintext)) + return decrypt_response +# [END kms_decrypt_symmetric] diff --git a/kms/snippets/destroy_key_version.py b/kms/snippets/destroy_key_version.py new file mode 100644 index 000000000000..7423ca7e099e --- /dev/null +++ b/kms/snippets/destroy_key_version.py @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_destroy_key_version] +def destroy_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Schedule destruction of the given key version. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to destroy (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + destroyed_version = client.destroy_crypto_key_version(key_version_name) + print('Destroyed key version: {}'.format(destroyed_version.name)) + return destroyed_version +# [END kms_destroy_key_version] diff --git a/kms/snippets/disable_key_version.py b/kms/snippets/disable_key_version.py new file mode 100644 index 000000000000..a4a16dd57a65 --- /dev/null +++ b/kms/snippets/disable_key_version.py @@ -0,0 +1,55 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_disable_key_version] +def disable_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Disable a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to disable (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Build the key version. We need to build a full proto instead of a dict due + # to https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key_version = resources_pb2.CryptoKeyVersion() + key_version.name = key_version_name + key_version.state = kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + # Build the update mask. + update_mask = {'paths': ['state']} + + # Call the API. + disabled_version = client.update_crypto_key_version(key_version, update_mask) + print('Disabled key version: {}'.format(disabled_version.name)) + return disabled_version +# [END kms_disable_key_version] diff --git a/kms/snippets/enable_key_version.py b/kms/snippets/enable_key_version.py new file mode 100644 index 000000000000..9cb8daadd66f --- /dev/null +++ b/kms/snippets/enable_key_version.py @@ -0,0 +1,55 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_enable_key_version] +def enable_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Enable a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to enable (e.g. '1'). + + Returns: + CryptoKeyVersion: The version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Build the key version. We need to build a full proto instead of a dict due + # to https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key_version = resources_pb2.CryptoKeyVersion() + key_version.name = key_version_name + key_version.state = kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + + # Build the update mask. + update_mask = {'paths': ['state']} + + # Call the API. + enabled_version = client.update_crypto_key_version(key_version, update_mask) + print('Enabled key version: {}'.format(enabled_version.name)) + return enabled_version +# [END kms_enable_key_version] diff --git a/kms/snippets/encrypt_asymmetric.py b/kms/snippets/encrypt_asymmetric.py new file mode 100644 index 000000000000..efe40322c425 --- /dev/null +++ b/kms/snippets/encrypt_asymmetric.py @@ -0,0 +1,69 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_encrypt_asymmetric] +def encrypt_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, plaintext): + """ + Encrypt plaintext using the public key portion of an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key version to use (e.g. '1'). + plaintext (string): message to encrypt + + Returns: + bytes: Encrypted ciphertext. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Import cryptographic helpers from the cryptography package. + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding + + # Convert the plaintext to bytes. + plaintext_bytes = plaintext.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + + # Construct the padding. Note that the padding differs based on key choice. + sha256 = hashes.SHA256() + mgf = padding.MGF1(algorithm=sha256) + pad = padding.OAEP(mgf=mgf, algorithm=sha256, label=None) + + # Encrypt the data using the public key. + ciphertext = rsa_key.encrypt(plaintext_bytes, pad) + print('Ciphertext: {}'.format(base64.b64encode(ciphertext))) + return ciphertext +# [END kms_encrypt_asymmetric] diff --git a/kms/snippets/encrypt_symmetric.py b/kms/snippets/encrypt_symmetric.py new file mode 100644 index 000000000000..b90da358f676 --- /dev/null +++ b/kms/snippets/encrypt_symmetric.py @@ -0,0 +1,51 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_encrypt_symmetric] +def encrypt_symmetric(project_id, location_id, key_ring_id, key_id, plaintext): + """ + Encrypt plaintext using a symmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + plaintext (string): message to encrypt + + Returns: + bytes: Encrypted ciphertext. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Convert the plaintext to bytes. + plaintext_bytes = plaintext.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + encrypt_response = client.encrypt(key_name, plaintext_bytes) + print('Ciphertext: {}'.format(base64.b64encode(encrypt_response.ciphertext))) + return encrypt_response +# [END kms_encrypt_symmetric] diff --git a/kms/snippets/get_key_labels.py b/kms/snippets/get_key_labels.py new file mode 100644 index 000000000000..363bcfbaf03b --- /dev/null +++ b/kms/snippets/get_key_labels.py @@ -0,0 +1,48 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_get_key_labels] +def get_key_labels(project_id, location_id, key_ring_id, key_id): + """ + Get a key and its labels. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + key = client.get_crypto_key(key_name) + + # Example of iterating over labels. + for k, v in key.labels.items(): + print('{} = {}'.format(k, v)) + + return key +# [END kms_get_key_labels] diff --git a/kms/snippets/get_key_version_attestation.py b/kms/snippets/get_key_version_attestation.py new file mode 100644 index 000000000000..615d4653d8ef --- /dev/null +++ b/kms/snippets/get_key_version_attestation.py @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_get_key_version_attestation] +def get_key_version_attestation(project_id, location_id, key_ring_id, key_id, version_id): + """ + Get an HSM-backend key's attestation. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + + Returns: + Attestation: Cloud KMS key attestation. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the attestation. + import base64 + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + version = client.get_crypto_key_version(key_version_name) + + # Only HSM keys have an attestation. For other key types, the attestion + # will be None. + attestation = version.attestation + if not attestation: + raise 'no attestation - attestations only exist on HSM keys' + + encoded_attestation = base64.b64encode(attestation.content) + print('Got key attestation: {}'.format(encoded_attestation)) + return attestation +# [END kms_get_key_version_attestation] diff --git a/kms/snippets/get_public_key.py b/kms/snippets/get_public_key.py new file mode 100644 index 000000000000..1b810d15f6ad --- /dev/null +++ b/kms/snippets/get_public_key.py @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_get_public_key] +def get_public_key(project_id, location_id, key_ring_id, key_id, version_id): + """ + Get the public key for an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key to use (e.g. '1'). + + Returns: + PublicKey: Cloud KMS public key response. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + public_key = client.get_public_key(key_version_name) + print('Public key: {}'.format(public_key.pem)) + return public_key +# [END kms_get_public_key] diff --git a/kms/snippets/iam_add_member.py b/kms/snippets/iam_add_member.py new file mode 100644 index 000000000000..442f248390de --- /dev/null +++ b/kms/snippets/iam_add_member.py @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_iam_add_member] +def iam_add_member(project_id, location_id, key_ring_id, key_id, member): + """ + Add an IAM member to a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + member (string): Member to add (e.g. 'user:foo@example.com') + + Returns: + Policy: Updated Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Add the member to the policy. + policy.bindings.add( + role='roles/cloudkms.cryptoKeyEncrypterDecrypter', + members=[member]) + + # Save the updated IAM policy. + updated_policy = client.set_iam_policy(resource_name, policy) + print('Added {} to {}'.format(member, resource_name)) + return updated_policy +# [END kms_iam_add_member] diff --git a/kms/snippets/iam_get_policy.py b/kms/snippets/iam_get_policy.py new file mode 100644 index 000000000000..c00172e98a5b --- /dev/null +++ b/kms/snippets/iam_get_policy.py @@ -0,0 +1,54 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_iam_get_policy] +def iam_get_policy(project_id, location_id, key_ring_id, key_id): + """ + Get the IAM policy for a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + Policy: Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Print the policy + print('IAM policy for {}'.format(resource_name)) + for binding in policy.bindings: + print(binding.role) + for member in binding.members: + print('- {}'.format(member)) + + return policy +# [END kms_iam_get_policy] diff --git a/kms/snippets/iam_remove_member.py b/kms/snippets/iam_remove_member.py new file mode 100644 index 000000000000..ad73fab943c5 --- /dev/null +++ b/kms/snippets/iam_remove_member.py @@ -0,0 +1,57 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_iam_remove_member] +def iam_remove_member(project_id, location_id, key_ring_id, key_id, member): + """ + Remove an IAM member from a resource. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + member (string): Member to remove (e.g. 'user:foo@example.com') + + Returns: + Policy: Updated Cloud IAM policy. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the resource name. + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # The resource name could also be a key ring. + # resource_name = client.key_ring_path(project_id, location_id, key_ring_id); + + # Get the current policy. + policy = client.get_iam_policy(resource_name) + + # Remove the member from the policy. + for binding in policy.bindings: + if binding.role == 'roles/cloudkms.cryptoKeyEncrypterDecrypter': + if member in binding.members: + binding.members.remove(member) + + # Save the updated IAM policy. + updated_policy = client.set_iam_policy(resource_name, policy) + print('Removed {} from {}'.format(member, resource_name)) + return updated_policy +# [END kms_iam_remove_member] diff --git a/kms/snippets/quickstart.py b/kms/snippets/quickstart.py index 2f97f38f70de..91b5a49ad41f 100644 --- a/kms/snippets/quickstart.py +++ b/kms/snippets/quickstart.py @@ -13,41 +13,37 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and -import os +import argparse -def run_quickstart(): - # [START kms_quickstart] - # Imports the Google APIs client library - from google.cloud import kms_v1 +# [START kms_quickstart] +def quickstart(project_id, location_id): + # Import the client library. + from google.cloud import kms - # Your Google Cloud Platform project ID - project_id = 'YOUR_PROJECT_ID' - # [END kms_quickstart] - project_id = os.environ['GCLOUD_PROJECT'] - # [START kms_quickstart] + # Create the client. + client = kms.KeyManagementServiceClient() - # Lists keys in the "global" location. - location = 'global' + # Build the parent location name. + location_name = client.location_path(project_id, location_id) - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() + # Call the API. + key_rings = client.list_key_rings(location_name) - # The resource name of the location associated with the key rings. - parent = client.location_path(project_id, location) + # Example of iterating over key rings. + for key_ring in key_rings: + print(key_ring.name) - # Lists key rings - response = client.list_key_rings(parent) - response_list = list(response) - - if len(response_list) > 0: - print('Key rings:') - for key_ring in response_list: - print(key_ring.name) - else: - print('No key rings found.') - # [END kms_quickstart] + return key_rings +# [END kms_quickstart] if __name__ == '__main__': - run_quickstart() + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('project_id', help='id of the GCP project') + parser.add_argument('location_id', help='id of the KMS location') + args = parser.parse_args() + + quickstart(args.project_id, args.location_id) diff --git a/kms/snippets/quickstart_test.py b/kms/snippets/quickstart_test.py deleted file mode 100644 index 5a457518c8fb..000000000000 --- a/kms/snippets/quickstart_test.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2017 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -def test_quickstart(): - import quickstart - - quickstart.run_quickstart() diff --git a/kms/snippets/requirements-test.txt b/kms/snippets/requirements-test.txt index 8855f3cf1f88..d3e30fa6c737 100644 --- a/kms/snippets/requirements-test.txt +++ b/kms/snippets/requirements-test.txt @@ -1,2 +1 @@ -backoff==1.10.0 -pytest==5.3.2 +pytest==5.4.1 diff --git a/kms/snippets/restore_key_version.py b/kms/snippets/restore_key_version.py new file mode 100644 index 000000000000..3c4668d6bedf --- /dev/null +++ b/kms/snippets/restore_key_version.py @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_restore_key_version] +def restore_key_version(project_id, location_id, key_ring_id, key_id, version_id): + """ + Restore a key version scheduled for destruction. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + + Returns: + CryptoKeyVersion: Restored Cloud KMS key version. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Call the API. + restored_version = client.restore_crypto_key_version(key_version_name) + print('Restored key version: {}'.format(restored_version.name)) + return restored_version +# [END kms_restore_key_version] diff --git a/kms/snippets/sign_asymmetric.py b/kms/snippets/sign_asymmetric.py new file mode 100644 index 000000000000..a92a13ec20e2 --- /dev/null +++ b/kms/snippets/sign_asymmetric.py @@ -0,0 +1,64 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_sign_asymmetric] +def sign_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, message): + """ + Sign a message using the public key part of an asymmetric key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): Version to use (e.g. '1'). + message (string): Message to sign. + + Returns: + AsymmetricSignResponse: Signature. + + """ + + # Import the client library. + from google.cloud import kms + + # Import base64 for printing the ciphertext. + import base64 + + # Import hashlib for calculating hashes. + import hashlib + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Calculate the hash. + hash_ = hashlib.sha256(message_bytes).digest() + + # Build the digest. + # + # Note: Key algorithms will require a varying hash function. For + # example, EC_SIGN_P384_SHA384 requires SHA-384. + digest = {'sha256': hash_} + + # Call the API + sign_response = client.asymmetric_sign(key_version_name, digest) + print('Signature: {}'.format(base64.b64encode(sign_response.signature))) + return sign_response +# [END kms_sign_asymmetric] diff --git a/kms/snippets/snippets.py b/kms/snippets/snippets.py deleted file mode 100644 index e09a3a799ace..000000000000 --- a/kms/snippets/snippets.py +++ /dev/null @@ -1,375 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2017 Google, Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and - - -# [START kms_create_keyring] -def create_key_ring(project_id, location_id, key_ring_id): - """Creates a KeyRing in the given location (e.g. global).""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the location associated with the KeyRing. - parent = client.location_path(project_id, location_id) - - # The keyring object template - keyring_name = client.key_ring_path(project_id, location_id, key_ring_id) - keyring = {'name': keyring_name} - - # Create a KeyRing - response = client.create_key_ring(parent, key_ring_id, keyring) - - print('Created KeyRing {}.'.format(response.name)) - return response -# [END kms_create_keyring] - - -# [START kms_create_cryptokey] -def create_crypto_key(project_id, location_id, key_ring_id, crypto_key_id): - """Creates a CryptoKey within a KeyRing in the given location.""" - - from google.cloud import kms_v1 - from google.cloud.kms_v1 import enums - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing associated with the CryptoKey. - parent = client.key_ring_path(project_id, location_id, key_ring_id) - - # Create the CryptoKey object template - purpose = enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT - crypto_key = {'purpose': purpose} - - # Create a CryptoKey for the given KeyRing. - response = client.create_crypto_key(parent, crypto_key_id, crypto_key) - - print('Created CryptoKey {}.'.format(response.name)) - return response -# [END kms_create_cryptokey] - - -# [START kms_encrypt] -def encrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, - plaintext): - """Encrypts input plaintext data using the provided symmetric CryptoKey.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - name = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - - # Use the KMS API to encrypt the data. - response = client.encrypt(name, plaintext) - return response.ciphertext -# [END kms_encrypt] - - -# [START kms_decrypt] -def decrypt_symmetric(project_id, location_id, key_ring_id, crypto_key_id, - ciphertext): - """Decrypts input ciphertext using the provided symmetric CryptoKey.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - name = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - # Use the KMS API to decrypt the data. - response = client.decrypt(name, ciphertext) - return response.plaintext -# [END kms_decrypt] - - -# [START kms_disable_cryptokey_version] -def disable_crypto_key_version(project_id, location_id, key_ring_id, - crypto_key_id, version_id): - """Disables a CryptoKeyVersion associated with a given CryptoKey and - KeyRing.""" - - from google.cloud import kms_v1 - from google.cloud.kms_v1 import enums - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to disable the CryptoKeyVersion. - new_state = enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED - version = {'name': name, 'state': new_state} - update_mask = {'paths': ["state"]} - - # Print results - response = client.update_crypto_key_version(version, update_mask) - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) -# [END kms_disable_cryptokey_version] - - -# [START kms_enable_cryptokey_version] -def enable_crypto_key_version(project_id, location_id, key_ring_id, - crypto_key_id, version_id): - """Enables a CryptoKeyVersion associated with a given CryptoKey and - KeyRing.""" - - from google.cloud import kms_v1 - from google.cloud.kms_v1 import enums - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to enable the CryptoKeyVersion. - new_state = enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED - version = {'name': name, 'state': new_state} - update_mask = {'paths': ["state"]} - - # Print results - response = client.update_crypto_key_version(version, update_mask) - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) -# [END kms_enable_cryptokey_version] - - -# [START kms_destroy_cryptokey_version] -def destroy_crypto_key_version( - project_id, location_id, key_ring_id, crypto_key_id, version_id): - """Schedules a CryptoKeyVersion associated with a given CryptoKey and - KeyRing for destruction 24 hours in the future.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to mark the CryptoKeyVersion for destruction. - response = client.destroy_crypto_key_version(name) - - # Print results - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) -# [END kms_destroy_cryptokey_version] - - -# [START kms_restore_cryptokey_version] -def restore_crypto_key_version( - project_id, location_id, key_ring_id, crypto_key_id, version_id): - """Restores a CryptoKeyVersion that is scheduled for destruction.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # Construct the resource name of the CryptoKeyVersion. - name = client.crypto_key_version_path(project_id, location_id, key_ring_id, - crypto_key_id, version_id) - - # Use the KMS API to restore the CryptoKeyVersion. - response = client.restore_crypto_key_version(name) - - # Print results - print('CryptoKeyVersion {}\'s state has been set to {}.'.format( - name, response.state)) - - -# [END kms_restore_cryptokey_version] - - -# [START kms_add_member_to_cryptokey_policy] -def add_member_to_crypto_key_policy( - project_id, location_id, key_ring_id, crypto_key_id, member, role): - """Adds a member with a given role to the Identity and Access Management - (IAM) policy for a given CryptoKey associated with a KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Add member - policy.bindings.add( - role=role, - members=[member]) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} added with role {} to policy for CryptoKey {} \ - in KeyRing {}'.format(member, role, crypto_key_id, key_ring_id)) -# [END kms_add_member_to_cryptokey_policy] - - -# [START kms_add_member_to_keyring_policy] -def add_member_to_key_ring_policy( - project_id, location_id, key_ring_id, member, role): - """Adds a member with a given role to the Identity and Access Management - (IAM) policy for a given KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing. - resource = client.key_ring_path(project_id, location_id, key_ring_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Add member - policy.bindings.add( - role=role, - members=[member]) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} added with role {} to policy in KeyRing {}' - .format(member, role, key_ring_id)) - -# [END kms_add_member_to_keyring_policy] - - -# [START kms_remove_member_from_cryptokey_policy] -def remove_member_from_crypto_key_policy( - project_id, location_id, key_ring_id, crypto_key_id, member, role): - """Removes a member with a given role from the Identity and Access - Management (IAM) policy for a given CryptoKey associated with a KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Remove member - for b in list(policy.bindings): - if b.role == role and member in b.members: - b.members.remove(member) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} removed from role {} for CryptoKey in KeyRing {}' - .format(member, role, crypto_key_id, key_ring_id)) -# [END kms_remove_member_from_cryptokey_policy] - - -def remove_member_from_key_ring_policy(project_id, location_id, key_ring_id, - member, role): - """Removes a member with a given role from the Identity and Access - Management (IAM) policy for a given KeyRing.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing. - resource = client.key_ring_path(project_id, location_id, key_ring_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Remove member - for b in list(policy.bindings): - if b.role == role and member in b.members: - b.members.remove(member) - - # Update the IAM Policy. - client.set_iam_policy(resource, policy) - - # Print results - print('Member {} removed from role {} for KeyRing {}' - .format(member, role, key_ring_id)) - - -# [START kms_get_keyring_policy] -def get_key_ring_policy(project_id, location_id, key_ring_id): - """Gets the Identity and Access Management (IAM) policy for a given KeyRing - and prints out roles and the members assigned to those roles.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the KeyRing. - resource = client.key_ring_path(project_id, location_id, key_ring_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Print results - print('Printing IAM policy for resource {}:'.format(resource)) - for b in policy.bindings: - for m in b.members: - print('Role: {} Member: {}'.format(b.role, m)) - return policy -# [END kms_get_keyring_policy] - - -def get_crypto_key_policy(project_id, location_id, key_ring_id, crypto_key_id): - """Gets the Identity and Access Management (IAM) policy for a given KeyRing - and prints out roles and the members assigned to those roles.""" - - from google.cloud import kms_v1 - - # Creates an API client for the KMS API. - client = kms_v1.KeyManagementServiceClient() - - # The resource name of the CryptoKey. - resource = client.crypto_key_path(project_id, location_id, key_ring_id, crypto_key_id) - - # Get the current IAM policy. - policy = client.get_iam_policy(resource) - - # Print results - print('Printing IAM policy for resource {}:'.format(resource)) - for b in policy.bindings: - for m in b.members: - print('Role: {} Member: {}'.format(b.role, m)) - return policy diff --git a/kms/snippets/snippets_test.py b/kms/snippets/snippets_test.py index 0b03bcda2193..c75b872c4121 100644 --- a/kms/snippets/snippets_test.py +++ b/kms/snippets/snippets_test.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright 2017 Google, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,226 +11,415 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and +import hashlib +import os import time -from os import environ import uuid -from google.api_core.exceptions import Aborted, GoogleAPICallError -from google.cloud import kms_v1 -from google.cloud.kms_v1 import enums -from google.iam.v1.policy_pb2 import Policy -import backoff import pytest -import snippets +from create_key_asymmetric_decrypt import create_key_asymmetric_decrypt +from create_key_asymmetric_sign import create_key_asymmetric_sign +from create_key_hsm import create_key_hsm +from create_key_labels import create_key_labels +from create_key_ring import create_key_ring +from create_key_rotation_schedule import create_key_rotation_schedule +from create_key_symmetric_encrypt_decrypt import create_key_symmetric_encrypt_decrypt +from create_key_version import create_key_version +from decrypt_asymmetric import decrypt_asymmetric +from decrypt_symmetric import decrypt_symmetric +from destroy_key_version import destroy_key_version +from disable_key_version import disable_key_version +from enable_key_version import enable_key_version +from encrypt_asymmetric import encrypt_asymmetric +from encrypt_symmetric import encrypt_symmetric +from get_key_labels import get_key_labels +from get_key_version_attestation import get_key_version_attestation +from get_public_key import get_public_key +from iam_add_member import iam_add_member +from iam_get_policy import iam_get_policy +from iam_remove_member import iam_remove_member +from quickstart import quickstart +from restore_key_version import restore_key_version +from sign_asymmetric import sign_asymmetric +from update_key_add_rotation import update_key_add_rotation +from update_key_remove_labels import update_key_remove_labels +from update_key_remove_rotation import update_key_remove_rotation +from update_key_set_primary import update_key_set_primary +from update_key_update_labels import update_key_update_labels +from verify_asymmetric_ec import verify_asymmetric_ec +from verify_asymmetric_rsa import verify_asymmetric_rsa +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, utils -def create_key_helper(key_id, purpose, algorithm, t): - try: - client = kms_v1.KeyManagementServiceClient() - parent = client.key_ring_path(t.project_id, t.location, t.keyring_id) - - crypto_key = {'purpose': purpose, - 'version_template': {'algorithm': algorithm}} - client.create_crypto_key(parent, key_id, crypto_key) - return True - except GoogleAPICallError: - # key already exists - return False - - -def setup_module(module): - """ - Set up keys in project if needed - """ - t = TestKMSSnippets() - try: - # create keyring - snippets.create_key_ring(t.project_id, t.location, t.keyring_id) - except GoogleAPICallError: - # keyring already exists - pass - s = create_key_helper(t.sym_id, - enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, - enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm. - GOOGLE_SYMMETRIC_ENCRYPTION, - t) - if s: - # leave time for key to initialize - time.sleep(20) - - -class TestKMSSnippets: - project_id = environ['GCLOUD_PROJECT'] - keyring_id = 'kms-samples-{}'.format(uuid.uuid4().hex) - location = 'global' - parent = 'projects/{}/locations/{}'.format(project_id, location) - keyring_path = '{}/keyRings/{}'.format(parent, keyring_id) - version = '1' - - sym_id = 'symmetric' - - sym = '{}/cryptoKeys/{}'.format(keyring_path, sym_id) - sym_version = '{}/cryptoKeyVersions/{}'.format(sym, version) - - message = 'test message 123' - message_bytes = message.encode('utf-8') +from google.cloud import kms +from google.cloud.kms_v1.proto import resources_pb2 + + +@pytest.fixture(scope="module") +def client(): + return kms.KeyManagementServiceClient() + + +@pytest.fixture(scope="module") +def project_id(): + return os.environ['GCLOUD_PROJECT'] + + +@pytest.fixture(scope="module") +def location_id(): + return "us-east1" + + +@pytest.fixture(scope="module") +def key_ring_id(client, project_id, location_id): + location_name = client.location_path(project_id, location_id) + key_ring_id = '{}'.format(uuid.uuid4()) + key_ring = client.create_key_ring(location_name, key_ring_id, {}) + + yield key_ring_id + + for key in client.list_crypto_keys(key_ring.name): + if key.rotation_period.seconds > 0 or key.next_rotation_time.seconds > 0: + # https://github.com/googleapis/gapic-generator-python/issues/364 + updated_key = resources_pb2.CryptoKey() + updated_key.name = key.name + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + client.update_crypto_key(updated_key, update_mask) + + f = 'state != DESTROYED AND state != DESTROY_SCHEDULED' + for version in client.list_crypto_key_versions(key.name, filter_=f): + client.destroy_crypto_key_version(version.name) + + +@pytest.fixture(scope="module") +def asymmetric_decrypt_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def asymmetric_sign_ec_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.EC_SIGN_P256_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def asymmetric_sign_rsa_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def hsm_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION, + 'protection_level': kms.enums.ProtectionLevel.HSM + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +@pytest.fixture(scope="module") +def symmetric_key_id(client, project_id, location_id, key_ring_id): + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key_id = '{}'.format(uuid.uuid4()) + key = client.create_crypto_key(key_ring_name, key_id, { + 'purpose': kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + 'version_template': { + 'algorithm': kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + }, + 'labels': {'foo': 'bar', 'zip': 'zap'} + }) + wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name)) + return key_id + + +def wait_for_ready(client, key_version_name): + for i in range(5): + key_version = client.get_crypto_key_version(key_version_name) + if key_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED: + return + time.sleep(0.1*(i**2)) + pytest.fail('{} not ready'.format(key_version_name)) + + +def test_create_key_asymmetric_decrypt(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_DECRYPT_OAEP_2048_SHA256 + + +def test_create_key_asymmetric_sign(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_asymmetric_sign(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256 + + +def test_create_key_hsm(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_hsm(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + assert key.version_template.protection_level == kms.enums.ProtectionLevel.HSM + + +def test_create_key_labels(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_labels(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + assert key.labels == {'team': 'alpha', 'cost_center': 'cc1234'} + + +def test_create_key_ring(project_id, location_id): + key_ring_id = '{}'.format(uuid.uuid4()) + key_ring = create_key_ring(project_id, location_id, key_ring_id) + assert key_ring + + +def test_create_key_rotation_schedule(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_rotation_schedule(project_id, location_id, key_ring_id, key_id) + assert key.rotation_period.seconds == 60*60*24*30 + assert key.next_rotation_time.seconds > 0 + + +def test_create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id): + key_id = '{}'.format(uuid.uuid4()) + key = create_key_symmetric_encrypt_decrypt(project_id, location_id, key_ring_id, key_id) + assert key.purpose == kms.enums.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT + assert key.version_template.algorithm == kms.enums.CryptoKeyVersion.CryptoKeyVersionAlgorithm.GOOGLE_SYMMETRIC_ENCRYPTION + + +def test_create_key_version(project_id, location_id, key_ring_id, symmetric_key_id): + version = create_key_version(project_id, location_id, key_ring_id, symmetric_key_id) + assert version + + +def test_decrypt_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + message = 'my message'.encode('utf-8') + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + public_key = client.get_public_key(key_version_name) + + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + + pad = padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None) + ciphertext = rsa_key.encrypt(message, pad) + + response = decrypt_asymmetric(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1', ciphertext) + assert response.plaintext == message + + +def test_decrypt_symmetric(client, project_id, location_id, key_ring_id, symmetric_key_id): + plaintext = 'my message'.encode('utf-8') + + key_version_name = client.crypto_key_path(project_id, location_id, key_ring_id, symmetric_key_id) + encrypt_response = client.encrypt(key_version_name, plaintext) + ciphertext = encrypt_response.ciphertext + + decrypt_response = decrypt_symmetric(project_id, location_id, key_ring_id, symmetric_key_id, ciphertext) + assert decrypt_response.plaintext == plaintext + + +def test_destroy_restore_key_version(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id) + version = client.create_crypto_key_version(key_name, {}) + version_id = version.name.split('/')[-1] + wait_for_ready(client, version.name) + + destroyed_version = destroy_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert destroyed_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DESTROY_SCHEDULED + + restored_version = restore_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert restored_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + +def test_disable_enable_key_version(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id) + version = client.create_crypto_key_version(key_name, {}) + version_id = version.name.split('/')[-1] + + wait_for_ready(client, version.name) + + disabled_version = disable_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert disabled_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.DISABLED + + enabled_version = enable_key_version(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, version_id) + assert enabled_version.state == kms.enums.CryptoKeyVersion.CryptoKeyVersionState.ENABLED + + +def test_encrypt_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + plaintext = 'my message' + ciphertext = encrypt_asymmetric(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1', plaintext) + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + response = client.asymmetric_decrypt(key_version_name, ciphertext) + assert response.plaintext == plaintext.encode('utf-8') + + +def test_encrypt_symmetric(client, project_id, location_id, key_ring_id, symmetric_key_id): + plaintext = 'my message' + encrypt_response = encrypt_symmetric(project_id, location_id, key_ring_id, symmetric_key_id, plaintext) + + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, symmetric_key_id) + decrypt_response = client.decrypt(key_name, encrypt_response.ciphertext) + assert decrypt_response.plaintext == plaintext.encode('utf-8') + + +def test_get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {'foo': 'bar', 'zip': 'zap'} + + +def test_get_key_version_attestation(project_id, location_id, key_ring_id, hsm_key_id): + attestation = get_key_version_attestation(project_id, location_id, key_ring_id, hsm_key_id, '1') + assert attestation.format + assert attestation.content + + +def test_get_public_key(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id): + public_key = get_public_key(project_id, location_id, key_ring_id, asymmetric_decrypt_key_id, '1') + assert public_key.pem + + +def test_iam_add_member(project_id, location_id, key_ring_id, symmetric_key_id): member = 'group:test@google.com' - role = 'roles/viewer' - - @pytest.mark.skip(reason="There's currently no method to delete keyrings, \ - so we should avoid creating resources") - def test_create_key_ring(self): - ring_id = self.keyring_id + '-test-create-{}'.format(uuid.uuid4().hex) - snippets.create_key_ring(self.project_id, self.location, ring_id) - client = kms_v1.KeyManagementServiceClient() - result = client.get_key_ring(client.key_ring_path(self.project_id, - self.location, - ring_id)) - assert ring_id in result.name - - @pytest.mark.skip(reason="Deleting keys isn't instant, so we should avoid \ - creating a large number of them in our tests") - def test_create_crypto_key(self): - key_id = self.sym_id + '-test-{}'.format(uuid.uuid4().hex) - snippets.create_crypto_key(self.project_id, self.location, - self.keyring_id, key_id) - c = kms_v1.KeyManagementServiceClient() - result = c.get_crypto_key(c.crypto_key_path(self.project_id, - self.location, - self.keyring_id, - key_id)) - assert key_id in result.name - - # tests disable/enable/destroy/restore - def test_key_change_version_state(self): - client = kms_v1.KeyManagementServiceClient() - name = client.crypto_key_version_path(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - state_enum = enums.CryptoKeyVersion.CryptoKeyVersionState - # test disable - snippets.disable_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.DISABLED - # test destroy - snippets.destroy_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.DESTROY_SCHEDULED - # test restore - snippets.restore_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.DISABLED - # test re-enable - snippets.enable_crypto_key_version(self.project_id, self.location, - self.keyring_id, self.sym_id, - self.version) - response = client.get_crypto_key_version(name) - assert response.state == state_enum.ENABLED - - def test_get_ring_policy(self): - policy = snippets.get_key_ring_policy(self.project_id, - self.location, self.keyring_id) - assert type(policy) is Policy - - # tests get/add/remove policy members - def test_ring_policy(self): - # add member - snippets.add_member_to_key_ring_policy(self.project_id, self.location, - self.keyring_id, self.member, - self.role) - policy = snippets.get_key_ring_policy(self.project_id, - self.location, self.keyring_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert found - # remove member - snippets.remove_member_from_key_ring_policy(self.project_id, - self.location, - self.keyring_id, - self.member, - self.role) - policy = snippets.get_key_ring_policy(self.project_id, - self.location, self.keyring_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert not found - - # tests get/add/remove policy members - def test_key_policy(self): - # add member - snippets.add_member_to_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id, - self.member, - self.role) - - @backoff.on_exception( - backoff.expo, (Aborted, AssertionError), max_time=60) - def check_policy(): - policy = snippets.get_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert found - - check_policy() - - # remove member - snippets.remove_member_from_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id, - self.member, - self.role) - - @backoff.on_exception( - backoff.expo, (Aborted, AssertionError), max_time=60) - def check_policy_again(): - policy = snippets.get_crypto_key_policy( - self.project_id, - self.location, - self.keyring_id, - self.sym_id) - found = False - for b in list(policy.bindings): - if b.role == self.role and self.member in b.members: - found = True - assert not found - - check_policy_again() - - def test_symmetric_encrypt_decrypt(self): - cipher_bytes = snippets.encrypt_symmetric(self.project_id, - self.location, - self.keyring_id, - self.sym_id, - self.message_bytes) - plain_bytes = snippets.decrypt_symmetric(self.project_id, - self.location, - self.keyring_id, - self.sym_id, - cipher_bytes) - assert plain_bytes == self.message_bytes - assert cipher_bytes != self.message_bytes - plaintext = plain_bytes.decode("utf-8") - assert plaintext == self.message + policy = iam_add_member(project_id, location_id, key_ring_id, symmetric_key_id, member) + assert any(member in b.members for b in policy.bindings) + + +def test_iam_get_policy(project_id, location_id, key_ring_id, symmetric_key_id): + policy = iam_get_policy(project_id, location_id, key_ring_id, symmetric_key_id) + assert policy + + +def test_iam_remove_member(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + resource_name = client.crypto_key_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id) + + policy = client.get_iam_policy(resource_name) + policy.bindings.add( + role='roles/cloudkms.cryptoKeyEncrypterDecrypter', + members=['group:test@google.com', 'group:tester@google.com']) + client.set_iam_policy(resource_name, policy) + + policy = iam_remove_member(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, 'group:test@google.com') + assert not any('group:test@google.com' in b.members for b in policy.bindings) + assert any('group:tester@google.com' in b.members for b in policy.bindings) + + +def test_sign_asymmetric(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + message = 'my message' + + sign_response = sign_asymmetric(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1', message) + assert sign_response.signature + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1') + public_key = client.get_public_key(key_version_name) + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + + try: + sha256 = hashes.SHA256() + pad = padding.PKCS1v15() + rsa_key.verify(sign_response.signature, hash_, pad, utils.Prehashed(sha256)) + except InvalidSignature: + pytest.fail('invalid signature') + + +def test_update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.rotation_period.seconds == 60*60*24*30 + assert key.next_rotation_time.seconds > 0 + + +def test_update_key_remove_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_remove_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {} + + +def test_update_key_remove_rotation(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_remove_rotation(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.rotation_period.seconds == 0 + assert key.next_rotation_time.seconds == 0 + + +def test_update_key_set_primary(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_set_primary(project_id, location_id, key_ring_id, symmetric_key_id, '1') + assert '1' in key.primary.name + + +def test_update_key_update_labels(project_id, location_id, key_ring_id, symmetric_key_id): + key = update_key_update_labels(project_id, location_id, key_ring_id, symmetric_key_id) + assert key.labels == {'new_label': 'new_value'} + + +def test_verify_asymmetric_ec(client, project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id): + message = 'my message' + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id, '1') + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + sign_response = client.asymmetric_sign(key_version_name, {'sha256': hash_}) + + verified = verify_asymmetric_ec(project_id, location_id, key_ring_id, asymmetric_sign_ec_key_id, '1', message, sign_response.signature) + assert verified + + +def test_verify_asymmetric_rsa(client, project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id): + message = 'my message' + + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1') + hash_ = hashlib.sha256(message.encode('utf-8')).digest() + sign_response = client.asymmetric_sign(key_version_name, {'sha256': hash_}) + + verified = verify_asymmetric_rsa(project_id, location_id, key_ring_id, asymmetric_sign_rsa_key_id, '1', message, sign_response.signature) + assert verified + + +def test_quickstart(project_id, location_id): + key_rings = quickstart(project_id, location_id) + assert key_rings diff --git a/kms/snippets/update_key_add_rotation.py b/kms/snippets/update_key_add_rotation.py new file mode 100644 index 000000000000..22dd6b6622fc --- /dev/null +++ b/kms/snippets/update_key_add_rotation.py @@ -0,0 +1,62 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_update_key_add_rotation_schedule] +def update_key_add_rotation(project_id, location_id, key_ring_id, key_id): + """ + Add a rotation schedule to an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Import time for getting the current time. + import time + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + + # Rotate the key every 30 days. + key.rotation_period.seconds = 60*60*24*30 + + # Start the first rotation in 24 hours. + key.next_rotation_time.seconds = int(time.time()) + 60*60*24 + + # Build the update mask. + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_add_rotation_schedule] diff --git a/kms/snippets/update_key_remove_labels.py b/kms/snippets/update_key_remove_labels.py new file mode 100644 index 000000000000..a44ab214b7a8 --- /dev/null +++ b/kms/snippets/update_key_remove_labels.py @@ -0,0 +1,54 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_update_key_remove_labels] +def update_key_remove_labels(project_id, location_id, key_ring_id, key_id): + """ + Remove labels from an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + key.labels.clear() + + # Build the update mask. + update_mask = {'paths': ['labels']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_remove_labels] diff --git a/kms/snippets/update_key_remove_rotation.py b/kms/snippets/update_key_remove_rotation.py new file mode 100644 index 000000000000..7f8707eb6eb0 --- /dev/null +++ b/kms/snippets/update_key_remove_rotation.py @@ -0,0 +1,53 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_update_key_remove_rotation_schedule] +def update_key_remove_rotation(project_id, location_id, key_ring_id, key_id): + """ + Remove a rotation schedule from an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + + # Build the update mask. + update_mask = {'paths': ['rotation_period', 'next_rotation_time']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_remove_rotation_schedule] diff --git a/kms/snippets/update_key_set_primary.py b/kms/snippets/update_key_set_primary.py new file mode 100644 index 000000000000..dd889dbd407f --- /dev/null +++ b/kms/snippets/update_key_set_primary.py @@ -0,0 +1,45 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_update_key_set_primary] +def update_key_set_primary(project_id, location_id, key_ring_id, key_id, version_id): + """ + Update the primary version of a key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the key to make primary (e.g. '2'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + updated_key = client.update_crypto_key_primary_version(key_name, version_id) + print('Updated {} primary to {}'.format(updated_key.name, version_id)) + return updated_key +# [END kms_update_key_set_primary] diff --git a/kms/snippets/update_key_update_labels.py b/kms/snippets/update_key_update_labels.py new file mode 100644 index 000000000000..21372472bc2a --- /dev/null +++ b/kms/snippets/update_key_update_labels.py @@ -0,0 +1,54 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_update_key_update_labels] +def update_key_update_labels(project_id, location_id, key_ring_id, key_id): + """ + Update labels on an existing key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + + Returns: + CryptoKey: Updated Cloud KMS key. + + """ + + # Import the client library. + from google.cloud import kms + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Build the key. We need to build a full proto instead of a dict due to + # https://github.com/googleapis/gapic-generator-python/issues/364. + from google.cloud.kms_v1.proto import resources_pb2 + key = resources_pb2.CryptoKey() + key.name = key_name + key.labels.update({'new_label': 'new_value'}) + + # Build the update mask. + update_mask = {'paths': ['labels']} + + # Call the API. + updated_key = client.update_crypto_key(key, update_mask) + print('Updated key: {}'.format(updated_key.name)) + return updated_key +# [END kms_update_key_update_labels] diff --git a/kms/snippets/verify_asymmetric_ec.py b/kms/snippets/verify_asymmetric_ec.py new file mode 100644 index 000000000000..ac77a64b8681 --- /dev/null +++ b/kms/snippets/verify_asymmetric_ec.py @@ -0,0 +1,72 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_verify_asymmetric_signature_ec] +def verify_asymmetric_ec(project_id, location_id, key_ring_id, key_id, version_id, message, signature): + """ + Verify the signature of an message signed with an asymmetric EC key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + message (string): Original message (e.g. 'my message') + signature (bytes): Signature from a sign request. + + Returns: + bool: True if verified, False otherwise + + """ + + # Import the client library. + from google.cloud import kms + + # Import cryptographic helpers from the cryptography package. + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import ec, utils + + # Import hashlib. + import hashlib + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + ec_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message_bytes).digest() + + # Attempt to verify. + try: + sha256 = hashes.SHA256() + ec_key.verify(signature, hash_, ec.ECDSA(utils.Prehashed(sha256))) + print('Signature verified') + return True + except InvalidSignature: + print('Signature failed to verify') + return False +# [END kms_verify_asymmetric_signature_ec] diff --git a/kms/snippets/verify_asymmetric_rsa.py b/kms/snippets/verify_asymmetric_rsa.py new file mode 100644 index 000000000000..6df3d862f83b --- /dev/null +++ b/kms/snippets/verify_asymmetric_rsa.py @@ -0,0 +1,73 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and + + +# [START kms_verify_asymmetric_signature_rsa] +def verify_asymmetric_rsa(project_id, location_id, key_ring_id, key_id, version_id, message, signature): + """ + Verify the signature of an message signed with an asymmetric RSA key. + + Args: + project_id (string): Google Cloud project ID (e.g. 'my-project'). + location_id (string): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (string): ID of the key to use (e.g. 'my-key'). + version_id (string): ID of the version to use (e.g. '1'). + message (string): Original message (e.g. 'my message') + signature (bytes): Signature from a sign request. + + Returns: + bool: True if verified, False otherwise + + """ + + # Import the client library. + from google.cloud import kms + + # Import cryptographic helpers from the cryptography package. + from cryptography.exceptions import InvalidSignature + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import padding, utils + + # Import hashlib. + import hashlib + + # Convert the message to bytes. + message_bytes = message.encode('utf-8') + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id) + + # Get the public key. + public_key = client.get_public_key(key_version_name) + + # Extract and parse the public key as a PEM-encoded RSA key. + pem = public_key.pem.encode('utf-8') + rsa_key = serialization.load_pem_public_key(pem, default_backend()) + hash_ = hashlib.sha256(message_bytes).digest() + + # Attempt to verify. + try: + sha256 = hashes.SHA256() + pad = padding.PKCS1v15() + rsa_key.verify(signature, hash_, pad, utils.Prehashed(sha256)) + print('Signature verified') + return True + except InvalidSignature: + print('Signature failed to verify') + return False +# [END kms_verify_asymmetric_signature_rsa]