Managing Wrapping Keys heading-link-icon

As described in the Architecture for Cluster Administrators section, Corda keys are stored in the cluster and virtual node Crypto databases and encrypted at rest with wrapping keys. These wrapping keys are created and managed automatically by Corda and known as managed wrapping keys. These managed wrapping keys are in turn encrypted with a master wrapping key and also stored in the cluster and virtual node Crypto databases. This section describes how Corda stores the master wrapping key and how to rotate master and managed wrapping keys. It contains the following:

In production deployments, the master wrapping key must be stored and managed outside Corda. When required, Corda retrieves the master key from an external key management system. For more information, see External Secrets Service.

For testing, development, or other non-production uses, a passphrase and salt can be provided to generate the master key, and Corda then stores the master key. For more information, see Default Secrets Service.

In both cases, Corda stores the master wrapping key settings in the corda.crypto configuration section. For information about the configuration fields in this section, see corda.crypto. For information about how to retrieve the current values of a configuration section, see Retrieving Current Configuration Values.

R3 recommend changing the wrapping keys frequently to limit the probability of a compromised key being in use. Corda supports the following key rotation:

Because the master key is not managed by Corda, you must first change the default key in the source system. Corda must then re-encrypt all of the keys that were wrapped with the old master key. To rotate the master wrapping key, do the following:

  1. Add the new master key to the source environment. For more information, see Configuring the Master Wrapping Key.

  2. Add the new key to the corda.crypto configuration section, setting it as the default key. The Setting Configuration Fields Dynamically section describes how to update configuration values. For example, to add a new master key, with the alias master2, generated by the Default Secrets Service:

    curl -k -u $REST_API_USER:$REST_API_PASSWORD  -X 'PUT' \
    '$REST_API_URL/config' \
    -d '{
    "section": "corda.crypto",
    "config": "{\"caching\":{\"expireAfterAccessMins\":{\"default\":60},\"maximumSize\":{\"default\":10000}},\"hsm\":{\"defaultWrappingKey\":\"master2\",\"retrying\":{\"attemptTimeoutMills\":20000,\"maxAttempts\":3},\"wrappingKeys\":[{\"alias\":\"master1\",\"passphrase\":{\"configSecret\":{\"encryptedSecret\":\"ZAmGcppwqUNsViSE06b801DVARbD9wT7SZnYP03WKZFOnC9KTsII04pWWq5Je2CUZdP+xIsHFUO3RkZVwhwnyQhWoxcW6JEo\"}},\"salt\":{\"configSecret\":{\"encryptedSecret\":\"cvNVJ4D7fNGm59Uh3Tlhi0W3pToDxl7vykUdZaxEuEPGUMdmu7G+xTEfl0CcC2R9aWl6FgrzmX5x+qCGNElUi/PBA40EGjEU\"}}},{\"alias\":\"master2\",\"passphrase\":{\"configSecret\":{\"encryptedSecret\":\"gzXRjE4AoZSW8GQhcqbw0shQfwWGy5O8alNDqXaqTUzyUnLBVUXLwhv+uAEZzecQixiaEaNtfOvuftYW9NjwwRyf6m4KZIag\"}},\"salt\":{\"configSecret\":{\"encryptedSecret\":\"flvAnf5G/aRmDIaahXHjftEpc0odufhzZsuiipxtQTFV+pccB+RHnIqle26jjDlDUBfn3HPbVezKhpRHL/NP5M+cX/rU15DE\"}}}]},\"retrying\":{\"maxAttempts\":{\"default\":3},\"waitBetweenMills\":{\"default\":[200]}}}",
    "schemaVersion": {
     "major": 1,
     "minor": 0
    },
    "version": 1
    }'
    
    Invoke-RestMethod -SkipCertificateCheck -Headers @{Authorization=("Basic {0}" -f ${REST_API_USER}:${REST_API_PASSWORD})} -Method Put -Uri "$REST_API_URL/config" -Body (ConvertTo-Json -Depth 4 @{
    {
      "config": "{
         "caching":{
           "expireAfterAccessMins":{
             "default":60
           },
           "maximumSize":{
             "default":10000
           }
         },
         "hsm":{
           "defaultWrappingKey": "master2",
           "retrying":{
             "attemptTimeoutMills": 20000,
             "maxAttempts": 3
           },
           "wrappingKeys":[{
             "alias": "master1",
             "passphrase":{
               "configSecret":
               {
                 "encryptedSecret": "gzXRjE4AoZSW8GQhcqbw0shQfwWGy5O8alNDqXaqTUzyUnLBVUXLwhv+uAEZzecQixiaEaNtfOvuftYW9NjwwRyf6m4KZIag"
               }
             },
             "salt":{
               "configSecret":
               {
                 "encryptedSecret": "cvNVJ4D7fNGm59Uh3Tlhi0W3pToDxl7vykUdZaxEuEPGUMdmu7G+xTEfl0CcC2R9aWl6FgrzmX5x+qCGNElUi/PBA40EGjEU"
               }
             },
           },
           {
             "alias": "master2",
             "passphrase":{
               "configSecret":{
                 "encryptedSecret": "ZAmGcppwqUNsViSE06b801DVARbD9wT7SZnYP03WKZFOnC9KTsII04pWWq5Je2CUZdP+xIsHFUO3RkZVwhwnyQhWoxcW6JEo"
               }
             },
             "salt":{
               "configSecret":{
                 "encryptedSecret": "flvAnf5G/aRmDIaahXHjftEpc0odufhzZsuiipxtQTFV+pccB+RHnIqle26jjDlDUBfn3HPbVezKhpRHL/NP5M+cX/rU15DE"
               }
             }
           }]
         "retrying":{
           "maxAttempts":{
             "default":3
           },
           "waitBetweenMills":{
             "default":[200]
           }
         }
       }",
       "schemaVersion": {
         "major": 1,
         "minor": 0
       },
       "section": "corda.crypto",
       "version": 1
    })
    

    Alternatively, to add a new master key maintained by Hashicorp Vault:

    curl -k -u $REST_API_USER:$REST_API_PASSWORD  -X 'PUT' \
    '$REST_API_URL/config' \
    -d '{
    "section": "corda.crypto",
    "config": "{\"caching\":{\"expireAfterAccessMins\":{\"default\":60},\"maximumSize\":{\"default\":10000}},\"hsm\":{\"defaultWrappingKey\":\"master2\",\"retrying\":{\"attemptTimeoutMills\":20000,\"maxAttempts\":3},\"wrappingKeys\":[{\"alias\":\"master1\",\"passphrase\":{\"configSecret\":{\"vaultKey\":\"passphrase\",\"vaultPath\":\"cryptosecrets\"}},\"salt\":{\"configSecret\":{\"vaultKey\":\"salt\",\"vaultPath\":\"cryptosecrets\"}}},{\"alias\":\"master2\",\"passphrase\":{\"configSecret\":{\"vaultKey\":\"passphrase2\",\"vaultPath\":\"cryptosecrets\"}},\"salt\":{\"configSecret\":{\"vaultKey\":\"salt2\",\"vaultPath\":\"cryptosecrets\"}}}]},\"retrying\":{\"maxAttempts\":{\"default\":3},\"waitBetweenMills\":{\"default\":[200]}}}",
    "schemaVersion": {
     "major": 1,
     "minor": 0
    },
    "version": 1
    }'
    
    Invoke-RestMethod -SkipCertificateCheck -Headers @{Authorization=("Basic {0}" -f ${REST_API_USER}:${REST_API_PASSWORD})} -Method Put -Uri "$REST_API_URL/config" -Body (ConvertTo-Json -Depth 4 @{
    {
      "config": "{
         "caching":{
           "expireAfterAccessMins":{
             "default":60
           },
           "maximumSize":{
             "default":10000
           }
         },
         "hsm":{
           "defaultWrappingKey": "master1",
           "retrying":{
             "attemptTimeoutMills": 20000,
             "maxAttempts": 3
           },
           "wrappingKeys":[{
             "alias": "master1",
             "passphrase":{
               "configSecret":{
                 "vaultKey":"passphrase",
                 "vaultPath":"cryptosecrets"
                 }
             },
             "salt":{
               "configSecret":{
                 "vaultKey":"salt"
                 "vaultPath":"cryptosecrets"
               }
             }
           },
           {
             "alias": "master2",
             "passphrase":{
               "configSecret":{
                 "vaultKey":"passphrase2",
                 "vaultPath":"cryptosecrets"
               }
             },
             "salt":{
               "configSecret":{
                 "vaultKey":"salt2"
                 "vaultPath":"cryptosecrets"                
               }
             }
           }]
         "retrying":{
           "maxAttempts":{
             "default":3
           },
           "waitBetweenMills":{
             "default":[200]
           }
         }
       }",
       "schemaVersion": {
         "major": 1,
         "minor": 0
       },
       "section": "corda.crypto",
       "version": 1
    })
    

    Corda will take some time to propagate the newly added master key across the cluster. Corda will wrap any new managed wrapping keys with this new master wrapping key.

  3. Rotate the old master key to the new default master key using the POST method of the /api/v5_2/wrappingkey/rotation/{tenantid} endpoint:

    curl -k -u $REST_API_USER:$REST_API_PASSWORD -X POST "$REST_API_URL/wrappingkey/rotation/master"
    
    Invoke-RestMethod -SkipCertificateCheck -Headers @{Authorization=("Basic {0}" -f ${REST_API_USER}:${REST_API_PASSWORD})} -Method POST -Uri "$REST_API_URL/wrappingkey/rotation/master" 
    
    You can use the the GET method of the /api/v5_2/wrappingkey/rotation/{tenantid} endpoint to check the status of the rotation:
    curl -k -u $REST_API_USER:$REST_API_PASSWORD -X GET "$REST_API_URL/wrappingkey/rotation/master"
    
    Invoke-RestMethod -SkipCertificateCheck -Headers @{Authorization=("Basic {0}" -f ${REST_API_USER}:${REST_API_PASSWORD})} -Method GET -Uri "$REST_API_URL/wrappingkey/rotation/master" 
    
    Corda rewraps all existing managed wrapping keys with the new master wrapping key. You can now remove the reference to the old master wrapping key from Corda by updating the crypto.config configuration section. You can also remove the key from any external key management system.

Virtual node wrapping keys are managed by Corda and can be rotated using the POST method of the /api/v5_2/wrappingkey/rotation/{tenantid} endpoint. Specify the short hash holding ID of the virtual node as the path parameter. For example:

curl -k -u $REST_API_USER:$REST_API_PASSWORD -X POST "$REST_API_URL/wrappingkey/rotation/{tenantid}"
Invoke-RestMethod -SkipCertificateCheck -Headers @{Authorization=("Basic {0}" -f ${REST_API_USER}:${REST_API_PASSWORD})} -Method POST -Uri "$REST_API_URL/wrappingkey/rotation/{tenantid}" 

You can use the the GET method of the /api/v5_2/wrappingkey/rotation/{tenantid} endpoint to check the status of the rotation. This returns the rotation status for all keys for the specified virtual node.

curl -k -u $REST_API_USER:$REST_API_PASSWORD -X GET "$REST_API_URL/wrappingkey/rotation/{tenantid}"
Invoke-RestMethod -SkipCertificateCheck -Headers @{Authorization=("Basic {0}" -f ${REST_API_USER}:${REST_API_PASSWORD})} -Method GET -Uri "$REST_API_URL/wrappingkey/rotation/{tenantid}" 

Was this page helpful?

Thanks for your feedback!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.

We're sorry this page wasn't helpful. Let us know how we can make it better!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Create an issue

Create a new GitHub issue in this repository - submit technical feedback, draw attention to a potential documentation bug, or share ideas for improvement and general feedback.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.