3

我已经能够使用 .net4 中的 ECDiffieHellmanCNG 生成私钥,并且我还使用 Bouncy Castle C# 库成功生成了私钥。我想知道为什么 .net 4 版本会生成一个字节数组,而 Bouncy Castle 的 ECDHBasicAgreement 会生成一种 BigInteger(手动实现)。我希望能够互换使用这些库。谢谢!

4

2 回答 2

3

您可能在 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.)

于 2012-02-02T07:12:15.457 回答
2

Eachy Diffie-Hellman 实现使用唯一的常量集从公钥+私钥中导出共享密钥。因此,这两种实现都不能从完全相同的密钥对中导出完全相同的共享密钥。你最好自己测试一下,或者在 BouncyCastle 邮件列表上询问它。

注意:ECDiffieHellmanCNG 仅适用于 Windows Vista/Windows 7 及更高版本。另一方面,您可以在 .net 1.1 及更高版本以及较旧的 Windows 版本(2000、XP 等)上使用 BouncyCastle

于 2011-04-15T21:51:15.127 回答