Skip to content

Vault providers — HashiCorp, Azure, AWS, GCP

All four expose the same three abstractions (ITransitEncryptionService, ISecretStore, IDatabaseCredentialProvider). Pick based on where the rest of your infrastructure lives:

HashiCorpAzure Key VaultAWSGCP
PackageGranit.Vault.HashiCorpGranit.Vault.AzureGranit.Vault.AwsGranit.Vault.GoogleCloud
Encryption backendTransit engineRSA-OAEP-256KMSCloud KMS (AES-256-GCM)
Native MAC (ITransitMacService)Transit HMACManaged HSM HS256 (premium)KMS HMAC_256Cloud KMS HMAC_SHA256
MAC key versioningmin_decryption_versionenabled-versionCurrent/Previous aliasenabled-version
Ciphertext carries versionvault:v{N}:…❌ opaque❌ opaque❌ opaque
Server-side rewrap/transit/rewrap❌ (decrypt+encrypt)❌ (decrypt+encrypt)❌ (decrypt+encrypt)
Secrets engine for ISecretStoreKV v2SecretSecrets ManagerSecret Manager
Native binary payloadvia __binary conventionvia application/octet-streamSecretBinaryByteString
Dynamic DB creds✅ lease-awarevia secret pollingvia secret pollingvia secret polling
Auth for productionKubernetes JWTManaged IdentityIAM roleWorkload Identity

All four modules auto-disable in Development — you don’t need a running vault locally.

[DependsOn(typeof(GranitVaultHashiCorpModule))]
public class AppModule : GranitModule { }
{
"Vault": {
"Address": "https://vault.example.com",
"AuthMethod": "Kubernetes",
"KubernetesRole": "my-backend"
}
}

Uses the Kubernetes service account token mounted at /var/run/secrets/kubernetes.io/serviceaccount/token.

{
"Vault": {
"Address": "https://vault.example.com",
"AuthMethod": "Kubernetes",
"KubernetesRole": "my-backend",
"KubernetesTokenPath": "/var/run/secrets/kubernetes.io/serviceaccount/token",
"DatabaseMountPoint": "database",
"DatabaseRoleName": "readwrite",
"TransitMountPoint": "transit",
"KvMountPoint": "secret",
"LeaseRenewalThreshold": 0.75,
"SecretStore": {
"CacheSeconds": 0,
"HealthCheckSecretName": "healthcheck/probe"
}
}
}
PropertyDefaultDescription
AddressVault server URL
AuthMethod"Kubernetes""Kubernetes" or "Token"
KubernetesRole"my-backend"Vault Kubernetes auth role
DatabaseMountPoint"database"Database secrets engine mount
DatabaseRoleName"readwrite"Database role for dynamic credentials
TransitMountPoint"transit"Transit secrets engine mount
KvMountPoint"secret"KV v2 secrets engine mount (used by ISecretStore)
LeaseRenewalThreshold0.75Renew lease at this fraction of TTL

The HashiCorp provider implements ITransitMacService via transit/hmac + transit/verify (SHA-256), with native rolling rotation through min_decryption_version. Provision the key with auto-rotation:

Terminal window
vault write -f transit/keys/granit-privacy-export-fragment-hmac \
type=hmac \
auto_rotate_period=2160h # 90 days, ISO 27001 A.10.1.2

App policy needs update on transit/hmac/<key> and transit/verify/<key>.

builder.Services.AddHealthChecks()
.AddGranitVaultHashiCorpHealthCheck() // Vault sys/health probe
.AddGranitSecretStoreHealthCheck(); // ISecretStore canary

The first check verifies Vault connectivity and authentication. The second (shared across all four providers) reads Vault:SecretStore:HealthCheckSecretName through ISecretStore — see Secret retrieval → Liveness probe.

[DependsOn(typeof(GranitVaultAzureModule))]
public class AppModule : GranitModule { }

Uses DefaultAzureCredential — Managed Identity in AKS, az login locally. No credentials in configuration.

{
"Vault": {
"Azure": {
"VaultUri": "https://my-vault.vault.azure.net/"
}
}
}
{
"Vault": {
"Azure": {
"VaultUri": "https://my-vault.vault.azure.net/",
"EncryptionKeyName": "string-encryption",
"EncryptionAlgorithm": "RSA-OAEP-256",
"DatabaseSecretName": "db-credentials",
"RotationCheckIntervalMinutes": 5,
"TimeoutSeconds": 30
},
"SecretStore": {
"CacheSeconds": 0,
"HealthCheckSecretName": "healthcheck/probe"
}
}
}
PropertyDefaultDescription
Vault:Azure:VaultUriAzure Key Vault URI (required)
Vault:Azure:EncryptionKeyName"string-encryption"Key name for encrypt/decrypt
Vault:Azure:EncryptionAlgorithm"RSA-OAEP-256""RSA-OAEP-256" or "RSA-OAEP"
Vault:Azure:DatabaseSecretNamenullSecret name for DB credentials (omit to disable)
Vault:Azure:RotationCheckIntervalMinutes5DB secret rotation polling interval
Vault:Azure:TimeoutSeconds30Azure SDK operation timeout

Azure splits by tier. Managed HSM (premium) exposes a native HS256 primitive on an oct-HSM key:

services.AddGranitVaultAzureManagedHsmMac();
{
"Vault": { "Azure": { "ManagedHsm": { "Mac": {
"HsmUri": "https://my-hsm.managedhsm.azure.net/",
"KeyName": "granit-privacy-export-mac"
}}}}
}

Azure Key Vault Standard tier has no HMAC primitive — register the portable SecretBackedMacService from Granit.Vault instead (the key material is stored in a Key Vault secret and HMAC runs locally — a documented trust-boundary trade-off).

builder.Services.AddHealthChecks()
.AddGranitAzureKeyVaultHealthCheck() // GetKey probe on EncryptionKeyName
.AddGranitSecretStoreHealthCheck(); // ISecretStore canary
[DependsOn(typeof(GranitVaultAwsModule))]
public class AppModule : GranitModule { }

When AccessKeyId / SecretAccessKey are null (recommended), the AWS SDK uses its default credential chain — IAM role on ECS/EKS, environment variables, EC2 instance profile, or shared credentials file (local dev only).

{
"Vault": {
"Aws": {
"Region": "eu-west-1",
"KmsKeyId": "alias/granit-encryption"
}
}
}

Set AccessKeyId / SecretAccessKey explicitly only for local dev against LocalStack or when the credential chain can’t find usable credentials.

{
"Vault": {
"Aws": {
"Region": "eu-west-1",
"KmsKeyId": "alias/granit-encryption",
"DatabaseSecretArn": "arn:aws:secretsmanager:eu-west-1:123…:secret:db-credentials",
"RotationCheckIntervalMinutes": 5,
"AccessKeyId": null,
"SecretAccessKey": null,
"TimeoutSeconds": 30
},
"SecretStore": {
"CacheSeconds": 0,
"HealthCheckSecretName": "healthcheck/probe"
}
}
}
PropertyDefaultDescription
Vault:Aws:RegionAWS region endpoint (required)
Vault:Aws:KmsKeyIdKMS key ARN or alias for transit encryption (required)
Vault:Aws:DatabaseSecretArnnullSecrets Manager ARN for DB credentials (omit to disable)
Vault:Aws:RotationCheckIntervalMinutes5DB secret rotation polling interval
Vault:Aws:AccessKeyIdnullOverride; defaults to SDK credential chain
Vault:Aws:SecretAccessKeynullOverride; defaults to SDK credential chain
Vault:Aws:TimeoutSeconds30API call timeout

AWS KMS implements ITransitMacService via GenerateMac / VerifyMac on HMAC_256 keys. KMS does not version an HMAC key in place, so rolling rotation uses a Current + Previous alias pair:

{
"Vault": { "Aws": { "Mac": {
"CurrentAlias": "alias/granit/privacy-export-mac-current",
"PreviousAlias": "alias/granit/privacy-export-mac-previous" // optional but recommended
}}}
}

Rotation runbook: provision a new HMAC_256 key, point PreviousAlias at the outgoing key, then CurrentAlias at the new one. Verify keeps accepting the previous tag throughout the grace window — both VerifyMac calls are always issued (constant-time fallback) to defeat timing oracles.

builder.Services.AddHealthChecks()
.AddGranitKmsHealthCheck() // DescribeKey on KmsKeyId
.AddGranitSecretStoreHealthCheck(); // ISecretStore canary
[DependsOn(typeof(GranitVaultGoogleCloudModule))]
public class AppModule : GranitModule { }

Uses Application Default Credentials — Workload Identity in GKE, gcloud auth application-default login locally. Override only for a service account JSON file.

{
"Vault": {
"GoogleCloud": {
"ProjectId": "my-project",
"Location": "europe-west1",
"KeyRing": "my-keyring",
"CryptoKey": "string-encryption"
}
}
}
{
"Vault": {
"GoogleCloud": {
"ProjectId": "my-project",
"Location": "europe-west1",
"KeyRing": "my-keyring",
"CryptoKey": "string-encryption",
"DatabaseSecretName": "db-credentials",
"RotationCheckIntervalMinutes": 5,
"CredentialFilePath": null,
"TimeoutSeconds": 30
},
"SecretStore": {
"CacheSeconds": 0,
"HealthCheckSecretName": "healthcheck/probe"
}
}
}
PropertyDefaultDescription
Vault:GoogleCloud:ProjectIdGCP project id (required)
Vault:GoogleCloud:LocationCloud KMS location, e.g. "europe-west1" (required)
Vault:GoogleCloud:KeyRingCloud KMS key ring name (required)
Vault:GoogleCloud:CryptoKeyCloud KMS crypto key for transit encryption (required)
Vault:GoogleCloud:DatabaseSecretNamenullSecret Manager secret name for DB credentials (omit to disable)
Vault:GoogleCloud:RotationCheckIntervalMinutes5DB secret rotation polling interval
Vault:GoogleCloud:CredentialFilePathnullService account key JSON; ADC when null
Vault:GoogleCloud:TimeoutSeconds30API call timeout

Google Cloud implements ITransitMacService via MacSign / MacVerify on a CryptoKey with purpose=MAC, algorithm=HMAC_SHA256. It is version-state aware — verification against DISABLED / DESTROYED versions is refused.

{
"Vault": { "GoogleCloud": {
"Mac": { "CryptoKeyId": "granit-privacy-export-mac" }
}}
}

Provision the CryptoKey via Terraform with purpose = "MAC", algorithm = "HMAC_SHA256", and rotation_period = "7776000s" (90 days, ISO 27001 A.10.1.2).

builder.Services.AddHealthChecks()
.AddGranitCloudKmsHealthCheck() // Verifies KMS key accessibility
.AddGranitSecretStoreHealthCheck(); // ISecretStore canary
CategoryTypesPackage
ModulesGranitEncryptionModule, GranitVaultHashiCorpModule, GranitVaultAzureModule, GranitVaultAwsModule, GranitVaultGoogleCloudModule
EncryptionIStringEncryptionService, IStringEncryptionProvider, AesStringEncryptionProviderGranit.Encryption
TransitITransitEncryptionService, ReEncryptionOptions, RetiredKeyVersionExceptionGranit.Vault
MACITransitMacService, TransitMacResult, SecretBackedMacService, SecretBackedMacOptionsGranit.Vault
SecretsISecretStore, SecretRequest, SecretVersion, SecretDescriptor, SecretMetadata, SecretStoreOptions, SecretVaultException + subclassesGranit.Vault
CredentialsIDatabaseCredentialProviderGranit.Vault
ObservabilityVaultMetrics, SecretStoreActivityTagsGranit.Vault
Health checksSecretStoreHealthCheck, VaultHealthCheck, AzureKeyVaultHealthCheck, KmsHealthCheck, CloudKmsHealthCheckper-provider
ExtensionsAddGranitEncryption(), AddGranitVaultHashiCorp(), AddGranitVaultAzure(), AddGranitVaultAws(), AddGranitVaultGoogleCloud(), AddGranitVaultAzureManagedHsmMac(), AddGranitSecretBackedMacService(), AddGranitSecretStore<T>(), AddGranitSecretStoreHealthCheck()