Создание подписи XAdES-T для взаимодействия с ГИС ГМП по спецификации версии 1.16.1 с использованием КриптоПро JCP

Публикация: 22 Июнь 2015 - 13:56, редакция: 30.06.2015 12:08

В связи с доработками «Формата взаимодействия ГИС ГМП с информационными системами участников» (http://www.roskazna.ru/gis-gmp/) мы продолжаем тему организации взаимодействия со СМЭВ с использованием наших продуктов. Предыдущие заметки вы можете посмотреть по ссылкам (1 и 2).

В начале этого года была выпущена новая версия документа под номером 1.16.1, описывающая новый формат взаимодействия с информационной системой ГИС ГМП. Данная спецификация, среди прочих изменений, предусматривает добавление в отправляемый в систему документ XML подписи формата XAdES-T.

Формат ЭЦП XML документа XAdES описан в стандарте «XML Advanced Electronic Signatures (XAdES)». Документ казначейства ссылается на спецификацию http://www.w3.org/TR/XAdES/. Вообще говоря, это устаревшая версия – v1.1.1, актуальная версия – v1.3.2. XAdES – это набор форматов усовершенствованной подписи документов XML, расширяющий возможности формата XMLDSig.

XAdES-T - это формат подписи с добавленным в неподписанные атрибуты штампом времени (time-stamp), который может быть получен при обращении к Службе Штампов Времени (TSA).

Для создания таких сообщений мы будем использовать КриптоПро JCP и открытую библиотеку xades4j. Такая связка позволяет создать подпись требуемого формата, и хотя xades4j не поддерживает XAdES v1.1.1, подпись версии 1.3.2 успешно принимается тестовым сервисом.

Начнём!

Исходное сообщение выглядит так:

<soapenv:Envelope xmlns:rev="http://smev.gosuslugi.ru/rev120315" xmlns:smev="http://roskazna.ru/gisgmp/02000000/SmevGISGMPService/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 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">
<soapenv:Header/>
<soapenv:Body wsu:Id="body">
<smev:GISGMPTransferMsg>
<rev:Message>
<rev:Sender>
<rev:Code>AN0000001</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>2015-06-15T10:25:00.0Z</rev:Date>
<rev:ExchangeType>6</rev:ExchangeType>
<rev:TestMsg>test</rev:TestMsg>
</rev:Message>
<rev:MessageData>
<rev:AppData>
<gisgmp:RequestMessage xmlns="http://roskazna.ru/gisgmp/xsd/116/PaymentInfo" xmlns:com="http://roskazna.ru/gisgmp/xsd/116/Common" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:gisgmp="http://roskazna.ru/gisgmp/xsd/116/Message" xmlns:msgd="http://roskazna.ru/gisgmp/xsd/116/MessageData" xmlns:n1="http://www.altova.com/samplexml/other-namespace" xmlns:pgu="http://roskazna.ru/gisgmp/xsd/116/PGU_ImportRequest" xmlns:bgi="http://roskazna.ru/gisgmp/xsd/116/BudgetIndex" xmlns:org="http://roskazna.ru/gisgmp/xsd/116/Organization" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="P_a7654321-8bcf-de90-123f-abcde0987654" senderIdentifier="000147" timestamp="2015-06-15T10:25:00.0Z">
<msgd:ImportRequest>
<pgu:Package>
<pgu:Document>
<FinalPayment Id="P_a1234567-bcf8-90de-f123-4567890abcde">
<SupplierBillID>0</SupplierBillID>
<Narrative>Оплата</Narrative>
<Amount>10000</Amount>
<PaymentDate>2015-06-15T10:25:00</PaymentDate>
<BudgetIndex>
<bgi:Status>01</bgi:Status>
<bgi:Purpose>0</bgi:Purpose>
<bgi:TaxPeriod>0</bgi:TaxPeriod>
<bgi:TaxDocNumber>0</bgi:TaxDocNumber>
<bgi:TaxDocDate>0</bgi:TaxDocDate>
<bgi:PaymentType>0</bgi:PaymentType>
</BudgetIndex>
<PaymentIdentificationData>
<Bank>
<org:Name>ВТБ24</org:Name>
<org:BIK>044525716</org:BIK>
<org:CorrespondentBankAccount>40602810000380000020</org:CorrespondentBankAccount>
</Bank>
<SystemIdentifier>10445257164525716000114101697208</SystemIdentifier>
</PaymentIdentificationData>
<Payer>
<com:PayerIdentifier>0100000000023456789012643</com:PayerIdentifier>
<PayerName>Иванов Иван Николаевич</PayerName>
</Payer>
<Payee>
<PayeeName>ГИБДД</PayeeName>
<payeeINN>3543655766</payeeINN>
<payeeKPP>354365576</payeeKPP>
<PayeeBankAcc>
<org:AccountNumber>40602810000380000020</org:AccountNumber>
<org:Bank>
<org:Name>Альфа</org:Name>
<org:BIK>044525716</org:BIK>
<org:CorrespondentBankAccount>30101810100000000716</org:CorrespondentBankAccount>
</org:Bank>
</PayeeBankAcc>
</Payee>
<ChangeStatus meaning="1"/>
<KBK>18851111111111111113</KBK>
<OKTMO>12345673</OKTMO>
</FinalPayment>
</pgu:Document>
</pgu:Package>
</msgd:ImportRequest>
</gisgmp:RequestMessage>
</rev:AppData>
</rev:MessageData>
</smev:GISGMPTransferMsg>
</soapenv:Body>
</soapenv:Envelope>

Инициализируем библиотеку JCPxml и включаем проверку цепочки сертификатов с помощью CRL по ссылкам в сертификатах:

JCPXMLDSigInit.init();
System.setProperty("com.sun.security.enableCRLDP", "true");
System.setProperty("com.ibm.security.enableCRLDP", "true");

Получаем XML в виде объекта Document:

final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
final Document sourceDocument = dbFactory.newDocumentBuilder().
 (new ByteArrayInputStream(<binary_xml_document>));

Теперь необходимо найти узел для подписания. Подписываемая сущность, согласно спецификации, имеет атрибут Id вида <буква [A-Z]>_<GUID>. Сама подпись в итоге будет помещена внутрь подписываемого узла. Предположим, что производится импорт платежа – документа finalPayment с Id равным P_a1234567-bcf8-90de-f123-4567890abcde.

final XPathFactory factory = XPathFactory.newInstance();
final XPath xpath = factory.newXPath();

final XPathExpression expr = xpath.compile(String.format("//*[@Id='%s']", "P_a1234567-bcf8-90de-f123-4567890abcde"));
final NodeList nodes = (NodeList) expr.evaluate(sourceDocument, XPathConstants.NODESET);

if (nodes.getLength() == 0) {
    throw new Exception("Can't find node with id=P_a1234567-bcf8-90de-f123-4567890abcde");
} // if

final Node nodeToSign = nodes.item(0);
final String referenceURI = "#P_a1234567-bcf8-90de-f123-4567890abcde";

Подпишем

Далее готовим ключ подписи и сертификат для добавления в подпись. На случай проверки ЭЦП выполняется загрузка списка доверенных корневых сертификатов из специального, заранее подготовленного хранилища. Кроме того, может быть задействован дополнительный список сертификатов (например, промежуточных) и CRL (на случай оффлайновой проверки цепочки сертификатов).

final KeyStore keyStore = KeyStore.getInstance("HDImageStore", JCP.PROVIDER_NAME);
keyStore.load(null, null);

final PrivateKey privateKey = (PrivateKey) keyStore.getKey(<key_alias>, <key_password>);
final X509Certificate cert = (X509Certificate) keyStore.getCertificate(<key_alias>);
// Загрузка списка доверенных корневых сертификатов (для последующей за созданием подписи проверкой цепочки сертификатов
final KeyStore trustStore = GostXAdESUtility.loadCertStore(trustStorePath, trustStorePassword);

// Список дополнительных сертификатов (например, промежуточных) и CRL
final CertStore intermediateCertsAndCRLStore = CertStore.getInstance("Collection",
new CollectionCertStoreParameters(intermediateCertsAndCRLs));

final KeyingDataProvider keyingProvider = new DirectKeyingDataProvider(cert, privateKey);

Затем необходимо создать профиль для подписи формата XAdES-T. В нашем примере каждому алгоритму хэширования соответствует адрес службы TSA. Для целей тестирования мы будем использовать службу по адресу http://www.cryptopro.ru/tsp/. Функции хэширования следует инициализировать с помощью OID’ов алгоритмов хэширования, а не их имён. Эти соответствия прописаны в служебном классе GostXAdESUtility. Также необходимо переопределить остальные функции определения алгоритмов, реализовав интерфейс DefaultAlgorithmsProviderEx.

Замечание. На момент написания статьи сервис ГИС ГМП проверял только наличие узла со штампом времени, но не проверял сам штамп времени. Поэтому мы использовали нашу тестовую службу TSA. В будущем, вполне вероятно, сервис станет доверять только определенным службам TSA.

final XadesSigningProfile sigProf = new XadesTSigningProfile(keyingProvider)

.withTimeStampTokenProvider(new GostTimeStampTokenProvider(…))

.withDigestEngineProvider(new DefaultMessageDigestProvider() {

    @Override
     public MessageDigest getEngine(String digestAlgorithmURI) throws UnsupportedAlgorithmException {

        // Определение OID’а алгоритма хэширования по URN
        final String digestAlgOid = GostXAdESUtility.digestUri2Digest(digestAlgorithmURI);

        try {
            return MessageDigest.getInstance(digestAlgOid);
        } catch (NoSuchAlgorithmException e) {
            throw new UnsupportedAlgorithmException(e.getMessage(), digestAlgorithmURI, e);
        }
    }

  })

.withAlgorithmsProviderEx(new DefaultAlgorithmsProviderEx() 

private String digestUrn = null;

@Override
public Algorithm getSignatureAlgorithm(String keyAlgorithmName)
       throws UnsupportedAlgorithmException {

    // Определение URN алгоритма хэширования по алгоритму ключа подписи
    digestUrn = GostXAdESUtility.key2DigestUrn(keyAlgorithmName);

    // Определение URN алгоритма подписи по алгоритму ключа подписи
    final String signatureUrn = GostXAdESUtility.key2SignatureUrn(keyAlgorithmName);

     return new GenericAlgorithm(signatureUrn);
     }
 @Override
       public String getDigestAlgorithmForReferenceProperties() { digestUrn; }

@Override
 public String getDigestAlgorithmForDataObjsReferences() { digestUrn; }

@Override
 public String getDigestAlgorithmForTimeStampProperties() { digestUrn; }

@Override
 public Algorithm getCanonicalizationAlgorithmForSignature() {
 		return new ExclusiveCanonicalXMLWithoutComments();
 }

 @Override
 public Algorithm getCanonicalizationAlgorithmForTimeStampProperties() {
 		return new ExclusiveCanonicalXMLWithoutComments();
 }

});

Далее подписываем узел с добавлением подписи в него самого (так называемая enveloped signature):

final XadesSigner signer = sigProf.newSigner();

final DataObjectDesc dataObj = new DataObjectReference(referenceURI);
dataObj.withTransform(new EnvelopedSignatureTransform());

final SignedDataObjects dataObjects = new SignedDataObjects(dataObj);

signer.sign(dataObjects, nodeToSign);

Класс GostTimeStampTokenProvider реализует интерфейс TimeStampTokenProvider. Данный класс является клоном аналогичного класса DefaultTimeStampTokenProvider библиотеки xades4j. Это было необходимо, т.к. класс DefaultTimeStampTokenProvider имеет метод identifierForDigest для вычисления алгоритма хэширования, а список собственных алгоритмов класса ограничен и не позволяет задать иные алгоритмы или провайдер. По этой же причине при проверке штампа используется своя реализация в виде класса GostTimeStampVerificationProvider (аналог родного класса библиотеки DefaultTimeStampVerificationProvider с интерфейсом TimeStampVerificationProvider).

Проверим

Для полноты картины хорошо бы ещё уметь проверять такие подписи. Для начала найдём узел подписи и создадим профиль проверки:

// Проверяемый документ
final Document verifyDocument = sourceDocument;

// Узел с подписью (предположительно, один).
final NodeList nl = verifyDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");

if (nl.getLength() == 0) {
    throw new Exception("Cannot find Signature element");
} // if

// Проверка цепочки сертификатов включена
final CertificateValidationProvider validationProvider = new PKIXCertificateValidationProvider(
    trustStore, true, intermediateCertsAndCRLStore);

    final XadesVerificationProfile verProf = new XadesVerificationProfile(validationProvider)
        .withTimeStampTokenVerifier(new GostTimeStampVerificationProvider(
        validationProvider, …))

        .withDigestEngineProvider(new DefaultMessageDigestProvider() {
                        @Override
                    	public MessageDigest getEngine(String digestAlgorithmURI) throws UnsupportedAlgorithmException {

	 	        // Определение OID’а алгоритма хэширования по URN
                	final String digestAlgOid = GostXAdESUtility.digestUri2Digest(digestAlgorithmURI);

                       	try {
                             return MessageDigest.getInstance(digestAlgOid);
                        } catch (NoSuchAlgorithmException e) {
                             throw new UnsupportedAlgorithmException(e.getMessage(), digestAlgorithmURI, e);
                        }

                    }

                });

Теперь можно проверить подпись и сертификаты:

final XadesVerifier verifier = verProf.newVerifier();
final Element signatureElement = (Element) nl.item(0); // предположительно, один узел с подписью

verifier.verify(signatureElement, null);

В итоге

Таким образом, в узел finalPayment документа в конце был добавлен новый узел с подписью. Подписанное сообщение выглядит следующим образом:

<soapenv:Envelope xmlns:soapenv="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">
<soapenv:Header/>
<soapenv:Body wsu:Id="body">
<smev:GISGMPTransferMsg>
<rev:Message>
<rev:Sender>
<rev:Code>AN0000001</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>2015-06-15T10:25:00.0Z</rev:Date>
<rev:ExchangeType>6</rev:ExchangeType>
<rev:TestMsg>test</rev:TestMsg>
</rev:Message>
<rev:MessageData>
<rev:AppData>
<gisgmp:RequestMessage xmlns:gisgmp="http://roskazna.ru/gisgmp/xsd/116/Message" xmlns="http://roskazna.ru/gisgmp/xsd/116/PaymentInfo" xmlns:bgi="http://roskazna.ru/gisgmp/xsd/116/BudgetIndex" 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:org="http://roskazna.ru/gisgmp/xsd/116/Organization" xmlns:pgu="http://roskazna.ru/gisgmp/xsd/116/PGU_ImportRequest" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="P_a7654321-8bcf-de90-123f-abcde0987654" senderIdentifier="000147" timestamp="2015-06-15T10:25:00.0Z">
<msgd:ImportRequest>
<pgu:Package>
<pgu:Document>
<FinalPayment Id="P_a1234567-bcf8-90de-f123-4567890abcde">
<SupplierBillID>0</SupplierBillID>
<Narrative>Оплата</Narrative>
<Amount>10000</Amount>
<PaymentDate>2015-06-15T10:25:00</PaymentDate>
<BudgetIndex>
<bgi:Status>01</bgi:Status>
<bgi:Purpose>0</bgi:Purpose>
<bgi:TaxPeriod>0</bgi:TaxPeriod>
<bgi:TaxDocNumber>0</bgi:TaxDocNumber>
<bgi:TaxDocDate>0</bgi:TaxDocDate>
<bgi:PaymentType>0</bgi:PaymentType>
</BudgetIndex>
<PaymentIdentificationData>
<Bank>
<org:Name>ВТБ24</org:Name>
<org:BIK>044525716</org:BIK>
<org:CorrespondentBankAccount>40602810000380000020</org:CorrespondentBankAccount>
</Bank>
<SystemIdentifier>10445257164525716000114101697208</SystemIdentifier>
</PaymentIdentificationData>
<Payer>
<com:PayerIdentifier>0100000000023456789012643</com:PayerIdentifier>
<PayerName>Иванов Иван Николаевич</PayerName>
</Payer>
<Payee>
<PayeeName>ГИБДД</PayeeName>
<payeeINN>3543655766</payeeINN>
<payeeKPP>354365576</payeeKPP>
<PayeeBankAcc>
<org:AccountNumber>40602810000380000020</org:AccountNumber>
<org:Bank>
<org:Name>Альфа</org:Name>
<org:BIK>044525716</org:BIK>
<org:CorrespondentBankAccount>30101810100000000716</org:CorrespondentBankAccount>
</org:Bank>
</PayeeBankAcc>
</Payee>
<ChangeStatus meaning="1"/>
<KBK>18851111111111111113</KBK>
<OKTMO>12345673</OKTMO>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-d543dd42-e1e3-4d6b-93d2-7be28ddb16c9">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"/>
<ds:Reference Id="xmldsig-d543dd42-e1e3-4d6b-93d2-7be28ddb16c9-ref0" URI="#P_a1234567-bcf8-90de-f123-4567890abcde">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>g6jg+PYUivyEymaJ4m0MREUsbjumqk/nrNoDcRDxObs=</ds:DigestValue>
</ds:Reference>
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-d543dd42-e1e3-4d6b-93d2-7be28ddb16c9-signedprops">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>/4WBWJuvE8rlPAA6nTKgSgJFnoz2TqCO9dX2fpaupSI=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-d543dd42-e1e3-4d6b-93d2-7be28ddb16c9-sigvalue">
OlCFVqO7P7MrRO4YQCsLIzHqpvUF1PCJHsCj6KrtIIW+8ckp23miwUO8wZdwBke2+i0sXkM1RrhU 6vNf2aPyuw==
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIDDTCCArygAwIBAgITEgADylAEJzpv3qM8FgAAAAPKUDAIBgYqhQMCAgMwfzEjMCEGCSqGSIb3 DQEJARYUc3VwcG9ydEBjcnlwdG9wcm8ucnUxCzAJBgNVBAYTAlJVMQ8wDQYDVQQHEwZNb3Njb3cx FzAVBgNVBAoTDkNSWVBUTy1QUk8gTExDMSEwHwYDVQQDExhDUllQVE8tUFJPIFRlc3QgQ2VudGVy IDIwHhcNMTUwNDI3MTI1NTQ1WhcNMTUwNzI3MTMwNTQ1WjAcMQ0wCwYDVQQDDAR0ZXN0MQswCQYD VQQGEwJSVTBjMBwGBiqFAwICEzASBgcqhQMCAiQABgcqhQMCAh4BA0MABEAc+wgTI3j+qtOlPyxJ dPeotFrRpWDRcGpN8Qaerh9C/B8S5CigWnUmywFtQfYBLn5AZZYlKyc+IKQ+bylhQrx4o4IBcDCC AWwwDgYDVR0PAQH/BAQDAgTwMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB0GA1UdDgQWBBTRXniY4glp DAFTC+1LfrtDiwRKtzAfBgNVHSMEGDAWgBQVMXywjRreZtcVnElSlxckuQF6gzBZBgNVHR8EUjBQ ME6gTKBKhkhodHRwOi8vdGVzdGNhLmNyeXB0b3Byby5ydS9DZXJ0RW5yb2xsL0NSWVBUTy1QUk8l MjBUZXN0JTIwQ2VudGVyJTIwMi5jcmwwgakGCCsGAQUFBwEBBIGcMIGZMGEGCCsGAQUFBzAChlVo dHRwOi8vdGVzdGNhLmNyeXB0b3Byby5ydS9DZXJ0RW5yb2xsL3Rlc3QtY2EtMjAxNF9DUllQVE8t UFJPJTIwVGVzdCUyMENlbnRlciUyMDIuY3J0MDQGCCsGAQUFBzABhihodHRwOi8vdGVzdGNhLmNy eXB0b3Byby5ydS9vY3NwL29jc3Auc3JmMAgGBiqFAwICAwNBAKWYnAV3+g6NqZQtFOTfsdIEzhZ3 vilddfAcLipmyp7oIRZ/uUWb4KlGFdxO7RG7oFEHb95DHsc4qcU5xc6iE1Y=
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
<ds:Object>
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Target="#xmldsig-d543dd42-e1e3-4d6b-93d2-7be28ddb16c9">
<xades:SignedProperties Id="xmldsig-d543dd42-e1e3-4d6b-93d2-7be28ddb16c9-signedprops">
<xades:SignedSignatureProperties>
<xades:SigningTime>2015-06-25T17:39:42.717+04:00</xades:SigningTime>
<xades:SigningCertificate>
<xades:Cert>
<xades:CertDigest>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>
<ds:DigestValue>UY1Od5EcmMj7FYpWwIs67IbTb2b1WGLs7ecAJvGB8PY=</ds:DigestValue>
</xades:CertDigest>
<xades:IssuerSerial>
<ds:X509IssuerName>
CN=CRYPTO-PRO Test Center 2,O=CRYPTO-PRO LLC,L=Moscow,C=RU,1.2.840.113549.1.9.1=#1614737570706f72744063727970746f70726f2e7275
</ds:X509IssuerName>
<ds:X509SerialNumber>401414703340175114220951230997187614686300752</ds:X509SerialNumber>
</xades:IssuerSerial>
</xades:Cert>
</xades:SigningCertificate>
</xades:SignedSignatureProperties>
</xades:SignedProperties>
<xades:UnsignedProperties>
<xades:UnsignedSignatureProperties>
<xades:SignatureTimeStamp>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<xades:EncapsulatedTimeStamp>
MIAGCSqGSIb3DQEHAqCAMIIOrQIBAzEMMAoGBiqFAwICCQUAMHwGCyqGSIb3DQEJEAEEoG0EazBp AgEBBgcqhQMCAiYEMC4wCgYGKoUDAgIJBQAEIB/EL84LaQU6lJenbg4scj0Ss0wrsED30m3vlRYm De0rAg0T+FyYogAAAAAAHxY9GA8yMDE1MDYyNTEzNDEwMVowAwIBZAIGAU4q8gwPoIIIvDCCCLgw gghnoAMCAQICCne4NNQAAwACocYwCAYGKoUDAgIDMIIBSDEYMBYGBSqFA2QBEg0xMDM3NzAwMDg1 NDQ0MRowGAYIKoUDA4EDAQESDDAwNzcxNzEwNzk5MTE5MDcGA1UECR4wBEMEOwAuACAEIQRDBEkE UQQyBEEEOgQ4BDkAIAQyBDAEOwAsACAENAAuACAAMQA4MSEwHwYDVQQIHhgANwA3ACAEMwAuACAE HAQ+BEEEOgQyBDAxFTATBgNVBAceDAQcBD4EQQQ6BDIEMDEgMB4GCSqGSIb3DQEJARYRaW5mb0Bj cnlwdG9wcm8ucnUxCzAJBgNVBAYTAlJVMSkwJwYDVQQKHiAEHgQeBB4AIAAiBBoEIAQYBB8EIgQe AC0EHwQgBB4AIjFBMD8GA1UEAx44BCIENQRBBEIEPgQyBEsEOQAgBCMEJgAgBB4EHgQeACAAIgQa BCAEGAQfBCIEHgAtBB8EIAQeACIwHhcNMTQwOTI1MTUwMzAwWhcNMTkwOTI1MTUxMzAwWjCCAdAx FjAUBgUqhQNkAxILMTIzNDU2Nzg5MDExGDAWBgUqhQNkARINMTAwNzcxMjM0NTY3ODEaMBgGCCqF AwOBAwEBEgwwMDc3MTIzNDU2NzgxIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAY3J5cHRvcHJvLnJ1 MQswCQYDVQQGEwJSVTEhMB8GA1UECB4YADcANwAgBDMALgAgBBwEPgRBBDoEMgQwMRUwEwYDVQQH HgwEHAQ+BEEEOgQyBDAxKTAnBgNVBAoeIAQeBB4EHgAgACIEGgQgBBgEHwQiBB4ALQQfBCAEHgAi MS0wKwYDVQQLHiQEHgRCBDQENQQ7ACAEQgQ1BEEEQgQ4BEAEPgQyBDAEPQQ4BE8xGjAYBgNVBAMT EURlbW8gVFNBIE9wZXJhdG9yMTkwNwYDVQQJHjAEQwQ7AC4AIAQhBEMESQRRBDIEQQQ6BDgEOQAg BDIEMAQ7ACwAIAQ0AC4AIAAxADgxJzAlBgNVBAweHgQiBDUEQQRCBDgEQAQ+BDIESQQ4BDoAIABU AFMAUDEjMCEGA1UEKh4aBCIENQRBBEIAIAQiBDUEQQRCBD4EMgQ4BEcxFTATBgNVBAQeDAQiBDUE QQRCBD4EMjBjMBwGBiqFAwICEzASBgcqhQMCAiMBBgcqhQMCAh4BA0MABECXVOagL4FCbIH+/dte 0a8it+AK8IShzzCcKc6W2OAy19RflLosGcLLOraSuYv6LT7QKPIvoGFFEwLwX1u57s9ko4IEozCC BJ8wDgYDVR0PAQH/BAQDAgbAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMB0GA1UdDgQWBBQ5qYEJ GROXhfSo6E4UY+Zc6pMmujCCAYkGA1UdIwSCAYAwggF8gBQrshA0ZoICrPDhqkCGeAFxRZ0z46GC AVCkggFMMIIBSDEYMBYGBSqFA2QBEg0xMDM3NzAwMDg1NDQ0MRowGAYIKoUDA4EDAQESDDAwNzcx NzEwNzk5MTE5MDcGA1UECR4wBEMEOwAuACAEIQRDBEkEUQQyBEEEOgQ4BDkAIAQyBDAEOwAsACAE NAAuACAAMQA4MSEwHwYDVQQIHhgANwA3ACAEMwAuACAEHAQ+BEEEOgQyBDAxFTATBgNVBAceDAQc BD4EQQQ6BDIEMDEgMB4GCSqGSIb3DQEJARYRaW5mb0BjcnlwdG9wcm8ucnUxCzAJBgNVBAYTAlJV MSkwJwYDVQQKHiAEHgQeBB4AIAAiBBoEIAQYBB8EIgQeAC0EHwQgBB4AIjFBMD8GA1UEAx44BCIE NQRBBEIEPgQyBEsEOQAgBCMEJgAgBB4EHgQeACAAIgQaBCAEGAQfBCIEHgAtBB8EIAQeACKCEESH 2ldJk2CeSHb2gnNE/xcwXAYDVR0fBFUwUzBRoE+gTYZLaHR0cDovL3d3dy5jcnlwdG9wcm8ucnUv cmEvY2RwLzJiYjIxMDM0NjY4MjAyYWNmMGUxYWE0MDg2NzgwMTcxNDU5ZDMzZTMuY3JsMIGxBggr BgEFBQcBAQSBpDCBoTA0BggrBgEFBQcwAYYoaHR0cDovL3d3dy5jcnlwdG9wcm8ucnUvb2NzcG5j Mi9vY3NwLnNyZjAyBggrBgEFBQcwAYYmaHR0cDovL3d3dy5jcnlwdG9wcm8ucnUvb2NzcDIvb2Nz cC5zcmYwNQYIKwYBBQUHMAKGKWh0dHA6Ly93d3cuY3J5cHRvcHJvLnJ1L3JhL2NkcC9jYWNlcjMu Y3J0MCsGA1UdEAQkMCKADzIwMTQwOTI1MTUwMzAwWoEPMjAxNTEyMjUxNTAzMDBaMB0GA1UdIAQW MBQwCAYGKoUDZHEBMAgGBiqFA2RxAjA0BgUqhQNkbwQrDCnQmtGA0LjQv9GC0L7Qn9GA0L4gQ1NQ ICjQstC10YDRgdC40Y8gMy42KTCCATMGBSqFA2RwBIIBKDCCASQMKyLQmtGA0LjQv9GC0L7Qn9GA 0L4gQ1NQIiAo0LLQtdGA0YHQuNGPIDMuNikMUyLQo9C00L7RgdGC0L7QstC10YDRj9GO0YnQuNC5 INGG0LXQvdGC0YAgItCa0YDQuNC/0YLQvtCf0YDQviDQo9CmIiDQstC10YDRgdC40LggMS41DE/Q odC10YDRgtC40YTQuNC60LDRgiDRgdC+0L7RgtCy0LXRgtGB0YLQstC40Y8g4oSWINCh0KQvMTI0 LTIyMzgg0L7RgiAwNC4xMC4yMDEzDE/QodC10YDRgtC40YTQuNC60LDRgiDRgdC+0L7RgtCy0LXR gtGB0YLQstC40Y8g4oSWINCh0KQvMTI4LTIzNTEg0L7RgiAxNS4wNC4yMDE0MAgGBiqFAwICAwNB AGCk/BinLYqEzWnAC/SwRl/Zm6fwx6DnKIg5zz0JsR9SBMxtwLojnyAxBHMxN4FO4vBNyivXxFOT yLTU6pgsKJUxggVaMIIFVgIBATCCAVgwggFIMRgwFgYFKoUDZAESDTEwMzc3MDAwODU0NDQxGjAY BggqhQMDgQMBARIMMDA3NzE3MTA3OTkxMTkwNwYDVQQJHjAEQwQ7AC4AIAQhBEMESQRRBDIEQQQ6 BDgEOQAgBDIEMAQ7ACwAIAQ0AC4AIAAxADgxITAfBgNVBAgeGAA3ADcAIAQzAC4AIAQcBD4EQQQ6 BDIEMDEVMBMGA1UEBx4MBBwEPgRBBDoEMgQwMSAwHgYJKoZIhvcNAQkBFhFpbmZvQGNyeXB0b3By by5ydTELMAkGA1UEBhMCUlUxKTAnBgNVBAoeIAQeBB4EHgAgACIEGgQgBBgEHwQiBB4ALQQfBCAE HgAiMUEwPwYDVQQDHjgEIgQ1BEEEQgQ+BDIESwQ5ACAEIwQmACAEHgQeBB4AIAAiBBoEIAQYBB8E IgQeAC0EHwQgBB4AIgIKd7g01AADAAKhxjAKBgYqhQMCAgkFAKCCA5kwGgYJKoZIhvcNAQkDMQ0G CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCC29+8pMk62hlgQ39kxlDUUz5nEMH0suDP8NDh3 uJ1aYTCCAZcGCyqGSIb3DQEJEAIMMYIBhjCCAYIwggF+MIIBegQUQFnWYhYsjcWzN0tpu7wHmF1+ HLEwggFgMIIBUKSCAUwwggFIMRgwFgYFKoUDZAESDTEwMzc3MDAwODU0NDQxGjAYBggqhQMDgQMB ARIMMDA3NzE3MTA3OTkxMTkwNwYDVQQJHjAEQwQ7AC4AIAQhBEMESQRRBDIEQQQ6BDgEOQAgBDIE MAQ7ACwAIAQ0AC4AIAAxADgxITAfBgNVBAgeGAA3ADcAIAQzAC4AIAQcBD4EQQQ6BDIEMDEVMBMG A1UEBx4MBBwEPgRBBDoEMgQwMSAwHgYJKoZIhvcNAQkBFhFpbmZvQGNyeXB0b3Byby5ydTELMAkG A1UEBhMCUlUxKTAnBgNVBAoeIAQeBB4EHgAgACIEGgQgBBgEHwQiBB4ALQQfBCAEHgAiMUEwPwYD VQQDHjgEIgQ1BEEEQgQ+BDIESwQ5ACAEIwQmACAEHgQeBB4AIAAiBBoEIAQYBB8EIgQeAC0EHwQg BB4AIgIKd7g01AADAAKhxjCCAa0GCyqGSIb3DQEJEAIvMYIBnDCCAZgwggGUMIIBkDAIBgYqhQMC AgkEIBnWPpb5e9Sq6OJx3daHv5Yr9hn5Jj1tbNbz8NskXryRMIIBYDCCAVCkggFMMIIBSDEYMBYG BSqFA2QBEg0xMDM3NzAwMDg1NDQ0MRowGAYIKoUDA4EDAQESDDAwNzcxNzEwNzk5MTE5MDcGA1UE CR4wBEMEOwAuACAEIQRDBEkEUQQyBEEEOgQ4BDkAIAQyBDAEOwAsACAENAAuACAAMQA4MSEwHwYD VQQIHhgANwA3ACAEMwAuACAEHAQ+BEEEOgQyBDAxFTATBgNVBAceDAQcBD4EQQQ6BDIEMDEgMB4G CSqGSIb3DQEJARYRaW5mb0BjcnlwdG9wcm8ucnUxCzAJBgNVBAYTAlJVMSkwJwYDVQQKHiAEHgQe BB4AIAAiBBoEIAQYBB8EIgQeAC0EHwQgBB4AIjFBMD8GA1UEAx44BCIENQRBBEIEPgQyBEsEOQAg BCMEJgAgBB4EHgQeACAAIgQaBCAEGAQfBCIEHgAtBB8EIAQeACICCne4NNQAAwACocYwCgYGKoUD AgITBQAEQNZgQ3+P7wMQIGyHS14QIQhW0xQNaSGbPQaLcfcfjqgSYPI9S+qXaK+p7O2AKFOHblwk osG1wsLu1UM2cFcOsL8AAAAA
</xades:EncapsulatedTimeStamp>
</xades:SignatureTimeStamp>
</xades:UnsignedSignatureProperties>
</xades:UnsignedProperties>
</xades:QualifyingProperties>
</ds:Object>
</ds:Signature>
</FinalPayment>
</pgu:Document>
</pgu:Package>
</msgd:ImportRequest>
</gisgmp:RequestMessage>
</rev:AppData>
</rev:MessageData>
</smev:GISGMPTransferMsg>
</soapenv:Body>
</soapenv:Envelope>

Т.к. взаимодействие с системой ГИС ГМП идет не напрямую, а через сервис СМЭВ, то тело сообщения надо ещё раз подписать соответствующим образом. Как это сделать, описано в прошлой статье.

Следует обратить внимание, что в зависимости от запроса, ответом на сообщение может быть как ответ от сервиса ГИС ГМП, так и ответ от СМЭВ с идентификатором запроса. Во втором случае требуется ещё одно обращение в СМЭВ для получения результата запроса от ГИС ГМП.

Полный текст примеров для создания сообщения можно скачать по ссылке. Разобранный пример реализован в классе XAdESExample. В архиве также содержатся примеры GisGmpServiceExampleGisGmpServiceCombinedExample и GisGmpServiceLowEnvelopeExample.

В GisGmpServiceExample происходит подпись существующего документа XML (заготовки лежат в папке xades\gisgmp\source) и запроса на обработку документа по package identifier.  Пример работает с двумя документами - SOAP_etalon_pay.xml и SOAP_etalon_pay_response_1_id.xml. Образцы этих документов также есть в xades\gisgmp\source.

Другой пример, GisGmpServiceCombinedExample, похож на GisGmpServiceExample, но формирование документа происходит прямо в коде с помощью классов, созданных по xsd-схемам, взятым из архива, вложенного в спецификацию форматов ГИС ГМП. Только необходимо добавить ещё одну схему, без которой генерируется неполный набор классов:

<wsdl:types>
<xsd:schema targetNamespace="http://roskazna.ru/gisgmp/02000000/SmevGISGMPService/" xmlns:smev="http://smev.gosuslugi.ru/rev120315">
<xsd:import schemaLocation="xsd/request/smev.unifo.rev120315.xsd" namespace="http://smev.gosuslugi.ru/rev120315"/>
<xsd:import schemaLocation="xsd/request/Message.xsd" namespace="http://roskazna.ru/gisgmp/xsd/116/Message"/>
<xsd:element name="GISGMPTransferMsg" type="smev:BaseMessageType"/>
</xsd:schema>
</wsdl:types>

В третьем примере, GisGmpServiceLowEnvelopeExample, документы формируются из текстовых блоков с указанием названий, пространств имен и хранимых значений.

Все примеры используют хранилище корневых сертификатов xadesTrustStore, в котором находятся несколько корневых сертификатов УЦ ООО «КРИПТО-ПРО». Образец хранилища находится в папке xades\gisgmp\source архива и имеет пароль 1. Перед запуском примеров их следует поместить в папку <корень_проекта>/data.

В этих примерах также используются два контейнера: один тестовый, test, с паролем 1. Такой ключ может быть выпущен в нашем тестовом УЦ. Другой – специальный контейнер с сертификатом, выпущенным аккредитованным УЦ, он необходим для создания внешней подписи для СМЭВ. Здесь необходимо использовать ваш действующий ключ.

С вопросами и замечаниями ждем вас на нашем форуме.

Афанасьев Евгений

Беляев Анатолий