NOTE: This example shows how to use ECC key in Managed HSM for nginx. The prefix will be "managedHsm", for example
managedHsm:<Hsm Name>:testecckey
- Create and activate an Managed HSM
- Create an Azure Linux VM and assign managed identity to the Azure Linux VM
- Create role assignment to allow Azure Linux VM to access keys in Managed HSM
- Build Azure Key Vault and Managed HSM engine on the Azure Linux VM
- On the Azure Linux VM, create an self-signed cert for the nginx. Test nginx with curl.
- Prepare your subscription
az login
az account set --subscription <your subscription>
az group create --name "ContosoResourceGroup" --location westus3
- Get the Managed HSM adminstrator ID and use it in the Managed HSM creation (remember to replace [HSM NAME] with your real HSM name and actual 'xxxx' value)
az ad signed-in-user show --query objectId -o tsv
xxxx
az keyvault create --hsm-name "[HSM NAME]" --resource-group "ContosoResourceGroup" --location "West US 3" --administrators xxxx --retention-days 28
- Activate the newly created Managed HSM (remember to replace [HSM NAME] with your real HSM name). You can use openssl.cnf under this sample directory.
openssl req -newkey rsa:2048 -nodes -keyout cert_1.key -x509 -days 365 -out cert_1.cer -config openssl.cnf
openssl req -newkey rsa:2048 -nodes -keyout cert_2.key -x509 -days 365 -out cert_2.cer -config openssl.cnf
openssl req -newkey rsa:2048 -nodes -keyout cert_3.key -x509 -days 365 -out cert_3.cer -config openssl.cnf
az keyvault security-domain download --hsm-name "[HSM NAME]" --sd-wrapping-keys ./cert_1.cer ./cert_2.cer ./cert_3.cer --sd-quorum 2 --security-domain-file SD.json
- Create an ECC key in the HSM (remember to replace [HSM NAME] with your real HSM name). Please make sure the current login user has the role assignment to create keys in managed HSM.
# Grant current login user permissions to manage keys using the "Managed HSM Crypto User" local built-in role.
# See https://learn.microsoft.com/azure/key-vault/managed-hsm/built-in-roles#built-in-roles
oid=$(az ad signed-in-user show --query id -o tsv)
az keyvault role assignment create --hsm-name [HSM NAME] --assignee $oid --scope / --role "Managed HSM Crypto User"
az keyvault key create --curve p-256 --kty EC-HSM --name testecckey --hsm-name [HSM NAME] --ops sign
- In your subscription, create an Azure Linux VM "hsmlinux" with ubuntu 20. For example,
azureuser@hsmlinux:~/$ uname -a
Linux hsmlinux 5.11.0-1021-azure #22~20.04.1-Ubuntu SMP Fri Oct 29 01:11:25 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
- Create managed identity to the Azure Linux VM
az vm identity assign --name hsmlinux --resource-group contosoresourcegroup
az vm identity show --name "hsmlinux" --resource-group "contosoresourcegroup" --query principalId -o tsv
yyyy
- Create role assignment to allow the Azure Linux VM to access keys in Managed HSM (remember to replace [HSM NAME] with your real HSM name and the actual 'yyyy')
az keyvault role assignment create --hsm-name [HSM NAME] --assignee yyyy --scope / --role "Managed HSM Crypto User"
After logon to your Azure Linux VM
- Install build-essential and nginx etc
sudo apt install -y build-essential libssl-dev libcurl4-openssl-dev libjson-c-dev cmake nginx
- Build Azure Key Vault and Managed HSM engine
cd ~
git clone https://github.com/microsoft/AzureKeyVaultManagedHSMEngine.git
cd AzureKeyVaultManagedHSMEngine/src
mkdir build
cd build
cmake ..
make
-
Install the engine shared object to openssl engine folder
The command
openssl version -a | grep ENGINESDIR
will show the engine folder.For example, if the folder is
/usr/lib/x86_64-linux-gnu/engines-1.1
Then run the following command
sudo cp e_akv.so /usr/lib/x86_64-linux-gnu/engines-1.1/e_akv.so
-
Create a self-signed certificate to be used by nginx (remember to replace [HSM NAME] with your real HSM name)
cd ~/AzureKeyVaultManagedHSMEngine/samples/nginx-managedHsm
openssl req -new -x509 -config openssl.cnf -engine e_akv -keyform engine -key managedHsm:[HSM NAME]:testecckey -out certecc.pem
sudo cp certecc.pem /etc/ssl/certs/contoso_rsa_cert.cer
-
Modify nginx to use the engine. Remember to replace the [HSM NAME] with your real HSM name.
The real change is to add "ssl_engine e_akv;" line to nginx.conf and the following changes in nginx 'default' file
ssl on;
ssl_certificate /etc/ssl/certs/contoso_rsa_cert.cer;
ssl_certificate_key "engine:e_akv:managedHsm:KEYVAULTNAME:testecckey";
- Now copy the files and remember to replace [HSM NAME] with your real HSM name in default file
sudo cp nginx.conf /etc/nginx/nginx.conf
sudo cp default /etc/nginx/sites-available/default
sudo sed -i "s/KEYVAULTNAME/[HSM NAME]/g" /etc/nginx/sites-available/default
- Restart nginx and test with Curl
sudo /etc/init.d/nginx restart
curl -k https://localhost:443 -vv
- You should see something like below
$ curl -k https://localhost:443 -vv
* Trying 127.0.0.1:443...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=US; ST=WA; L=Redmond; O=Contoso; OU=AKV; CN=www.Contoso.com
* start date: Dec 2 05:37:14 2021 GMT
* expire date: Jan 1 05:37:14 2022 GMT
* issuer: C=US; ST=WA; L=Redmond; O=Contoso; OU=AKV; CN=www.Contoso.com
* SSL certificate verify result: self signed certificate (18), continuing anyway.
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0 (Ubuntu)
< Date: Thu, 02 Dec 2021 06:28:40 GMT
< Content-Type: text/html
< Content-Length: 612
< Last-Modified: Thu, 28 Oct 2021 03:11:13 GMT
< Connection: keep-alive
< ETag: "617a14d1-264"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host localhost left intact