C# 中的 RFC3394 密钥封装算法
RFC3394 AES 密钥封装算法的实现。
引言
本文介绍了 C# 实现 RFC3394 密钥封装算法。随附的程序集提供了两个简单的操作:一个用于封装密钥数据,另一个用于解封装密钥数据。此代码还包含一个相当全面的单元测试库,该库确保了实现已针对 RFC3394 中提供的测试向量进行了验证。
背景
密钥封装是使用密码算法与密钥加密密钥一起封装一个或多个加密密钥的过程。有关密钥封装的更多一般信息,请访问 维基百科。
RFC3394 规定了一种对称密钥封装算法,也称为 AES 密钥封装规范。正如 RFC 所述,它的设计目标如下:
设计一种名为密钥封装的加密算法,该算法使用高级加密标准 (AES) 作为原语,以安全地加密明文密钥及其任何关联的完整性信息和数据,从而使组合可以比 AES 块大小(128 位)更长。每个密文位都应该是每个明文位的高度非线性函数,并且(在解封装时)每个明文位都应该是每个密文位的高度非线性函数。这足以近似理想的伪随机置换,其程度是利用不良现象的可能性与猜测 AES 引擎密钥的可能性一样小。
此算法涉及少量迭代,在这些迭代中,AES 运算、XOR 运算和少量旋转会改变输入。以下来自 维基百科的图片很好地说明了这个过程
请注意,此标准特别要求将 AES 用作封装机制,因此,需要一个有效的 AES 密钥作为密钥加密密钥。
文档
随附的程序集仅包含一个公共类 KeyWrapAlgorithm
。此类具有以下成员
静态方法
public static byte[] WrapKey(byte[] kek, byte[] plaintext)
public static byte[] UnwrapKey(byte[] kek, byte[] ciphertext)
这些方法是该类的主要接口。WrapKey
在单个调用中执行完整的密钥封装操作,而 UnwrapKey
,正如人们所期望的那样,执行完整的密钥解封装操作。
参数信息
kek
- 密钥加密密钥,一个有效的 AES 密钥,可以是 128 位、192 位或 256 位。plaintext
,ciphertext
- 要封装或解封装的密钥数据。正如 RFC 中指定的那样,这必须由 *n* 个 64 位块组成,其中 *n* 至少为 2。
Returns
封装或解封装的密钥数据,具体取决于我们分别调用 WrapKey
还是 UnwrapKey
。
构造函数
public KeyWrapAlgorithm(byte[] kek)
通常不需要构造 KeyWrapAlgorithm
类,但如果,例如,我们使用相同的密钥执行许多密钥封装操作,则该对象将存储密钥加密密钥。
参数信息
kek
- 密钥加密密钥,一个有效的 AES 密钥,可以是 128 位、192 位或 256 位。
非静态方法
public byte[] WrapKey(byte[] plaintext)
public byte[] UnwrapKey(byte[] ciphertext)
这些方法是上面静态方法的替代方法。这些调用使用在构造期间指定的密钥执行密钥封装操作。
参数信息
plaintext
,ciphertext
- 要封装或解封装的密钥数据。正如 RFC 中指定的那样,这必须由 *n* 个 64 位块组成,其中 *n* 至少为 2。
Returns
封装或解封装的密钥数据,具体取决于我们分别调用 WrapKey
还是 UnwrapKey
。
示例
使用该类非常简单,因为大多数用户只需要两行代码。在单元测试中,我创建了快速方法,用于将 RFC 测试向量中存在的十六进制字符串转换为 byte[]
,以便 RFC3394 库轻松使用。
private void StaticWrap(string kek, string pt, string ct, string test)
{
// Convert hex strings to byte[]s.
byte[] key = SoapHexBinary.Parse(kek).Value;
byte[] input = SoapHexBinary.Parse(pt).Value;
// Static call to the RFC3394 library is here.
byte[] output = KeyWrapAlgorithm.WrapKey(key, input);
// Verify the test passes.
Assert.AreEqual(ct, new SoapHexBinary(output).ToString(), test);
}
[Test]
public void Wrap_128key_128kek_Static()
{
// Test vectors from RFC3394.
string kek = "000102030405060708090A0B0C0D0E0F";
string pt = "00112233445566778899AABBCCDDEEFF";
string ct = "1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5";
// Call the above method to perform the key wrap.
StaticWrap(kek, pt, ct, "Wrap_128key_128kek_Static");
}
当然,使用 UnwrapKey
的工作方式非常相似。
(请注意使用 SoapHexBinary
将十六进制字符串转换为 byte[]
。您可以在不幸命名的 System.Runtime.Remoting.Metadata.W3cXsd2001
命名空间中找到这个方便的类。)
历史
- 2008 年 10 月 31 日:初始发布。