Подпись ИЭМК
Перейти к навигации
Перейти к поиску
Подпись с помощью CryptoPro 1. Установка Crypro Pro. 2. Установка Crypro Pro.Net 3. Установка Crypro Pro.Net SDK
using System; using System.Linq; using System.Text; using System.Collections.Generic; using System.Threading.Tasks; using System.Security.Cryptography.X509Certificates; using System.Xml; using System.Xml.Linq; using System.Security.Cryptography; using System.Security.Cryptography.Xml; using CryptoPro.Sharpei; using CryptoPro.Sharpei.Xml; namespace Samples.Xml.cs { class SignSmevRequest { [STAThread] static void Main(string[] args) { X509Store certStore = new X509Store(StoreLocation.CurrentUser); certStore.Open(OpenFlags.ReadOnly); X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection( certStore.Certificates, "Выберите сертификат", "Пожалуйста, выберите сертификат электронной подписи", X509SelectionFlag.SingleSelection); if (certs.Count == 0) { Console.WriteLine("Сертификат не выбран."); return; } // Подписываем запрос SignXmlFile(args[0], args[1], certs[0]); // Проверяем подпись VerifyXmlFile(args[1]); Console.ReadKey(); } static void SignXmlFile(string FileName, string SignedFileName, X509Certificate2 Certificate) { // Создаем новый документ XML. XmlDocument doc = new XmlDocument(); doc.PreserveWhitespace = true; // Читаем документ из файла. doc.Load(new XmlTextReader(FileName)); // Создаём объект SmevSignedXml - наследник класса SignedXml с перегруженным GetIdElement // для корректной обработки атрибута wsu:Id. SmevSignedXml signedXml = new SmevSignedXml(doc); // Задаём ключ подписи для документа SmevSignedXml. signedXml.SigningKey = Certificate.PrivateKey; var sign = Certificate.PrivateKey; // Создаем ссылку на подписываемый узел XML. В данном примере и в методических // рекомендациях СМЭВ подписываемый узел soapenv:Body помечен идентификатором "body". Reference reference = new Reference(); reference.Uri = "#BODY"; // Задаём алгоритм хэширования подписываемого узла - ГОСТ Р 34.11-94. Необходимо // использовать устаревший идентификатор данного алгоритма, т.к. именно такой // идентификатор используется в СМЭВ. #pragma warning disable 612 //warning CS0612: 'CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete' is obsolete reference.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 // Добавляем преобразование для приведения подписываемого узла к каноническому виду // по алгоритму http://www.w3.org/2001/10/xml-exc-c14n# в соответствии с методическими // рекомендациями СМЭВ. XmlDsigExcC14NTransform c14 = new XmlDsigExcC14NTransform(); reference.AddTransform(c14); signedXml.AddReference(reference); // #wsa-300 !MessageID Reference referenceWSA300 = new Reference(); referenceWSA300.Uri = "#WSA-300"; #pragma warning disable 612 referenceWSA300.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 referenceWSA300.AddTransform(c14); signedXml.AddReference(referenceWSA300); // #wsa-301 !Action Reference referenceWSA301 = new Reference(); referenceWSA301.Uri = "#WSA-301"; #pragma warning disable 612 referenceWSA301.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 referenceWSA301.AddTransform(c14); signedXml.AddReference(referenceWSA301); // WSA-302 ! ReplyTo Reference referenceWSA302 = new Reference(); referenceWSA302.Uri = "#WSA-302"; #pragma warning disable 612 referenceWSA302.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 referenceWSA302.AddTransform(c14); signedXml.AddReference(referenceWSA302); // WSA-303 ! To Reference referenceWSA303 = new Reference(); referenceWSA303.Uri = "#WSA-303"; #pragma warning disable 612 referenceWSA303.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 referenceWSA303.AddTransform(c14); signedXml.AddReference(referenceWSA303); // #TRHEAD Reference referenceTH = new Reference(); referenceTH.Uri = "#TRHEAD"; #pragma warning disable 612 referenceTH.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 referenceTH.AddTransform(c14); signedXml.AddReference(referenceTH); // doc.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText = Convert.ToBase64String(Certificate.RawData); // #SenderCertificate Reference referenceSS = new Reference(); referenceSS.Uri = "#CertId"; #pragma warning disable 612 referenceSS.DigestMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete; #pragma warning restore 612 referenceSS.AddTransform(c14); signedXml.AddReference(referenceSS); // Задаём преобразование для приведения узла ds:SignedInfo к каноническому виду // по алгоритму http://www.w3.org/2001/10/xml-exc-c14n# в соответствии с методическими // рекомендациями СМЭВ. signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; // Задаём алгоритм подписи - ГОСТ Р 34.10-2001. Необходимо использовать устаревший // идентификатор данного алгоритма, т.к. именно такой идентификатор используется в // СМЭВ. #pragma warning disable 612 //warning CS0612: 'CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3411UrlObsolete' is obsolete signedXml.SignedInfo.SignatureMethod = CryptoPro.Sharpei.Xml.CPSignedXml.XmlDsigGost3410UrlObsolete; #pragma warning restore 612 // Вычисляем подпись. signedXml.ComputeSignature(); // Получаем представление подписи в виде XML. XmlElement xmlDigitalSignature = signedXml.GetXml(); // Добавляем необходимые узлы подписи в исходный документ в заготовленное место. doc.GetElementsByTagName("Signature")[0].PrependChild( doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignatureValue")[0], true)); doc.GetElementsByTagName("Signature")[0].PrependChild( doc.ImportNode(xmlDigitalSignature.GetElementsByTagName("SignedInfo")[0], true)); // Добавляем сертификат в исходный документ в заготовленный узел // wsse:BinarySecurityToken. doc.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText = Convert.ToBase64String(Certificate.RawData); // Сохраняем подписанный документ в файл. using (XmlTextWriter xmltw = new XmlTextWriter(SignedFileName, new UTF8Encoding(false))) { doc.WriteTo(xmltw); } } static void VerifyXmlFile(string SignedFileName) { // Создаем новый документ XML. XmlDocument xmlDocument = new XmlDocument(); // Форматируем документ с сохранением всех пробельных символов, т.к. они // важны при проверке подписи. xmlDocument.PreserveWhitespace = true; // Загружаем подписанный документ XML из файла. xmlDocument.Load(SignedFileName); // Ищем все узлы ds:Signature и сохраняем их в объекте XmlNodeList XmlNodeList nodeList = xmlDocument.GetElementsByTagName( "Signature", SignedXml.XmlDsigNamespaceUrl); Console.WriteLine("Найдено подписей: {0}.", nodeList.Count); // Проверяем все подписи. for (int curSignature = 0; curSignature < nodeList.Count; curSignature++) { // Создаём объект SmevSignedXml - наследник класса SignedXml с перегруженным // GetIdElement для корректной обработки атрибута wsu:Id. SmevSignedXml signedXml = new SmevSignedXml(xmlDocument); // Загружаем узел с подписью. signedXml.LoadXml((XmlElement)nodeList[curSignature]); // Получаем идентификатор ссылки на узел wsse:BinarySecurityToken, // содержащий сертификат подписи. XmlNodeList referenceList = signedXml.KeyInfo.GetXml().GetElementsByTagName( "Reference", WSSecurityWSSENamespaceUrl); if (referenceList.Count == 0) { throw new XmlException("Не удалось найти ссылку на сертификат"); } // Ищем среди аттрибутов ссылку на сертификат. string binaryTokenReference = ((XmlElement)referenceList[0]).GetAttribute("URI"); // Ссылка должна быть на узел внутри данного документа XML, т.е. она имеет вид // #ID, где ID - идентификатор целевого узла if (string.IsNullOrEmpty(binaryTokenReference) || binaryTokenReference[0] != '#') { throw new XmlException("Не удалось найти ссылку на сертификат"); } // Получаем узел BinarySecurityToken с закодированным в base64 сертификатом XmlElement binaryTokenElement = signedXml.GetIdElement( xmlDocument, binaryTokenReference.Substring(1)); if (binaryTokenElement == null) { throw new XmlException("Не удалось найти сертификат"); } // Создаём объект X509Certificate2 X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(binaryTokenElement.InnerText)); // Проверяем подпись. // ВНИМАНИЕ! Проверка сертификата в данном примере не осуществляется. Её необходимо // реализовать самостоятельно в соответствии с требованиями к подписи проверяемого // типа сообщения СМЭВ. bool result = signedXml.CheckSignature(cert.PublicKey.Key); // Выводим результат проверки подписи в консоль if (result) { Console.WriteLine("Подпись №{0} верна.", curSignature + 1); } else { Console.WriteLine("Подпись №{0} не верна.", curSignature + 1); } } } class SmevSignedXml : SignedXml { public SmevSignedXml(XmlDocument document) : base(document) { } public override XmlElement GetIdElement(XmlDocument document, string idValue) { XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable); nsmgr.AddNamespace("wsu", WSSecurityWSUNamespaceUrl); return document.SelectSingleNode("//*[@wsu:Id='" + idValue + "']", nsmgr) as XmlElement; } } public const string WSSecurityWSSENamespaceUrl = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; public const string WSSecurityWSUNamespaceUrl = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; } }