# BouncyCastle 扩展库 - 快速入门 

参考链接： 🅰 [aardio 文档 - .NET 调用指南](https://www.aardio.com/zh-cn/docs/library-guide/std/dotNet/_.html) 📄 [BouncyCastle 文档增强检索](https://www.aardio.com/zh-cn/docs/library-guide/ext/BouncyCastle/search/)

> [本文档](https://www.aardio.com/zh-cn/docs/library-guide/ext/BouncyCastle/) 根据 [BouncyCastle 用户手册](https://downloads.bouncycastle.org/csharp/docs/BC-CSharpDotNet-UserGuide.pdf) 由 AI 自动翻译为 aardio 版 本，适用 aardio `v40.7` 以上版本，以及 aardio 扩展库 BouncyCastle `v2.6.1` 以上版本。  

## Bouncy Castle 介绍

 开源组件 [Bouncy Castle](https://www.bouncycastle.org/documentation/documentation-c) 是基于 .NET 的加密算法与协议实现库，不仅提供常用基础加密算法，还支持 CMS、OpenPGP、(D)TLS、TSP、X.509 证书生成等功能。此外，它还实现了美国国家标准与技术研究院（NIST）的后量子密码学标准化算法，如ML - DSA、ML - KEM、SLH - DSA、Falcon、经典McEliece、FrodoKEM、NTRU、NTRU Prime、Picnic、Saber 和 BIKE。

 Bouncy Castle 仅需要系统自带的 NET  .NET 4.6.1（ Win10 1709 就已经自带，这基本是目前主流 .NET 程序的最低要求，即使更老的操作系统也都已普及安装了 ）。

## 在 aardio 中使用 BouncyCastle 扩展库

aardio 提供了 BouncyCastle 扩展库，实际上这个扩展库的源代码只有几句代码，主要是添加了内存加载 BouncyCastle.Cryptography.dll 的代码，以实现生成无依赖的独立 EXE 文件。其他 BouncyCastle 的 .NET 接口在 aardio 里可以直接使用，不需要再额外封装。

### 1. 安装扩阿展库

- 在 aardio 中打开 `工具 » 扩展库` 搜索并勾选 `BouncyCastle` 扩展库，  
- 点击 `安装 / 更新` 按钮。

> ✅ 在 aardio 中直接运行包含  `import BouncyCastle` 语句的代码也会自动安装扩展库。

### 2. 导入 BouncyCastle 名字空间

`import BouncyCastle`  语句会导入 .NET 的 `BouncyCastle` 名字空间。

要特别注意 aardio 的 `import` 语句与  C# 的 `using` 语句存在区别：

- aardio 的 `import` 只会引入名字空间，但并不会污染现有名字空间。
- 而 C# 的 using 语句则会将引入名字空间的成员放到当前作用域，从而可以缩短访问名称，但也会使来源混淆不清并带来名字污染问题，导致同名冲突。

aardio 中访问 .NET 对象要写完整名字空间，示例：

```aardio
import BouncyCastle; 
var sha3 = BouncyCastle.Crypto.Digests.Sha3Digest(256);
```

aardio 不能像 C# 那样用 Sha3Digest 代替 BouncyCastle.Crypto.Digests.Sha3Digest， 必须指定完整的名字空间。

但是在 aardio 里名字空间或者类都可以通过赋值语句指定更短的名称，例如：

```aardio
//用局部变量指向名字空间
var Sha3Digest = BouncyCastle.Crypto.Digests.Sha3Digest;

//使用更短的名字直接访问对象
var sha3 = Sha3Digest(256);
```

> 注意在 aardio 中构造类的实例不需要使用 new 关键字。

## 随机数简介

在密码学领域，随机数至关重要。随机数有许多应用场景，例如为对称密码生成密钥、帮助为不对称算法生成大数（通常是素数），以及在加密算法中引入 nonce¹ 以防止（或减少）所谓的重放²攻击。

> ¹ **nonce（ Number once ）** 是指定范围内的随机整数或伪随机整数。  
> ² **重放攻击（ Replay Attacks）** 简单来说，就是当攻击者截获加密消息/数据并在稍后重新发送该消息/数据时发生的攻击。

当提到随机数时，至少有以下三种含义：

1. **TRNG**（真随机数生成器）- 这些系统生成的数字（或比特串）来自物理随机源，如热/电压耗散噪声、放射性衰变噪声或大气噪声（参见 <http://www.random.org>）等许多随机物理过程。如果您的硬件支持 TRNG，则可以从 aardio 代码中访问它。

2. **PRNG**（伪随机数生成器，也称为 DRBG，确定性随机比特生成器）- 这些是数学算法，通常使用称为种子值的整数初始化函数来生成数字。这些数字本质上不是真正的随机数，因为可以生成的此类值的数量是有限的 - 最终数字序列将重复。（与此形成对比的是像大气噪声 TRNG 这样的东西，它可以近似为连续的值集。因此，在实际应用中，使用 TRNG 我们可以生成无限数量的不同值。）

3. **CSPRNG**（加密安全伪随机数生成器）- 这些同样使用数学算法生成随机比特串。PRNG 和 CSPRNG 之间的区别在于后者必须能够抵抗加密攻击。

在 Bouncy Castle .NET 加密程序集中，可以使用上述所有三种方式。.NET 直接支持线性同余算法的修改版本，可用作 PRNG。我们在下面的示例中使用它，因为我们希望在测试期间获得一致的密钥和密文。

aardio 提供了基于 PRNG 的 `math.random()`, 而生成随机字符串的 `string.random()` 函数同样基于  `math.random()` 生成的随机数。需要注意的是所有 aardio 线程启动时都会单独生成一个加密安全的随机数作为 `math.random() `, `string.random()` 函数的随机数种子，以避免多线程并发时默认可能生成相同的随机数序列的问题。也可以在运行时手动调用 `math.randomize( seedNumber )`  重新设置  `math.random()` 的随机数种子。aardio 标准库的 crypt.random 则提供了基于系统熵源（System Entropy Source）的加密钱数随机数生成器（CSPRNG）。请参考：[aardio 随机数与随机字符串](https://www.aardio.com/zh-cn/docs/library-guide/builtin/string/rand.html)

#### 示例 - aardio 随机数

PRNG：

```aardio 

//设置固定的随机数种子
math.randomize(151) //除了测试目的，最好不要指定固定的随机数种子

//生成固定系列的伪随机数
for(i=1;10;1){
	print( math.random(1,0xFFFFFFFF) );
}
```

CSPRNG：

```aardio 
import crypt.random;

//创建随机数生成器
var rng = crypt.random();

//生成 5 个字节的随机 buffer
var buf = rng.buffer(5);

//生成 1 到 9 范围的随机整数
var n = rng.integer(1,9);

//生成 0 到 1 之间的随机小数
var num = rng.number();

```

#### 示例 -  C# 默认 PRNG

```aardio
import BouncyCastle;
import console;

// 设置固定随机种子
var fixedRandomSeed = 151;
var fixedRandom = System.Random(fixedRandomSeed);

// 生成 32 字节随机数
var bytes = dotNet.buffer(32);
fixedRandom.NextBytes(bytes);

var number = raw.convert(bytes.Value,{INT num}).num;

console.log("固定种子随机数:", number);
console.pause();
```

上面的代码将为您提供一组一致的"随机"数字，用于测试。执行上述代码两次将生成相同的字节数组。

请注意，.NET 还在 System.Security.Cryptography.RandomNumberGenerator 类中提供了加密安全的 PRNG。BC API 也提供了 PRNG 和 CSPRNG，如下例所示。

#### 示例  - BC 默认 SecureRandom

```aardio
import BouncyCastle;
import console;

// 创建默认安全随机数生成器
var defaultSecureRandom = BouncyCastle.Security.SecureRandom();

// 生成32字节随机数
var bytes = dotNet.buffer(32);
defaultSecureRandom.NextBytes(bytes);

//安全随机数
console.hex(bytes.Value);
console.pause();
```

默认的 SecureRandom 基于 SHA256 摘要算法。种子值通过 .NET 内部类 System.Security.Cryptography.RandomNumberGenerator  提供。执行上述代码两次显示生成的字节集是不同的。如上例生成的 SecureRandom 在加密上是安全的，在大多数加密情况下已经足够。然而，BC API 还提供了几种 NIST SP.800-90A 推荐的方法来生成加密安全的随机数。

NIST 文档推荐基于以下内容的 DRBG：
- 哈希函数
- **HMAC**（称为密钥哈希消息认证码或基于哈希的消息认证码）
- 分组密码

下面的示例展示了基于 HMAC 的 SecureRandom 对象的生成。

#### 示例 - BC HMAC SecureRandom

```aardio
import BouncyCastle;
import console;

// 创建SHA256摘要
var digest = BouncyCastle.Crypto.Digests.Sha256Digest();

// 创建HMAC随机数生成器构建器
var hMacSecureRandomBuilder = BouncyCastle.Crypto.Prng.SP800SecureRandomBuilder(
    BouncyCastle.Security.SecureRandom(),
    false
);

// 设置个性化字符串
hMacSecureRandomBuilder.SetPersonalizationString(raw.buffer("aardio-app"));

// 创建HMAC摘要
var hmac = BouncyCastle.Crypto.Macs.HMac(digest);

// 构建随机数生成器
var hmacSecureRandom = hMacSecureRandomBuilder.BuildHMac(
    hmac, 
    null, // 不使用nonce
    false
);

// 生成 32 字节随机数
var bytes = dotNet.buffer(32);
hmacSecureRandom.NextBytes(bytes);

console.log("HMAC 安全随机数:", string.hex(bytes.Value));
console.pause();
```