## 密钥包装与密钥封装

在最广义的定义中， <mark>密钥包装(Key Wrapping) </mark> 是指使用一个密钥（称为包装密钥）对另一个重要密钥进行加密的过程。

如第4.1节所述，<mark>密钥封装(Key Encapsulation)</mark> 通常被视为一种特定的密钥协商方式，它使用非对称加密技术：公钥用于加密对称密钥，私钥则用于解密该对称密钥以便在数据交换中使用。因此我们区分了密钥包装和密钥封装这两个概念。（注意：有些作者并不区分密钥包装——按我们的定义——与密钥封装。对这些作者而言，密钥封装只是密钥包装的另一种形式。）

密钥包装和密钥封装是常见的保护手段，既用于在非安全通道上传输密钥，也常用于密钥存储（例如存入数据库）。由于应用广泛，相关标准和最佳实践自然应运而生。美国国家标准与技术研究院（NIST）制定了使用AES等对称算法（参见《NIST特别出版物800-38F》）和 RSA 非对称密钥对进行密钥包装的标准——尽管在RSA场景中，NIST使用的术语将其描述为与密钥传输相关的包装功能。（实际上，提供各类密码学标准的NIST将密钥封装细节归类在密钥传输主题下——参见《NIST特别出版物800-56B修订版2》。）

关于密钥包装的核心安全原则是：绝不要用安全强度不足的包装密钥来保护高安全强度的密钥。虽然使用更高安全强度的包装密钥显然更理想，但这并非总能实现。 BouncyCastle .NET API遵循的标准主要来自上述NIST文档以及 RFC 3394 和 RFC 5649（后者允许采用与 RFC 3394 不同的填充方案进行对称密钥包装）。

理解密钥包装的典型应用场景：假设需要通过某种通道（无论是否安全）传输密钥。包装密钥会存储在硬件安全模块（HSM）中，而密钥的包装和解包操作都在HSM内完成。这体现了_密钥管理_的范例——虽然这超出了本文档的讨论范围。但关键要强调的是：包装密钥的安全强度必须不低于被包装密钥，且包装密钥本身也需要严密保护。

### 5.1 使用对称密钥进行密钥包装...

最常用的对称密钥标准是 AES-KW 系列，即使用 AES 作为分组密码（密钥长度为 128、192 或 256 位），输出以64位为单位的加密块。以下示例基于RFC 3394标准，采用默认初始化向量，展示最简单的无填充场景。

输入参数包括：AES 包装密钥（即示例中的 keyParam ）、待包装数据（或密钥，即示例中的 unwrappedData，以 64 位块为单位）；输出则是加密数据（或包装后的密钥）。解包过程类似，需要提供原始包装密钥和已包装数据。

#### 示例29 – RFC3394 标准密钥包装（ 默认 IV ）

```aardio
import BouncyCastle;

// RFC3394标准密钥包装（默认IV）
wrapKeyRFC3394 = function(keyParam, unwrappedData){
    // 创建AES引擎
    var symmetricBlockCipher = BouncyCastle.Crypto.Engines.AesEngine();
    
    // 创建RFC3394包装引擎
    var wrapEngine = BouncyCastle.Crypto.Engines.Rfc3394WrapEngine(symmetricBlockCipher);
    
    // 初始化包装模式（true表示加密）
    wrapEngine.Init(true, keyParam);
    
    // 执行密钥包装
    return wrapEngine.Wrap(unwrappedData, 0, #unwrappedData);
}

// RFC3394标准密钥解包
unwrapKeyRFC3394 = function(keyParam, wrappedData){
    // 创建AES引擎
    var symmetricBlockCipher = BouncyCastle.Crypto.Engines.AesEngine();
    
    // 创建RFC3394包装引擎
    var wrapEngine = BouncyCastle.Crypto.Engines.Rfc3394WrapEngine(symmetricBlockCipher);
    
    // 初始化解包模式（false表示解密）
    wrapEngine.Init(false, keyParam);
    
    // 执行密钥解包
    return wrapEngine.Unwrap(wrappedData, 0, #wrappedData);
}

// 生成随机密钥
var keyParam = BouncyCastle.Crypto.Parameters.KeyParameter(raw.buffer(32)); // 256位AES密钥

// 生成随机数据
var unwrappedBuffer1 = raw.buffer(string.random(64)); // 64字节随机数据

// 包装密钥
var wrappedData = wrapKeyRFC3394(keyParam, unwrappedBuffer1);

// 解包密钥
var unwrappedBuffer2 = unwrapKeyRFC3394(keyParam, wrappedData);

print(unwrappedBuffer1)
print(unwrappedBuffer2)
```

RFC 3394标准还描述了可自定义IV的变体方案，以下示例展示具体实现方式：

#### 示例30 – RFC3394 密钥包装（ 自定义 IV ）

```aardio
import BouncyCastle;

// RFC3394 密钥包装（带 IV 参数）
wrapKeyRFC3394WithIV = function(keyParam, IV, unwrappedData){
    // 组合密钥与IV参数
    var keyParamWithIV = BouncyCastle.Crypto.Parameters.ParametersWithIV(keyParam, IV);
    
    // 创建AES引擎
    var symmetricBlockCipher = BouncyCastle.Crypto.Engines.AesEngine();
    
    // 创建RFC3394包装引擎
    var wrapEngine = BouncyCastle.Crypto.Engines.Rfc3394WrapEngine(symmetricBlockCipher);
    
    // 初始化包装模式
    wrapEngine.Init(true, keyParamWithIV);
    
    // 执行密钥包装
    return wrapEngine.Wrap(unwrappedData, 0, #unwrappedData);
}

// RFC3394 密钥解包（带 IV 参数）
unwrapKeyRFC3394WithIV = function(keyParam, IV, wrappedData){
    // 组合密钥与 IV 参数
    var keyParamWithIV = BouncyCastle.Crypto.Parameters.ParametersWithIV(keyParam, IV);
    
    // 创建 AES 引擎
    var symmetricBlockCipher = BouncyCastle.Crypto.Engines.AesEngine();
    
    // 创建 RFC3394 包装引擎
    var wrapEngine = BouncyCastle.Crypto.Engines.Rfc3394WrapEngine(symmetricBlockCipher);
    
    // 初始化解包模式
    wrapEngine.Init(false, keyParamWithIV);
    
    // 执行密钥解包
    return wrapEngine.Unwrap(wrappedData, 0, #wrappedData);
}

// 示例数据
var keyParam = BouncyCastle.Crypto.Parameters.KeyParameter(raw.buffer("0123456789ABCDEF")); // 16 字节密钥
var IV = raw.buffer("12345678"); // 8 字节 IV
var unwrappedData = raw.buffer(16,"Hello, World!"); // 16 字节待包装数据

// 包装密钥
var wrappedData = wrapKeyRFC3394WithIV(keyParam, IV, unwrappedData);
print("包装后的数据:", wrappedData);

// 解包密钥
var unwrappedResult = unwrapKeyRFC3394WithIV(keyParam, IV, wrappedData);
print("解包后的数据:", unwrappedResult);
```

> **关键说明：**
> 1. aardio中使用`#`操作符获取buffer对象的字节长度
> 2. 包装密钥(keyParam)应为`KeyParameter`类型对象，可通过`BouncyCastle.Crypto.Parameters.KeyParameter()`创建
> 3. IV 参数需要是 8 字节长度的 buffer 对象（RFC3394 标准要求）
> 4. 被包装数据(unwrappedData) 的长度必须是 8 字节的整数倍