Взаимодействие с ГИС ГМП посредством веб-сервисов

ГИС ГМП - Государственная информационная система о государственных и муниципальных платежах. Взаимодействие внешних информационных систем с ГИС ГМП происходит посредством веб-сервисов, предоставляемых единой системой межведомственного электронного взаимодействия (СМЭВ). В данной статье речь пойдет о том как организовать данное взаимодействие средствами Java, Spring Framework и КриптоПро JCP.


1. Что необходимо для взаимодействия с ГИС ГМП?

  • Электронный ключ eToken с хранилищем ключей и сертификатов для электронно-цифровой подписи сообщений, отправляемых в ГИС ГМП
  • Участник взаимодействия должен быть подключен к СМЭВ
  • Участник взаимодействия должен быть зарегистрирован в ГИС ГМП и иметь уникальный регистрационный номер


2. Установка КриптоПро JCP

Для успешного взаимодействия, сообщения, отправляемые в ГИС ГМП, должны быть подписаны электронно-цифровой подписью, для чего используется средство криптографической защиты КриптоПро JCP. Для корректной работы JCP необходимо:
  • скачать дистрибутив с официального сайта и установить в директорию с используемой JRE
  • установить eToken PKI client для нужной версии ОС и выгрузить с eToken ключа хранилище ключей на жесткий диск
  • cкачать и подключить библиотеки WSS4J к проекту
  • скачать и подключить библиотеки JCP.jar, JCPxml.jar, xmlsec-1.5.0.jar, xalan-2.7.0.jar, XMLDSigRI.jar к проекту (большинство из них можно найти в папке с установленным JCP)


3. WSDL to Java

Для формирования сообщений, отправляемых веб-сервису, предоставляемому СМЭВ для взаимодействия с ГИС ГМП, необходимо конвертировать wsdl веб-сервиса в java классы, объекты которых в дальнейшем будут использованы для построения этих сообщений. Сделать это можно с помощью команды wsdl2java Apache CXF. В результате должны получиться полностью аннотированные java классы такого типа:
@javax.xml.bind.annotation.XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD)
@javax.xml.bind.annotation.XmlType(name = "", propOrder = {"message", "messageData"})
@javax.xml.bind.annotation.XmlRootElement(name = "UnifoTransferMsg")
public class UnifoTransferMsg {
    @javax.xml.bind.annotation.XmlElement(name = "Message", namespace = "http://smev.gosuslugi.ru/rev111111", required = true)
    protected ru.gosuslugi.smev.rev111111.MessageType message;
    @javax.xml.bind.annotation.XmlElement(name = "MessageData", namespace = "http://smev.gosuslugi.ru/rev111111", required = true)
    protected ru.gosuslugi.smev.rev111111.MessageDataType messageData;

    public UnifoTransferMsg() { /* compiled code */ }

    public ru.gosuslugi.smev.rev111111.MessageType getMessage() { /* compiled code */ }

    public void setMessage(ru.gosuslugi.smev.rev111111.MessageType value) { /* compiled code */ }

    public ru.gosuslugi.smev.rev111111.MessageDataType getMessageData() { /* compiled code */ }

    public void setMessageData(ru.gosuslugi.smev.rev111111.MessageDataType value) { /* compiled code */ }
}
После этого нужно добавить полученные классы в jar архив и подключить его к проекту.


4. Разработка клиента к веб-сервису

4.1 WebServiceTemplate

WebServiceTemplate это центральный класс в Spring-WS API, который отвечает за отправку и получение сообщений от веб-сервиса. Для использования WebServiceTemplate объявим его как bean в конфигурационном файле Spring'а:
<bean id="smevGISGMPWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <property name="messageFactory">
            <bean class="org.springframework.ws.pox.dom.DomPoxMessageFactory"/>
        </property>
        <property name="defaultUri" value="http://smev-mvf.test.gosuslugi.ru:7777/gateway/services/SID0003218?WSDL"/>
        <property name="messageSender" ref="smevGISGMPMessageSender"/>
</bean>

<bean id="smevGISGMPMessageSender" class="org.springframework.ws.transport.http.HttpUrlConnectionMessageSender"/>
Рассмотрим подробнее каждое из свойств smevGISGMPWebServiceTemplate:
  • messageFactory отвечает за формирование сообщений, отправляемых веб-сервису

  • Свойство messageFactory должно быть связано с одной из реализаций интерфейса WebServiceMessageFactory. Spring предоставляет на выбор три реализации данного интерфейса:
    В нашем случае используем DomPoxMessageFactory, т.к. остальные реализации messageFactory добавляют в сообщение лишние SOAP заголовки, которые уже будут добавлены нами в ручную перед подписыванием нашего сообщения электронно-цифровой подписью.
  • messageSender отвечает за отправку сообщений веб-сервису

  • Свойство messageSender должно быть связано с одной из реализаций интерфейса WebServiceMessageSender. Spring предлагает две реализации данного интерфейса:
    Мы будем использовать HttpUrlConnectionMessageSender реализацию, т.к. нам не требуется никакой расширенной функциональности для нашего messageSender'a.
  • defaultUri определяет адрес веб-сервиса, с которым мы будем взаимодействовать

4.2 Формирование сообщения

После того как мы сконфигурировали smevGISGMPWebServiceTemplate можно перейти к формированию, подписи и отправке сообщения ГИС ГМП. Допустим нам необходимо отправить сообщение для импорта начисления в ГИС ГМП, пример такого сообщения:
  <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="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">
   <SOAP-ENV:Body wsu:Id="body">
      <n1:UnifoTransferMsg xsi:schemaLocation="http://roskazna.ru/SmevUnifoService/ MethodMsg.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:n1="http://roskazna.ru/SmevUnifoService/" xmlns:n3="http://www.altova.com/samplexml/other-namespace" xmlns:pirq="http://roskazna.ru/xsd/PGU_ImportRequest" xmlns:smev="http://smev.gosuslugi.ru/rev111111" xmlns:unifo="http://rosrazna.ru/xsd/SmevUnifoService" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
         <smev:Message>
            <smev:Sender>
               <smev:Code>RKZN35001</smev:Code>
               <smev:Name>Казначейство России</smev:Name>
            </smev:Sender>
            <smev:Recipient>
               <smev:Code>RKZN35001</smev:Code>
               <smev:Name>Казначейство России</smev:Name>
            </smev:Recipient>
            <smev:TypeCode>GFNC</smev:TypeCode>
            <smev:Status>REQUEST</smev:Status>
            <smev:Date>2012-07-25T09:40:48.0Z</smev:Date>
            <smev:ExchangeType>6</smev:ExchangeType>
         </smev:Message>
         <smev:MessageData>
            <smev:AppData>
               <unifo:ImportData>
                  <pirq:ImportRequest>
                     <PostBlock>
                        <ID>13454</ID>
                        <TimeStamp>2012-07-25T09:40:47.0Z</TimeStamp>
                        <SenderIdentifier>00022</SenderIdentifier>
                     </PostBlock>
                     <Charge SupplierBillID="19255500000000000079">
                        <BillDate>2011-03-10</BillDate>
                        <SupplierOrgInfo>
                           <Name>Управление Федеральной миграционной службы по Республике Татарстан</Name>
                           <INN>1655102196</INN>
                           <KPP>165501001</KPP>
                           <Account>
                              <Account>40101810800000010001</Account>
                              <Bank>
                                 <Name>ГРКЦ НБ РТ г. Казани</Name>
                                 <BIK>049205001</BIK>
                              </Bank>
                           </Account>
                        </SupplierOrgInfo>
                        <BillFor>Госпошлина за выдачу загранпаспорта</BillFor>
                        <TotalAmount>100000</TotalAmount>
                        <ChangeStatus>1</ChangeStatus>
                        <TreasureBranch>УФК по Республике Татарстан</TreasureBranch>
                        <KBK>19210806000011000110</KBK>
                        <OKATO>92401000000</OKATO>
                        <BudgetIndex>
                           <Status>0</Status>
                           <PaymentType>0</PaymentType>
                           <Purpose>0</Purpose>
                           <TaxPeriod>0</TaxPeriod>
                           <TaxDocNumber>0</TaxDocNumber>
                           <TaxDocDate>0</TaxDocDate>
                        </BudgetIndex>
                        <ApplicationID>455555</ApplicationID>
                        <UnifiedPayerIdentifier>0100000000006667775555643</UnifiedPayerIdentifier>
                     </Charge>
                  </pirq:ImportRequest>
               </unifo:ImportData>
            </smev:AppData>
         </smev:MessageData>
      </n1:UnifoTransferMsg>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
То есть все что необходимо для отправки сообщения это:
  • сформировать его используя объекты java классов, которые мы получили из wsdl веб-сервиса
  • добавить в сообщение SOAP заголовки, т.к. с помощью SOAP протокола происходит обмен сообщениями с сервисом ГИС ГМП
  • добавить в него электронно-цифровую подпись с помощью КриптоПро JCP
Для формирования объекта сообщения UnifoTransferMsg, понятного сервису ГИС ГМП, в нашем случае мы используем следующий метод:
public UnifoTransferMsg createImportChargeRequestMsg(ChargeType charge) throws DatatypeConfigurationException {
        UnifoTransferMsg msg = new UnifoTransferMsg();

        GregorianCalendar c = new GregorianCalendar();
        c.setTime(new Date());
        XMLGregorianCalendar date = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);

        MessageType messageType = initServiceData(date);
        MessageDataType messageDataType = new MessageDataType();

        AppDataType appData = new AppDataType();
        ImportData importData = new ImportData();
        ImportRequest importRequest = new ImportRequest();

        PostBlock postBlock = getPostBlockInfo(date);

        importRequest.setPostBlock(postBlock);
        importRequest.setCharge(charge);
        importData.setImportRequest(importRequest);
        appData.setImportData(importData);
        messageDataType.setAppData(appData);

        msg.setMessage(messageType);
        msg.setMessageData(messageDataType);

        return msg;
}

private MessageType initServiceData(XMLGregorianCalendar date) {
        MessageType messageType = new MessageType();

        OrgExternalType senderType = gisGMPDAO.getSenderType().get(0);
        OrgExternalType recipietnType = gisGMPDAO.getRecipientType().get(0);

        messageType.setSender(senderType);
        messageType.setRecipient(recipietnType);
        messageType.setTypeCode(TypeCodeType.GFNC);
        messageType.setStatus(StatusType.REQUEST);
        messageType.setDate(date);
        messageType.setExchangeType(EXCHANGE_TYPE);

        return messageType;
}

private PostBlock getPostBlockInfo(XMLGregorianCalendar date) {
        UUID requestId = UUID.randomUUID();

        PostBlock postBlock = new PostBlock();
        postBlock.setID(requestId.toString());
        postBlock.setSenderIdentifier(gisGMPDAO.getSenderIdentifier());
        postBlock.setTimeStamp(date);
        return postBlock;
}
Где на вход методу createImportChargeRequestMsg поступает объект начисления, содержащий все необходимые данные начисления в контексте нашей системы и возвращает необходимый нам объект UnifoTransferMsg веб-сервиса. Сам процесс формирования объекта UnifoTransferMsg не является ключевым и для разных информационных систем может быть индивидуальным.
Далее необходимо преобразовать наш объект UnifoTransferMsg в объект SOAPMessage, для последующего добавления электронно-цифровой подписи в наше сообщение:
public SOAPMessage unifoTransferToSOAPMessage(UnifoTransferMsg msg) throws ParserConfigurationException, JAXBException, SOAPException {
        SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        JAXBContext jaxbCtx = JAXBContext.newInstance(UnifoTransferMsg.class, Include.class);

        jaxbCtx.createMarshaller().marshal(msg, document);
        soapMessage.getSOAPBody().addDocument(document);

        return soapMessage;
}

4.3 Подпись сообщения с помощью КриптоПро JCP

Процесс подписи SOAP сообщения с помощью JCP подробно изложен на официальном сайте, но лично у меня с данным примером возникали некоторые проблемы, поэтому вот рабочий вариант класса, используемого для подписи SOAP сообщения, отдельного тега SOAP сообщения и проверки корректности подписи:
public class SOAPXMLSignatureManager {
    private static final String WSSECURITY_SECEXT_URI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
    private static final String WSSECURITY_UTILITY_URI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";

    private Provider xmlDSigProvider;
    private KeyStore keyStore;
    private PrivateKey privateKey;
    private X509Certificate cert;

    private static SOAPXMLSignatureManager instance;

    public static synchronized SOAPXMLSignatureManager getInstance(){
        if (instance == null){
            instance = new SOAPXMLSignatureManager();
        }

        return instance;
    }

    private SOAPXMLSignatureManager() {
        org.apache.xml.security.Init.init();
        xmlDSigProvider = new ru.CryptoPro.JCPxml.dsig.internal.dom.XMLDSigRI();
        //путь до хранилища ключей, которое мы выгрузили предварительно с eToken
        HDImageStore.setDir(ResourceBundle.getBundle("application").getString("config.cryptoPro.keyStore.path"));

        try {
            //тип хранилища ключей, в нашем случае - HDImageStore
            keyStore = KeyStore.getInstance(ResourceBundle.getBundle("application").getString("config.cryptoPro.keyStore.type"));
            keyStore.load(null, null);
            //имя хранилища и пароль к нему
            privateKey = (PrivateKey) keyStore.getKey(
                    ResourceBundle.getBundle("application").getString("config.cryptoPro.keyStore.alias"),
                    ResourceBundle.getBundle("application").getString("config.cryptoPro.keyStore.password").toCharArray()
            );
            cert = (X509Certificate) keyStore.getCertificate(ResourceBundle.getBundle("application").getString("config.cryptoPro.keyStore.alias"));
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        }
    }
   
    public void signSOAPMessage(SOAPMessage soapMessage) throws SOAPException, WSSecurityException,
            KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, TransformationException,
            InvalidAlgorithmParameterException, MarshalException, XMLSignatureException, TransformerException, UnrecoverableKeyException {
        prepareMessageForSigning(soapMessage);

        Document doc = soapMessage.getSOAPPart().getEnvelope().getOwnerDocument();

        final Transforms transforms = new Transforms(doc);
        transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", xmlDSigProvider);

        List transformList = new ArrayList();
        Transform transformC14N = fac.newTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (XMLStructure) null);
        transformList.add(transformC14N);

        Reference ref = fac.newReference(
                "#body",
                fac.newDigestMethod("http://www.w3.org/2001/04/xmldsig-more#gostr3411", null),
                transformList,
                null,
                null
        );

        SignedInfo si = fac.newSignedInfo(
                fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null),
                fac.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411", null),
                Collections.singletonList(ref)
        );

        KeyInfoFactory kif = fac.getKeyInfoFactory();
        X509Data x509d = kif.newX509Data(Collections.singletonList(cert));
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(x509d));

        Element token = (Element) soapMessage.getSOAPHeader().getChildElements().next();
        javax.xml.crypto.dsig.XMLSignature sig = fac.newXMLSignature(si, ki);
        DOMSignContext signContext = new DOMSignContext(privateKey, token);
        sig.sign(signContext);

        Element sigE = (Element) XPathAPI.selectSingleNode(signContext.getParent(), "//ds:Signature");
        Node keyE = XPathAPI.selectSingleNode(sigE, "//ds:KeyInfo", sigE);
        token.getFirstChild().setTextContent(XPathAPI.selectSingleNode(keyE, "//ds:X509Certificate", keyE).getFirstChild().getNodeValue());
        keyE.removeChild(XPathAPI.selectSingleNode(keyE, "//ds:X509Data", keyE));
        NodeList chl = keyE.getChildNodes();

        for (int i = 0; i < chl.getLength(); i++) {
            keyE.removeChild(chl.item(i));
        }

        Node str = keyE.appendChild(doc.createElementNS(
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
                "wsse:SecurityTokenReference"
        ));
        Element strRef = (Element) str.appendChild(doc.createElementNS(
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
                "wsse:Reference"
        ));
        strRef.setAttribute(
                "ValueType",
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
        );
        strRef.setAttribute("URI", "#CertId");
        token.appendChild(sigE);
    }

    public void signElementByTag(SOAPMessage soapMessage, String tag) throws Exception{
        NodeList tagNodeList = soapMessage.getSOAPPart().getElementsByTagName(tag);
        Document newXMLDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        Node copyNode = newXMLDocument.importNode(tagNodeList.item(0), true);
        newXMLDocument.appendChild(copyNode);

        NodeList newNodeList = newXMLDocument.getElementsByTagName(tag);
        Element signedNode = (Element)newNodeList.item(0);

        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", xmlDSigProvider);

        List transformList = new ArrayList();

        Transform transform = fac.newTransform(Transform.ENVELOPED, (XMLStructure) null);
        Transform transformC14N = fac.newTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (XMLStructure) null);
        transformList.add(transform);
        transformList.add(transformC14N);

        Reference ref = fac.newReference("", fac.newDigestMethod("http://www.w3.org/2001/04/xmldsig-more#gostr3411", null), transformList, null, null);

        SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE,
                (C14NMethodParameterSpec) null),
                fac.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411", null),
                Collections.singletonList(ref));

        KeyInfoFactory kif = fac.getKeyInfoFactory();
        X509Data x509d = kif.newX509Data(Collections.singletonList(cert));
        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(x509d));

        javax.xml.crypto.dsig.XMLSignature sig = fac.newXMLSignature(si, ki);

        DOMSignContext signContext = new DOMSignContext(privateKey, signedNode);
        signContext.putNamespacePrefix(javax.xml.crypto.dsig.XMLSignature.XMLNS, "ds");
        sig.sign(signContext);

        Document doc = soapMessage.getSOAPPart().getEnvelope().getOwnerDocument();
        Node signedTag = newXMLDocument.getFirstChild();
        Node oldNode = tagNodeList.item(0);
        Node parentNode = oldNode.getParentNode();
        parentNode.removeChild(oldNode);
        Node newNode = doc.importNode(signedTag, true);
        parentNode.appendChild(newNode);
    }

    private void prepareMessageForSigning(SOAPMessage soapMessage) throws SOAPException, WSSecurityException {
        soapMessage.getSOAPPart().getEnvelope().addNamespaceDeclaration(
                "wsse",
                WSSECURITY_SECEXT_URI
        );
        soapMessage.getSOAPPart().getEnvelope().addNamespaceDeclaration(
                "wsu",
                WSSECURITY_UTILITY_URI
        );

        soapMessage.getSOAPPart().getEnvelope().addNamespaceDeclaration("ds", "http://www.w3.org/2000/09/xmldsig#");
        soapMessage.getSOAPBody().setAttributeNS(WSSECURITY_UTILITY_URI, "wsu:Id", "body");

        WSSecHeader header = new WSSecHeader();
        header.setActor("http://smev.gosuslugi.ru/actors/smev");
        header.setMustUnderstand(false);

        Element sec = header.insertSecurityHeader(soapMessage.getSOAPPart());
        Document doc = soapMessage.getSOAPPart().getEnvelope().getOwnerDocument();

        Element token = (Element) sec.appendChild(doc.createElementNS(WSSECURITY_SECEXT_URI, "wsse:BinarySecurityToken"));
        token.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
        token.setAttribute("ValueType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
        token.setAttribute("wsu:Id", "CertId");
        header.getSecurityHeader().appendChild(token);
    }

    public boolean verifySecuredMessage(SOAPMessage message) throws Exception {
        Document doc = message.getSOAPPart().getEnvelope().getOwnerDocument();
        final Element wssecontext = doc.createElementNS(null, "namespaceContext");
        wssecontext.setAttributeNS(
                "http://www.w3.org/2000/xmlns/",
                "xmlns:"+"wsse".trim(),
                "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        );
        NodeList secnodeList = XPathAPI.selectNodeList(doc.getDocumentElement(), "//wsse:Security");

        Element r = null;
        Element el;
        if( secnodeList != null&&secnodeList.getLength() > 0 ) {
            String actorAttr;
            for( int i = 0; i<secnodeList.getLength(); i++ ) {
                el = (Element) secnodeList.item(i);
                actorAttr = el.getAttributeNS("http://schemas.xmlsoap.org/soap/envelope/", "actor");
                if(actorAttr != null&&actorAttr.equals("http://smev.gosuslugi.ru/actors/smev")) {
                    r = (Element)XPathAPI.selectSingleNode(el, "//wsse:BinarySecurityToken[1]", wssecontext);
                    break;
                }
            }
        }
        if(r == null)
            return false;

        final X509Security x509 = new X509Security(r);
        if(x509 == null)
            return false;

        final X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new   ByteArrayInputStream(x509.getToken()));

        if (cert == null) {
            throw new Exception("Cannot find certificate to verify signature");
        }

        NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature");
        if (nl.getLength() == 0) {
            throw new Exception("Cannot find Signature element");
        }

        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", xmlDSigProvider);
        DOMValidateContext valContext = new DOMValidateContext(KeySelector.singletonKeySelector(cert.getPublicKey()), nl.item(0));
        javax.xml.crypto.dsig.XMLSignature signature = fac.unmarshalXMLSignature(valContext);

        return signature.validate(valContext);
    }
}
Метод signElementByTag используется в том случае, когда мы импортируем какую-либо сущность в ГИС ГМП, например начисление, тогда этот метод позволяет подписать определенную часть SOAP сообщения, которая соответствует данным импортируемой сущности.

4.4 Отправка и получение сообщения

Последний этап это непосредственно сама отправка сообщения веб-сервису ГИС ГМП и получение ответа от него. Ниже представлен метод, сожержащий в себе все этапы сообщения после формирования его данных - подпись части сообщения, подпись сообщения целиком, проверка корректности подписи сообщения, отправка и прием сообщения:
private synchronized UnifoTransferMsg sendAndRecieveMessage(UnifoTransferMsg requestMessage, String signingElementTagName) throws Exception {
        SOAPMessage importRequestSoapMsg = smevGISGMPMessagesService.unifoTransferToSOAPMessage(requestMessage);
        SOAPXMLSignatureManager signatureManager =  SOAPXMLSignatureManager.getInstance();

        if (signingElementTagName != null){
            signatureManager.signElementByTag(importRequestSoapMsg, CHARGE_ELEMENT_TAG_NAME);
        }

        signatureManager.signSOAPMessage(importRequestSoapMsg);

        if (signatureManager.verifySecuredMessage(importRequestSoapMsg)){
            JAXBContext jaxbCtx = JAXBContext.newInstance(UnifoTransferMsg.class, Include.class);
            JDOMResult result = new JDOMResult();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            importRequestSoapMsg.writeTo(outputStream);

            smevGISGMPWebServiceTemplate.sendSourceAndReceiveToResult(new StringSource(outputStream.toString()), result);

            Document resultDocument = result.getDocument();
            Element responseElement = resultDocument.getRootElement();
            Element unifoTransferMsg = responseElement.getChildren().get(1).getChildren().get(0);
            StringSource resultStringSource = new StringSource(new XMLOutputter().outputString(unifoTransferMsg));

            UnifoTransferMsg responseMsg = (UnifoTransferMsg)jaxbCtx.createUnmarshaller().unmarshal(resultStringSource);

            return  responseMsg;
        } else {
            throw new Exception(gisgmpMessages.getSignatureVerificationError());
        }
}
Метод принимает как входные параметры объект UnifoTransferMsg , содержащий в себе отправляемые данные в ГИС ГМП и название сущности импортируемой в ГИС ГМП (например Charge), если отправляется запрос на импорт, иначе signingElementTagName == null. И возвращает объект UnifoTransferMsg, содержащий данные ответа сервиса ГИС ГМП на наше сообщение.