Экспорт сертификата X.509 БЕЗ закрытого ключа

Я думал, что это будет просто, но, видимо, это не так. У меня установлен сертификат с закрытым ключом, который можно экспортировать, и я хочу программно экспортировать его ТОЛЬКО с открытым ключом. Другими словами, мне нужен результат, эквивалентный выбору «Не экспортировать закрытый ключ» при экспорте через certmgr и экспорте в .CER.

Кажется, что все методы X509Certificate2.Export будут экспортировать закрытый ключ, если он существует, как PKCS #12, что противоположно тому, что я хочу.

Есть ли способ использовать C # для этого, или мне нужно начать копаться в CAPICOM?


person Aaronaught    schedule 21.05.2009    source источник


Ответы (3)


Для всех, кто мог наткнуться на это, я понял это. Если вы укажете X509ContentType.Cert в качестве первого (и единственного) параметра для X509Certificate.Export, будет экспортирован только открытый ключ. С другой стороны, указание X509ContentType.Pfx включает закрытый ключ, если он существует.

Я мог бы поклясться, что на прошлой неделе я наблюдал другое поведение, но, должно быть, у меня уже был установлен закрытый ключ, когда я тестировал. Когда сегодня я удалил этот сертификат и снова начал с нуля, я увидел, что в экспортированном сертификате нет закрытого ключа.

person Aaronaught    schedule 26.05.2009
comment
знаете ли вы, есть ли способ экспортировать только закрытый ключ без всего сертификата? Мне нужно извлечь закрытый ключ в виде массива байтов, и я не нахожу способа сделать это.... - person RRR; 24.10.2011
comment
@RRR: Что бы вы ни пытались сделать, я бы не советовал этого делать, потому что закрытый ключ сертификата — это гораздо больше, чем просто массив байтов, это криптографический алгоритм, в частности AsymmetricAlgorithm, а разные сертификаты могут иметь совершенно разные алгоритмы. Если вы потеряете эту информацию, будет очень сложно восстановить и расшифровать/проверить что-либо, зашифрованное/подписанное открытым ключом. Если вы действительно хотите попробовать с этим повозиться, посмотрите на X509Certificate2.PrivateKey и работайте оттуда. - person Aaronaught; 24.10.2011
comment
@Aaronaught: Обычно вы никогда не хотите экспортировать закрытый ключ вместе с сертификатом. Закрытый ключ должен оставаться в строжайшем секрете. Вы можете проверить все, что подписано закрытым ключом, имея только сертификат — сертификаты содержат только открытый ключ, и это все, что нужно для проверки подписи. Как правило, вы не хотите использовать закрытый ключ для шифрования данных. Кроме того, закрытый и открытый ключи не являются взаимозаменяемыми — по открытому ключу почти невозможно угадать закрытый ключ, но не наоборот. Так что держите этот закрытый ключ дома. - person Jim Flood; 17.01.2014
comment
Обратите внимание, что это дает другой формат файла, поэтому он больше не сохраняется как файл .pfx/.p12. - person Peter; 02.01.2018

Я нашел следующую программу полезной для того, чтобы убедиться, что свойство RawData сертификата содержит только открытый ключ (в MSDN это неясно), и что приведенный выше ответ относительно X509ContentType.Cert против X509ContentType.Pfx работает, как и ожидалось:

using System;
using System.Linq;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;

class Program
{
    static void Main( string[] args )
    {
        var certPath = @"C:\blah\somecert.pfx";
        var certPassword = "somepassword";

        var orig = new X509Certificate2( certPath, certPassword, X509KeyStorageFlags.Exportable );
        Console.WriteLine( "Orig   : RawData.Length = {0}, HasPrivateKey = {1}", orig.RawData.Length, orig.HasPrivateKey );

        var certBytes = orig.Export( X509ContentType.Cert );
        var certA = new X509Certificate2( certBytes );
        Console.WriteLine( "cert A : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certA.RawData.Length, certA.HasPrivateKey, certBytes.Length );

        // NOTE that this the only place the byte count differs from the others
        certBytes = orig.Export( X509ContentType.Pfx );
        var certB = new X509Certificate2( certBytes );
        Console.WriteLine( "cert B : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certB.RawData.Length, certB.HasPrivateKey, certBytes.Length );

        var keyIdentifier = ( new X509SecurityToken( orig ) ).CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
        certBytes = keyIdentifier.GetX509RawData();
        var certC = new X509Certificate2( certBytes );
        Console.WriteLine( "cert C : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certC.RawData.Length, certC.HasPrivateKey, certBytes.Length );

        Console.WriteLine( "RawData equals original RawData: {0}", certC.RawData.SequenceEqual( orig.RawData ) );

        Console.ReadLine();
    }
}

Он выводит следующее:

Orig   : RawData.Length = 1337, HasPrivateKey = True
cert A : RawData.Length = 1337, HasPrivateKey = False, certBytes.Length = 1337
cert B : RawData.Length = 1337, HasPrivateKey = True, certBytes.Length = 3187
cert C : RawData.Length = 1337, HasPrivateKey = False, certBytes.Length = 1337
RawData equals original RawData: True
person explunit    schedule 07.04.2015

Существует оболочка OpenSSL .NET, которая может оказаться полезной.

person Nathan    schedule 21.05.2009