Подписание 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));
}

person Imran    schedule 03.11.2017    source источник


Ответы (1)


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

Заменять

var SigningKey = myCert.PrivateKey;

с участием

var SigningKey = myCert.GetRSAPrivateKey();

(s / RSA / DSA / в зависимости от ситуации)

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

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

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

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

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

Версия PFX

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

person bartonjs    schedule 03.11.2017
comment
Спасибо за объяснение. myCert.GetRSAPrivateKey () работал с .Net Framework 4.6.1, однако я получаю следующую ошибку с другой частью кода (signedXml.ComputeSignature ()), надеюсь, вы можете помочь. System.NotSupportedException Метод не поддерживается. Byte [] DecryptValue (Byte []) в System.Security.Cryptography.RSA.DecryptValue (Byte [] rgb) в System.Security.Cryptography.Xml.SignedXml.ComputeSignature () - person Imran; 07.11.2017
comment
@imran вам понадобится 4.6.2, как объяснено в ответе. - person bartonjs; 08.11.2017