1

Тема: Взаимодействие со СМЭВ

Добрый день!
Поставлена задача организовать взаимодействие со СМЭВ с использованием криптосредств Валидата. Необходимо подписывать отправляемые сообщения (xml-данные) по следующему алгоритму:
3.1    Открытый ключ подписи, закодированный по алгоритму «http://www.w3.org/2000/09/xmldsig#base64», добавляется к элементу X509Certificate как дочерний текстовый узел.
3.2    Подписываются элементы документа, выбранные посредством XPATH выражения на основе значения атрибута URI элемента Reference (если элемент URI имеет пустое значение, то подписывается полностью весь тег сущности). Полученное значение кодируется по алгоритму «http://www.w3.org/2000/09/xmldsig#base64» и добавляется как дочерний текстовый узел к элементу DigestValue первого элемента Reference.
3.3    Элемент SignedInfo трансформируется в соответствии с алгоритмом «http://www.w3.org/2001/10/xml-exc-c14n#». Затем на основании полученной строки и ключа подписи формируется значение ЭП в соответствии с алгоритмом  «http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411». Полученное значение ЭП кодируется в соответствии с алгоритмом «http://www.w3.org/2000/09/xmldsig#base64», и значение добавляется как дочерний текстовый узел к элементу SignatureValue.

Ниже приведен пример готового xml с подписью:

<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/" xmlns:rev="http://smev.gosuslugi.ru/rev120315" xmlns:smev="http://roskazna.ru/gisgmp/02000000/SmevGISGMPService/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
   <S:Header>
      <wsse:Security S:actor="http://smev.gosuslugi.ru/actors/smev">
         <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
               <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
               <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411" />
               <Reference URI="#body">
                  <Transforms>
                     <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                  </Transforms>
                  <DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411" />
                  <DigestValue>R/HhBTokGjCkEt10hwBwpBeEtU71mXa9WzU8yF1FtsA=</DigestValue>
               </Reference>
            </SignedInfo>
            <SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#">ppjO9ScclKcGrMMxUFx5u1sSRXUmN/zpQTa7INhHbDntfp4sBHSPuP8bPyKyXF0U43m+CCJDiL2W4RyolxUqhQ==</SignatureValue>
            <ds:KeyInfo>
               <wsse:SecurityTokenReference>
                  <wsse:Reference URI="#SenderCertificate" />
               </wsse:SecurityTokenReference>
            </ds:KeyInfo>
         </ds:Signature>
         <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="SenderCertificate">MIIC/zCCAq6gAwIBAgITEgACzs+JbKisyovIVAAAAALOzzAIBgYqhQMCAgMwfzEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBjcnlwdG9wcm8ucnUxCzAJBgNVBAYTAlJVMQ8wDQYDVQQHEwZNb3Njb3cxFzAVBgNVBAoTDkNSWVBUTy1QUk8gTExDMSEwHwYDVQQDExhDUllQVE8tUFJPIFRlc3QgQ2VudGVyIDIwHhcNMTUwMzE3MTMwNzUwWhcNMTUwNjE3MTMxNzUwWjAOMQwwCgYDVQQDDANsdHQwYzAcBgYqhQMCAhMwEgYHKoUDAgIkAAYHKoUDAgIeAQNDAARAZ7M/gIf4eBP2zopA9imuhHpxUteEw6E/nFiR4HZl/mgmLccDfnEkALUyrfoFK1jqwAss3KXqw179b2NXb34Kb6OCAXAwggFsMA4GA1UdDwEB/wQEAwIE8DATBgNVHSUEDDAKBggrBgEFBQcDAjAdBgNVHQ4EFgQUFZkbfqXAVa7DrADxgsEbd5fP0AIwHwYDVR0jBBgwFoAUFTF8sI0a3mbXFZxJUpcXJLkBeoMwWQYDVR0fBFIwUDBOoEygSoZIaHR0cDovL3Rlc3RjYS5jcnlwdG9wcm8ucnUvQ2VydEVucm9sbC9DUllQVE8tUFJPJTIwVGVzdCUyMENlbnRlciUyMDIuY3JsMIGpBggrBgEFBQcBAQSBnDCBmTBhBggrBgEFBQcwAoZVaHR0cDovL3Rlc3RjYS5jcnlwdG9wcm8ucnUvQ2VydEVucm9sbC90ZXN0LWNhLTIwMTRfQ1JZUFRPLVBSTyUyMFRlc3QlMjBDZW50ZXIlMjAyLmNydDA0BggrBgEFBQcwAYYoaHR0cDovL3Rlc3RjYS5jcnlwdG9wcm8ucnUvb2NzcC9vY3NwLnNyZjAIBgYqhQMCAgMDQQBaRu5Ed1u/TIZcuHphLCy7dpHViqPDRI+X6axCtCjcnKwKlBLVMuiDQgQjb8hQcHLAG0CvSgO1QwtkUHxHwrq/</wsse:BinarySecurityToken>
      </wsse:Security>
   </S:Header>
   <S:Body wsu:Id="body">
      <smev:GISGMPTransferMsg>
         <rev:Message>
            <rev:Sender>
               <rev:Code>AN0010001</rev:Code>
               <rev:Name>ИС АН 1</rev:Name>
            </rev:Sender>
            <rev:Recipient>
               <rev:Code>RKZN35001</rev:Code>
               <rev:Name>Казначейство России</rev:Name>
            </rev:Recipient>
            <rev:ServiceName>GISGMP</rev:ServiceName>
            <rev:TypeCode>GFNC</rev:TypeCode>
            <rev:Status>REQUEST</rev:Status>
            <rev:Date>2014-11-12T09:30:47.0Z</rev:Date>
            <rev:ExchangeType>6</rev:ExchangeType>
         </rev:Message>
         <rev:MessageData>
            <rev:AppData>
               <gisgmp:RequestMessage xmlns:gisgmp="http://roskazna.ru/gisgmp/xsd/116/Message" xmlns:com="http://roskazna.ru/gisgmp/xsd/116/Common" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:msgd="http://roskazna.ru/gisgmp/xsd/116/MessageData" xmlns:n1="http://www.altova.com/samplexml/other-namespace" xmlns:pdr="http://roskazna.ru/gisgmp/xsd/116/PGU_DataRequest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="ID_1" callBackURL="http://www.altova.com" senderIdentifier="000147" timestamp="2014-11-12T09:30:47.0Z" xsi:schemaLocation="http://roskazna.ru/gisgmp/xsd/116/Message Message.xsd">
                  <msgd:ExportRequest Id="ID_2" kind="CHARGE">
                     <pdr:Filter>
                        <pdr:Conditions>
                           <pdr:ChargesIdentifiers>
                              <pdr:SupplierBillID>0000016101005733332038899</pdr:SupplierBillID>
                           </pdr:ChargesIdentifiers>
                        </pdr:Conditions>
                     </pdr:Filter>
                  </msgd:ExportRequest>
               </gisgmp:RequestMessage>
            </rev:AppData>
         </rev:MessageData>
      </smev:GISGMPTransferMsg>
   </S:Body>
</S:Envelope>

Вопрос в том, имеются ли в Валидата SDK под .Net (C#, используется cvpia2.dll) методы, необходимые для реализации приведенного алгоритма подписи (если возможно, какие конкретно)? Может, есть какой-то пример подписи xml-данных?

2

Re: Взаимодействие со СМЭВ

Добрый день!

Судя по всему, Вам необходима функция подписи блока данных SignMemory.
Краткий пример кода:

VcertObject vcert = new VcertObject();
vcert.Initialize();

SignParameters sp = new SignParameters();
sp.Detached = true;
sp.Pkcs7 = true;

byte[] dataToSign;
// ... fill dataToSign
byte[] sign = vcert.SignMemory(sp, dataToSign, null);

3

Re: Взаимодействие со СМЭВ

При попытке обращения к закрытому ключу (Certificate.PrivateKey) для подписи данных (по аналогии с примерами https://msdn.microsoft.com/ru-ru/library/system.security.cryptography.x509certificates.x509certificate2%28v=vs.110%29.aspx) вылетает ошибка "The certificate key algorithm is not supported", хотя Certificate.HasPrivateKey возвращает true. Другие установленные в ОС сертификаты со связанным закрытым ключом корректно возвращают объект типа System.Security.Cryptography.AsymmetricAlgorithm. Подскажите, в чем может быть проблема?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;
using System.Security.Cryptography.Xml;

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            string clientCertCriteria = "aa aa bf 6e 05 1d 3b 0b 5f c7 a6 85 d0 9a de 07 63 27 7c db";

            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.ReadOnly);

            //сертификат
            X509Certificate2Collection coll = store.Certificates.Find(X509FindType.FindByThumbprint, clientCertCriteria, false);

            if (coll.Count == 0)
            {
                throw new System.IO.FileNotFoundException(string.Format("Сертификат клиента с признаком \"{0}\" не найден", clientCertCriteria));
            }
            X509Certificate2 clientCert = coll[0];

            Certificate.PrivateKey;  // ВЫЛЕТАЕТ ОШИБКА !

            // Подписываем
            Sign(clientCert);
        }
    }
}

4

Re: Взаимодействие со СМЭВ

Из документации (https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.privatekey(v=vs.110).aspx)

X509Certificate2.PrivateKey Property
Property Value
Type: System.Security.Cryptography.AsymmetricAlgorithm
An AsymmetricAlgorithm object, which is either an RSA or DSA cryptographic service provider.

следует, что свойство PrivateKey поддерживается только для алгоритмов RSA и DSA.

Для алгоритма ГОСТ Р 34.10-2001 выдается ошибка.

5

Re: Взаимодействие со СМЭВ

alart пишет:

свойство PrivateKey поддерживается только для алгоритмов RSA и DSA.

Для алгоритма ГОСТ Р 34.10-2001 выдается ошибка.

Спасибо! Тогда, видимо, придется использовать Валидатовский SDK, но тут же возникает вопрос: как получить открытый ключ подписи?

6

Re: Взаимодействие со СМЭВ

SDK Валидаты не позволяет получить открытый ключ.
Чтобы получить открытый ключ, можно воспользоваться функцией X509Certificate.GetPublicKey.

7

Re: Взаимодействие со СМЭВ

Для хеширования строки использую метод HashMemory(byte[]).  Как указать алгоритм хеширования данных?

8

Re: Взаимодействие со СМЭВ

Алгоритм хеширования - ГОСТ Р 34.11-94

9

Re: Взаимодействие со СМЭВ

Мы можем предложить следующий алгоритм проверки XML-подписи для совместимости со СМЭВ:

1.    Из XML документа выделяется элемент SignedInfo;
2.    Все начальные пробелы в строках перед XML-разметкой в элементе SignedInfo удаляются;
3.    Элемент SignedInfo канонизируется (например, средством http://soapclient.com/xmlcanon.html, разработчику для этого придется использовать библиотеку XML-канонизатора);
4.    В канонизированном элементе SignedInfo удаляются все переводы строк (‘\r’, ‘\n’). В результате должно получиться ровно 575 байт текстовых данных;
5.    Из XML документа выделяется элемент SignatureValue, из которого берутся данные подписи в кодировке Base64;
6.    Данные подписи преобразуются в бинарный вид (64 байта) и ПЕРЕВОРАЧИВАЮТСЯ (т.е. 1-ый байт становится 64-ым, 2-ой байт – 63-им, и т.д.);
7.    Из XML документа выделяется элемент BinarySecurityToken типа SenderCertificate – в данных этого элемента находится сертификат отправителя (подписанта) сообщения в формате Base64. Сертификат следует преобразовать в бинарный вид;
8.    С помощью API АПК “Валидата Клиент” (VCERT_HashMem(), VCERT_SignHashMem(), VCERT_VerifyHashMem() – C/C++; HashMemory(), SignHash(), VerifyHash() - .Net) следует проверить подпись.

Также подпись можно проверить с помощью Microsoft Crypto API. Это API используется тестовой утилитой testcsp.exe из состава СКЗИ “Валидата CSP” версия 4.0 (testcsp.exe -cpsign -verify -in signed-info-canon.xml -signature signature.swap -my ltt).

Далее приведен алгоритм, показывающий как вычислять хэш-значение, соответствующее подписываемым данным СМЭВ из XML-документа.

1.    Из исходного XML документа выделяется элемент S:Body, для которого следует указать заголовок <?xml version="1.0" encoding="windows-1251"?> (поскольку строковые данные документа находятся в кодировке CP1251, а не в UTF-8);
2.    В элемент S:Body (который становится корневым элементом нового XML документа) необходимо перенести все пространства имен XML, указанные в родительском элементе S:Envelope (xmlns:);
3.    Полученный XML документ с помощью какого либо средства (например, средство http://sourceforge.net/projects/xmlstar/files/, разработчику придется работать с XML библиотекой) следует канонизировать (xml.exe c14n --exc-without-comments document.xml >doc-canon.xml) в режиме http://www.w3.org/TR/xml-exc-c14n/;
4.    Все начальные пробелы в строках перед XML-разметкой в канонизированном документе следует удалить; все переводы строк (‘\r’, ‘\n’) также следует удалить.

Для полученного XML документа следует вычислить хэш-значение, которое и следует внести в элемент SignedInfo/DigestValue в кодировке Base64.