aardio 文档

密钥协商与交换算法

4.1 非对称加密的现状

非对称加密(公钥加密)由密钥对(K_priv, K_pub)组成,其中 K_priv 表示密钥对的私密部分,K_pub 表示公开可用的部分。需要注意的是,使用哪部分密钥(K_priv 或 K_pub)来加密明文消息并不是预先确定的。这只是非对称加密用于大数据块加密时面临的问题之一。

假设 K_enc 是用于加密的密钥(可能是 K_priv 或 K_pub),K_dec 是解密密钥。简单来说,给定明文 M 和加密/解密函数 E/D,我们可以得到密文 C = E(K_enc, M) 和 M = D(K_dec, C)。这里我们注意到另一个问题:虽然 D(K_dec, E(K_enc, M)) = M,但通常我们并不满足 E(K_enc, D(K_dec, M)) = M(RSA 非对称加密满足此性质,但基于椭圆曲线的加密不一定满足)。

其他问题包括:

使用非对称加密大数据时最大的问题是:

  1. 底层数学原理要求明文数据必须映射到密钥大小。例如椭圆曲线本身没有加密方案,需要额外添加双射函数。
  2. 非对称加密比对称加密慢得多。例如 RSA 加密(理论上可用于 ECB 模式)比相同模式下的 AES 慢数千倍。

这些限制可以通过混合加密方案克服。混合加密通常使用非对称密钥对生成和交换对称密钥,然后使用对称密钥在非安全通道上高效加密数据。这类对称密钥通常是短期的(临时密钥)。

以下是后续示例中会用到的术语:

4.2 Diffie-Hellman 密钥协商

Diffie-Hellman(DH)密钥协商算法需要生成域参数集 (p, q, g),其中 p 是足够大的素数,q 是 p-1 的大素数因子,g 是有限域乘法群中阶为 q 的循环子群的生成元。这个集合是公开信息。

生成 DH 域参数非常耗时(取决于硬件和 p 或 q 的大小,可能需要几分钟到几小时)。密码学安全对 p 值有各种限制,这类密码学素数称为强素数。其中安全素数(形式为 p=2q+1,q 也是素数)是强素数的子集,这类素数称为 Sophie Germain 素数。RFC7919 和 RFC3526 提供了预定义的安全素数列表。

示例19 - 生成 DH 有限域参数(aardio 实现)

import BouncyCastle;
import console;

// 使用预定义的 DH 参数组(RFC7919 3072位)
var dhParams = BouncyCastle.Crypto.Agreement.DHStandardGroups.rfc7919_ffdhe3072;

console.dump(dhParams);
console.pause();

示例20 - Diffie-Hellman 密钥对生成(aardio 实现)

import BouncyCastle;
import console;

// 使用预定义的 DH 参数
var dhParams = BouncyCastle.Crypto.Agreement.DHStandardGroups.rfc7919_ffdhe3072;

// 创建密钥生成参数
var dhKeyGenParams = BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters(
    BouncyCastle.Security.SecureRandom(),
    dhParams
);

// 创建密钥对生成器
var dhKeyPairGen = BouncyCastle.Crypto.Generators.DHKeyPairGenerator();
dhKeyPairGen.Init(dhKeyGenParams);

// 生成密钥对
var dhKeyPair = dhKeyPairGen.GenerateKeyPair();

console.dump(dhKeyPair);
console.pause();

最简单的 DH 密钥交换是双方交换公钥。A 方生成密钥对 (a, g^a mod p),B 方生成 (b, g^b mod p)。基本密钥交换就是:

A 方计算:(g^b)^a mod p = g^(ab) mod p
B 方计算:(g^a)^b mod p = g^(ab) mod p

示例21 - 基本 DH 密钥协商(aardio 实现)

import BouncyCastle;
import console;

// 使用预定义的 DH 参数
var dhParams = BouncyCastle.Crypto.Agreement.DHStandardGroups.rfc7919_ffdhe3072;

// 生成双方密钥对
var generator = BouncyCastle.Crypto.Generators.DHKeyPairGenerator();
var params = BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters(
    BouncyCastle.Security.SecureRandom(),
    dhParams
);
generator.Init(params);

var keyPairA = generator.GenerateKeyPair();
var keyPairB = generator.GenerateKeyPair();

// 进行密钥协商
var agreementA = BouncyCastle.Crypto.Agreement.DHBasicAgreement();
agreementA.Init(keyPairA.Private);
var secretA = agreementA.CalculateAgreement(keyPairB.Public);

var agreementB = BouncyCastle.Crypto.Agreement.DHBasicAgreement();
agreementB.Init(keyPairB.Private);
var secretB = agreementB.CalculateAgreement(keyPairA.Public);

console.assert(secretA.Equals(secretB), "协商的密钥应该相同");
console.log("协商的共享密钥:", secretA.ToString(16));
console.pause();

更安全的 DH 密钥协商方法是 Matsumoto-Takashima-Imai MTI/A0 密钥协商方案( BouncyCastle .NET API 仅提供 MTI/A0 变体)。这种方案本质上是基本密钥协商的两阶段版本,主要安全性在于第一次公钥交换应该以某种方式进行认证。

示例22 - MTI/A0 DH 密钥协商(aardio 实现)

import BouncyCastle;
import console;

// 使用预定义的 DH 参数
var dhParams = BouncyCastle.Crypto.Agreement.DHStandardGroups.rfc7919_ffdhe3072;

// 生成静态密钥对
var generator = BouncyCastle.Crypto.Generators.DHKeyPairGenerator();
var params = BouncyCastle.Crypto.Parameters.DHKeyGenerationParameters(
    BouncyCastle.Security.SecureRandom(),
    dhParams
);
generator.Init(params);

var staticKeyPairA = generator.GenerateKeyPair();
var staticKeyPairB = generator.GenerateKeyPair();

// A 方生成临时密钥对发送给 B 方
var agreementA = BouncyCastle.Crypto.Agreement.DHAgreement();
agreementA.Init(staticKeyPairA.Private);
var ephemeralPublicKeyA = agreementA.CalculateMessage();

// B 方生成临时密钥对发送给 A 方
var agreementB = BouncyCastle.Crypto.Agreement.DHAgreement();
agreementB.Init(staticKeyPairB.Private);
var ephemeralPublicKeyB = agreementB.CalculateMessage();

// 计算共享密钥
var secretA = agreementA.CalculateAgreement(staticKeyPairB.Public, ephemeralPublicKeyB);
var secretB = agreementB.CalculateAgreement(staticKeyPairA.Public, ephemeralPublicKeyA);

console.assert(secretA.Equals(secretB), "协商的密钥应该相同");
console.log("A 方计算的共享密钥:", secretA.ToString(16));
console.log("B 方计算的共享密钥:", secretB.ToString(16));
console.pause();

4.3 椭圆曲线 Diffie-Hellman 密钥协商

Diffie-Hellman 协议也可用于椭圆曲线。定义在有限域上的椭圆曲线点形成阿贝尔群。如果这个群的阶是素数,那么它是循环群,所有满足曲线方程的点都在群中(此时协因子 h=1)。

椭圆曲线密码学主要关注两种类型:

  1. 素数域上的椭圆曲线(F_p 或 GF(p),p 是素数)
  2. 二进制有限域上的椭圆曲线(F_{2^m})

示例23 - 直接生成 EC GF(p) 参数(aardio 实现)

import BouncyCastle;
import console;

// 获取预定义的椭圆曲线参数
var ecParams = BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("brainpoolp160r1");

// 创建域参数
var domainParams = BouncyCastle.Crypto.Parameters.ECDomainParameters(
    ecParams.Curve,
    ecParams.G,
    ecParams.N,
    ecParams.H,
    ecParams.GetSeed()
);

console.dump(domainParams);
console.pause();

示例24 - 使用内置 EC GF(p) 参数(aardio 实现)

import BouncyCastle;
import console;

// 更简单的方式获取内置曲线参数
var ecParams = BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("brainpoolp160r1");
var domainParams = BouncyCastle.Crypto.Parameters.ECDomainParameters(
    ecParams.Curve,
    ecParams.G,
    ecParams.N,
    ecParams.H,
    ecParams.GetSeed()
);

console.dump(domainParams);
console.pause();

示例25 - 直接生成二进制 EC 参数(aardio 实现)

import BouncyCastle;
import console;

// 获取预定义的二进制椭圆曲线参数
var ecParams = BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("K-283");

// 创建域参数
var domainParams = BouncyCastle.Crypto.Parameters.ECDomainParameters(
    ecParams.Curve,
    ecParams.G,
    ecParams.N,
    ecParams.H,
    ecParams.GetSeed()
);

console.dump(domainParams);
console.pause();

示例26 - 使用内置二进制 EC 参数(aardio 实现)

import BouncyCastle;
import console;

// 更简单的方式获取内置二进制曲线参数
var ecParams = BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("K-283");
var domainParams = BouncyCastle.Crypto.Parameters.ECDomainParameters(
    ecParams.Curve,
    ecParams.G,
    ecParams.N,
    ecParams.H,
    ecParams.GetSeed()
);

console.dump(domainParams);
console.pause();

示例27 - EC Diffie-Hellman 密钥对生成(aardio 实现)

import BouncyCastle;
import console;

// 获取椭圆曲线参数
var ecParams = BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("secp256r1");
var domainParams = BouncyCastle.Crypto.Parameters.ECDomainParameters(
    ecParams.Curve,
    ecParams.G,
    ecParams.N,
    ecParams.H,
    ecParams.GetSeed()
);

// 创建密钥生成参数
var ecKeyGenParams = BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters(
    domainParams,
    BouncyCastle.Security.SecureRandom()
);

// 生成密钥对
var ecKeyPairGen = BouncyCastle.Crypto.Generators.ECKeyPairGenerator();
ecKeyPairGen.Init(ecKeyGenParams);
var ecKeyPair = ecKeyPairGen.GenerateKeyPair();

console.dump(ecKeyPair);
console.pause();

示例28 - EC Diffie-Hellman 密钥协商(aardio 实现)

import BouncyCastle;
import console;

// 获取椭圆曲线参数
var ecParams = BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("secp256r1");
var domainParams = BouncyCastle.Crypto.Parameters.ECDomainParameters(
    ecParams.Curve,
    ecParams.G,
    ecParams.N,
    ecParams.H,
    ecParams.GetSeed()
);

// 生成双方密钥对
var generator = BouncyCastle.Crypto.Generators.ECKeyPairGenerator();
var params = BouncyCastle.Crypto.Parameters.ECKeyGenerationParameters(
    domainParams,
    BouncyCastle.Security.SecureRandom()
);
generator.Init(params);

var keyPairA = generator.GenerateKeyPair();
var keyPairB = generator.GenerateKeyPair();

// 进行密钥协商
var agreementA = BouncyCastle.Crypto.Agreement.ECDHBasicAgreement();
agreementA.Init(keyPairA.Private);
var secretA = agreementA.CalculateAgreement(keyPairB.Public);

var agreementB = BouncyCastle.Crypto.Agreement.ECDHBasicAgreement();
agreementB.Init(keyPairB.Private);
var secretB = agreementB.CalculateAgreement(keyPairA.Public);

console.assert(secretA.Equals(secretB), "协商的密钥应该相同");
console.log("协商的共享密钥:", secretA.ToString(16));
console.pause();

注意:在示例27和28中,我们使用了 sect283k1 椭圆曲线(协因子 h=4),因此需要使用 ECDHCBasicAgreement 对象来考虑协因子。如果协因子 h=1,则应使用 ECDHBasicAgreement 对象。

Markdown 格式