IBM Cloud Docs
Tutorial: Creating and importing encryption keys

Tutorial: Creating and importing encryption keys

Learn how to create, encrypt, and bring your encryption keys to the cloud by using Key Protect.

As a security professional for your organization, you're always looking for ways to enhance the security of your at rest data on the cloud.

To comply with strict data governance and regulatory audit requirements, you want to integrate your apps with a key management service that offers fine-grained access control to encryption keys, audit trail capabilities, and flexible options for uploading encryption keys that you generate on-premises.

With Key Protect, you can create encryption keys by using your internal key management system, and then upload those keys for use on the cloud.

You can choose from different options for uploading keys based on your ongoing security needs. As you manage the lifecycle of encryption keys, you control access to resources by using Cloud Identity and Access Management, and you monitor API activity to the service with Activity Tracker.

In this tutorial, you use an import token to upload an encryption key to Key Protect. To learn more about your options for importing keys to Key Protect, see Planning ahead for importing key material.

To learn about importing a key without an import token, see Importing a root key

Objectives

This tutorial walks you through creating and securely importing encryption keys into the Key Protect service. It's intended for users who are new to Key Protect, but who might have some familiarity with key management systems. The following steps should take about 20 minutes to complete.

  • Setting up the Key Protect CLI

  • Preparing your Key Protect service instance to begin importing keys

  • Creating and encrypting keys using the OpenSSL cryptography toolkit

  • Importing an encrypted key to your Key Protect instance

This tutorial won't incur any charges to your IBM Cloud account.

Before you begin

To get started, you need the IBM Cloud CLI so that you can interact with services that you provision on IBM Cloud. You also need the openssl and jq packages installed locally on your computer.

  1. Create an IBM Cloud account.

  2. Download and install the IBM Cloud base CLI for your operating system.

  3. Configure and setup the Key Protect CLI Plugin to start managing keys. If you've already completed the first two steps listed previously, start with step number 3 at the link before returning to this tutorial.

  4. Download and install the OpenSSL cryptography library.

    You can use openssl commands to generate encryption keys on your local computer if you're trying out Key Protect for the first time. This tutorial requires OpenSSL version 1.0.2r or later.

    If you're using a Mac, you can download OpenSSL by using Homebrew. Run brew install openssl if you're installing the package for the first time, or run brew upgrade openssl to upgrade your existing package to the latest version.

  5. Download and install jq.

    jq helps you slice up JSON data. You use jq in this tutorial to capture specific data that's returned when you call the Key Protect API.

Step 1. Create a Key Protect instance

After you set up an IBM Cloud account, complete the following steps to provision a Key Protect instance.

  1. In a terminal window, run the following command to log in to IBM Cloud with the IBM Cloud CLI.

    ibmcloud login
    

    If the login fails, run the ibmcloud login --sso command to try again. The --sso parameter is required when you log in with a federated ID. If this option is used, go to the link listed in the CLI output to generate a one-time passcode.

  2. Select the account and resource group where you would like to create a Key Protect instance.

    In this tutorial, you interact with the Washington DC region. If you're logged into a different region, be sure to set Washington DC as your target region by running the following command.

    ibmcloud target -r us-east
    
  3. Provision an instance of Key Protect within that account and resource group.

    First, specify the resource group for the instance by issuing:

    ibmcloud target -g <your-resource-group>
    

    For example, ibmcloud target -g Default

    Then you can create the instance by issuing:

    ibmcloud resource service-instance-create "import-keys-demo" kms tiered-pricing us-east
    

    This tutorial won't incur any charges to your IBM Cloud account.

  4. Optional: Verify that the Key Protect instance was created successfully by listing your available Key Protect instances.

    ibmcloud resource service-instances
    

    Success! You're now set with the Key Protect instance where you can store and manage your encryption keys. Continue to the next step.

Step 2. Set up the Key Protect API

Now that you've provisioned an instance of Key Protect, you're ready to start using the API.

Key Protect provides a graphical user interface and a REST API to create, track, and manage encryption keys. The Key Protect API requires a valid IBM Cloud IAM token and an instance ID to authenticate with the service.

In this step, you use the IBM Cloud CLI to gather the authentication credentials you need to start interacting with Key Protect APIs. To retrieve and prepare your credentials for later steps, you also set the credentials as environment variables in your terminal.

  1. In your terminal window, set the Key Protect API endpoint as an environment variable.

    export KP_API_URL=https://<region>.kms.cloud.ibm.com
    
  2. Generate an IBM Cloud access token using the Key Protect CLI Plugin, and set it as an environment variable.

    The environment variable should begin with the authorization type, Bearer. The CLI command, as shown in the example, will automatically include the correct type.

    export ACCESS_TOKEN=`ibmcloud iam oauth-tokens | grep IAM | cut -d \: -f 2 | sed 's/^ *//'`
    

    IBM Cloud access tokens are valid for 1 hour, but you can regenerate them as needed. To generate a new access token, run the ibmcloud iam oauth-tokens command. To find out more about retrieving IBM Cloud access tokens, see Retrieving an access token.

  3. Retrieve the identifier that is associated with your Key Protect instance, and then set the value as an environment variable.

    export INSTANCE_ID=`ibmcloud resource service-instance "import-keys-demo" --output json | jq -r '.[].guid'`
    
  4. Optional: Verify that the environment variables are set correctly by printing them to your terminal screen.

    $ echo $KP_API_URL
    https://us-east.kms.cloud.ibm.com
    $ echo $ACCESS_TOKEN
    Bearer eyJraWQiOiIyM...
    $ echo $INSTANCE_ID
    c1cf624b-6bed-4d4d-bd54-8e2534258a88
    

    Success! You're now set with the service credentials that you need to authenticate to the Key Protect API. Continue to the next step.

Step 3. Create an import token

With your service credentials, you can start interacting with the Key Protect APIs to create and bring your encryption keys to the service.

In the following step, you create a import token for your Key Protect instance. By creating an import token based on a policy that you specify, you enable extra security for your encryption key while it's in flight to the service.

  1. Using your terminal session, change into a new key-protect-test directory.

    mkdir key-protect-test && cd key-protect-test
    

    You use this directory to store files for later steps.

  2. Create an import token for your Key Protect instance, and then save the response to a JSON file.

    $ curl -X POST \
        "$KP_API_URL/api/v2/import_token" \
        -H "accept: application/vnd.ibm.collection+json" \
        -H "authorization: $ACCESS_TOKEN" \
        -H "bluemix-instance: $INSTANCE_ID" \
        -H "content-type: application/json" \
        -d '{
                "expiration": 1200,
                "maxAllowedRetrievals": 1
            }' > createImportTokenResponse.json
    

    In the request body, you can specify a policy on the import token that limits its use based on time and usage count. In this example, you set the expiration time for the import token to 1200 seconds (20 minutes), and you also allow only one retrieval of that token within the expiration time.

  3. View details for the import token.

    jq '.' createImportTokenResponse.json
    

    The output displays the metadata that is associated with your import token, such as its creation date and policy details. The following snippet shows example output.

    {
        "creationDate": "2019-04-08T16:58:29Z",
        "expirationDate": "2019-04-08T17:18:29Z",
        "maxAllowedRetrievals": 1,
        "remainingRetrievals": 1
    }
    

Step 4. Retrieve the import token

In the previous step, you created an import token and you viewed the metadata that is associated with the token.

{
    "creationDate": "2019-04-08T16:58:29Z",
    "expirationDate": "2019-04-08T17:18:29Z",
    "maxAllowedRetrievals": 1,
    "remainingRetrievals": 1
}

In this step, you retrieve the public encryption key and nonce value that are associated with the import token. You need the public key to encrypt data in a later step, and the nonce to verify your secure import request to the Key Protect service.

To retrieve the import token contents:

  1. Retrieve the import token that you generated the previous step, and then save the response to a JSON file.

    $ curl -X GET \
        "$KP_API_URL/api/v2/import_token" \
        -H "accept: application/vnd.ibm.collection+json" \
        -H "authorization: $ACCESS_TOKEN" \
        -H "bluemix-instance: $INSTANCE_ID" > getImportTokenResponse.json
    
  2. Optional: Inspect the contents of the import token.

    jq '.' getImportTokenResponse.json
    

    The output displays detailed information about the import token. The following snippet shows example output with truncated values.

    {
        "creationDate": "2019-04-08T16:58:29Z",
        "expirationDate": "2019-04-08T17:18:29Z",
        "maxAllowedRetrievals": 1,
        "remainingRetrievals": 0,
        "payload": "Rm91ciBzY29yZSBhbmQgc2V2ZW4geWVhcnMgYWdv...",
        "nonce": "8zJE9pKVdXVe/nLb"
    }
    

    The payload value represents the public key that is associated with the import token. This value is base64 encoded. The nonce value is used to verify the originality of a request to the service. You need to encrypt and provide this value when you import your encryption key in a later step.

  3. Decode and save the public key to a file called PublicKey.pem.

    jq -r '.payload' getImportTokenResponse.json | base64 --decode -o PublicKey.pem
    

    The public key is now downloaded to your computer in PEM format. Continue to the next step.

Step 5. Create an encryption key

With Key Protect, you can enable the security benefits of Bring Your Own Key (BYOK) by creating and uploading your own keys for use on IBM Cloud.

In the following step, you create a 256-bit AES symmetric key on your local computer.

This tutorial uses the OpenSSL cryptography toolkit to generate a pseudo-random key, but you might want to explore different options for generating stronger keys based on your security needs. For example, you might want to use your organization's internal key management system, backed by an on-premises hardware security module (HSM), to create and export keys.

  1. In a terminal window, run the following openssl command to create a 256-bit encryption key.

    openssl rand 32 > PlainTextKey.bin
    

    Success! Your encryption key is now saved in a file called PlainTextKey.bin. Continue to the next step.

Step 6. Encrypt the nonce

To verify that the bits we receive are exactly the same as the bits that you send as part of a request, Key Protect requires nonce verification when you upload the symmetric key to the service.

In cryptography, a nonce serves as a session token that checks the originality of a request to protect against malicious attacks and unauthorized calls. By using the same nonce that was distributed by Key Protect, you help to ensure that your request to upload a key is valid. The nonce value must be encrypted by using the same key that you want to import into the service.

To encrypt the nonce value:

  1. Encode the key that you generated in the previous step, and set the encoded value as an environment variable.

    KEY_MATERIAL=$(base64 PlainTextKey.bin)
    
  2. Gather the nonce value that you retrieved in step 4.

    NONCE=$(jq -r '.nonce' getImportTokenResponse.json)
    
  3. Execute the following to encrypt the nonce value with the encryption key that you generated in step 5. Then, save the response to a file called EncryptedValues.json.

    ibmcloud kp import-token nonce-encrypt -k $KEY_MATERIAL -n $NONCE --output json > EncryptedValues.json
    
  4. Optional: Inspect the contents of the JSON file by using jq as shown.

    jq '.' EncryptedValues.json
    

    The output displays the values that you need to provide for the next step. The following snippet shows example output with truncated values.

    {
        "encryptedNonce": "DVy/Dbk37X8gSVwRA5U6vrHdWQy8T2ej+riIVw==",
        "iv": "puQrzDX7gU1TcTTx"
    }
    

    The encryptedNonce value represents the original nonce that is wrapped (or encrypted) by the encryption key that you generated using OpenSSL. The iv value is the initialization vector (IV) that is created by the AES-GCM algorithm, and it's required later so that Key Protect can successfully decrypt the nonce.

Step 7. Encrypt the key

Next, use the public key that was distributed by Key Protect to encrypt the symmetric key that you generated using OpenSSL.

  1. Encrypt the generated key by using the the public key that you retrieved in step 4.

    openssl pkeyutl \
        -encrypt \
        -pubin \
        -keyform PEM \
        -inkey PublicKey.pem \
        -pkeyopt rsa_padding_mode:oaep \
        -pkeyopt rsa_oaep_md:sha256 \
        -in PlainTextKey.bin \
        -out EncryptedKey.bin
    

    If you run into a parameter settings error when you run the openssl command on Mac OSX, you might need to ensure that OpenSSL is properly configured for your environment. If you installed OpenSSL by using Homebrew, run brew update and then brew install openssl to get the latest version. Then, run export PATH="/usr/local/opt/openssl/bin:$PATH" >> ~/.bash_profile to symlink the package. Open a new terminal session, and then run which openssl && openssl version to verify that the latest version of OpenSSL is available under the /usr/local/ location. If you continue to encounter errors, be sure to use only the parameters that are listed in this example.

    Success! Your encrypted key is now saved to a file called EncryptedKey.bin. You're all set to upload your encrypted key into Key Protect. Continue to the next step.

Step 8. Import the key

You can now import the encrypted key using the Key Protect API.

To import the key:

  1. Gather the encrypted key, the encrypted nonce, and the initialization vector (IV) values.

    ENCRYPTED_KEY=$(openssl enc -base64 -A -in EncryptedKey.bin)
    
    ENCRYPTED_NONCE=$(jq -r '.encryptedNonce' EncryptedValues.json)
    
    IV=$(jq -r '.iv' EncryptedValues.json)
    
  2. Store the encrypted key in your Key Protect instance by running the following curl command.

    $ curl -X POST \
        "$KP_API_URL/api/v2/keys" \
        -H "accept: application/vnd.ibm.collection+json" \
        -H "authorization: $ACCESS_TOKEN" \
        -H "bluemix-instance: $INSTANCE_ID" \
        -H "content-type: application/json" \
        -d '{
                "metadata": {
                    "collectionType": "application/vnd.ibm.kms.key+json",
                    "collectionTotal": 1
                },
                "resources": [
                    {
                        "name": "encrypted-root-key",
                        "type": "application/vnd.ibm.kms.key+json",
                        "payload": "'"$ENCRYPTED_KEY"'",
                        "extractable": false,
                        "encryptionAlgorithm": "RSAES_OAEP_SHA_256",
                        "encryptedNonce": "'"$ENCRYPTED_NONCE"'",
                        "iv": "'"$IV"'"
                    }
                ]
            }' > createRootKeyResponse.json
    

    In the request body, you provide the encryption key that you prepared in the previous step. You also supply the encrypted nonce and the IV values that are required to verify the request. Finally, the extractable value set to false designates your new key as a root key in the service that you can use for envelope encryption.

    Key Protect receives your encrypted packet over the TLS 1.2 or 1.3 protocol. Within a hardware security module, the system uses the private key to decrypt the symmetric key. Finally, the system uses the symmetric key and the IV to decrypt the nonce and verify the request.

    If the API request fails with an import token expired error, return to step 3 to create a new import token. Remember that import tokens and their associated public keys expire based on the policy that you specify at creation time.

  3. View details for the encryption key.

    jq '.' createRootKeyResponse.json
    

    The following snippet shows an example output.

    {
        "metadata": {
            "collectionType": "application/vnd.ibm.kms.key+json",
            "collectionTotal": 1
        },
        "resources": [
            {
                "id": "02fd6835-6001-4482-a892-13bd2085f75d",
                "type": "application/vnd.ibm.kms.key+json",
                "name": "encrypted-root-key",
                "state": 1,
                "crn": "crn:v1:bluemix:public:kms:us-south:a/f047b55a3362ac06afad8a3f2f5586ea:12e8c9c2-a162-472d-b7d6-8b9a86b815a6:key:02fd6835-6001-4482-a892-13bd2085f75d",
                "extractable": false,
                "imported": true
            }
        ]
    }
    

    The id value is a unique identifier that is assigned to your key and is used for subsequent calls to the Key Protect API. The state value set to 1 indicates that your encryption key is now in the Active key state. The crn value provides the full scoped path to the key that specifies where the resource resides within IBM Cloud. Finally, the extractable and imported values describe this resource as a root key that you imported to the service.

  4. Optional: Navigate to the Key Protect dashboard to view and manage your encryption key.

    The image shows the Key Protect dashboard view.

    You can browse the general characteristics of your keys from the application details page. Choose from a list of options for managing your key, such as rotating the key or deleting the key.

Step 9. Clean up

  1. Gather the identifier for the encryption key that you imported in the previous step.

    ROOT_KEY_ID=$(jq -r '.resources[].id' createRootKeyResponse.json)
    
  2. Remove the encryption key from your Key Protect instance.

    $ curl -X DELETE \
        "$KP_API_URL/api/v2/keys/$ROOT_KEY_ID" \
        -H "accept: application/vnd.ibm.collection+json" \
        -H "authorization: $ACCESS_TOKEN" \
        -H "bluemix-instance: $INSTANCE_ID" | jq .
    
  3. Remove the all the local files associated with this tutorial.

    rm *.json *.bin *.pem
    
  4. Delete the test directory that you created for this tutorial.

    cd .. && rm -r key-protect-test
    
  5. Optional: Remove your Key Protect service instance.

    ibmcloud resource service-instance-delete import-keys-demo
    

    If you created more test keys in your Key Protect instance, be sure to remove all encryption keys from your instance before you delete or deprovision the instance.

Next steps

In this tutorial, you learned how to set up the Key Protect API, create an encryption key, and securely import an encrypted key into your Key Protect instance.