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!
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.)
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.)
精彩评论