GitHub Copilot
ChatGPT
Claude
Codex CLI
Cursor
opencode
Skill
Text
azure-keyvault-keys-ts
Manage cryptographic keys using Azure Key Vault Keys SDK for JavaScript (@azure/keyvault-keys). Use when creating, encrypting/decrypting, signing, or rotating keys.
Virus-scanned
Reviewed automatically before listing.
Download
microsoft-skills-.github_plugins_azure-sdk-typescript_skills_azure-keyvault-keys-ts-e58528d.zip · 10 KB
Install
skills CLI
npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/azure-keyvault-keys-ts
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Azure Key Vault Keys SDK for TypeScript
Manage cryptographic keys with Azure Key Vault.
Installation
# Keys SDK
npm install @azure/keyvault-keys @azure/identity
Environment Variables
KEY_VAULT_URL=https://<vault-name>.vault.azure.net
# Or
AZURE_KEYVAULT_NAME=<vault-name>
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
Authentication
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
import { KeyClient, CryptographyClient } from "@azure/keyvault-keys";
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();
const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`;
const keyClient = new KeyClient(vaultUrl, credential);
const secretClient = new SecretClient(vaultUrl, credential);
Secrets Operations
Create/Set Secret
const secret = await secretClient.setSecret("MySecret", "secret-value");
// With attributes
const secretWithAttrs = await secretClient.setSecret("MySecret", "value", {
enabled: true,
expiresOn: new Date("2025-12-31"),
contentType: "application/json",
tags: { environment: "production" }
});
Get Secret
// Get latest version
const secret = await secretClient.getSecret("MySecret");
console.log(secret.value);
// Get specific version
const specificSecret = await secretClient.getSecret("MySecret", {
version: secret.properties.version
});
List Secrets
for await (const secretProperties of secretClient.listPropertiesOfSecrets()) {
console.log(secretProperties.name);
}
// List versions
for await (const version of secretClient.listPropertiesOfSecretVersions("MySecret")) {
console.log(version.version);
}
Delete Secret
// Soft delete
const deletePoller = await secretClient.beginDeleteSecret("MySecret");
await deletePoller.pollUntilDone();
// Purge (permanent)
await secretClient.purgeDeletedSecret("MySecret");
// Recover
const recoverPoller = await secretClient.beginRecoverDeletedSecret("MySecret");
await recoverPoller.pollUntilDone();
Keys Operations
Create Keys
// Generic key
const key = await keyClient.createKey("MyKey", "RSA");
// RSA key with size
const rsaKey = await keyClient.createRsaKey("MyRsaKey", { keySize: 2048 });
// Elliptic Curve key
const ecKey = await keyClient.createEcKey("MyEcKey", { curve: "P-256" });
// With attributes
const keyWithAttrs = await keyClient.createKey("MyKey", "RSA", {
enabled: true,
expiresOn: new Date("2025-12-31"),
tags: { purpose: "encryption" },
keyOps: ["encrypt", "decrypt", "sign", "verify"]
});
Get Key
const key = await keyClient.getKey("MyKey");
console.log(key.name, key.keyType);
List Keys
for await (const keyProperties of keyClient.listPropertiesOfKeys()) {
console.log(keyProperties.name);
}
Rotate Key
// Manual rotation
const rotatedKey = await keyClient.rotateKey("MyKey");
// Set rotation policy
await keyClient.updateKeyRotationPolicy("MyKey", {
lifetimeActions: [{ action: "Rotate", timeBeforeExpiry: "P30D" }],
expiresIn: "P90D"
});
Delete Key
const deletePoller = await keyClient.beginDeleteKey("MyKey");
await deletePoller.pollUntilDone();
// Purge
await keyClient.purgeDeletedKey("MyKey");
Cryptographic Operations
Create CryptographyClient
import { CryptographyClient } from "@azure/keyvault-keys";
// From key object
const cryptoClient = new CryptographyClient(key, credential);
// From key ID
const cryptoClient = new CryptographyClient(key.id!, credential);
Encrypt/Decrypt
// Encrypt
const encryptResult = await cryptoClient.encrypt({
algorithm: "RSA-OAEP",
plaintext: Buffer.from("My secret message")
});
// Decrypt
const decryptResult = await cryptoClient.decrypt({
algorithm: "RSA-OAEP",
ciphertext: encryptResult.result
});
console.log(decryptResult.result.toString());
Sign/Verify
import { createHash } from "node:crypto";
// Create digest
const hash = createHash("sha256").update("My message").digest();
// Sign
const signResult = await cryptoClient.sign("RS256", hash);
// Verify
const verifyResult = await cryptoClient.verify("RS256", hash, signResult.result);
console.log("Valid:", verifyResult.result);
Wrap/Unwrap Keys
// Wrap a key (encrypt it for storage)
const wrapResult = await cryptoClient.wrapKey("RSA-OAEP", Buffer.from("key-material"));
// Unwrap
const unwrapResult = await cryptoClient.unwrapKey("RSA-OAEP", wrapResult.result);
Backup and Restore
// Backup
const keyBackup = await keyClient.backupKey("MyKey");
const secretBackup = await secretClient.backupSecret("MySecret");
// Restore (can restore to different vault)
const restoredKey = await keyClient.restoreKeyBackup(keyBackup!);
const restoredSecret = await secretClient.restoreSecretBackup(secretBackup!);
Key Types
import {
KeyClient,
KeyVaultKey,
KeyProperties,
DeletedKey,
CryptographyClient,
KnownEncryptionAlgorithms,
KnownSignatureAlgorithms
} from "@azure/keyvault-keys";
import {
SecretClient,
KeyVaultSecret,
SecretProperties,
DeletedSecret
} from "@azure/keyvault-secrets";
Error Handling
try {
const secret = await secretClient.getSecret("NonExistent");
} catch (error: any) {
if (error.code === "SecretNotFound") {
console.log("Secret does not exist");
} else {
throw error;
}
}
Best Practices
- Use
DefaultAzureCredentialfor local development; useManagedIdentityCredentialorWorkloadIdentityCredentialfor production - Enable soft-delete - Required for production vaults
- Set expiration dates - On both keys and secrets
- Use key rotation policies - Automate key rotation
- Limit key operations - Only grant needed operations (encrypt, sign, etc.)
- Browser not supported - These SDKs are Node.js only
Files (skills)
-
references
-
keys.md 11.8 KB
# Keys Reference Cryptographic key management and operations using @azure/keyvault-keys SDK. ## Overview The Key Vault Keys SDK provides two main clients: - **KeyClient** - CRUD operations for keys (create, get, list, rotate, delete) - **CryptographyClient** - Cryptographic operations using keys (encrypt, decrypt, sign, verify, wrap, unwrap) ## Core Types ```typescript import { KeyClient, CryptographyClient, KeyVaultKey, KeyProperties, DeletedKey, KeyRotationPolicy, KeyRotationPolicyProperties, KeyRotationLifetimeAction, CreateKeyOptions, CreateRsaKeyOptions, CreateEcKeyOptions, EncryptParameters, DecryptParameters, SignResult, VerifyResult, WrapResult, UnwrapResult, KnownEncryptionAlgorithms, KnownSignatureAlgorithms, KnownKeyTypes, KnownKeyCurveNames } from "@azure/keyvault-keys"; ``` ## KeyClient Initialization ```typescript import { KeyClient } from "@azure/keyvault-keys"; import { DefaultAzureCredential } from "@azure/identity"; const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`; const credential = new DefaultAzureCredential(); const keyClient = new KeyClient(vaultUrl, credential); ``` ## Creating Keys ### RSA Keys ```typescript // Basic RSA key (default 2048-bit) const rsaKey = await keyClient.createRsaKey("my-rsa-key"); // RSA key with specific size const rsaKey2048 = await keyClient.createRsaKey("my-rsa-2048", { keySize: 2048 }); const rsaKey4096 = await keyClient.createRsaKey("my-rsa-4096", { keySize: 4096 }); // RSA-HSM (Hardware Security Module backed) const rsaHsmKey = await keyClient.createRsaKey("my-rsa-hsm", { keySize: 2048, hsm: true // Requires Premium vault }); ``` ### Elliptic Curve Keys ```typescript // P-256 curve (default) const ecKey = await keyClient.createEcKey("my-ec-key"); // Specific curves const ecKeyP256 = await keyClient.createEcKey("my-ec-p256", { curve: "P-256" }); const ecKeyP384 = await keyClient.createEcKey("my-ec-p384", { curve: "P-384" }); const ecKeyP521 = await keyClient.createEcKey("my-ec-p521", { curve: "P-521" }); // EC-HSM const ecHsmKey = await keyClient.createEcKey("my-ec-hsm", { curve: "P-256", hsm: true }); ``` ### Oct Keys (Symmetric) ```typescript // Symmetric key for wrap/unwrap operations const octKey = await keyClient.createOctKey("my-oct-key", { keySize: 256 // 128, 192, or 256 bits }); // Oct-HSM const octHsmKey = await keyClient.createOctKey("my-oct-hsm", { keySize: 256, hsm: true }); ``` ### Generic Create with Options ```typescript const key = await keyClient.createKey("my-key", "RSA", { keySize: 2048, enabled: true, expiresOn: new Date("2025-12-31"), notBefore: new Date("2024-01-01"), tags: { environment: "production", application: "my-app" }, keyOps: ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey"], exportable: false, releasePolicy: undefined // For Managed HSM key release }); ``` ## Key Operations ### Get Key ```typescript // Get latest version const key = await keyClient.getKey("my-key"); console.log(`Key: ${key.name}, Type: ${key.keyType}, ID: ${key.id}`); // Get specific version const keyVersion = await keyClient.getKey("my-key", { version: "abc123..." }); ``` ### List Keys ```typescript // List all keys (properties only, not key material) for await (const keyProperties of keyClient.listPropertiesOfKeys()) { console.log(`Key: ${keyProperties.name}, Created: ${keyProperties.createdOn}`); } // List all versions of a key for await (const version of keyClient.listPropertiesOfKeyVersions("my-key")) { console.log(`Version: ${version.version}, Enabled: ${version.enabled}`); } // List deleted keys (soft-delete enabled vaults) for await (const deletedKey of keyClient.listDeletedKeys()) { console.log(`Deleted: ${deletedKey.name}, Scheduled purge: ${deletedKey.scheduledPurgeDate}`); } ``` ### Update Key Properties ```typescript const updated = await keyClient.updateKeyProperties("my-key", { enabled: false, expiresOn: new Date("2026-12-31"), tags: { status: "deprecated" } }); // Update specific version const updatedVersion = await keyClient.updateKeyProperties("my-key", "version-id", { enabled: true }); ``` ### Import Key ```typescript import { JsonWebKey } from "@azure/keyvault-keys"; // Import existing key material const jwk: JsonWebKey = { kty: "RSA", n: Buffer.from("...modulus..."), e: Buffer.from("...exponent..."), d: Buffer.from("...private exponent..."), // Optional for public key // ... other RSA parameters }; const importedKey = await keyClient.importKey("imported-key", jwk, { hardwareProtected: false // true for HSM }); ``` ## Key Rotation ### Manual Rotation ```typescript // Creates new version, previous versions remain valid const rotatedKey = await keyClient.rotateKey("my-key"); console.log(`New version: ${rotatedKey.properties.version}`); ``` ### Rotation Policy ```typescript // Get current policy const policy = await keyClient.getKeyRotationPolicy("my-key"); // Update rotation policy const updatedPolicy = await keyClient.updateKeyRotationPolicy("my-key", { expiresIn: "P90D", // ISO 8601 duration - key expires 90 days after creation lifetimeActions: [ { action: "Rotate", timeAfterCreate: "P30D" // Auto-rotate 30 days after creation }, { action: "Notify", timeBeforeExpiry: "P7D" // Notify 7 days before expiry } ] }); // Rotation policy with multiple actions const complexPolicy = await keyClient.updateKeyRotationPolicy("my-key", { expiresIn: "P1Y", // 1 year lifetimeActions: [ { action: "Rotate", timeAfterCreate: "P90D" }, // Rotate every 90 days { action: "Notify", timeBeforeExpiry: "P30D" } // Notify 30 days before expiry ] }); ``` ### ISO 8601 Duration Format | Duration | Meaning | |----------|---------| | `P30D` | 30 days | | `P90D` | 90 days | | `P1Y` | 1 year | | `P6M` | 6 months | | `P1Y6M` | 1 year 6 months | ## Key Deletion and Recovery ### Soft Delete (Default) ```typescript // Begin delete (returns poller for long-running operation) const deletePoller = await keyClient.beginDeleteKey("my-key"); // Wait for deletion to complete const deletedKey = await deletePoller.pollUntilDone(); console.log(`Deleted: ${deletedKey.name}, Recovery ID: ${deletedKey.recoveryId}`); // Get deleted key info const deleted = await keyClient.getDeletedKey("my-key"); // Recover deleted key const recoverPoller = await keyClient.beginRecoverDeletedKey("my-key"); const recoveredKey = await recoverPoller.pollUntilDone(); // Permanently delete (purge) - irreversible await keyClient.purgeDeletedKey("my-key"); ``` ### Immediate Deletion (Non-blocking) ```typescript // Start deletion without waiting const poller = await keyClient.beginDeleteKey("my-key"); // Check status periodically while (!poller.isDone()) { await poller.poll(); console.log(`State: ${poller.getOperationState().status}`); await new Promise(resolve => setTimeout(resolve, 1000)); } ``` ## CryptographyClient ### Initialization ```typescript import { CryptographyClient } from "@azure/keyvault-keys"; // From KeyVaultKey object const key = await keyClient.getKey("my-key"); const cryptoClient = new CryptographyClient(key, credential); // From key ID (URL) const cryptoClientFromId = new CryptographyClient( "https://my-vault.vault.azure.net/keys/my-key/version", credential ); // From key ID without version (uses latest) const cryptoClientLatest = new CryptographyClient( "https://my-vault.vault.azure.net/keys/my-key", credential ); ``` ### Encrypt / Decrypt ```typescript // RSA encryption const plaintext = Buffer.from("Secret message"); // Encrypt with RSA-OAEP const encryptResult = await cryptoClient.encrypt({ algorithm: "RSA-OAEP", plaintext }); console.log(`Encrypted (${encryptResult.result.length} bytes)`); // Decrypt const decryptResult = await cryptoClient.decrypt({ algorithm: "RSA-OAEP", ciphertext: encryptResult.result }); console.log(`Decrypted: ${decryptResult.result.toString()}`); ``` ### Encryption Algorithms | Algorithm | Key Type | Description | |-----------|----------|-------------| | `RSA1_5` | RSA | RSA with PKCS#1 v1.5 padding | | `RSA-OAEP` | RSA | RSA with OAEP padding (SHA-1) | | `RSA-OAEP-256` | RSA | RSA with OAEP padding (SHA-256) | | `A128GCM` | oct | AES-128-GCM | | `A192GCM` | oct | AES-192-GCM | | `A256GCM` | oct | AES-256-GCM | | `A128CBC` | oct | AES-128-CBC | | `A192CBC` | oct | AES-192-CBC | | `A256CBC` | oct | AES-256-CBC | ### Sign / Verify ```typescript import { createHash } from "node:crypto"; // Create SHA-256 hash of data to sign const data = Buffer.from("Data to sign"); const hash = createHash("sha256").update(data).digest(); // Sign with RSA key const signResult = await cryptoClient.sign("RS256", hash); console.log(`Signature (${signResult.result.length} bytes)`); // Verify signature const verifyResult = await cryptoClient.verify("RS256", hash, signResult.result); console.log(`Valid: ${verifyResult.result}`); // Sign data directly (SDK computes hash) const signDataResult = await cryptoClient.signData("RS256", data); const verifyDataResult = await cryptoClient.verifyData("RS256", data, signDataResult.result); ``` ### Signature Algorithms | Algorithm | Key Type | Hash | Description | |-----------|----------|------|-------------| | `RS256` | RSA | SHA-256 | RSASSA-PKCS1-v1_5 | | `RS384` | RSA | SHA-384 | RSASSA-PKCS1-v1_5 | | `RS512` | RSA | SHA-512 | RSASSA-PKCS1-v1_5 | | `PS256` | RSA | SHA-256 | RSASSA-PSS | | `PS384` | RSA | SHA-384 | RSASSA-PSS | | `PS512` | RSA | SHA-512 | RSASSA-PSS | | `ES256` | EC P-256 | SHA-256 | ECDSA | | `ES384` | EC P-384 | SHA-384 | ECDSA | | `ES512` | EC P-521 | SHA-512 | ECDSA | ### Wrap / Unwrap Keys ```typescript // Key encryption key (KEK) wraps another key const keyMaterial = Buffer.from("32-byte-key-material-here!!!!!"); // 32 bytes for AES-256 // Wrap (encrypt) the key material const wrapResult = await cryptoClient.wrapKey("RSA-OAEP", keyMaterial); console.log(`Wrapped key (${wrapResult.result.length} bytes)`); // Unwrap (decrypt) the key material const unwrapResult = await cryptoClient.unwrapKey("RSA-OAEP", wrapResult.result); console.log(`Unwrapped: ${unwrapResult.result.length} bytes`); ``` ## Backup and Restore ```typescript // Backup key (returns encrypted blob) const backup = await keyClient.backupKey("my-key"); if (backup) { // Store backup securely (e.g., blob storage) console.log(`Backup size: ${backup.length} bytes`); } // Restore key (can restore to different vault in same region/subscription) const restoredKey = await keyClient.restoreKeyBackup(backup!); console.log(`Restored: ${restoredKey.name}`); ``` ## Error Handling ```typescript import { RestError } from "@azure/core-rest-pipeline"; try { const key = await keyClient.getKey("non-existent-key"); } catch (error) { if (error instanceof RestError) { switch (error.statusCode) { case 404: console.log("Key not found"); break; case 403: console.log("Access denied - check RBAC permissions"); break; case 409: console.log("Conflict - key already exists or is being deleted"); break; default: console.log(`Error ${error.statusCode}: ${error.message}`); } } throw error; } ``` ## Best Practices 1. **Use managed identity in production** - DefaultAzureCredential handles this automatically 2. **Enable soft-delete and purge protection** - Required for production vaults 3. **Set key expiration** - Use `expiresOn` to enforce key lifecycle 4. **Use rotation policies** - Automate key rotation for security compliance 5. **Limit key operations** - Only grant needed operations (`keyOps`) 6. **Use HSM for sensitive keys** - Hardware protection for critical cryptographic material 7. **Backup keys before deletion** - Soft-delete has retention limits 8. **Use specific key versions** - Pin to versions in production for stability ## See Also - [secrets.md](./secrets.md) - Secret management operations -
secrets.md 13.3 KB
# Secrets Reference Secret management operations using @azure/keyvault-secrets SDK. ## Overview The SecretClient provides operations for managing secrets in Azure Key Vault: - Create, update, and delete secrets - List secrets and versions - Soft-delete and purge operations - Backup and restore capabilities ## Core Types ```typescript import { SecretClient, KeyVaultSecret, SecretProperties, DeletedSecret, SetSecretOptions, GetSecretOptions, UpdateSecretPropertiesOptions, BeginDeleteSecretOptions, BeginRecoverDeletedSecretOptions, ListPropertiesOfSecretsOptions, ListPropertiesOfSecretVersionsOptions, ListDeletedSecretsOptions } from "@azure/keyvault-secrets"; ``` ## SecretClient Initialization ```typescript import { SecretClient } from "@azure/keyvault-secrets"; import { DefaultAzureCredential } from "@azure/identity"; const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`; const credential = new DefaultAzureCredential(); const secretClient = new SecretClient(vaultUrl, credential); ``` ## Creating and Updating Secrets ### Set Secret (Create or Update) ```typescript // Basic secret const secret = await secretClient.setSecret("MySecret", "secret-value"); console.log(`Secret: ${secret.name}, Version: ${secret.properties.version}`); // Secret with options const secretWithOptions = await secretClient.setSecret("MySecret", "secret-value", { enabled: true, expiresOn: new Date("2025-12-31"), notBefore: new Date("2024-01-01"), contentType: "text/plain", tags: { environment: "production", application: "my-app", owner: "team-a" } }); ``` ### Content Types ```typescript // JSON content const jsonSecret = await secretClient.setSecret( "config-secret", JSON.stringify({ apiKey: "xyz", endpoint: "https://api.example.com" }), { contentType: "application/json" } ); // Connection string const connString = await secretClient.setSecret( "db-connection", "Server=tcp:myserver.database.windows.net;Database=mydb;...", { contentType: "text/plain; charset=utf-8" } ); // Base64 encoded binary const binarySecret = await secretClient.setSecret( "certificate-data", Buffer.from(certificateBytes).toString("base64"), { contentType: "application/x-pkcs12" } ); ``` ### Versioning Behavior ```typescript // Each setSecret creates a new version const v1 = await secretClient.setSecret("MySecret", "value-1"); console.log(`Version 1: ${v1.properties.version}`); const v2 = await secretClient.setSecret("MySecret", "value-2"); console.log(`Version 2: ${v2.properties.version}`); // v1 still exists and is accessible by version ID const v1Retrieved = await secretClient.getSecret("MySecret", { version: v1.properties.version }); ``` ## Retrieving Secrets ### Get Secret ```typescript // Get latest version const secret = await secretClient.getSecret("MySecret"); console.log(`Value: ${secret.value}`); console.log(`Version: ${secret.properties.version}`); console.log(`Created: ${secret.properties.createdOn}`); // Get specific version const specificVersion = await secretClient.getSecret("MySecret", { version: "abc123def456..." }); ``` ### KeyVaultSecret Structure ```typescript interface KeyVaultSecret { name: string; value?: string; // Only present when retrieved, not in list operations properties: SecretProperties; } interface SecretProperties { id?: string; // Full secret identifier URL name: string; version?: string; vaultUrl: string; enabled?: boolean; notBefore?: Date; expiresOn?: Date; createdOn?: Date; updatedOn?: Date; contentType?: string; tags?: { [key: string]: string }; managed?: boolean; // True if managed by Key Vault (e.g., storage account keys) recoverableDays?: number; recoveryLevel?: string; } ``` ## Listing Secrets ### List All Secrets ```typescript // List secret properties (not values - use getSecret for values) for await (const secretProperties of secretClient.listPropertiesOfSecrets()) { console.log(`Secret: ${secretProperties.name}`); console.log(` Enabled: ${secretProperties.enabled}`); console.log(` Content Type: ${secretProperties.contentType}`); console.log(` Tags: ${JSON.stringify(secretProperties.tags)}`); } ``` ### List Secret Versions ```typescript // List all versions of a specific secret for await (const version of secretClient.listPropertiesOfSecretVersions("MySecret")) { console.log(`Version: ${version.version}`); console.log(` Created: ${version.createdOn}`); console.log(` Enabled: ${version.enabled}`); console.log(` Expires: ${version.expiresOn}`); } ``` ### Collect to Array ```typescript // Collect all secrets to array const allSecrets: SecretProperties[] = []; for await (const secret of secretClient.listPropertiesOfSecrets()) { allSecrets.push(secret); } // Or use byPage for pagination control const pages = secretClient.listPropertiesOfSecrets().byPage({ maxPageSize: 25 }); for await (const page of pages) { console.log(`Page with ${page.length} secrets`); } ``` ### Filter by Tags ```typescript // SDK doesn't support server-side filtering, filter client-side const productionSecrets: SecretProperties[] = []; for await (const secret of secretClient.listPropertiesOfSecrets()) { if (secret.tags?.environment === "production") { productionSecrets.push(secret); } } ``` ## Updating Secret Properties ```typescript // Update properties without changing value const updated = await secretClient.updateSecretProperties("MySecret", "version-id", { enabled: false, expiresOn: new Date("2026-12-31"), tags: { status: "deprecated", deprecatedOn: new Date().toISOString() } }); // Update latest version (get version first) const current = await secretClient.getSecret("MySecret"); await secretClient.updateSecretProperties("MySecret", current.properties.version!, { enabled: true }); ``` ## Soft Delete Operations ### Delete Secret ```typescript // Begin delete (long-running operation) const deletePoller = await secretClient.beginDeleteSecret("MySecret"); // Option 1: Wait for completion const deletedSecret = await deletePoller.pollUntilDone(); console.log(`Deleted: ${deletedSecret.name}`); console.log(`Scheduled purge: ${deletedSecret.scheduledPurgeDate}`); console.log(`Deleted on: ${deletedSecret.deletedOn}`); // Option 2: Non-blocking with periodic checks const poller = await secretClient.beginDeleteSecret("MySecret"); while (!poller.isDone()) { await poller.poll(); const state = poller.getOperationState(); console.log(`Delete status: ${state.isStarted ? "in progress" : "pending"}`); await new Promise(resolve => setTimeout(resolve, 2000)); } ``` ### Get Deleted Secret ```typescript // Get info about a deleted secret const deleted = await secretClient.getDeletedSecret("MySecret"); console.log(`Recovery ID: ${deleted.recoveryId}`); console.log(`Scheduled purge: ${deleted.scheduledPurgeDate}`); ``` ### List Deleted Secrets ```typescript // List all deleted secrets in vault for await (const deletedSecret of secretClient.listDeletedSecrets()) { console.log(`Deleted secret: ${deletedSecret.name}`); console.log(` Deleted on: ${deletedSecret.deletedOn}`); console.log(` Purge date: ${deletedSecret.scheduledPurgeDate}`); } ``` ### Recover Deleted Secret ```typescript // Recover a soft-deleted secret const recoverPoller = await secretClient.beginRecoverDeletedSecret("MySecret"); const recoveredSecret = await recoverPoller.pollUntilDone(); console.log(`Recovered: ${recoveredSecret.name}`); ``` ### Purge Secret (Permanent Delete) ```typescript // Permanently delete - IRREVERSIBLE // Requires "purge" permission in RBAC await secretClient.purgeDeletedSecret("MySecret"); // Common pattern: delete then purge const deletePoller = await secretClient.beginDeleteSecret("MySecret"); await deletePoller.pollUntilDone(); await secretClient.purgeDeletedSecret("MySecret"); ``` ## Backup and Restore ### Backup Secret ```typescript // Backup returns encrypted blob containing all versions const backup = await secretClient.backupSecret("MySecret"); if (backup) { // Store backup securely (e.g., blob storage, local file) console.log(`Backup size: ${backup.length} bytes`); // Save to file import { writeFileSync } from "node:fs"; writeFileSync("secret-backup.bin", backup); } ``` ### Restore Secret ```typescript import { readFileSync } from "node:fs"; // Read backup from storage const backupData = readFileSync("secret-backup.bin"); // Restore to vault (can be different vault in same region/subscription) const restoredSecret = await secretClient.restoreSecretBackup(backupData); console.log(`Restored: ${restoredSecret.name}`); ``` ### Backup Constraints | Constraint | Description | |------------|-------------| | Same subscription | Backup can only be restored to vault in same Azure subscription | | Same geography | Target vault must be in same Azure geography | | All versions | Backup includes all versions of the secret | | Encrypted | Backup blob is encrypted with Microsoft-managed keys | ## Error Handling ```typescript import { RestError } from "@azure/core-rest-pipeline"; async function getSecretSafely(name: string): Promise<KeyVaultSecret | null> { try { return await secretClient.getSecret(name); } catch (error) { if (error instanceof RestError) { switch (error.statusCode) { case 404: console.log(`Secret '${name}' not found`); return null; case 403: console.log("Access denied - check RBAC permissions"); throw error; case 409: console.log("Conflict - secret is being deleted or already exists"); throw error; default: console.log(`Error ${error.statusCode}: ${error.message}`); throw error; } } throw error; } } ``` ### Common Error Codes | Code | Meaning | |------|---------| | 404 | Secret not found (or deleted) | | 403 | Access denied (RBAC permission missing) | | 409 | Conflict (secret exists in deleted state) | | 429 | Rate limited (too many requests) | ## Common Patterns ### Secret Rotation ```typescript async function rotateSecret(name: string, newValue: string): Promise<KeyVaultSecret> { // Get current secret to preserve metadata const current = await secretClient.getSecret(name); // Disable old version await secretClient.updateSecretProperties(name, current.properties.version!, { enabled: false, tags: { ...current.properties.tags, rotatedOn: new Date().toISOString(), status: "rotated" } }); // Create new version with same settings const newSecret = await secretClient.setSecret(name, newValue, { enabled: true, contentType: current.properties.contentType, expiresOn: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days tags: { ...current.properties.tags, status: "active", createdOn: new Date().toISOString() } }); return newSecret; } ``` ### Bulk Secret Operations ```typescript async function exportAllSecrets(): Promise<Map<string, string>> { const secrets = new Map<string, string>(); for await (const properties of secretClient.listPropertiesOfSecrets()) { if (properties.enabled) { const secret = await secretClient.getSecret(properties.name); if (secret.value) { secrets.set(properties.name, secret.value); } } } return secrets; } async function importSecrets(secrets: Map<string, string>, tags?: Record<string, string>): Promise<void> { for (const [name, value] of secrets) { await secretClient.setSecret(name, value, { tags }); console.log(`Imported: ${name}`); } } ``` ### Check Secret Expiration ```typescript async function getExpiringSecrets(daysThreshold: number = 30): Promise<SecretProperties[]> { const expiringSecrets: SecretProperties[] = []; const thresholdDate = new Date(Date.now() + daysThreshold * 24 * 60 * 60 * 1000); for await (const secret of secretClient.listPropertiesOfSecrets()) { if (secret.enabled && secret.expiresOn && secret.expiresOn <= thresholdDate) { expiringSecrets.push(secret); } } return expiringSecrets; } ``` ## Best Practices 1. **Use managed identity** - DefaultAzureCredential handles MI in Azure, dev credentials locally 2. **Set expiration dates** - Enforce secret rotation with `expiresOn` 3. **Use content types** - Helps consumers understand secret format 4. **Tag secrets** - Environment, application, owner for organization 5. **Enable soft-delete** - Required for production vaults (default for new vaults) 6. **Enable purge protection** - Prevents accidental permanent deletion 7. **Backup before rotation** - Backup secrets before making changes 8. **Least privilege** - Grant only needed permissions (Get vs List vs Set) 9. **Monitor expiration** - Alert on secrets expiring within threshold 10. **Avoid storing in code** - Use Key Vault references in App Service/Functions ## RBAC Permissions | Operation | Required Permission | |-----------|---------------------| | Get secret | `Microsoft.KeyVault/vaults/secrets/getSecret/action` | | List secrets | `Microsoft.KeyVault/vaults/secrets/readMetadata/action` | | Set secret | `Microsoft.KeyVault/vaults/secrets/setSecret/action` | | Delete secret | `Microsoft.KeyVault/vaults/secrets/delete` | | Purge secret | `Microsoft.KeyVault/vaults/secrets/purge/action` | | Backup | `Microsoft.KeyVault/vaults/secrets/backup/action` | | Restore | `Microsoft.KeyVault/vaults/secrets/restore/action` | ## See Also - [keys.md](./keys.md) - Cryptographic key management
-
-
SKILL.md 6.6 KB
--- name: azure-keyvault-keys-ts description: Manage cryptographic keys using Azure Key Vault Keys SDK for JavaScript (@azure/keyvault-keys). Use when creating, encrypting/decrypting, signing, or rotating keys. license: MIT metadata: author: Microsoft version: "1.0.0" package: '@azure/keyvault-keys' --- # Azure Key Vault Keys SDK for TypeScript Manage cryptographic keys with Azure Key Vault. ## Installation ```bash # Keys SDK npm install @azure/keyvault-keys @azure/identity ``` ## Environment Variables ```bash KEY_VAULT_URL=https://<vault-name>.vault.azure.net # Or AZURE_KEYVAULT_NAME=<vault-name> AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production ``` ## Authentication ```typescript import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity"; import { KeyClient, CryptographyClient } from "@azure/keyvault-keys"; // Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential> const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]}); // Or use a specific credential directly in production: // See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes // const credential = new ManagedIdentityCredential(); const vaultUrl = `https://${process.env.AZURE_KEYVAULT_NAME}.vault.azure.net`; const keyClient = new KeyClient(vaultUrl, credential); const secretClient = new SecretClient(vaultUrl, credential); ``` ## Secrets Operations ### Create/Set Secret ```typescript const secret = await secretClient.setSecret("MySecret", "secret-value"); // With attributes const secretWithAttrs = await secretClient.setSecret("MySecret", "value", { enabled: true, expiresOn: new Date("2025-12-31"), contentType: "application/json", tags: { environment: "production" } }); ``` ### Get Secret ```typescript // Get latest version const secret = await secretClient.getSecret("MySecret"); console.log(secret.value); // Get specific version const specificSecret = await secretClient.getSecret("MySecret", { version: secret.properties.version }); ``` ### List Secrets ```typescript for await (const secretProperties of secretClient.listPropertiesOfSecrets()) { console.log(secretProperties.name); } // List versions for await (const version of secretClient.listPropertiesOfSecretVersions("MySecret")) { console.log(version.version); } ``` ### Delete Secret ```typescript // Soft delete const deletePoller = await secretClient.beginDeleteSecret("MySecret"); await deletePoller.pollUntilDone(); // Purge (permanent) await secretClient.purgeDeletedSecret("MySecret"); // Recover const recoverPoller = await secretClient.beginRecoverDeletedSecret("MySecret"); await recoverPoller.pollUntilDone(); ``` ## Keys Operations ### Create Keys ```typescript // Generic key const key = await keyClient.createKey("MyKey", "RSA"); // RSA key with size const rsaKey = await keyClient.createRsaKey("MyRsaKey", { keySize: 2048 }); // Elliptic Curve key const ecKey = await keyClient.createEcKey("MyEcKey", { curve: "P-256" }); // With attributes const keyWithAttrs = await keyClient.createKey("MyKey", "RSA", { enabled: true, expiresOn: new Date("2025-12-31"), tags: { purpose: "encryption" }, keyOps: ["encrypt", "decrypt", "sign", "verify"] }); ``` ### Get Key ```typescript const key = await keyClient.getKey("MyKey"); console.log(key.name, key.keyType); ``` ### List Keys ```typescript for await (const keyProperties of keyClient.listPropertiesOfKeys()) { console.log(keyProperties.name); } ``` ### Rotate Key ```typescript // Manual rotation const rotatedKey = await keyClient.rotateKey("MyKey"); // Set rotation policy await keyClient.updateKeyRotationPolicy("MyKey", { lifetimeActions: [{ action: "Rotate", timeBeforeExpiry: "P30D" }], expiresIn: "P90D" }); ``` ### Delete Key ```typescript const deletePoller = await keyClient.beginDeleteKey("MyKey"); await deletePoller.pollUntilDone(); // Purge await keyClient.purgeDeletedKey("MyKey"); ``` ## Cryptographic Operations ### Create CryptographyClient ```typescript import { CryptographyClient } from "@azure/keyvault-keys"; // From key object const cryptoClient = new CryptographyClient(key, credential); // From key ID const cryptoClient = new CryptographyClient(key.id!, credential); ``` ### Encrypt/Decrypt ```typescript // Encrypt const encryptResult = await cryptoClient.encrypt({ algorithm: "RSA-OAEP", plaintext: Buffer.from("My secret message") }); // Decrypt const decryptResult = await cryptoClient.decrypt({ algorithm: "RSA-OAEP", ciphertext: encryptResult.result }); console.log(decryptResult.result.toString()); ``` ### Sign/Verify ```typescript import { createHash } from "node:crypto"; // Create digest const hash = createHash("sha256").update("My message").digest(); // Sign const signResult = await cryptoClient.sign("RS256", hash); // Verify const verifyResult = await cryptoClient.verify("RS256", hash, signResult.result); console.log("Valid:", verifyResult.result); ``` ### Wrap/Unwrap Keys ```typescript // Wrap a key (encrypt it for storage) const wrapResult = await cryptoClient.wrapKey("RSA-OAEP", Buffer.from("key-material")); // Unwrap const unwrapResult = await cryptoClient.unwrapKey("RSA-OAEP", wrapResult.result); ``` ## Backup and Restore ```typescript // Backup const keyBackup = await keyClient.backupKey("MyKey"); const secretBackup = await secretClient.backupSecret("MySecret"); // Restore (can restore to different vault) const restoredKey = await keyClient.restoreKeyBackup(keyBackup!); const restoredSecret = await secretClient.restoreSecretBackup(secretBackup!); ``` ## Key Types ```typescript import { KeyClient, KeyVaultKey, KeyProperties, DeletedKey, CryptographyClient, KnownEncryptionAlgorithms, KnownSignatureAlgorithms } from "@azure/keyvault-keys"; import { SecretClient, KeyVaultSecret, SecretProperties, DeletedSecret } from "@azure/keyvault-secrets"; ``` ## Error Handling ```typescript try { const secret = await secretClient.getSecret("NonExistent"); } catch (error: any) { if (error.code === "SecretNotFound") { console.log("Secret does not exist"); } else { throw error; } } ``` ## Best Practices 1. **Use `DefaultAzureCredential` for local development; use `ManagedIdentityCredential` or `WorkloadIdentityCredential` for production** 2. **Enable soft-delete** - Required for production vaults 3. **Set expiration dates** - On both keys and secrets 4. **Use key rotation policies** - Automate key rotation 5. **Limit key operations** - Only grant needed operations (encrypt, sign, etc.) 6. **Browser not supported** - These SDKs are Node.js only
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.