Search by

symfony / azure-keyvault-key-management

fabpot

Symfony Azure Key Vault Key Management Bridge

Package info

github.com/symfony/azure-keyvault-key-management

Homepage

Type:symfony-key-management-bridge

pkg:composer/symfony/azure-keyvault-key-management

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v8.2.0-BETA1 2026-10-31 16:39 UTC

This package is auto-updated.

Last update: 2026-11-04 12:39:01 UTC


README

Provides an implementation of Symfony\Component\KeyManagement\EncrypterInterface, Symfony\Component\KeyManagement\DecrypterInterface and Symfony\Component\KeyManagement\DataKeyGeneratorInterface backed by Azure Key Vault (and Managed HSM) over its REST API. Encryption, decryption and data-key wrapping never expose the master key: Azure performs them server-side.

This Bridge is experimental. Experimental features are not covered by Symfony's Backward Compatibility Promise.

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\KeyManagement\Bridge\AzureKeyVault\AzureKeyVault;
use Symfony\Component\KeyManagement\Bridge\AzureKeyVault\ClientCredentialsTokenProvider;

$client = HttpClient::createForBaseUri('https://my-vault.vault.azure.net/');
$tokens = new ClientCredentialsTokenProvider(
    $client,
    $_SERVER['AZURE_TENANT_ID'],
    $_SERVER['AZURE_CLIENT_ID'],
    $_SERVER['AZURE_CLIENT_SECRET'],
);

$kms = new AzureKeyVault($client, $tokens);

// Use the key name for the latest version on writes, or append `/<version>` to select one.
$ciphertext = $kms->encrypt('app-key', 'hello world');
$plaintext  = $kms->decrypt($ciphertext);

$dataKey = $kms->generateDataKey('app-key', 32);
$result  = $dataKey->use(fn (string $dek): string => /* local AEAD encrypt */);

Each new ciphertext and wrapped data key records the key version it was produced under, and the bridge checks that Azure's response names the requested key. Keep older key versions enabled while their ciphertexts or wrapped data keys must remain readable.

Authentication

Bring your own TokenProviderInterface to plug any Azure AD flow: the bundled ClientCredentialsTokenProvider covers the client_credentials grant (tenant id + client id + client secret) and caches the token in memory until 60 seconds before its advertised expiration. Managed Identity, Workload Identity, federated credentials, on-behalf-of, ... are out of scope for the default provider; implement TokenProviderInterface for your authentication flow.

Custom token providers implement invalidateToken($token) to discard a cached token only when it matches the rejected value. If Key Vault responds with HTTP 401, the bridge obtains a new token and retries the request once if that token differs. HTTP 403 is reported directly without invalidating the token.

Algorithms

The bridge accepts two configurable algorithms:

  • encryptAlgorithm (default RSA-OAEP-256): selects the algorithm for encrypt(). The AEAD variants A128GCM/A192GCM/A256GCM are also accepted. These require a symmetric key, available on Managed HSM or Key Vault Premium in preview.
  • wrapAlgorithm (default RSA-OAEP-256): selects the algorithm for generateDataKey() and wrapDataKey(). The same algorithms are accepted; with a symmetric key you can also select A128KW / A192KW / A256KW explicitly.

The constructor and DSN factory reject any other algorithm. RSA-OAEP and RSA1_5 are excluded following Microsoft's recommendations. CBC variants are excluded because the bridge does not preserve their IV and they do not authenticate ciphertexts. AES-KW is accepted only for data-key wrapping.

Every blob records its algorithm, so a configuration change only affects new writes. Reads use the recorded algorithm, subject to the same payload or wrapping allowlist. Unprefixed blobs and malformed algorithm prefixes are rejected.

encrypt() / decrypt() go through the RSA path by default and are therefore suited to small payloads (config secrets, tokens). For arbitrary-size payloads, use Symfony\Component\KeyManagement\EnvelopeEncrypter on top of the bridge: the master key only sees the wrapped DEK.

DSN scheme

azure-keyvault://<clientId>:<clientSecret>@<vault-name>.vault.azure.net?tenant=<tenantId>[&algorithm=...&wrap_algorithm=...&api_version=...&audience=...&timeout=...&max_duration=...]

The host is the full vault DNS (<name>.vault.azure.net, or <name>.managedhsm.azure.net for Managed HSM). The factory selects a Key Vault or Managed HSM audience from the host, which audience can override, and it always authenticates against the public Microsoft Entra authority. A sovereign cloud (US government, China) needs another authority, so construct AzureKeyVault there manually with a token provider configured for that cloud.

The timeout (idle timeout) and max_duration (whole request) options, in seconds, set the HTTP client's timeouts, for the token requests too. When the factory is given no client, it builds one with a 5-second timeout and a 10-second max_duration. A client given to the factory, like the application's http_client, keeps its own settings unless the DSN sets the timeouts. Requests don't follow redirects, whatever the client. On a 307 or 308 redirect to another host, the client secret or the plaintext would be sent again.

Examples:

azure-keyvault://CLIENT_ID:CLIENT_SECRET@my-vault.vault.azure.net?tenant=TENANT_ID
azure-keyvault://CLIENT_ID:CLIENT_SECRET@my-vault.vault.azure.net?tenant=TENANT_ID&algorithm=RSA-OAEP-256&api_version=7.4
azure-keyvault://CLIENT_ID:CLIENT_SECRET@my-hsm.managedhsm.azure.net?tenant=TENANT_ID&algorithm=A256GCM&wrap_algorithm=A256KW

AAD support

AEAD algorithms (A128GCM/A192GCM/A256GCM) accept Azure's aad parameter natively and are integrity-protected. RSA and AES-KW have no AAD support; passing a non-empty $aad with either algorithm raises UnsupportedOperationException.

For AEAD ciphertexts and wrapped keys, the blob is <alg>.<iv>.<tag>.<value>. For RSA ciphertexts and RSA or AES-KW wrapped keys, it is <alg>.<value>. The IV, tag and value are the base64url fields returned by Azure; the algorithm prefix is added by the bridge and removed before sending a read request. Callers store the complete opaque blob, together with its key ID.

Resources