Разница между Bouncy Castle ECDH и собственным ECDiffieHellmanCNG для .net4

Мне удалось сгенерировать закрытый ключ с помощью ECDiffieHellmanCNG в .net4, а также использовать библиотеку Bouncy Castle C# для успешного создания закрытых ключей. Я хочу знать, почему версия .net 4 генерирует байтовый массив символов, а ECDHBasicAgreement от Bouncy Castle генерирует тип BigInteger (вручную). Я хотел бы иметь возможность использовать эти библиотеки взаимозаменяемо. Спасибо!


person hobeau    schedule 15.04.2011    source источник


Ответы (2)


Каждая реализация Диффи-Хеллмана использует уникальный набор констант для получения общего секрета из открытого + закрытого ключа. Таким образом, ни одна из реализаций не может получить один и тот же общий секрет из одних и тех же пар ключей. Вам лучше проверить это на себе или спросить об этом в списках рассылки BouncyCastle.

Примечание. ECDiffieHellmanCNG доступен только в Windows Vista/Windows 7 и более поздних версиях. С другой стороны, вы можете использовать BouncyCastle в .net 1.1 и выше, а также в более ранних версиях Windows (2000, XP и т. д.).

person Teoman Soygul    schedule 15.04.2011

Вы, вероятно, находитесь в неправильной области иерархии классов BouncyCastle для того, что вы хотите сделать. (Я споткнулся в том же месте, вероятно, по тем же причинам.) Если вы хотите реализовать ECDH, который должен быть совместимым, вы определенно ошиблись местом.

Почему он построен так неинтуитивно? Ну, причина в том, что абстракции в BouncyCastle — это то, на чем они фокусируют свое внимание и обеспечивают свою ценность. Вместо того, чтобы ориентироваться на людей, которые говорят: «Я собираюсь использовать ключи шифрования ключей ECDH» и хотят иметь дело с низкоуровневыми криптографическими деталями, BC ожидает, что вы будете использовать абстракции уровня менеджера, такие как «открытый ключ», «закрытый ключ», и "сертификат" и заполните параметры безопасности, такие как "вид" и "битность" в середине.

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, потому что именно так он реализует вычисления bignum с 64-битными длинными числами. Это наиболее эффективное представление для этой цели, потому что накладные расходы на обработку 8-битного байта за 8-битным байтом намного более чем в 8 раз превышают затраты на обработку 64-битного байта на 64-битного. В любом случае требуется итеративный вызов BitConverter для другого раздела входного byte[]. Эти итерации и вызовы методов складываются, поэтому BigInteger является «внутренним представлением числа».

Это даже отдаленно не сопоставимые способы использования, поэтому, вероятно, это не то, что вы хотите делать.

Если вы хотите получить byte[] из BigInteger, используйте его метод ToByteArray(). Если вы хотите преобразовать byte[] в BigInteger, создайте новый объект BigInteger с byte[], содержащим строку битов, которую вы хотите вычислить. новый BigInteger(oldBigInteger.ToByteArray()) работает так, как вы ожидаете (новый объект BigInteger, который имеет то же значение, что и старый). Напрямую работать с ними обычно нецелесообразно, т.к. открытые ключи EC состоят из двух чисел. Кроме того, ToByteArray() выводит только значение целого числа, он не включает никакой информации о кодировании DER, чтобы идентифицировать его как целое число любой длины.

(Кроме того, в C# «byte» и «char» — это разные вещи с разными размерами. «byte» имеет длину 8 бит. «char» — это кодовая точка Unicode, и они потенциально больше 8 бит. «char» ( вместе со строкой, которая концептуально представляет собой последовательность символов) требует кодирования/декодирования, прежде чем она будет вписываться в фрагменты размером в байт.)

person sjcaged    schedule 02.02.2012