def test_calls_service_for_operations_unsupported_locally(): """When an operation can't be performed locally, the client should request Key Vault perform it""" mock_client = mock.Mock() key = mock.Mock(spec=KeyVaultKey, id="https://localhost/fake/key/version") client = CryptographyClient(key, mock.Mock()) client._client = mock_client supports_nothing = mock.Mock(supports=mock.Mock(return_value=False)) with mock.patch( CryptographyClient.__module__ + ".get_local_cryptography_provider", lambda *_: supports_nothing): client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.decrypt.call_count == 1 assert supports_nothing.decrypt.call_count == 0 client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.encrypt.call_count == 1 assert supports_nothing.encrypt.call_count == 0 client.sign(SignatureAlgorithm.rs256, b"...") assert mock_client.sign.call_count == 1 assert supports_nothing.sign.call_count == 0 client.verify(SignatureAlgorithm.rs256, b"...", b"...") assert mock_client.verify.call_count == 1 assert supports_nothing.verify.call_count == 0 client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.unwrap_key.call_count == 1 assert supports_nothing.unwrap_key.call_count == 0 client.wrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.wrap_key.call_count == 1 assert supports_nothing.wrap_key.call_count == 0
def test_decrypt_argument_validation(): mock_client = mock.Mock() key = mock.Mock( spec=KeyVaultKey, id="https://localhost/fake/key/version", properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC), expires_on=datetime(3000, 1, 1, tzinfo=_UTC)), ) client = CryptographyClient(key, mock.Mock()) client._client = mock_client with pytest.raises(ValueError) as ex: client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...", iv=b"...") assert "iv" in str(ex.value) with pytest.raises(ValueError) as ex: client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...", additional_authenticated_data=b"...") assert "additional_authenticated_data" in str(ex.value) with pytest.raises(ValueError) as ex: client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...", authentication_tag=b"...") assert "authentication_tag" in str(ex.value) with pytest.raises(ValueError) as ex: client.decrypt(EncryptionAlgorithm.a128_gcm, b"...", iv=b"...") assert "authentication_tag" in str(ex.value) and "required" in str( ex.value) with pytest.raises(ValueError) as ex: client.decrypt(EncryptionAlgorithm.a192_cbcpad, b"...") assert "iv" in str(ex.value) and "required" in str(ex.value)
class DecryptTest(PerfStressTest): def __init__(self, arguments): super().__init__(arguments) # Auth configuration self.credential = DefaultAzureCredential() self.async_credential = AsyncDefaultAzureCredential() # Create clients vault_url = self.get_from_env("AZURE_KEYVAULT_URL") self.client = KeyClient(vault_url, self.credential, **self._client_kwargs) self.async_client = AsyncKeyClient(vault_url, self.async_credential, **self._client_kwargs) self.key_name = "livekvtestdecryptperfkey" async def global_setup(self): """The global setup is run only once.""" await super().global_setup() rsa_key = await self.async_client.create_rsa_key(self.key_name) self.crypto_client = CryptographyClient(rsa_key.id, self.credential, permissions=NO_GET, **self._client_kwargs) self.async_crypto_client = AsyncCryptographyClient( rsa_key.id, self.async_credential, permissions=NO_GET, **self._client_kwargs) self.test_algorithm = EncryptionAlgorithm.rsa_oaep_256 plaintext = os.urandom(32) self.ciphertext = self.crypto_client.encrypt(self.test_algorithm, plaintext).ciphertext async def global_cleanup(self): """The global cleanup is run only once.""" await self.async_client.delete_key(self.key_name) await self.async_client.purge_deleted_key(self.key_name) await super().global_cleanup() async def close(self): """This is run after cleanup.""" await self.async_client.close() await self.async_crypto_client.close() await self.async_credential.close() await super().close() def run_sync(self): """The synchronous perf test.""" self.crypto_client.decrypt(self.test_algorithm, self.ciphertext) async def run_async(self): """The asynchronous perf test.""" await self.async_crypto_client.decrypt(self.test_algorithm, self.ciphertext)
def test_encrypt_decrypt(self, key_client, credential, **kwargs): key_name = self.get_resource_name("crypto-test-encrypt-key") key = key_client.create_rsa_key(key_name) client = CryptographyClient(key, credential) # [START encrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm # the result holds the ciphertext and identifies the encryption key and algorithm used result = client.encrypt(EncryptionAlgorithm.rsa_oaep, b"plaintext") ciphertext = result.ciphertext print(result.key_id) print(result.algorithm) # [END encrypt] # [START decrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm result = client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext) print(result.plaintext) # [END decrypt] pass
def test_encrypt_decrypt(self, key_client, **kwargs): credential = self.get_credential(CryptographyClient) key_name = self.get_resource_name("crypto-test-encrypt-key") key_client.create_rsa_key(key_name) # [START create_client] # create a CryptographyClient using a KeyVaultKey instance key = key_client.get_key(key_name) crypto_client = CryptographyClient(key, credential) # or a key's id, which must include a version key_id = "https://<your vault>.vault.azure.net/keys/<key name>/fe4fdcab688c479a9aa80f01ffeac26" crypto_client = CryptographyClient(key_id, credential) # [END create_client] client = CryptographyClient(key, credential, api_version=key_client.api_version) # [START encrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm # the result holds the ciphertext and identifies the encryption key and algorithm used result = client.encrypt(EncryptionAlgorithm.rsa_oaep, b"plaintext") ciphertext = result.ciphertext print(result.key_id) print(result.algorithm) # [END encrypt] # [START decrypt] from azure.keyvault.keys.crypto import EncryptionAlgorithm result = client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext) print(result.plaintext)
def decrypt_secret(self, cipher_text, vault_name, keyid, subscription_id, region): logger.info("secret to decrypt: {0}".format(cipher_text)) logger.info("keyid: {0}".format(keyid)) logger.info("Azure subscription id: {0}".format(subscription_id)) logger.info("Azure region: {0}".format(region)) vault_url = "https://{0}.vault.azure.net/".format(vault_name) logger.info(vault_url) ''' convert string to byte literal ''' byte_literal_value = cipher_text.encode() ''' You can use str.decode() with encoding as unicode-escape . Then decode it back using the required encoding to get back your bytes array. ''' byte_literal_value = byte_literal_value.decode( 'unicode-escape').encode('ISO-8859-1') credential = DefaultAzureCredential() key_client = KeyClient(vault_url=vault_url, credential=credential) key = key_client.get_key(keyid) crypto_client = CryptographyClient(key, credential=credential) decrypted = crypto_client.decrypt(EncryptionAlgorithm.rsa_oaep, byte_literal_value) print("decrypted: ", decrypted.plaintext)
def test_encrypt_local(self, key_client, credential, **kwargs): """Encrypt locally, decrypt with Key Vault""" key = key_client.create_rsa_key("encrypt-local", size=4096) crypto_client = CryptographyClient(key, credential) for encrypt_algorithm in EncryptionAlgorithm: result = crypto_client.encrypt(encrypt_algorithm, self.plaintext) self.assertEqual(result.key_id, key.id) result = crypto_client.decrypt(result.algorithm, result.ciphertext) self.assertEqual(result.plaintext, self.plaintext)
def test_prefers_local_provider(): """The client should complete operations locally whenever possible""" mock_client = mock.Mock() key = mock.Mock( spec=KeyVaultKey, id="https://localhost/fake/key/version", properties=mock.Mock(not_before=datetime(2000, 1, 1, tzinfo=_UTC), expires_on=datetime(3000, 1, 1, tzinfo=_UTC)), ) client = CryptographyClient(key, mock.Mock()) client._client = mock_client supports_everything = mock.Mock(supports=mock.Mock(return_value=True)) with mock.patch( CryptographyClient.__module__ + ".get_local_cryptography_provider", lambda *_: supports_everything): client.decrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.decrypt.call_count == 0 assert supports_everything.decrypt.call_count == 1 client.encrypt(EncryptionAlgorithm.rsa_oaep, b"...") assert mock_client.encrypt.call_count == 0 assert supports_everything.encrypt.call_count == 1 client.sign(SignatureAlgorithm.rs256, b"...") assert mock_client.sign.call_count == 0 assert supports_everything.sign.call_count == 1 client.verify(SignatureAlgorithm.rs256, b"...", b"...") assert mock_client.verify.call_count == 0 assert supports_everything.verify.call_count == 1 client.unwrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.unwrap_key.call_count == 0 assert supports_everything.unwrap_key.call_count == 1 client.wrap_key(KeyWrapAlgorithm.rsa_oaep, b"...") assert mock_client.wrap_key.call_count == 0 assert supports_everything.wrap_key.call_count == 1
def test_encrypt_and_decrypt(self, key_client, credential, **kwargs): key_name = self.get_resource_name("keycrypt") imported_key = self._import_test_key(key_client, key_name) crypto_client = CryptographyClient(imported_key.id, credential) result = crypto_client.encrypt(EncryptionAlgorithm.rsa_oaep, self.plaintext) self.assertEqual(result.key_id, imported_key.id) result = crypto_client.decrypt(result.algorithm, result.ciphertext) self.assertEqual(result.key_id, imported_key.id) self.assertEqual(EncryptionAlgorithm.rsa_oaep, result.algorithm) self.assertEqual(self.plaintext, result.plaintext)
def decrypt(request, json_key_attributes_dict, pin, version): """ This method will be called by the application entry point for decrypting the payload. request.value has the plaintext payload request.alg contains the padding algorithm for encryption. """ set_env(json_key_attributes_dict, pin) credential = DefaultAzureCredential() key_vault_key = get_akv_key(json_key_attributes_dict, credential) crypto_client = CryptographyClient(key_vault_key, credential=credential) decrypted_payload = crypto_client.decrypt(EncryptionAlgorithm.rsa_oaep, request.value) response = EncryptDecryptResponse(decrypted_payload.plaintext) return response
def rotate_key(self, envelope: EncryptionEnvelope, version: Optional[str] = None) -> EncryptionEnvelope: old_key = self.key_client.get_key(self.key_encryption_key.name, version=envelope.version) old_crypto_client = CryptographyClient(old_key, self.cred) # decode from base64 ciphertext = base64.b64decode(envelope.data) encrypted_data_key = base64.b64decode(envelope.key) nonce = base64.b64decode(envelope.iv) # decrypt the data key result = old_crypto_client.decrypt(ENCRYPTION_ALGORITHM, encrypted_data_key) data_key = result.plaintext # decrypt the data locally using the nonce and decrypted data key decrypted_data = self._data_decrypt(ciphertext, data_key, nonce) # now re-encrypt with the latest key encryption key return self.encrypt(decrypted_data)
class AzureEncryptionProvider(EncryptionProvider): """An EncryptionProvider implementation for Azure Key Vault. To authenticate, please provide Azure AD service principal info as 'tenant_id', 'client_id', and 'client_secret' kwargs, or as AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET environment variables. Attributes: tenant_id (str): The tenant ID of the Azure SP to connect with. client_id (str): The client ID of the Azure SP to connect with. client_secret (str): The client secret of the Azure SP to connect with. key_client (KeyClient): Azure Key Vault key client. crypto_client (CryptographyClient): Azure Key Vault crypto client. Args: vault_url (str): The URL of the key vault to connect to. key (str): The name of the key encryption key to use for envelope encryption. auth_via_cli (bool, kwarg): If we should auth via Azure CLI. **kwargs: Authentication information. Raises: TypeError: If authentication information is not provided correctly. """ def __init__(self, vault_url: str, key: str, **kwargs) -> None: auth_via_cli = bool(kwargs.pop("auth_via_cli", False)) if auth_via_cli: try: self.key_client = get_client_from_cli_profile( KeyClient, vault_url=vault_url) self.key_encryption_key = self.key_client.get_key(key) self.crypto_client = get_client_from_cli_profile( CryptographyClient, key=self.key_encryption_key) except CLIError: logging.error( "ERROR: Unable to authenticate via Azure CLI, have you " "logged in with 'az login'?") raise SystemExit(1) else: tenant_id = kwargs.pop("tenant_id", os.getenv(TENANT_ID_ENVVAR)) client_id = kwargs.pop("client_id", os.getenv(CLIENT_ID_ENVVAR)) client_secret = kwargs.pop("client_secret", os.getenv(CLIENT_SECRET_ENVVAR)) if tenant_id is None or client_id is None or client_secret is None: raise TypeError( "Please specify tenant_id, client_id, and client_secret " "in config or in environment variables as in " "https://github.com/Azure/azure-sdk-for-python/tree/master/sdk/identity/azure-identity#service-principal-with-secret" ) self.cred = ClientSecretCredential(tenant_id, client_id, client_secret) self.key_client = KeyClient(vault_url, credential=self.cred, logger=None) self.key_encryption_key = self.key_client.get_key(key) self.crypto_client = CryptographyClient(self.key_encryption_key, self.cred) # overrides EncryptionProvider.encrypt() def encrypt(self, data: bytes) -> EncryptionEnvelope: # encrypt the data locally, generating a data key and a nonce ciphertext, data_key, nonce = self._data_encrypt(data) # encrypt the data key using the key from the vault result = self.crypto_client.encrypt(ENCRYPTION_ALGORITHM, data_key) del data_key # we don't wanna keep this around after we've encrypted it encrypted_data_key = result.ciphertext # encode to base64 for storage/transmission b64_ciphertext = base64.b64encode(ciphertext).decode("utf-8") b64_encrypted_data_key = base64.b64encode(encrypted_data_key).decode( "utf-8") b64_nonce = base64.b64encode(nonce).decode("utf-8") return EncryptionEnvelope(b64_ciphertext, b64_encrypted_data_key, b64_nonce, self.key_encryption_key.properties.version) # overrides EncryptionProvider.decrypt() def decrypt(self, envelope: EncryptionEnvelope) -> Union[bytes, None]: if envelope.version != self.key_encryption_key.properties.version: logging.error( "Encryption key version %s is out of " "date, please re-encrypt with 'victoria encrypt rotate'", envelope.version) return None # decode from base64 ciphertext = base64.b64decode(envelope.data) encrypted_data_key = base64.b64decode(envelope.key) nonce = base64.b64decode(envelope.iv) # decrypt the data key result = self.crypto_client.decrypt(ENCRYPTION_ALGORITHM, encrypted_data_key) data_key = result.plaintext # decrypt the data locally using the nonce and decrypted data key return self._data_decrypt(ciphertext, data_key, nonce) # overrides EncryptionProvider.rotate_key() def rotate_key(self, envelope: EncryptionEnvelope, version: Optional[str] = None) -> EncryptionEnvelope: old_key = self.key_client.get_key(self.key_encryption_key.name, version=envelope.version) old_crypto_client = CryptographyClient(old_key, self.cred) # decode from base64 ciphertext = base64.b64decode(envelope.data) encrypted_data_key = base64.b64decode(envelope.key) nonce = base64.b64decode(envelope.iv) # decrypt the data key result = old_crypto_client.decrypt(ENCRYPTION_ALGORITHM, encrypted_data_key) data_key = result.plaintext # decrypt the data locally using the nonce and decrypted data key decrypted_data = self._data_decrypt(ciphertext, data_key, nonce) # now re-encrypt with the latest key encryption key return self.encrypt(decrypted_data)
def decrypt(self, ciphertext, key_name): key = self.key_client.get_key(key_name) crypto_client = CryptographyClient(key, credential=self.credential) text = crypto_client.decrypt(EncryptionAlgorithm.rsa_oaep, ciphertext) return text.plaintext.decode()