我已经能够使用.net4中的ECDiffieHellmanCNG生成私钥,并且我还使用了Bouncy Castle C#库来成功生成私钥。我想知道为什么.net 4版本生成字符数组的字符,而Bouncy Castle的ECDHBasicAgreement生成一种BigInteger(手动实现)。我希望能够互换使用这些库。谢谢!
答案 0 :(得分:3)
您可能在BouncyCastle类层次结构的错误区域中执行了您想要执行的操作。 (我在同一个地方偶然发现,原因可能相同。)如果你想要实现必须可互操作的ECDH,你肯定是在错误的地方。
为什么它的结构如此不直观?好吧,原因是BouncyCastle的抽象是他们关注并提供价值的地方。而不是为那些说“我将要使用ECDH密钥加密密钥”并且想要处理低级加密细节的人做准备,BC希望你使用管理员级别的抽象,如“公钥”,“私钥”,和“证书”,并填写中间的“kind”和“bitstrength”等安全参数。
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位长的bignum计算。它是用于此目的的最有效表示,因为8位字节处理8位字节的开销远远超过64位长64位长处理的成本的8倍。无论哪种方式,它都需要在输入字节[]的不同部分迭代地调用BitConverter。那些迭代和方法调用加起来,因此BigInteger是“数字的内部表示”。
这些甚至不具有远程可比性,因此这可能不是您想要做的。
如果要从BigInteger获取byte [],请使用其ToByteArray()方法。如果要将byte []转换为BigInteger,请构造一个新的BigInteger对象,其byte []包含要计算的位字符串。 new BigInteger(oldBigInteger.ToByteArray())按预期工作(一个新的BigInteger对象,它与旧的具有相同的值)。直接使用它们通常是不合适的,因为EC公钥由两个数字组成。此外,ToByteArray()仅转储整数的值,它不包含任何DER编码信息,以将其标识为任何长度的整数。
(另外,在C#中,'byte'和'char'是不同大小的不同东西。'byte'是8位长.'char'是一个Unicode代码点,那些可能大于8位。 char'(以及'string',在概念上是一系列字符)需要编码/解码才能适合字节大小的片段。)
答案 1 :(得分:2)
每个Diffie-Hellman实现使用唯一的常量集从公钥+私钥中派生共享密钥。因此,两种实现都不能从完全相同的密钥对中获得完全相同的共享秘密。你最好自己测试它,或者在BouncyCastle邮件列表上询问它。
注意:ECDiffieHellmanCNG仅适用于Windows Vista / Windows 7及更高版本。另一方面,您可以在.net 1.1及更高版本和旧版Windows(2000,XP等)上使用BouncyCastle。