This document contains instructions for building, testing, and developing the Azure Managed HSM OpenSSL 3.x Provider.
- Location:
src_provider_rust/ - Language: Rust
- Output:
libakv_provider.so(Linux) /akv_provider.dll(Windows) - Purpose: Enables OpenSSL to use keys stored in Azure Managed HSM for cryptographic operations
- URI scheme:
managedhsm:<vault-name>:<key-name>
Read this section before debugging anything provider-related on Linux.
OpenSSL 3.0.2 (the Ubuntu 22.04 default) has a bug in the OSSL_STORE
object callback path: EVP_KEYMGMT_fetch with a NULL property query string
returns the wrong provider's keymgmt, so loading HSM keys via
managedhsm: URIs fails with RSA object callback failed (returned 0)
or EC object callback failed. Fixed upstream in 3.0.7
(openssl#18221).
Recommendation: Ubuntu 24.04+ (ships OpenSSL 3.0.13). Every example
runner script sources check-openssl.sh and aborts early if the host
OpenSSL is too old. Windows is unaffected — the provider DLL statically
links a bundled vcpkg OpenSSL.
Cargo builds libakv_provider.so (the standard Rust lib-prefix
convention). OpenSSL's -provider akv_provider CLI flag, however, looks
for akv_provider.so. Every example runner creates the symlink once:
ln -sf libakv_provider.so "$PROVIDER_PATH/akv_provider.so"If you load via OPENSSL_CONF with an explicit module = .../libakv_provider.so
line, the symlink is not needed.
default and base MUST be listed before akv_provider. If
akv_provider comes first, every RSA/EC operation (including outbound
HTTPS to Azure) is routed through our keymgmt, which breaks TLS cert
validation against public CAs (we don't implement classical key parsing).
[provider_sect]
default = default_sect # MUST come first
base = base_sect
akv_provider = akv_provider_sectShell scripts, OpenSSL config templates, and .env* files must be LF
on Linux. The .gitattributes files in nginx-example/,
grpc-example/, and grpc-example-tonic-mtls/ enforce this. If you add
new *.sh or *.cnf under a directory without one, add a
.gitattributes first or scripts will fail with $'\r': command not found
on WSL.
Values containing spaces (e.g. CERT_OU=Azure HSM gRPC Tonic Demo) must
be quoted in .env* files. Bash's set -a; . .env; set +a treats
unquoted spaces as command boundaries. PowerShell's .env parsers in
the *.ps1 scripts strip surrounding quotes automatically, so quoted
values work on both shells.
When the provider is loaded only via OPENSSL_CONF, the
EVP_KEYMGMT_fetch(libctx, "RSA", NULL) call inside OSSL_STORE's
object callback can resolve to the default provider's keymgmt instead
of ours (this is the 3.0.2 bug). When loaded also with
-provider akv_provider on the CLI, the explicit selection wins. This
is why runtest.sh works on all OpenSSL versions but grpc-example-*
needs 3.0.7+.
- OS: Linux (Ubuntu 24.04+ recommended) or Windows
- OpenSSL: >= 3.0.7 on Linux (Ubuntu 24.04 ships 3.0.13); 3.x on Windows (vcpkg-bundled)
- Rust: Latest stable (install via rustup)
- Azure CLI: For authentication (
az login)
Note: The values below are examples. Configure your own Azure Managed HSM and keys, then set the corresponding environment variables.
- Managed HSM: e.g.,
MyManagedHSM(set viaAKV_VAULTenv var) - Keys:
- RSA key: e.g.,
my-rsa-key(RSA-HSM 2048/3072/4096-bit) - set viaAKV_RSA_KEY - EC key: e.g.,
my-ec-key(EC-HSM P-256/P-384/P-521) - set viaAKV_EC_KEY - AES key: e.g.,
my-aes-key(oct-HSM 128/192/256-bit) - set viaAKV_AES_KEY
- RSA key: e.g.,
Create keys in Azure Managed HSM:
# RSA key (3072-bit)
az keyvault key create --hsm-name <your-hsm> --name <rsa-key-name> --kty RSA-HSM --size 3072
# EC key (P-256)
az keyvault key create --hsm-name <your-hsm> --name <ec-key-name> --kty EC-HSM --curve P-256
# AES key (256-bit)
az keyvault key create --hsm-name <your-hsm> --name <aes-key-name> --kty oct-HSM --size 256# Find OpenSSL modules directory
openssl version -m
# Typically: /usr/lib/x86_64-linux-gnu/ossl-modulesThe ubuntubuild.sh script handles dependency checks, building, and deployment:
cd ~/AzureKeyVaultManagedHSMEngine/src_provider_rust
./ubuntubuild.shOptions:
--debug: Build in debug mode instead of release--skip-deps: Skip dependency checks (faster for rebuilds)
The script will:
- Check Rust toolchain
- Check OpenSSL dependencies
- Build the provider
- Deploy to OpenSSL modules directory (requires sudo)
cd ~/AzureKeyVaultManagedHSMEngine/src_provider_rustcargo build --releaseOutput: target/release/libakv_provider.so
sudo cp target/release/libakv_provider.so /usr/lib/x86_64-linux-gnu/ossl-modules/akv_provider.soopenssl list -providers -provider akv_provider -provider defaultexport AZURE_CLI_ACCESS_TOKEN=$(az account get-access-token \
--resource https://managedhsm.azure.net \
--query accessToken -o tsv)If AZURE_CLI_ACCESS_TOKEN is not set, the provider falls back to Azure SDK's DefaultAzureCredential chain (requires az login).
cd ~/AzureKeyVaultManagedHSMEngine/src_provider_rust
./runtest.shOptions:
--validate: Run full HSM/key validation (slower)--noenv: Use DefaultAzureCredential instead of env var
| Test | Description |
|---|---|
| RSA PS256 | PSS padding with SHA-256 sign/verify |
| RSA RS256 | PKCS#1 v1.5 padding sign/verify |
| RSA OAEP | Encrypt/decrypt with OAEP padding |
| EC ES256 | ECDSA P-256 sign/verify |
| RSA CSR | Certificate signing request generation |
| RSA Cert | Self-signed certificate generation |
| EC CSR | EC certificate signing request |
| EC Cert | EC self-signed certificate |
| AES Wrap | Key wrap/unwrap operations |
| Tamper Test | Negative test for tamper detection |
The nginx-example folder includes a template-based environment setup:
cd ~/AzureKeyVaultManagedHSMEngine/src_provider_rust/nginx-example
# Create .env from template
./setup-env.sh
# Edit .env with your settings
nano .env.env.example template settings:
# Azure Managed HSM Configuration
HSM_NAME=<your-hsm-name>
HSM_KEY_NAME=<your-key-name>
AZURE_TENANT_ID=<your-tenant-id>
# Certificate Settings
CERT_CN=localhost
CERT_ORG=Microsoft
CERT_DAYS=365
# Nginx Settings
NGINX_PORT=8443
SERVER_NAME=localhost
# Logging (optional)
# AKV_LOG_FILE=./logs/akv_provider.log
# AKV_LOG_LEVEL=2cd ~/AzureKeyVaultManagedHSMEngine/src_provider_rust/nginx-example
# Generate certificates (RSA and EC)
./generate-cert.sh
# Start nginx (requires nginx 1.27+ for OSSL_STORE support)
sudo nginx -c $(pwd)/nginx.conf
# Test both servers
./test-client.shPorts:
8443: RSA TLS server (uses RSA key)8444: EC TLS server (uses EC key)
The provider uses managedhsm: URI scheme:
managedhsm:<vault-name>:<key-name>
Examples:
managedhsm:ManagedHSMOpenSSLEngine:myrsakeymanagedhsm:ManagedHSMOpenSSLEngine:ecckey
Located at src_provider_rust/testOpenssl.cnf - configures providers and certificate subject.
Located at src_provider_rust/nginx-example/openssl-provider.cnf:
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
akv_provider = akv_provider_sect
[default_sect]
activate = 1
[akv_provider_sect]
activate = 1Important: default provider must come before akv_provider to avoid digest algorithm issues.
Cause: Provider order wrong or SHA256 not available
Solution: Ensure default provider is listed before akv_provider in config
Symptoms: HTTP 401 errors in logs Solution: Refresh token:
export AZURE_CLI_ACCESS_TOKEN=$(az account get-access-token \
--resource https://managedhsm.azure.net \
--query accessToken -o tsv)Solution: Verify installation:
ls -la /usr/lib/x86_64-linux-gnu/ossl-modules/akv_provider.soCause: nginx version < 1.27 doesn't support OSSL_STORE Solution: Use nginx 1.27+ or build from source
| Variable | Description | Default |
|---|---|---|
AZURE_CLI_ACCESS_TOKEN |
Azure access token for HSM | (uses DefaultAzureCredential) |
AKV_VAULT |
Managed HSM name | ManagedHSMOpenSSLEngine |
AKV_RSA_KEY |
RSA key name | myrsakey |
AKV_EC_KEY |
EC key name | ecckey |
AKV_AES_KEY |
AES key name | myaeskey |
AKV_LOG_FILE |
Log file path | (none) |
AKV_LOG_LEVEL |
Log level (0-5) | 3 |
RUST_LOG |
Rust logging filter | akv_provider=debug |
src_provider_rust/
├── Cargo.toml # Rust dependencies
├── src/
│ ├── lib.rs # Provider entry point
│ ├── store.rs # OSSL_STORE implementation
│ ├── keymgmt.rs # Key management (RSA, EC, AES)
│ ├── signature.rs # Signature operations
│ ├── asymcipher.rs # RSA encrypt/decrypt
│ └── hsm_client.rs # Azure Managed HSM client
├── testOpenssl.cnf # OpenSSL config for tests
├── runtest.sh # Main test script
└── nginx-example/
├── nginx.conf # nginx configuration
├── openssl-provider.cnf
├── generate-cert.sh # Certificate generation
├── test-client.sh # TLS client tests
└── README.md
# Build
cd src_provider_rust && cargo build --release
# Install
sudo cp target/release/libakv_provider.so /usr/lib/x86_64-linux-gnu/ossl-modules/akv_provider.so
# Get token
export AZURE_CLI_ACCESS_TOKEN=$(az account get-access-token --resource https://managedhsm.azure.net --query accessToken -o tsv)
# Test
./runtest.sh
# nginx test
cd nginx-example && ./generate-cert.sh && sudo nginx -c $(pwd)/nginx.conf && ./test-client.sh