Exporting SDKMS keys to Cloud Providers for BYOK

Overview

Here we will go over several ways to export SDKMS keys to major cloud providers that support BYOK for server-side encryption. Requisite: Download SDKMS CLI from here.

Google Cloud

GCS (Cloud Storage)

For GCS, actual base64 customer keys are needed to be provided for every upload and download of objects to GCS.

1. Create a 256-bit AES key in SDKMS with the EXPORT key operation enabled.

$ python sdkms-cli create-key --obj-type AES --key-size 256 --name Google-Cloud-Master-Key --exportable

2. Export this key on your application environment.

$ python sdkms-cli export-object --name Google-Cloud-Master-Key

3. Add the following option to the GSUtil section of GSUtil boto configuration file:

encryption_key = [YOUR_ENCRYPTION_KEY]
decryption_key1 = [YOUR_ENCRYPTION_KEY]

4. Now you can upload and download objects in GCS with encryption with your own keys.

$ gsutil cp [LOCAL_OBJECT_LOCATION] gs://[DESTINATION_BUCKET_NAME]/
$ gsutil cp gs://[BUCKET_NAME]/[OBJECT_NAME] [OBJECT_DESTINATION]

5. GCS browser shows that the object is customer encrypted.

GCE (Compute Engine)

GCE supports import of customer keys wrapped by a Google public key. Since SDKMS supports wrapping natively, actual material of the customer keys are never exposed.

1. Create a 256-bit AES key in SDKMS with the EXPORT key operation enabled.

$ python sdkms-cli create-key --obj-type AES --key-size 256 --name Google-Cloud-Master-Key --exportable

2. Fetch Google public key.

$ curl "https://cloud-certs.storage.googleapis.com/google-cloud-csek-ingress.pem" -o google-cloud-csek-ingress.pem
$ openssl x509 -pubkey -noout -in google-cloud-csek-ingress.pem > google-cloud-csek-public.pem

3. Import the Google public key in SDKMS

$ python sdkms-cli import-key --obj-type RSA --in google-cloud-csek-public.pem --name Google-Cloud-Public-Key

4. Wrap SDKMS master key with Google public key, using SDKMS

$ sdkms-cli wrap-key --kid (kid of master key) --alg RSA --mode OAEP_MGF1_SHA1 --wrapping-kid (kid of the Google public key) --out rsawrappedkey.txt
$ openssl enc -base64 -in rsawrappedkey.txt | tr -d '\n' | sed -e '$a\' > rsawrappedbase64key.txt

5. Set the key data in GCE as a wrapped key

6. The disk says that it's encrypted with customer keys.

AWS

AWS KMS provides a wrapping key and a token in order to import customer keys. The steps are very similar to Google Cloud GCE setup:

1. Create a 256-bit AES key in SDKMS withEXPORT key operation enabled.

$ python sdkms-cli create-key --obj-type AES --key-size 256 --name AWS-Master-Key --exportable

2. Initiate creation of key of external origin in KMS.

3. Download the zip containing the wrapping key and import token.

4. Import the AWS wrapping key in SDKMS

$ python sdkms-cli import-key --obj-type RSA --in wrappingKey_fcb572d3-6680-449c-91ab-ac3a5c07dc09_080410435 --name AWS-Wrapping-Key

5. Wrap SDKMS master key with AWS wrapping key, using SDKMS

$ sdkms-cli wrap-key --kid (kid of master key) --alg RSA --mode OAEP_MGF1_SHA256 --wrapping-kid (kid of the AWS wrapping key) --out rsawrappedkey.txt
AWS KMS supports importing raw value of a key. If you require base-64 encoded value of the key, use the following command:
$ openssl enc -base64 -in rsawrappedkey.txt | tr -d '\n' | sed -e '$a\' > rsawrappedbase64key.txt

 

6. Upload this wrapped key and the downloaded token to complete the import.

7. Use this imported key for server-side encryption in AWS Services. In S3 for example, one can enable this during bucket creation itself.

AWS

Use the following script to automate BYOK in AWS

#!/bin/bash

# Install aws cli, sdkms-cli before running this script

# Setup environment variable and temporary files for storing key material
export FORTANIX_API_ENDPOINT=https://sdkms.fortanix.com
wrappingkey_file=$(mktemp)
import_token_file=$(mktemp)
wrapped_blob=$(mktemp)

# run aws configure and enter your access key, secret key, region, and default output format (text)
aws configure

# Create external key in AWS
aws_kid=$(aws kms create-key --origin EXTERNAL | awk '{print $6}')

# Get description of key
aws kms describe-key --key-id $aws_kid

# Get import parameters for external key created in AWS
params=$(aws kms get-parameters-for-import --key-id $aws_kid --wrapping-algorithm RSAES_OAEP_SHA_256 --wrapping-key-spec RSA_2048)
echo $params | awk '{print $4}' | base64 -D > $wrappingkey_file
echo $params | awk '{print $1}' | base64 -D > $import_token_file

# Login to SDKMS
sdkms-cli app-login
 
# Generate Key in SDKMS
key_name="AWS Key"$RANDOM
kid=$(sdkms-cli create-key --name "$key_name" --obj-type AES --key-size 256 --exportable -f)
 
# Import public key to SDKMS
wrapping_key_name="AWS wrapping key"$RANDOM
wrapping_kid=$(sdkms-cli import-key --in $wrappingkey_file --der --name "$wrapping_key_name" --obj-type RSA)
 
# Wrap SDKMS key with wrapping key obtained from SDKMS
blobfile=$(mktemp)
sdkms-cli wrap-key --wrapping-kid $wrapping_kid --kid $kid --alg RSA --mode OAEP_MGF1_SHA256 --out $wrapped_blob
 
# Logout from SDKMS
sdkms-cli app-logout

# Import key to AWS
aws kms import-key-material --key-id $aws_kid --encrypted-key-material fileb://$wrapped_blob --import-token fileb://$import_token_file --expiration-model KEY_MATERIAL_DOES_NOT_EXPIRE

# Get description of key
aws kms describe-key --key-id $aws_kid

# Cleanup
rm $wrappingkey_file $import_token_file $wrapped_blob

Azure

Azure Key Vault supports direct import of key material. Generate an exportable AES key in SDKMS and export its value to upload the key to Azure.

1. Create a 256-bit AES key in SDKMS with the EXPORT key operation enabled.

$ python sdkms-cli create-key --obj-type AES --key-size 256 --name Azure-Cloud-Master-Key --exportable
 

2. Export this key on your application environment.

$ python sdkms-cli export-object --name Azure-Cloud-Master-Key

You have to choose to upload your key either as a software or hardware key depending on your requirement.

Alibaba

1. Create an external key in Alibaba.

1a. Create a new key by selecting key material source as “External”.

Alibaba-1a.png

1b. Newly created key should show up with status as “Pending Import” and key material source as “External”.

Alibaba-1b.png

2. Download key encryption material.

Download the key encryption material, you will need it for key wrapping in SDKMS and key importing into Alibaba.

• Public key
• Import Token

Alibaba-2.png

3. Import Alibaba public key into SDKMS.

Import the public key from previous step into SDKMS as RSA key.

Alibaba-3.png

4. Create Customer Master Key in SDKMS.

Create a new AES key and make sure to select the “exportable” option.

Alibaba-4.png

5. Wrap customer master key with Alibaba public key.

5a. Use SDKMS-Cli to wrap the newly created AES key (customer master key) with imported Alibaba public key.

$ sdkms-cli wrap-key --kid <customer master key> --alg RSA --mode OAEP_MGF1_SHA1 --wrapping-kid <Alibaba public key> --out alibabawrap.key 

5b. Apply base64 encoding on wrap key.

$ openssl enc -e -base64 -A -in alibabawrap.key -out alibabawrapbase64.key

6. Upload key into Alibaba KMS.

Import the encoded wrap key into Alibaba. You will also need the import token which we downloaded from Alibaba in Step 2.

Alibaba-6.png

7. Alibaba KMS should have external key enabled now.

With successful import your external key should be "Enabled" now.

Alibaba-7.png

Salesforce

Salesforce's Shield Platform Encryption is introducing a new pilot feature called Cache-Only Keys. This capability enhances the existing Bring Your Own Key (BYOK) capability by allowing customers to host their key material in a wrapped format which Salesforce will fetch as required. While this will be cached in an encrypted form, Salesforce will not retain or persist the key material in any system of record or backups.

Fortanix SDKMS can be used as HSM backed Software-as-a-service(SAAS) for Fortanix - Salesforce Cache only BYOK solution. This guide explains how to use Fortanix SDKMS to securely generate encryption key and configure in Salesforce’s Shield Platform.

Prerequisite

1. Salesforce account with permission to below settings.

a. Name Credentials
b. Certificate & Key Management
c. Key Management

2. SDKMS account with appropriate permissions to create groups, app, security object and plugin.

SDKMS Setup

Step 1: Create Group in SDKMS

1. Login to Fortanix SDKMS (https://sdkms.fortanix.com)

2. Click on left navigation bar to navigate to “Groups”

SalesforceBYOKSdkms-Step1.2.png

3. Click on (+) icon to create new group.

SalesforceBYOKSdkms-Step1.3.png

Step 2: Create an APP in Fortanix SDKMS

1. Click on left navigation bar to navigate to “Apps”

SalesforceBYOKSdkms-Step2.1.png

2. Click on (+) icon to create new app.

3. Enter desired information (refer below screenshot) and select above created group and click save.

SalesforceBYOKSdkms-Step2.3.png

4. Navigate to Apps dashboard to see newly created app.

5. Click on copy API key. It will open a model window.

6. Go to USERNAME/PASSWORD tab in model window.

SalesforceBYOKSdkms-Step2.6.png

7. Copy and Save username / password. Details will be required later to configure “Named Credentials” in salesforce later.

Step 3: Create plugin in SDKMS

1. Click on “Plugins” tab on left navigation panel.

2. Click on (+) button to create new plugin.

3. Enter <plugin name>

SalesforceBYOKSdkms-Step3.3.png

4. Select “Group” created in Step 1.

5. Copy and paste code plugin code and save. Show Plugin Code

-- start of plugin
-----------------------------
-- start of helper methods
-----------------------------
function Blob:base64_urlsafe()
return (self:base64():gsub('+', '-'):gsub('/', '_'):gsub('=', ''))
end
function getNextKeyVersionLabelForVersion(name, version)
avialableName = name .. '_V' .. version
key = Sobject { name = avialableName }
if not key then
return avialableName
else
return getNextKeyVersionLabelForVersion(name, version+1);
end
end

function getNextKeyVersionLabel(name)
return getNextKeyVersionLabelForVersion(name, 1)
end

function createKey(keyname, isTransient)
return Sobject.create {
obj_type = 'AES',
key_size = 256,
key_ops = {'ENCRYPT', 'DECRYPT', 'WRAPKEY', 'UNWRAPKEY', 'DERIVEKEY', 'EXPORT',
'APPMANAGEABLE'},
name = keyname,
transient = isTransient
}
end
function findOrCreateKey(kid, name)
if not kid then
cek = createKey(name, false)
else
cek = Sobject { kid = kid }
end
return cek;
end

function saveToOpaqueObject(name, value, desc)
return Sobject.import {
name = name,
obj_type = 'OPAQUE',
value = value,
description = desc,
transient = false
};
end
function validateInput(input)
if not Sobject { kid = input.cert } then
return 'Unable to generate Key data, Cert not found'
end
--check if dek uuid is passed, else perform already exist validation
if not input.dek then
if not input.name then
return 'Pass uuid of existing key or name for new dek'
end
if Sobject { name = input.name } then
return 'Key ['.. input.name .. '] already generated for input name [' .. input.name
.. ']'
end
if Sobject { name = input.name .. '_meta_info'} then
return 'Meta info ['.. input.name .. '_meta_info' .. '] already generated for input
name [' .. input.name .. ']'
end
else
--check if input.dek is valid and meta info not already generated for same
dek = Sobject { kid = input.dek }
if not dek then
return 'Invalid dek [' .. input.dek .. ']'
else
if Sobject { name = dek.name .. '_meta_info' } then
return 'Meta info ['.. dek.name .. '_meta_info' .. '] already generated for
input dek uuid [' .. input.dek .. ']'
end
end
end
return nil
end
function generateKeyAndMetaData(input)
errormsg = validateInput(input)
if errormsg then
return { error = errormsg }
end
-- use dek passed or generate new one
dek = findOrCreateKey(input.dek, input.name)
key_info = '{ "dek":"' .. dek.kid
.. '", "cert": "' .. input.cert .. '"}';
opq_obj = saveToOpaqueObject(
dek.name .. '_meta_info',
Blob.from_bytes(key_info),
'DEK = ' .. dek.kid .. ', cert = ' .. input.cert
)
return { opq_key_identifier = opq_obj.kid, dek = dek.kid};
end
function generateJWEForKey(input, opq_key_identifier)
-- Generate JWE token and return using meta info store in OPQ object --
-- Search for opaque object having BYOK key meta info
if opq_key_identifier == nil then
return 'Key identificer [opq_key_identifier] missing'
else
opq_meta_info = Sobject { kid = opq_key_identifier }
end
if not opq_meta_info then
return 'Invalid key identificer passed ['.. opq_key_identifier ..']'
end
meta_info = json.decode(opq_meta_info.value:bytes())
dek = Sobject { kid = meta_info.dek }
if not dek then
return 'Invalid dek reference stored in metadata'
end
local salesforce_cert = Sobject { kid = meta_info.cert }
if not salesforce_cert then
return nil, 'Invalid cert stored in metadata'
end
--get next avialable name to avoid name conflict
local keyname = getNextKeyVersionLabel(this_plugin().name);
cek = createKey(keyname .. '_cek', true)
local header = '{"alg":"RSA-OAEP","enc":"A256GCM","kid":"'
.. opq_key_identifier .. '"}' --
header = Blob.from_bytes(header):base64_urlsafe()
local wcek = assert(salesforce_cert:wrap {
alg = 'RSA',
subject = cek,
mode = 'OAEP_MGF1_SHA1'
})
local wdek = assert(cek:wrap {
ad = Blob.from_bytes(header), --authentication data
tag_len = 128,
mode = 'GCM',
subject = dek,
alg = 'AES'
})
local jweval = header .. '.' ..
wcek.wrapped_key:base64_urlsafe() .. '.' ..
wdek.iv:base64_urlsafe() .. '.' ..
wdek.wrapped_key:base64_urlsafe() .. '.' ..
wdek.tag:base64_urlsafe()
return {kid= opq_key_identifier, jwe = jweval};
end
-----------------------------
-- end of helper methods ----
-----------------------------
-----------------------------
-- start of main method
-----------------------------
function run(input, url, method)
-- Start Plugin called to generate a new BYOK Key
if method == "POST" then
-- Generate DEK and store in opq_obj along with other meta info for future API calls
return generateKeyAndMetaData(input)
else
-- Generate JWE token and return using meta info store in OPQ object --
return generateJWEForKey(input, url.path[6])
end
-----------------------------
-- End of main method
-----------------------------
end
-- end of plugin

6. Copy and save UUID of plugin created for future configuration.

Step 4: Generate and Download Self Signed Certificate in Salesforce

1. Login to Salesforce. Go to “Setup”.

2. Create a (Self Signed) certificate under Security >> Certificate and Key Management with below setting.

3. Disable Exportable Private key.

4. Check box to “Use Platform Encryption.

SalesforceBYOKSdkms-Step4.4.png

Please refer Salesforce documentation for more info on “Certificate and Key Management”.

5. Once certificate is created, please download it.

SalesforceBYOKSdkms-Step4.5.png

Download certificate and save to your desired location.

Step 5: Import Certificate to Fortanix SDKMS

1. Login to SDKMS

2. Click on left navigation bar to navigate to “Security Objects”

SalesforceBYOKSdkms-Step5.2.png

3. Click on (+) button to create new Security object.

4. Enter name of security object and select Group created in Step 1.

5. Click on “IMPORT” button.

SalesforceBYOKSdkms-Step5.5.png

6. Choose value format as “BASE 64”

7. Choose Security Object type as “Certificate”

8. Click on “Upload a file” button to upload converted certificate at Step 4.5

9. Click IMPORT button to import the certificate into SDKMS as security object.

SalesforceBYOKSdkms-Step5.9.png

Salesforce Setup

Step 6: Define Name Credential in Salesforce

1. Login to Salesforce. Go to “Setup”.

2. Click on “Named Credentials” under Security in left navigation bar.

SalesforceBYOKSdkms-Step6.2.png

Click on button: New Named Credential. It will open screen to create Name Credential.

SalesforceBYOKSdkms-Step6.2.1.png

3. Enter details for named credential

a. Enter Label & Name of your choice b. Enter URL as below (uuid: uuid of plugin created in SDKMS setup Step 3 )https://www.sdkms.fortanix.com/sys/v1/plugins/invoke/uuid

c. Select Identity Type as “Named Principal” and Authentication protocol as “Password Authentication”.

d. Enter username and password of SDKMS created in SDKMS setup Step 2 and click Save.

SalesforceBYOKSdkms-Step6.3.png

Steps to generate encryption keys and import to Salesforce

We can generate as many keys as we want with SDKMS and configure in Salesforce using steps mentioned below. Whenever customer wants to rotate key, simply execute the plugin and generate a new key. The same needs to be configured in Salesforce afterwards.

Step 1: Generate JWE Token (BYOK Cache only KEY) using plugin

1. Go to plugin created in Step 3 of SDKMS Setup.

2. Click on “ADD TEST INPUT” on right hand side.

3. Enter below payload in text box

{
"cert" : <uuid of certificate imported in SDKMS>,
"name": "<unique name of key eg: salesforce_ency_key_v1>"
}

4. Click “RUN TEST”

SalesforceBYOKSdkms_TokenUsingPluginStep1_4.png

5. Plugin generates security objects (AES encryption key and meta information) in SDKMS and return their uuid.

Copy the value of “opq_key_identifier” field in response body.
This would be required while setting up BYOK in Salesforce.

dek: is uuid of AES encryption key generated by plugin and stored securely in SDKMS. Salesforce will use it as data encryption key.

opq_key_identifier: SDKMS plugin also generates a security object of type “OPAQUE”. It contains meta information to generate response (JWE Token) required by Salesforce. Meta information has below information:

a) AES Encryption key UUID (dek)
b) UUID of certificate used.

When Salesforce platform call SDKMS plugin to fetch encryption keys. Plugin reads meta information from opaque object and processes dek key material and certificate used (while generating meta info and AES initially) to generate JWE token. Same is return to salesforce in desired JSON format.

Refer salesforce documentation for more info on JWE token.

6. "dek” value is AES encryption key which generated by plugin and the key is stored in SDKMS. The key material would be securely transferred to Salesforce as part of JWE token.

7. Go to Security Objects screen to see newly created object.

SalesforceBYOKSdkms_TokenUsingPluginStep1_7.png

Step 2: Configure Salesforce to use SDKMS to fetch Cache-only Key at runtime

1. Go to Setup >> Security >> Platform Encryption >> Key Management

SalesforceBYOKSdkms_TokenUsingPluginStep2_1.png>

2. Click on Bring Your Own Key button

SalesforceBYOKSdkms_TokenUsingPluginStep2_2.png

3. Select the desired certificate to be used (it should be same as one used while executing plugin to generate encryption key and meta information).

4. Select “Use a Cache-Only Key” radio button.

5. Select “Named Credential” created with SDKMS endpoint.

6. Enter BYOK Id (opq_key_identifier) generated by SDKMS plugin in previous step.

7. Click save

SalesforceBYOKSdkms_TokenUsingPluginStep2_7.png

8. Once configuration is saved, Salesforce will call SDKMS to fetch JWE token and decrypt it with private key of the certificate.

9. You can see newly imported key on “Key Management” screen.

SalesforceBYOKSdkms_TokenUsingPluginStep2_9.png

Step 3: Verify Key Import in Fortanix SDKMS Event logs

1. Logs are generated in Fortanix SDKMS while fetching encryption keys during setup (after step 2.9).

2. Go to Event logs in SDKMS to verify (refer below screenshot).

3. Logs are also generated later when Salesforce call SDKMS to fetch encryption keys at runtime.

SalesforceBYOKSdkms_TokenUsingPluginStep3_3.png

 

Was this article helpful?
0 out of 0 found this helpful