Bouncy Castle ECDH 和 .net4 原生 ECDiffieHellmanCNG 之间的差异

发布于 2024-11-01 20:06:19 字数 181 浏览 0 评论 0原文

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

I've been able to generate a private key using the ECDiffieHellmanCNG in .net4 and I've also used the Bouncy Castle C# library to successfully generate private keys. I want to know why the .net 4 version generates a byte array of chars and the ECDHBasicAgreement from Bouncy Castle generates a type of BigInteger (manually implemented). I would like to be able to use these libraries interchangeably. Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

﹎☆浅夏丿初晴 2024-11-08 20:06:19

对于您想要执行的操作,您可能处于 BouncyCastle 类层次结构的错误区域。 (我在同一个地方偶然发现,可能出于相同的原因。)如果您希望实现必须可互操作的 ECDH,那么您肯定来错地方了。

为什么它的结构如此不直观?原因是 BouncyCastle 中的抽象是他们集中注意力并提供价值的地方。 BC 希望您使用管理者级别的抽象,例如“公钥”、“私钥”,而不是为那些说“我将使用 ECDH 密钥加密密钥”并想要处理低级加密细节的人做好准备。和“certificate”,中间填写“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 位 long 实现 bignum 计算的方式。它是用于此目的的最有效的表示形式,因为处理 8 位字节乘 8 位字节的开销远远超过处理 64 位长乘 64 位长的开销的 8 倍。无论哪种方式,都需要在输入 byte[] 的不同部分上迭代调用 BitConverter。这些迭代和方法调用相加,因此 BigInteger 是“数字的内部表示”。

这些用途甚至没有可比性,因此这可能不是您想要做的。

如果您想从 BigInteger 获取 byte[],请使用其 ToByteArray() 方法。如果要将 byte[] 转换为 BigInteger,请使用包含要计算的位字符串的 byte[] 构造一个新的 BigInteger 对象。 new BigInteger(oldBigInteger.ToByteArray()) 按您的预期工作(一个新的 BigInteger 对象,其值与旧对象相同)。直接使用它们通常是不合适的,因为 EC 公钥由两个数字组成。此外,ToByteArray() 仅转储整数的值,它不包含任何 DER 编码信息来将其标识为任意长度的整数。

(此外,在 C# 中,“byte”和“char”是具有不同大小的不同事物。“byte”是 8 位长。“char”是一个 Unicode 代码点,它们可能大于 8 位。“char”(与“字符串”(概念上是字符序列)一起)需要编码/解码才能适合字节大小的片段。)

You're probably in the wrong area of the BouncyCastle class hierarchy for what you want to do. (I stumbled around in the same place, for probably the same reasons.) If you're looking to implement ECDH that must be interoperable, you're definitely in the wrong place.

Why is it structured so unintuitively? Well, the reason is that the abstractions in BouncyCastle are where they focus their attention and provide their value. Instead of gearing for people saying "I'm going to use ECDH key-encrypting keys" and wanting to deal with low-level crypto details, BC expects you to use manager-level abstractions like "public key", "private key", and "certificate", and fill in the security parameters like "kind" and "bitstrength" in the middle of it.

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 will quite happily waste time and electricity recalculating the public key every time you load this, unless you take care to save _keypair.Public in something like a self-signed 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();

And here's how to load the keypair from the two structures.

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);

Now, that all said, here's the answer your actual question.

.NET 4 provides a byte[] because it's calling OLE platform-native code which does all of the abstraction for you. It's the most efficient representation for this purpose, because it doesn't parse what it gets back from CNG, performing the least amount of object boxing back into the CLR object space and relying on the programmer to deal with what's essentially an opaque blob.

BouncyCastle uses its BigInteger class because it's how it implements bignum calculations with 64-bit longs. It is the most efficient representation for this purpose, because the overhead of processing 8-bit byte by 8-bit byte is far more than 8 times the cost of processing 64-bit long by 64-bit long. Either way, it requires iteratively calling BitConverter on a different section of the input byte[]. Those iterations and method calls add up, so BigInteger is the "internal representation of a number".

These are not even remotely comparable uses, so this is probably not what you want to do.

If you want to get a byte[] from BigInteger, use its ToByteArray() method. If you want to transform a byte[] to a BigInteger, construct a new BigInteger object with the byte[] containing the bit string you want to calculate with. new BigInteger(oldBigInteger.ToByteArray()) works as you'd expect (a new BigInteger object which has the same value as the old one). Directly working with them is usually inappropriate, because EC public keys are made up of two numbers. Also, ToByteArray() only dumps the value of the integer, it doesn't include any DER encoding information to identify it as an integer of whatever length.

(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.)

清风无影 2024-11-08 20:06:19

每个 Diffie-Hellman 实现都使用独特的常量集从公钥+私钥导出共享秘密。因此,这两种实现都不能从完全相同的密钥对中派生出完全相同的共享秘密。您最好亲自测试一下或在 BouncyCastle 邮件列表上询问。

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

Eachy Diffie-Hellman implementation uses unique set of constants to derive the shared secret from the public+private key. So neither implementation may derive the exact same shared secret from the very same key pairs. You are better off testing it for yourself or asking it on the BouncyCastle mailing lists.

Note: ECDiffieHellmanCNG is only available on Windows Vista/Windows 7 and above. On the other hand, you can use BouncyCastle on .net 1.1 and above and older Windows versions (2000, XP etc.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文