Introduction
This article presents a C# implementation of the RFC3394 key-wrapping algorithm. The attached assembly provides two simple operations: one to wrap key data, and another to unwrap the key data. This code also includes a fairly comprehensive unit test library which, among other things, ensures that the implementation is verified against the test vectors provided in RFC3394.
Background
Key wrapping is the process of encapsulating one or more encryption keys using a cryptographic algorithm in concert with a key-encryption key. Additional general information on key wrapping is available at Wikipedia.
RFC3394 specifies a symmetric key encapsulation algorithm also known as the AES Key Wrap Specification. As stated in the RFC, it was designed with the following goal in mind:
Design a cryptographic algorithm called a Key Wrap that uses the Advanced Encryption Standard (AES) as a primitive to securely encrypt plaintext key(s) with any associated integrity information and data, such that the combination could be longer than the width of the AES block size (128-bits). Each ciphertext bit should be a highly non-linear function of each plaintext bit, and (when unwrapping) each plaintext bit should be a highly non-linear function of each ciphertext bit. It is sufficient to approximate an ideal pseudorandom permutation to the degree that exploitation of undesirable phenomena is as unlikely as guessing the AES engine key.
This algorithm involves a small number of iterations, over which an AES operation, an XOR operation, and a bit of rotation alter the input. The following picture from Wikipedia illustrates the process nicely:
Note that this standard specifically requires AES as the encapsulation mechanism and, therefore, a valid AES key as the key-encryption key.
Documentation
The attached assembly contains only one public class, KeyWrapAlgorithm
. This class has the following members:
Static methods
public static byte[] WrapKey(byte[] kek, byte[] plaintext)
public static byte[] UnwrapKey(byte[] kek, byte[] ciphertext)
These methods are the primary interface to the class. WrapKey
performs, in a single call, the full key-wrapping operation, while UnwrapKey
, as one might expect, performs the full key-unwrapping operation.
Argument information:
kek
- the key-encryption key, a valid AES key of either 128, 192, or 256 bits.plaintext
, ciphertext
- the key data to encapsulate or unencapsulate. As specified in the RFC, this must be made up of n 64-bit blocks, where n is at least 2.
Returns:
Encapsulated or unencapsulated key data depending on whether we are calling WrapKey
or UnwrapKey
, respectively.
Constructor
public KeyWrapAlgorithm(byte[] kek)
Constructing a KeyWrapAlgorithm
class is not usually necessary, but the object will store a key-encryption key in case we are, for example, performing many key wrap operations with the same key.
Argument information:
kek
- the key-encryption key, a valid AES key of either 128, 192, or 256 bits.
Non-static methods
public byte[] WrapKey(byte[] plaintext)
public byte[] UnwrapKey(byte[] ciphertext)
These methods are alternatives to the static methods, above. The calls perform key-wrapping operations using the key specified during construction.
Argument information:
plaintext
, ciphertext
- the key data to encapsulate or unencapsulate. As specified in the RFC, this must be made up of n 64-bit blocks, where n is at least 2.
Returns:
Encapsulated or unencapsulated key data depending on whether we are calling WrapKey
or UnwrapKey
, respectively.
Examples
Using the class is straightforward, as most users will only ever need two lines of code. In the unit tests, I created quick methods to convert from the hex strings present in the RFC test vectors to byte[]
for easy consumption by the RFC3394 library.
private void StaticWrap(string kek, string pt, string ct, string test)
{
byte[] key = SoapHexBinary.Parse(kek).Value;
byte[] input = SoapHexBinary.Parse(pt).Value;
byte[] output = KeyWrapAlgorithm.WrapKey(key, input);
Assert.AreEqual(ct, new SoapHexBinary(output).ToString(), test);
}
[Test]
public void Wrap_128key_128kek_Static()
{
string kek = "000102030405060708090A0B0C0D0E0F";
string pt = "00112233445566778899AABBCCDDEEFF";
string ct = "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5";
StaticWrap(kek, pt, ct, "Wrap_128key_128kek_Static");
}
Using UnwrapKey
works, of course, very similarly.
(Note the use of SoapHexBinary
to convert hex strings to byte[]
s. You can find this handy class in the unfortunately named System.Runtime.Remoting.Metadata.W3cXsd2001
namespace.)
History
- 2008, October 31: Initial release.