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.
-
Create an IBM Cloud account.
-
Download and install the IBM Cloud base CLI for your operating system.
-
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.
-
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 version1.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 runbrew upgrade openssl
to upgrade your existing package to the latest version. -
Download and install jq.
jq
helps you slice up JSON data. You usejq
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.
-
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. -
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
-
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.
-
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.
-
In your terminal window, set the Key Protect API endpoint as an environment variable.
export KP_API_URL=https://<region>.kms.cloud.ibm.com
-
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. -
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'`
-
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.
-
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.
-
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.
-
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:
-
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
-
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. Thenonce
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. -
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.
-
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:
-
Encode the key that you generated in the previous step, and set the encoded value as an environment variable.
KEY_MATERIAL=$(base64 PlainTextKey.bin)
-
Gather the nonce value that you retrieved in step 4.
NONCE=$(jq -r '.nonce' getImportTokenResponse.json)
-
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
-
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. Theiv
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.
-
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, runbrew update
and thenbrew install openssl
to get the latest version. Then, runexport PATH="/usr/local/opt/openssl/bin:$PATH" >> ~/.bash_profile
to symlink the package. Open a new terminal session, and then runwhich 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:
-
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)
-
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 tofalse
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.
-
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. Thestate
value set to 1 indicates that your encryption key is now in the Active key state. Thecrn
value provides the full scoped path to the key that specifies where the resource resides within IBM Cloud. Finally, theextractable
andimported
values describe this resource as a root key that you imported to the service. -
Optional: Navigate to the Key Protect dashboard to view and manage your encryption key.
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
-
Gather the identifier for the encryption key that you imported in the previous step.
ROOT_KEY_ID=$(jq -r '.resources[].id' createRootKeyResponse.json)
-
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 .
-
Remove the all the local files associated with this tutorial.
rm *.json *.bin *.pem
-
Delete the test directory that you created for this tutorial.
cd .. && rm -r key-protect-test
-
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.
-
Learn more about using your root key to protect data at rest.
-
Deploy your root key across supported cloud services.
-
Learn more about the Key Protect API.