Подписание XML-документа с установленным сертификатом X.509

У меня возникают проблемы с подписью XML-документа с установленным сертификатом. Я попытался с сертифицированным установленным в LocalMachine, CurrentUser и Initialize экземпляра X509Certificate2 с использованием файла сертификата (.pfx). У каждого свои проблемы; Я предпочитаю использовать сертификат, который установлен в хранилище LocalMachine. Ниже я изложил три метода и проблемы с каждым:

StoreLocation LocalMachine - предпочтительный метод

var certStore = new X509Store(StoreLocation.LocalMachine);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbPrint, true);
var myCert = certCollection[0];

// I can get the correct certificate but the following line throws "Invalid provider type specified." error
var SigningKey = myCert.PrivateKey;

StoreLocation CurrentUser

var certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
var certCollection = certStore.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbPrint, true);
var myCert = certCollection[0];

// I can get the correct certificate but the following line throws "Keyset does not exist" error
var SigningKey = myCert.PrivateKey;

Я могу получить PrivateKey только в том случае, если я изменил разрешения для следующей папки%ALLUSERSPROFILE%\Application Data\Microsoft\Crypto\RSA\MachineKeys. Это не выглядит правильным способом для реализации подписания.

Использование файла сертификата

var certificateFile = @"C:\CertificateFolder\AuthorizedCertificate.pfx";
var myCert = new X509Certificate2(certificateFile, password, X509KeyStorageFlags.UserKeySet);

Этот метод работает, однако я должен предоставить файл сертификата и пароль, который нежелателен.

Как я могу заставить работать первый метод (LocalMachine)? Каков рекомендуемый / лучший способ сделать это?

Для справки следующий код используется для подписи XML-документа

private void SignXml(XmlDocument xmlDoc, X509Certificate2 cert)
{
    // Create a SignedXml object.
    SignedXml signedXml = new SignedXml(xmlDoc);

    // Add the key to the SignedXml document.
    signedXml.SigningKey = cert.PrivateKey;

    // Create a reference to be signed.
    Reference reference = new Reference();
    reference.Uri = "";

    // Add an enveloped transformation to the reference.
    var env = new XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(env);

    // Include the public key of the certificate in the assertion.
    signedXml.KeyInfo = new KeyInfo();
    signedXml.KeyInfo.AddClause(new KeyInfoX509Data(cert, X509IncludeOption.WholeChain));

    // Add the reference to the SignedXml object.
    signedXml.AddReference(reference);

    // Compute the signature.
    signedXml.ComputeSignature();

    // Get the XML representation of the signature and save
    // it to an XmlElement object.
    XmlElement xmlDigitalSignature = signedXml.GetXml();

    // Append the element to the XML document.
    xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true));
}

1 ответ

Решение

Версия магазина LocalMachine

замещать

var SigningKey = myCert.PrivateKey;

с

var SigningKey = myCert.GetRSAPrivateKey();

(s/RSA/DSA/ при необходимости)

Свойство PrivateKey может возвращать только ключи, которые можно преобразовать в RSACryptoServiceProvider или DSACryptoServiceProvider. "Указан неверный тип провайдера" означает, что ваш закрытый ключ хранится в CNG, а не в CAPI.

Это будет работать только в том случае, если у вас установлен.NET 4.6.2 или новее, потому что тогда были зафиксированы определенные ограничения (в отношении RSA, не относящихся к RSACryptoServiceProvider) в SignedXml и его вспомогательных классах.

(Альтернатива: обновление до Windows 10, где ОС добавила мост CAPI к CNG для решения этой проблемы)

Текущая версия пользовательского магазина

Эта версия дает сбой, потому что когда вы импортировали сертификат из PFX, вы импортировали его с помощью MachineKeySet (или вы не указали UserKeySet, и он был ранее экспортирован из хранилища ключей машины). Копия сертификата в хранилище пользователя говорит о том, что его закрытый ключ находится в хранилище машины. И по какой-то причине у вас нет доступа к нему. ("По какой-то причине", потому что обычно это предполагает, что вы не могли бы добавить это...)

Версия PFX

Это работает, потому что PFX говорит, что ключ должен храниться в CAPI CSP (PFX несут много метаданных), позволяя функционировать свойству сертификата PrivateKey.

Другие вопросы по тегам