我已经能够使用 .net4 中的 ECDiffieHellmanCNG 生成私钥,并且我还使用 Bouncy Castle C# 库成功生成了私钥。我想知道为什么 .net 4 版本会生成一个字节数组,而 Bouncy Castle 的 ECDHBasicAgreement 会生成一种 BigInteger(手动实现)。我希望能够互换使用这些库。谢谢!
2 回答
您可能在 BouncyCastle 类层次结构的错误区域中执行您想要执行的操作。(我在同一个地方偶然发现,可能出于同样的原因。)如果您正在寻求实现必须可互操作的 ECDH,那么您肯定是在错误的地方。
为什么它的结构如此不直观?嗯,原因是 BouncyCastle 中的抽象是他们集中注意力并提供价值的地方。BC 不希望人们说“我将使用 ECDH 密钥加密密钥”并希望处理低级加密细节,而是希望您使用管理器级别的抽象,如“公钥”、“私钥”、和“证书”,并在中间填写“种类”和“位强度”等安全参数。
var _keypair = new ECKeyPairGenerator("EC").Init(
new KeyGenerationParameters(_SecureRandomSingleton, 256)).GenerateKeyPair();
// For the love of all that's holy don't do this in production, encrypt your keys!
var pkcs8gen = new Pkcs8Generator(_keypair.Private);
Stream pkcs8stream = new MemoryStream();
using(System.IO.TextWriter pkcs8writer = new StreamWriter(pkcs8stream))
{
var mywriter = new Org.BouncyCastle.OpenSsl.PemWriter(pkcs8writer);
mywriter.WriteObject(pkcs8gen.Generate());
mywriter.Writer.Flush();
}
每次加载时,BouncyCastle 都会很乐意浪费时间和精力来重新计算公钥,除非你小心地将 _keypair.Public 保存在自签名 X509Certificate 之类的东西中。
var _cgen = new X509V3CertificateGenerator();
_cgen.Reset();
_cgen.SetNotBefore(DateTime.Now);
_cgen.SetNotAfter(new DateTime(2999, 12, 31, 23, 59, 59, DateTimeKind.Utc));
var DN = new X509Name("CN=Self Signed Certificate");
_cgen.SetIssuerDN(DN);
_cgen.SetSubjectDN(DN);
_cgen.SetPublicKey(_keypair.Public);
_cgen.SetSignatureAlgorithm( // Can be anything ECDsaWith*
Org.BouncyCastle.Asn1.X9.X9ObjectIdentifiers.ECDsaWithSha256.ToString());
_cgen.SetSerialNumber( // Serial number collisions suck
new Org.BouncyCastle.Math.BigInteger(
8 * 8 - 1, // number of bits to generate
_SecureRandomSingleton)); // source to generate from
var _cert = _cgen.Generate(_keypair.Private);
try
{
_cert.Verify(_keypair.Public);
} catch (Org.BouncyCastle.Security.Certificates.CertificateException E)
{
// error-handling code for Verify failure
// Ridiculous here because we know that _keypair is correct, but good practice
// to ensure that your keypair is correct and intact
}
Stream certStream = new MemoryStream();
TextWriter certWriter = new StreamWriter(certStream);
var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(certWriter);
pemWriter.WriteObject(_cert);
pemWriter.Writer.Flush();
以下是如何从两个结构中加载密钥对。
AsymmetricKeyParameter privateKey;
AsymmetricKeyParameter publicKey;
AsymmetricKeyPair reconstitutedPair;
certStream.Position = 0;
pkcs8Stream.Position = 0;
using (TextReader pkcs8reader = new StreamReader(pkcs8stream))
{
PemReader pemreader = new PemReader(pkcs8reader);
var privateKey = pemreader.ReadObject() as ECPrivateKeyParameters;
if (thisprivate == null)
throw new GeneralSecurityException("failed to read private key");
}
}
var certificate = new Org.BouncyCastle.X509.X509CertificateParser()
.ReadCertificate(certStream);
var publicKey = certificate.GetPublicKey();
reconstitutedPair = new AsymmetricKeyPair(publicKey,privateKey);
现在,说了这么多,这是您实际问题的答案。
.NET 4 提供了一个 byte[] 因为它调用了 OLE 平台原生代码,它为您完成了所有的抽象。为此目的,它是最有效的表示形式,因为它不解析从 CNG 返回的内容,执行最少量的对象装箱返回 CLR 对象空间,并依靠程序员处理本质上是不透明 blob 的内容。
BouncyCastle 使用它的 BigInteger 类,因为它是用 64 位 long 实现 bignum 计算的方式。它是为此目的最有效的表示,因为处理 8 位字节到 8 位字节的开销远远超过处理 64 位长和 64 位长的成本的 8 倍。无论哪种方式,它都需要在输入字节 [] 的不同部分上迭代调用 BitConverter。这些迭代和方法调用加起来,所以 BigInteger 是“数字的内部表示”。
这些甚至都不是可比的用途,因此这可能不是您想要做的。
如果你想从 BigInteger 获取一个 byte[],使用它的 ToByteArray() 方法。如果要将 byte[] 转换为 BigInteger,请使用包含要计算的位字符串的 byte[] 构造一个新的 BigInteger 对象。new BigInteger(oldBigInteger.ToByteArray()) 像您期望的那样工作(一个新的 BigInteger 对象,它与旧对象具有相同的值)。直接使用它们通常是不合适的,因为 EC 公钥由两个数字组成。此外,ToByteArray() 仅转储整数的值,它不包含任何 DER 编码信息以将其标识为任何长度的整数。
(Also, in C#, 'byte' and 'char' are different things with different sizes. 'byte' is 8 bits long. 'char' is a Unicode code point, and those are potentially larger than 8 bits. 'char' (along with 'string', which is conceptually a sequence of chars) requires encoding/decoding before it'll fit into byte-sized pieces.)
Eachy Diffie-Hellman 实现使用唯一的常量集从公钥+私钥中导出共享密钥。因此,这两种实现都不能从完全相同的密钥对中导出完全相同的共享密钥。你最好自己测试一下,或者在 BouncyCastle 邮件列表上询问它。
注意:ECDiffieHellmanCNG 仅适用于 Windows Vista/Windows 7 及更高版本。另一方面,您可以在 .net 1.1 及更高版本以及较旧的 Windows 版本(2000、XP 等)上使用 BouncyCastle