Encrypting The Encryption Key … It’s a Wrap!


Encrypting The Encryption Key … It’s a Wrap!

The protection of encryption keys is important, and where they often have to be protected. This is especially important for a symmetric key or for a private key of a public key pair. For this, we can use key wrapping and make sure the key cannot be used, unless we have a secret master key. One standard for this is RFC 5649 [here] and which defines the Advanced Encryption Standard (AES) Key Wrap algorithm (AES-KW1, AES-KW2).

With AES-KW, we use an AES key-encryption key (KEK) with a length of 128, 192, or 256 bits, and where we will get 64-bit blocks as an output. We can then encrypt data with a key (K1) and which will then be wrapped to give WRAP(K1). To decrypt, we then need the KEK to recover K1. The unwrapping process also checks the integrity of the key.

The protection of the keys by the KEK means that the wrapped keys could then be stored within a Cloud-based system (the red key in Figure 1), but where the KEK will then be protected from access. When the symmetric keys are required to be unwrapped, the KEK can be revealed within a trusted environment, and then produce the actual encryption key. Thus the actual encryption keys are never stored anywhere in the core form.

Figure 1: Key wrapping and unwrapping with KEK (key-encryption key)

Within the Cloud, AWS CloudHSM (hardware security module) supports AES key wrapping with the default initialization vector — 0xA6A6A6A6A6A6A6A6- or a user-defined value. This provides a FIPS 140–2 Level 3 environment and where the keys are handled within a trusted cloud instance. The wrapped keys can then exist outside this but only converted into their actual form within the CloudHSM. A key generated within the CloudHSM can then be wrapped for export from the environment, or imported from an external wrapped key. The AWS CLI is on the form which defines a key handle (with -k) and the wrapping key handle (with -w):

> wrapKey -k 7 -w 14 -out mykey.key -m 5

Key Wrapped.

Wrapped Key written to file "mykey.key: length 612

Cfm2WrapKey returned: 0x00 : HSM Return: SUCCESS

The modes for the -m option are AES_KEY_WRAP_PAD_PKCS5 (4) NIST_AES_WRAP_NO_PAD (5) NIST_AES_WRAP_PAD ( 6) RSA_AES (7) RSA_OAEP (8) NIST_TDEA_WRAP (9), AES_GCM (10) and CLOUDHSM_AES_GCM (11). A -iv option supports the additional of a specific initialisation vector.

The following is the Python code for key wrapping [here]:

import binascii
import struct
from Crypto.Cipher import AES
import sys
QUAD = struct.Struct('>Q')
# code from https://github.com/kurtbrose/aes_keywrap/blob/master/aes_keywrap.py
def aes_unwrap_key(kek, wrapped):
n = len(wrapped)//8 - 1
#NOTE: R[0] is never accessed, left in for consistency with RFC indices
R = [None]+[wrapped[i*8:i*8+8] for i in range(1, n+1)]
A = QUAD.unpack(wrapped[:8])[0]
decrypt = AES.new(kek, AES.MODE_ECB).decrypt
for j in range(5,-1,-1): #counting down
for i in range(n, 0, -1): #(n, n-1, ..., 1)
ciphertext = QUAD.pack(A^(n*j+i)) + R[i]
B = decrypt(ciphertext)
A = QUAD.unpack(B[:8])[0]
R[i] = B[8:]
return b"".join(R[1:]), A

# code from https://github.com/kurtbrose/aes_keywrap/blob/master/aes_keywrap.py
def aes_wrap_key(kek, plaintext, iv=0xa6a6a6a6a6a6a6a6):
n = len(plaintext)//8
R = [None]+[plaintext[i*8:i*8+8] for i in range(0, n)]
A = iv
encrypt = AES.new(kek, AES.MODE_ECB).encrypt
for j in range(6):
for i in range(1, n+1):
B = encrypt(QUAD.pack(A) + R[i])
A = QUAD.unpack(B[:8])[0] ^ (n*j + i)
R[i] = B[8:]
return QUAD.pack(A) + b"".join(R[1:])
kek="000102030405060708090A0B0C0D0E0F"
key="00112233445566778899AABBCCDDEEFF"
if (len(sys.argv)>1):
kek=str(sys.argv[1])
if (len(sys.argv)>2):
key=str(sys.argv[2])
KEK = binascii.unhexlify(kek)
KEY = binascii.unhexlify(key)
wrapped=aes_wrap_key(KEK,KEY)
rtn,iv=aes_unwrap_key(KEK,wrapped)
print ("KEK: ",kek)
print ("Key: ",key)
print ("Wrapped Key: ",binascii.hexlify(wrapped))
print ("Unwrapped key: ",binascii.hexlify(rtn))
# Test from RFC5649
# KEK: 000102030405060708090A0B0C0D0E0F
# Key: 00112233445566778899AABBCCDDEEFF
# Wrap: 1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5

A sample run is [here]:

KEK:  000102030405060708090A0B0C0D0E0F
Key: 00112233445566778899AABBCCDDEEFF
Wrapped Key: b'1fa68b0a8112b447aef34bd8fb5a7b829d3e862371d2cfe5'
Unwrapped key: b'00112233445566778899aabbccddeeff'