Подпись ИЭМК с помощью VipNet CSP

Материал из Wiki МИАЦ ВО
Версия от 23:38, 31 октября 2016; Dmitriy (обсуждение | вклад)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к навигации Перейти к поиску

Поиск нужного сертификата. Ищем по конкретному СНИЛСу и действующий на текущую дату.

X509Certificate2 CertItog = find_certificate_();
            Console.WriteLine(CertItog.SignatureAlgorithm.Value);
            Console.WriteLine(CertItog.GetPrivateKeyInfo().KeyContainerName);
            Console.WriteLine(CertItog.GetPrivateKeyInfo().ProviderName);
            Console.WriteLine(CertItog.GetPrivateKeyInfo().ProviderType);
            Console.WriteLine(CertItog.GetPrivateKeyInfo());
        public static X509Certificate2 find_certificate_()
        {

            X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            X509Certificate2 cert = new X509Certificate2();
            X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
            X509Certificate2Collection fcollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
            string str_SN = "";
            foreach (X509Certificate2 x509 in fcollection)
            {
                try
                {
                    if (x509.Subject.ToUpper().Contains("SNILS") || x509.Subject.ToUpper().Contains("СНИЛС"))
                    {

                        str_SN = x509.Subject.Substring(x509.Subject.IndexOf("SNILS") + 6, 11);
                        int n;
                        if (!int.TryParse(str_SN.Substring(0, 10), out n))
                        {
                            str_SN = x509.Subject.Substring(x509.Subject.ToUpper().IndexOf("СНИЛС") + 6, 11);
                            if (!int.TryParse(str_SN.Substring(0, 10), out n))
                            {
                                continue;
                                //                                System.Windows.Forms.MessageBox.Show("Снилс в сертификате не найден или пустой");
                            }
                        }
                        if (String.CompareOrdinal(str_SN, "NNNNNNNNNNNNN") == 0)
                        {
                            cert = x509;
                            //                            Console.WriteLine(cert.Subject);
                            Console.WriteLine(str_SN);
//                            Console.ReadKey();
                            break;
                        }
                    }
                    x509.Reset();
                }
                catch (CryptographicException)
                {
                    Console.WriteLine("Information could not be written out for this certificate.");
                }
            }
            store.Close();
            return cert;
        }

Далее. Для подписи требуется библиотека GostCryptography (https://github.com/AlexMAS/GostCryptography). Заливаем через nuget, подключаем.

using GostCryptography;
using GostCryptography.Cryptography;
using GostCryptography.Xml;

Для СМЭВ пользуем доп. класс Добавляем константы

        private const string WsSecurityExtNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
        private const string WsSecurityUtilityNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
        private static XmlElement GetSmevIdElement(XmlDocument document, string idValue)
        {
                var namespaceManager = new XmlNamespaceManager(document.NameTable);
                namespaceManager.AddNamespace("wsu", WsSecurityUtilityNamespace);
                return document.SelectSingleNode("//*[@wsu:Id='" + idValue + "']", namespaceManager) as XmlElement;
        }

И, собственно алгоритм подписи/ На выходе формируется отдельный файл.

        private static XmlDocument SignSmevRequestVip(string FileName, string SignedFileName, X509Certificate2 signingCertificate)
        {
            XmlDocument smevRequest = new XmlDocument();
            smevRequest.PreserveWhitespace = true;
            smevRequest.Load(new XmlTextReader(FileName));

            // Создание подписчика XML-документа
            var signedXml = new GostSignedXml(smevRequest) { GetIdElementHandler = GetSmevIdElement };

            // Установка ключа для создания подписи
            signedXml.SetSigningCertificate(signingCertificate);
//            signedXml.SigningKey = signingCertificate.PrivateKey;
//            var SigningKey = signingCertificate.GetPrivateKeyAlgorithm();
            signedXml.SigningKey = signingCertificate.GetPrivateKeyAlgorithm();
//            var gostFormatter = new GostSignatureFormatter(signingCertificate.GetPrivateKeyAlgorithm()); 
//            Console.WriteLine(signatureXml1.InnerText); Console.ReadKey();
            // Ссылка на узел, который нужно подписать, с указанием алгоритма хэширования ГОСТ Р 34.11-94 (в соответствии с методическими рекомендациями СМЭВ)
            var dataReference = new Reference { Uri = "#BODY", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            // Метод преобразования, применяемый к данным перед их подписью (в соответствии с методическими рекомендациями СМЭВ)
            var dataTransform = new XmlDsigExcC14NTransform();
            dataReference.AddTransform(dataTransform);
            // Установка ссылки на узел
            signedXml.AddReference(dataReference);
//reference #wsa300
            var dataReferenceWSA300 = new Reference { Uri = "#WSA-300", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            var dataTransformWSA300 = new XmlDsigExcC14NTransform();
            dataReferenceWSA300.AddTransform(dataTransformWSA300);
            signedXml.AddReference(dataReferenceWSA300);
//reference #WSA-301
            var dataReferenceWSA301 = new Reference { Uri = "#WSA-301", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            var dataTransformWSA301 = new XmlDsigExcC14NTransform();
            dataReferenceWSA301.AddTransform(dataTransformWSA301);
            signedXml.AddReference(dataReferenceWSA301);
//reference #WSA-302
            var dataReferenceWSA302 = new Reference { Uri = "#WSA-302", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            var dataTransformWSA302 = new XmlDsigExcC14NTransform();
            dataReferenceWSA302.AddTransform(dataTransformWSA302);
            signedXml.AddReference(dataReferenceWSA302);
//reference #WSA-303
            var dataReferenceWSA303 = new Reference { Uri = "#WSA-303", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            var dataTransformWSA303 = new XmlDsigExcC14NTransform();
            dataReferenceWSA303.AddTransform(dataTransformWSA303);
            signedXml.AddReference(dataReferenceWSA303);
//reference #TRHEAD
            var dataReferenceTRHEAD = new Reference { Uri = "#TRHEAD", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            var dataTransformTRHEAD = new XmlDsigExcC14NTransform();
            dataReferenceTRHEAD.AddTransform(dataTransformTRHEAD);
            signedXml.AddReference(dataReferenceTRHEAD);
//
            smevRequest.GetElementsByTagName("wsse:BinarySecurityToken")[0].InnerText = Convert.ToBase64String(signingCertificate.RawData);

//reference #CertId
            var dataReferenceCertId = new Reference { Uri = "#CertId", DigestMethod = GostSignedXml.XmlDsigGost3411ObsoleteUrl };
            var dataTransformCertId = new XmlDsigExcC14NTransform();
            dataReferenceCertId.AddTransform(dataTransformCertId);
            signedXml.AddReference(dataReferenceCertId);

            var keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(signingCertificate));
            signedXml.KeyInfo = keyInfo;

            // Установка алгоритма нормализации узла SignedInfo (в соответствии с методическими рекомендациями СМЭВ)
            signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

            // Установка алгоритма подписи ГОСТ Р 34.10-2001 (в соответствии с методическими рекомендациями СМЭВ)
            signedXml.SignedInfo.SignatureMethod = GostSignedXml.XmlDsigGost3410ObsoleteUrl;

            // Вычисление подписи
            
            signedXml.ComputeSignature();

            // Получение XML-представления подписи
            var signatureXml = signedXml.GetXml();
//            Console.WriteLine(signatureXml.GetElementsByTagName("SignatureValue")[0].InnerText); Console.ReadKey();
            // Добавление подписи в исходный документ

            smevRequest.GetElementsByTagName("Signature")[0].PrependChild(smevRequest.ImportNode(signatureXml.GetElementsByTagName("SignatureValue")[0], true));
            smevRequest.GetElementsByTagName("Signature")[0].PrependChild(smevRequest.ImportNode(signatureXml.GetElementsByTagName("SignedInfo")[0], true));

            using (XmlTextWriter xmltw = new XmlTextWriter(SignedFileName,
                new UTF8Encoding(false)))
            {
                smevRequest.WriteTo(xmltw);
            }
            return smevRequest;
        }

Верификация подписи

        private static bool VerifySmevRequestSignatureVip(string FileName)
        {
            XmlDocument signedSmevRequest = new XmlDocument();
            signedSmevRequest.PreserveWhitespace = true;
            signedSmevRequest.Load(new XmlTextReader(FileName));

            // Создание подписчика XML-документа
            var signedXml = new GostSignedXml(signedSmevRequest) { GetIdElementHandler = GetSmevIdElement };

            // Поиск узла с подписью
            var nodeList = signedSmevRequest.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl);

            // Загрузка найденной подписи
            signedXml.LoadXml((XmlElement)nodeList[0]);

            // Поиск ссылки на BinarySecurityToken
            var references = signedXml.KeyInfo.GetXml().GetElementsByTagName("Reference", WsSecurityExtNamespace);

            if (references.Count > 0)
            {
                // Определение ссылки на сертификат (ссылка на узел документа)
                var binaryTokenReference = ((XmlElement)references[0]).GetAttribute("URI");

                if (!String.IsNullOrEmpty(binaryTokenReference) && binaryTokenReference[0] == '#')
                {
                    // Поиск элемента с закодированным в Base64 сертификатом
                    var binaryTokenElement = signedXml.GetIdElement(signedSmevRequest, binaryTokenReference.Substring(1));

                    if (binaryTokenElement != null)
                    {
                        // Загрузка сертификата, который был использован для подписи
                        var signingCertificate = new X509Certificate2(Convert.FromBase64String(binaryTokenElement.InnerText));

                        // Проверка подписи
                        bool result = signedXml.CheckSignature(signingCertificate.GetPublicKeyAlgorithm());
                        if (result)
                        {
                            Console.WriteLine("Подпись №{0} верна.");
                        }
                        else
                        {
                            Console.WriteLine("Подпись №{0} не верна.");
                        }
                        return result;
                    }
                }
            }

            return false;
        }

Вызов класса

SignSmevRequestVip(args[1], args[2], CertItog);
VerifySmevRequestSignatureVip(args[2]);