Используйте PrivateKey для X509Certificate2, хранящегося в хранилище сертификатов и помеченного как неэкспортируемый с помощью BouncyCastle и C #

Я пытаюсь создать небольшой центр сертификации, используя bouncycastle.

Сертификат создается с помощью BouncyCastle и сохраняется в хранилище сертификатов текущего пользователя вместе с закрытым ключом.

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

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

X509Certificate2 caCert = GetRootKey(); // Fetches the certificate from the store
AsymmetricCipherKeyPair caPrivKey = DotNetUtilities.GetKeyPair(caCert.PrivateKey);

К сожалению, я получаю CryptographicException - потому что закрытый ключ помечен как не экспортируемый, и это то, что я намеревался ;-)

У меня вопрос: как я могу использовать закрытый ключ сертификата без доступа к нему? Я почти уверен, что упускаю какой-то важный момент, так как было бы бессмысленно хранить закрытый ключ без какого-либо доступа к нему ...

Я ищу это часами, но ничего не нахожу в stackoverflow или Google.

Может кто подскажет пожалуйста, что я делаю в корне не так? Есть ли другой способ что-то подписать ?!

Заранее спасибо!

Крис


person Chris    schedule 17.05.2012    source источник


Ответы (3)


Краткий ответ: не с BouncyCastle.

Вы не можете поместить ключ в библиотеку BouncyCastle (или OpenSSL или любую другую) и использовать его оттуда. Невозможно сделать это, не обойдя флаг «нет экспорта» и не экспортируя его, что, как вы указываете, возможно, если вы являетесь администратором и имеете необходимое Fu.

Что вы можете сделать, так это получить дескриптор CryptoAPI для ключа и попросить CryptoAPI выполнить операции подписи или шифрования за вас. Крипто-классы DotNet инкапсулируют CyptoAPI.

По сути, вам нужно учебное пособие по использованию криптоклассов DotNet. Их много, например, в этой статье журнала MSDN Magazine есть пример цифровых подписей примерно на три четверти ниже:

http://msdn.microsoft.com/en-us/magazine/cc163454.aspx

person Ben    schedule 17.05.2012
comment
Привет, Бен, спасибо за ссылку и указание на Crypto-API! Я постараюсь реализовать с этим функционал. Возможно, BouncyCastle - не лучший выбор - я просто хотел создать клиент-серверное приложение с внутренним центром сертификации для аутентификации клиента и подумал, что с BC это будет легко. - person Chris; 18.05.2012

Этот вопрос как раз то, что мне нужно, но в ответе есть ссылка, которая не работает. После некоторого поиска я нашел эту реализацию с использованием библиотеки NCrypt: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/security/cryptoapi/CertSign/CPP/Sign.cpp

К сожалению, это написано на C, а мне нужна реализация на C #. Итак, я создал эту оболочку C ++ \ CLI (SignerWrapper.h), которую можно использовать в управляемом коде C #: https://github.com/DmitriNymi/StoreCertificateSigner

Я также написал собственный ISignatureFactory для использования в BouncyCastle, см. PksAsn1SignatureFactory.cs в том же репо. Он нужен только для создания моего настраиваемого ISigner (мой класс: PksEcdsaSigner).

Теперь вы можете передать PksAsn1SignatureFactory как ISignatureFactory в BouncyCastle для создания новых сертификатов, например, см. Модульный тест PksAsn1SignatureFactoryTest.cs - явный вызов ISignatureFactory и CertificateIssuerTest.cs - использование вспомогательного класса для создания новых сертификатов, подписанных закрытыми ключами из другого сертификата в хранилище

person Searcher    schedule 03.05.2019

Я не хотел добавлять C ++ / CLI в свой проект, поэтому придумал решение на чистом C # с использованием System.Security.Pkcs.SignedCms. Подобно ответу Searcher, это обеспечивает реализацию ISignatureFactory, которую можно использовать для подписи сертификатов в BouncyCastle.

using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;

public class WindowsSignatureFactory : ISignatureFactory
{
    public AlgorithmIdentifier Algorithm { get; }
    public X509Certificate2 Cert { get; }

    public object AlgorithmDetails => Algorithm;

    public WindowsSignatureFactory(AlgorithmIdentifier algorithm,
            X509Certificate2 cert)
    {
        Algorithm = algorithm;
        Cert = cert;
    }

    public IStreamCalculator CreateCalculator()
    {
        return new WindowsStreamCalculator(Algorithm, Cert);
    }

    private class WindowsStreamCalculator : IStreamCalculator
    {
        public AlgorithmIdentifier Algorithm { get; }
        public X509Certificate2 Cert { get; }
        private MemoryStream MemoryStream { get; } = new MemoryStream();
        public Stream Stream => MemoryStream;

        public WindowsStreamCalculator(AlgorithmIdentifier algorithm,
                X509Certificate2 cert)
        {
            Algorithm = algorithm;
            Cert = cert;
        }

        public object GetResult()
        {
            var signer = new System.Security.Cryptography.Pkcs.CmsSigner(Cert);
            // This maps e.g. "rsa with sha256" (OID 1.2.840.113549.1.1.11) to
            // "sha256" (OID 2.16.840.1.101.3.4.1)
            var hashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(Algorithm);
            var hashOid = hashAlgorithm.Algorithm.Oid;
            signer.DigestAlgorithm = new System.Security.Cryptography.Oid(hashOid);

            var dataToSign = MemoryStream.ToArray();
            var info = new System.Security.Cryptography.Pkcs.ContentInfo(dataToSign);
            var cms = new System.Security.Cryptography.Pkcs.SignedCms(info, true);
            cms.ComputeSignature(signer, true);

            // This is the tricky part: usually you would call cms.Export() to get a signed
            // message, but we only want the signature
            var cmsSigner = cms.SignerInfos
                .Cast<System.Security.Cryptography.Pkcs.SignerInfo>().ToArray().Single();
            var signatureData = cmsSigner.GetSignature();

            return new SimpleBlockResult(signatureData);
        }
    }
}

Его можно использовать как замену Asn1SignatureFactory следующим образом:

public static Org.BouncyCastle.X509.X509Certificate SignCertificate(
        X509V3CertificateGenerator generator,
        X509Certificate2 nonExportableSigningCertificate)
{
    var signer = new WindowsSignatureFactory(
        new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha256),
        nonExportableSigningCertificate);

    var signed = generator.Generate(signer);

    // Make sure it worked
    var signingCertBouncy = DotNetUtilities.FromX509Certificate(nonExportableSigningCertificate);
    signed.Verify(signingCertBouncy.GetPublicKey());

    return signed;
}
person wartmanm    schedule 11.05.2021