|
| 1 | +Using Member Keys Stored in HSM |
| 2 | +=============================== |
| 3 | + |
| 4 | +This page explains how members' identity certificates and encryption keys stored in an `HSM <https://en.wikipedia.org/wiki/Hardware_security_module>`_ can be used with CCF. The following guide describes the usage of `Azure Key Vault <https://azure.microsoft.com/en-gb/services/key-vault>`_ |
| 5 | + |
| 6 | +.. note:: |
| 7 | + |
| 8 | + It is assumed that CCF members already have access to an existing Azure Key Vault. See `here <https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-portal#create-a-vault>`_ for more details on how to create one. Using the `Azure CLI <https://docs.microsoft.com/en-us/cli/azure/install-azure-cli>`_, it is possible to check the list of available Key Vault instances: |
| 9 | + |
| 10 | + .. code-block:: bash |
| 11 | +
|
| 12 | + $ az keyvault list |
| 13 | + # Outputs list of available vaults, including name |
| 14 | + $ export VAULT_NAME="<vault_name>" |
| 15 | +
|
| 16 | +Certificate and Key Generation |
| 17 | +------------------------------ |
| 18 | + |
| 19 | +Members' identity certificates should be generated on the `secp384r1` elliptic curve, using the `az keyvault certificate create <https://docs.microsoft.com/en-us/cli/azure/keyvault/certificate?view=azure-cli-latest#az_keyvault_certificate_create>`_ command, with the following ``identity_cert_policy_example.json`` policy: |
| 20 | + |
| 21 | +.. include:: akv_identity_cert_policy.json |
| 22 | + :literal: |
| 23 | + |
| 24 | +.. code-block:: bash |
| 25 | +
|
| 26 | + $ export IDENTITY_CERT_NAME="<identity-cert-name>" |
| 27 | + $ az keyvault certificate create --vault-name $VAULT_NAME -n $IDENTITY_CERT_NAME -p @identity_cert_policy_example.json |
| 28 | + # Outputs certificate details |
| 29 | +
|
| 30 | + # Corresponding private key is accessible at the same URL (substituting /certificate/ with /key/) |
| 31 | + $ az keyvault key show --vault-name $VAULT_NAME --name $IDENTITY_CERT_NAME |
| 32 | + # Outputs key information, including kid url |
| 33 | +
|
| 34 | +Members' encryption keys should be RSA 2048 keys, generated with the `az keyvault key create <https://docs.microsoft.com/en-us/cli/azure/keyvault/key?view=azure-cli-latest#az_keyvault_key_create>`_ command: |
| 35 | + |
| 36 | +.. code-block:: bash |
| 37 | +
|
| 38 | + $ export ENCRYPTION_KEY_NAME="<encryption-key-name>" |
| 39 | + $ az keyvault key create --vault-name $VAULT_NAME --name $ENCRYPTION_KEY_NAME --kty RSA --ops decrypt |
| 40 | + # Outputs key details, including kid url |
| 41 | +
|
| 42 | +The identity certificate and public encryption key can be downloaded to a PEM file and be passed on to members to be registered in a CCF service as a trusted member identity (see :ref:`members/adding_member:Registering a New Member`). Alternatively, if the service has not yet been started, the public member identity can be passed on to operators and registered via the ``cchost --member-info`` option (see :ref:`operators/start_network:Starting the First Node`): |
| 43 | + |
| 44 | +.. code-block:: bash |
| 45 | +
|
| 46 | + $ az keyvault certificate download --file $IDENTITY_CERT_NAME.pem --vault-name $VAULT_NAME --name $IDENTITY_CERT_NAME |
| 47 | + # Downloads PEM identity certificate |
| 48 | +
|
| 49 | + $ az keyvault key download --file $ENCRYPTION_KEY_NAME.pem --vault-name $VAULT_NAME --name $ENCRYPTION_KEY_NAME |
| 50 | + # Downloads PEM encryption public key |
| 51 | +
|
| 52 | +HTTP Request Signature |
| 53 | +---------------------- |
| 54 | + |
| 55 | +As the Azure CLI (``az keyvault ...``) does not currently support signing/verifying, it is required to use the `corresponding REST API <https://docs.microsoft.com/en-us/rest/api/keyvault/sign/sign>`_ instead. To do so, it is necessary to create a service principal that will be used for authentication: |
| 56 | + |
| 57 | +.. code-block:: bash |
| 58 | +
|
| 59 | + $ export SP_NAME="<sp-name>" |
| 60 | + $ az ad sp create-for-rbac --name $SP_NAME |
| 61 | + # Returns client id (appId), client secret (password) |
| 62 | +
|
| 63 | +.. note:: To retrieve the service principal credentials after its creation, the credentials should be refreshed: |
| 64 | + |
| 65 | + .. code-block:: bash |
| 66 | +
|
| 67 | + $ az ad sp credential reset --name <app_id> |
| 68 | + # Returns client id (appId), updated client secret (password) |
| 69 | +
|
| 70 | +Once created, the service principal should be given access to Key Vault in Azure. This can be done through the Azure Portal, under the "Access policies" setting of the vault. The service principal should be given access to the vault with "Sign" key permission. See `here <https://docs.microsoft.com/en-us/azure/key-vault/general/assign-access-policy-portal>`_ for more details. |
| 71 | + |
| 72 | +Then, the following command should be run to retrieve an access token, replacing the values for ``<appid>``, ``<password>`` and ``<tenant>`` with the service principal credentials: |
| 73 | + |
| 74 | +.. code-block:: bash |
| 75 | +
|
| 76 | + export AZ_TOKEN=$(curl -X POST -d "grant_type=client_credentials&client_id=<appid>&client_secret=<password>&resource=https://vault.azure.net" https://login.microsoftonline.com/<tenant>/oauth2/token | jq -r .access_token) |
| 77 | +
|
| 78 | +The member's identity key is now ready to be used for signing HTTP requests. The ``scurl.sh`` script can be used with the ``--print-digest-to-sign`` option to print the SHA384 to be signed as well as the required headers for HTTP signatures (following the scheme described `here <https://tools.ietf.org/html/draft-cavage-http-signatures-12>`_): |
| 79 | + |
| 80 | +.. code-block:: bash |
| 81 | +
|
| 82 | + # First, retrieve the hash to be signed |
| 83 | + $ scurl.sh https://<ccf-node-address>/gov/<endpoint> -X [GET|POST] --cert $IDENTITY_CERT_NAME.pem --print-digest-to-sign |
| 84 | + Hash to sign: <hash_to_sign> # To be signed by AKV |
| 85 | + Request headers: |
| 86 | + -H 'Digest: SHA-256=...' |
| 87 | + -H 'Authorization: Signature keyId="...",signature_algorithm="hs2019",headers="(request-target) digest content-length",signature="<insert_base64_signature_here>"' # Replace signature with AKV signature here |
| 88 | + -H 'content-length: 0' |
| 89 | +
|
| 90 | + # Then, retrieve the kid url for the identity key |
| 91 | + $ export IDENTITY_AKV_KID=$(az keyvault key show --vault-name $VAULT_NAME --name $IDENTITY_CERT_NAME --query key.kid --output tsv) |
| 92 | +
|
| 93 | + # Then, sign the request hash to be signed (as output by scurl.sh --print-digest-to-sign) |
| 94 | + $ export base64url_signature=$(curl -s -X POST $IDENTITY_AKV_KID/sign?api-version=7.1 --data '{alg: "ES384", "value": "<hash_to_sign>"}' -H "Authorization: Bearer ${AZ_TOKEN}" -H "Content-Type: application/json" | jq -r .value) |
| 95 | +
|
| 96 | +.. note:: The signatures returned by AKV are returned as a `JWS signature <https://tools.ietf.org/html/rfc7518#section-3.4>`_ and encoded in `base64url <https://tools.ietf.org/html/rfc4648#section-5>`_ format and are not directly compatible with the signatures supported by CCF. |
| 97 | + |
| 98 | +The `jws_to_der.py <https://github.com/microsoft/CCF/blob/master/doc/members/jws_to_der.py>`_ Python script can be used to convert a JWS signature generated by AKV to a DER signature compatible with CCF: |
| 99 | + |
| 100 | +.. code-block:: bash |
| 101 | +
|
| 102 | + $ pip install pyasn1 |
| 103 | + $ export ccf_signature=$(python3.8 jws_to_der.py $base64url_signature) |
| 104 | +
|
| 105 | +Finally, the signed HTTP request can be issued, using the request headers printed by ``scurl.sh --print-digest-to-sign``: |
| 106 | + |
| 107 | +.. code-block:: bash |
| 108 | +
|
| 109 | + $ curl https://<ccf-node-address>/gov/<endpoint> -X [GET|POST] --cert $IDENTITY_CERT_NAME.pem \ |
| 110 | + -H 'Digest: SHA-256=...' \ |
| 111 | + -H 'Authorization: Signature keyId="...",signature_algorithm="hs2019",headers="(request-target) digest content-length",signature="$ccf_signature"' \ |
| 112 | + -H 'content-length: <content-length>' |
| 113 | +
|
| 114 | +Recovery Share Decryption |
| 115 | +------------------------- |
| 116 | + |
| 117 | +To retrieve their encrypted recovery share, a member should issue a signed HTTP request against the ``/gov/recovery_share`` endpoint (see :ref:`members/accept_recovery:Submitting Recovery Shares`). Signing the request will allow the member to authenticate themself to CCF (see :ref:`members/hsm_keys:HTTP Request Signature`). |
| 118 | + |
| 119 | +The retrieved encrypted recovery share can be decrypted with the encryption key stored in Key Vault: |
| 120 | + |
| 121 | +.. code-block:: |
| 122 | +
|
| 123 | + $ az keyvault key decrypt --vault-name $VAULT_NAME --name $ENCRYPTION_KEY_NAME --algorithm RSA-OAEP-256 --value <base64_encrypted_share> |
| 124 | + # Outputs base64 decrypted share |
| 125 | +
|
| 126 | +The decrypted recovery share can then be submitted to the CCF recovered service (see :ref:`members/accept_recovery:Submitting Recovery Shares`). |
0 commit comments