非对称加密(公钥加密)由密钥对(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 非对称加密满足此性质,但基于椭圆曲线的加密不一定满足)。
其他问题包括:
使用非对称加密大数据时最大的问题是:
这些限制可以通过混合加密方案克服。混合加密通常使用非对称密钥对生成和交换对称密钥,然后使用对称密钥在非安全通道上高效加密数据。这类对称密钥通常是短期的(临时密钥)。
以下是后续示例中会用到的术语:
Diffie-Hellman(DH)密钥协商算法需要生成域参数集 (p, q, g),其中 p 是足够大的素数,q 是 p-1 的大素数因子,g 是有限域乘法群中阶为 q 的循环子群的生成元。这个集合是公开信息。
生成 DH 域参数非常耗时(取决于硬件和 p 或 q 的大小,可能需要几分钟到几小时)。密码学安全对 p 值有各种限制,这类密码学素数称为强素数。其中安全素数(形式为 p=2q+1,q 也是素数)是强素数的子集,这类素数称为 Sophie Germain 素数。RFC7919 和 RFC3526 提供了预定义的安全素数列表。
import BouncyCastle;
import console;
// 使用预定义的 DH 参数组(RFC7919 3072位)
var dhParams = BouncyCastle.Crypto.Agreement.DHStandardGroups.rfc7919_ffdhe3072;
console.dump(dhParams);
console.pause();
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
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 变体)。这种方案本质上是基本密钥协商的两阶段版本,主要安全性在于第一次公钥交换应该以某种方式进行认证。
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();
Diffie-Hellman 协议也可用于椭圆曲线。定义在有限域上的椭圆曲线点形成阿贝尔群。如果这个群的阶是素数,那么它是循环群,所有满足曲线方程的点都在群中(此时协因子 h=1)。
椭圆曲线密码学主要关注两种类型:
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();
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();
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();
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();
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();
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 对象。