Back to Home

SMEV 3. Electronic Signature of Messages in Java and CryptoPro / Alfa-Bank Blog

SMEV Β· SMEV3 Β· SMEV-3 Β· SMEV 3 Β· SMEV Β· SMEV3 Β· XMLDSig Β· XAdES Β· public key Β· public key infrastructure Β· java Β· cryptopro Β· cryptographic provider Β· cryptography Β· information security Β· digital security Β· digital signature Β· electronic certificates Β· electronic signature

SMEV 3. Electronic Signing of Messages in Java and CryptoPro

  • Tutorial


The system of interagency electronic interaction (SMEV) was conceived as a digital environment for the provision of services and the performance of state and municipal functions in electronic form.

Currently, SMEV continues to expand its capabilities and involve an increasing number of participants in the interaction.

Which turned out to be very opportune, including for commercial organizations, in particular banks, which are increasingly striving to translate their services into numbers and serialize processes.

In this article, we will talk about how to sign inquiries and verify electronic signatures of SMEV version 3.0 responses on our own, and about a couple of interesting nuances that I had to deal with.

Hello!

A question may arise. Why on your own? When for SMEV 3 there is a wholeTechnology portal where

  • all documentation and guidelines published
  • there is a section with frequently asked questions ,
  • You can download the current version of SMEV 3 client libraries,
  • examples of complete message envelopes with signatures are provided,
  • you can even check your message online or from an example for compliance with the SMEV service schemes and for the validity of its electronic signature

That's right, the portal is certainly extremely useful, and you can and should use all of its tips and tools, but here we will write your own Java code.

For the simple reason that we already have our own information system that works with XMLDSig, XAdES electronic signature formats, which use the Apache Santuario project libraries that implement the basic security standards for XML. As well as the libraries that are part of CryptoPro JCSP , in addition to working with XML, they provide API cryptographic functions of the CryptoPro CSPI CSP .

Writing your own methods for working with SMEV 3 electronic signatures in this case looks more appropriate than deploying the complete client supplied:
FSBI Research Institute "Voskhod" (until March 21, 2016 FSUE Research Institute "Voskhod") or the integration of its individual classes and packages.

At the same time, a glimpse into the client’s open source code is always useful, and its presence alone indicates a system’s maturity and a high level of support.

Source Data Analysis


Download from the SMEV 3 portal:


If we already know how to create a regular XMLDSig or sign, for example, SMEV 2 message envelopes, then I’m most interested in how the envelope with SMEV 3 signature differs from SMEV 2.

We open an example of SMEV 3 envelope SendRequestRequestNoAttach.xml

db0486d0-3c08-11e5-95e2-d4c9eff07b77Π’785Π•Π‘57Π“Π˜Π‘Π”Π” Π Π€GIBDDΠ—Π°Π³ΡƒΡ€ΡΠΊΠΈΠΉΠΠ½Π΄Ρ€Π΅ΠΉΠŸΠ΅Ρ‚Ρ€ΠΎΠ²ΠΈΡ‡/jXl70XwnttJB5sSokwh8SaVHwo2gjgILSu0qBaLUAo=J3746ks34pOcPGQpKzc0sz3n9+gjPtzZbSEEs4c3sTwbtfdaY7N/hxXzEIvXc+3ad9bc35Y8yBhZ/BYbloGt+Q==MIIBcDCCAR2gAwIBAgIEHVmVKDAKBgYqhQMCAgMFADAtMRAwDgYDVQQLEwdTWVNURU0xMQwwCgYDVQQKEwNJUzIxCzAJBgNVBAYTAlJVMB4XDTE1MDUwNzEyMTUzMFoXDTE4MDUwNjEyMTUzMFowLTEQMA4GA1UECxMHU1lTVEVNMTEMMAoGA1UEChMDSVMyMQswCQYDVQQGEwJSVTBjMBwGBiqFAwICEzASBgcqhQMCAiMBBgcqhQMCAh4BA0MABEDoWGZlTUWD43G1N7TEm14+QyXrJWProrzoDoCJRem169q4bezFOUODcNooQJNg3PtAizkWeFcX4b93u8fpVy7RoyEwHzAdBgNVHQ4EFgQUaRG++MAcPZvK/E2vR1BBl5G7s5EwCgYGKoUDAgIDBQADQQCg25vA3RJL3kgcJhVOHA86vnkMAtZYr6HBPa7LpEo0HJrbBF0ygKk50app1lzPdZ5TtK2itfmNgTYiuQHX3+nE


Using the deductive method, it turns out that:
  • no longer used the technique of taking out the BinarySecurityToken element header with the certificate of the public key for verifying the electronic signature and referring to it through the SecurityTokenReference in the body of the Signature itself from the Signature tag in the Security, as, for example, in the MEIS 2.4.6. The certificate should now be inside Signature.
  • The second and, in fact, the most significant and important change that has a great influence on the signature process is the addition of a new proprietary transformation:



Through this transformation, the own rules for the canonicalization of SMEV are disseminated. 3.

Canonicalization is the process of bringing data with several possible forms of presentation into one normalized standard form.

Before calculating the hash of the signed attribute in the XML envelope and signing it, it is necessary to convert it to the form specified by the SEEM 3 rules.

In search of a description of the transformation urn: // smev-gov-ru / xmldsig / transform, we open Methodological recommendations 3.4.0.3 We get acquainted

with clause 4.4.2.1 Rules for the formation of electronic signature of messages

Signature format XMLDSig detached (https://www.w3.org/TR/xmldsig-core/)

Transformation, in addition to canonicalization urn: // smev-gov-ru / xmldsig / transform

Formatting requirements In the XML structure of the signature between elements text nodes, including line feeds, are not allowed.

Point of Methodical instructions 12.4. APPENDIX 4: SAMPLE IMPLEMENTATION OF TRANSFORMATION URN: // SMEV-GOV-RU / XMLDSIG / TRANSFORM
contains the Java class SmevTransformSpi.java , which implements the transformation algorithm "urn: // smev-gov-ru / xmldsig / transform", the heir to org.apache. xml.security.transforms.TransformSpi from the Apache Santuario library.

Thus, in order to ensure the canonicalization of the signed envelope SMEV 3, you can use this transformation class in your code.

The only condition and limitation in this case will be that to process the XML document when generating the signature or verifying it, you need to use precisely org.apache.xml.security.signature.XMLSignature from the Apache Santuario project.

Using the tools from javax.xml.crypto.dsig or ru.CryptoPro.JCPxml.xmldsig packages just doesn’t work out that way.

Preparation for signature according to the rules of the CMEA 3


Apache Santuario initially knows nothing about GOST cryptographic algorithms and CryptoPro cryptographic information protection tools.

The xmlsec-1.5.0.jar library in the \ org \ apache \ xml \ security \ resource \ config.xml file contains settings only for working with foreign cryptographic algorithms.

In order for him to begin to recognize and apply GOST, it is necessary to initialize it.

The old fashioned way was:

//APACHE-SANTUARIO INIT WITH CryptoPro JCP
        System.setProperty("org.apache.xml.security.resource.config", "resource/jcp.xml");
        org.apache.xml.security.Init.init();
        String cfile1 = System.getProperty("org.apache.xml.security.resource.config");
        LOGGER.log(Level.INFO, "Init class URL: " + org.apache.xml.security.Init.class.getProtectionDomain().getCodeSource().getLocation());
        LOGGER.log(Level.INFO, cfile1);

In new versions of CryptoPro JCP (JCSP), one line will perform initialization:

ru.CryptoPro.JCPxml.xmldsig.JCPXMLDSigInit.init();

Now we need to teach Apache Santuario the new transformation rules dictated by SMEV 3. To do this, we register the transformation class:

  try {
                Transform.register(SmevTransformSpi.ALGORITHM_URN, SmevTransformSpi.class.getName());
                santuarioIgnoreLineBreaks(true);
                LOGGER.log(Level.INFO, "SmevTransformSpi has been initialized");
            } catch (AlgorithmAlreadyRegisteredException e) {
                LOGGER.log(Level.INFO, "SmevTransformSpi Algorithm already registered: " + e.getMessage());
            } 

At the same time, we immediately fulfill the requirement from the Guidelines:

Formatting requirements In the XML structure of the signature between elements, text nodes, including line feeds, are not allowed.

santuarioIgnoreLineBreaks(true);
    private static final String IGNORE_LINE_BREAKS_FIELD = "ignoreLineBreaks";
/**
     * Apache Santuario privileged switch IgnoreLineBreaks property
     * 
     * @param mode
     */
    private void santuarioIgnoreLineBreaks(Boolean mode) {
        try {
            Boolean currMode = mode;
            AccessController.doPrivileged(new PrivilegedExceptionAction() {
                public Boolean run() throws Exception {
                    Field f = XMLUtils.class.getDeclaredField(IGNORE_LINE_BREAKS_FIELD);
                    f.setAccessible(true);
                    f.set(null, currMode);
                    return false;
                }
            });
        } catch (Exception e) {
            LOGGER.warning("santuarioIgnoreLineBreaks " + ExceptionUtils.getFullStackTrace(e));
        }
    }

This is done in the privileged block AccessController.doPrivileged
and through reflection, due to the particular implementation of the ignoreLineBreaks property in Santuario.

Just through setting the system property:

System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");

does not work.

Through setting up the JVM option:

-Dcom.sun.org.apache.xml.internal.security.ignoreLineBreaks=true

works.

If you look at the code of the class org.apache.xml.security.utils.XMLUtils , you can see that the ignoreLineBreaks field is static, initialized in the privileged block from the system property "org.apache.xml.security.ignoreLineBreaks" .

private static boolean ignoreLineBreaks =
        AccessController.doPrivileged(new PrivilegedAction() {
            public Boolean run() {
                return Boolean.valueOf(Boolean.getBoolean
                    ("org.apache.xml.security.ignoreLineBreaks"));
            }
        }).booleanValue();
public static boolean ignoreLineBreaks() {
        return ignoreLineBreaks;
    }

Such an implementation makes it impossible to flexibly configure in one Java process for part of the methods to ignore the line feed, and for the other part not to ignore.

That is, if one application performs XMLDsig, SMEV 2 and SMEV 3 signatures, all XML documents processed by Santuario should lose the line feed at the output.

With this property, of course, the question arises for Apache Santuario:



Signature messages SMEV 3


Everything is ready to sign the documents of SMEV 3.

The signing code is as follows:

private static final String XMLDSIG_MORE_GOSTR34102001_GOSTR3411 = "http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411";
    private static final String XMLDSIG_MORE_GOSTR3411 = "http://www.w3.org/2001/04/xmldsig-more#gostr3411";
    private static final String CANONICALIZATION_METHOD = "http://www.w3.org/2001/10/xml-exc-c14n#";
    private static final String DS_SIGNATURE = "//ds:Signature";
    private static final String SIG_ID = "sigID";
    private static final String COULD_NOT_FIND_XML_ELEMENT_NAME = "ERROR! Could not find xmlElementName = ";
    private static final String GRID = "#";
    private static final String XML_SIGNATURE_ERROR = "xmlDSignature ERROR: ";
try {
            // инициализация ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° чтСния XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            // установка Ρ„Π»Π°Π³Π°, ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‰Π΅Π³ΠΎ ΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ±Π΅Π»ΠΎΠ² Π²
            // содСрТимом элСмСнтов ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
            dbf.setIgnoringElementContentWhitespace(true);
            // установка Ρ„Π»Π°Π³Π°, ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‰Π΅Π³ΠΎ ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡƒΠ·Π»ΠΎΠ² CDATA Π²
            // тСкстовыС ΡƒΠ·Π»Ρ‹ ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
            dbf.setCoalescing(true);
            // установка Ρ„Π»Π°Π³Π°, ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‰Π΅Π³ΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΡƒ пространств ΠΈΠΌΠ΅Π½ ΠΏΡ€ΠΈ
            // ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
            dbf.setNamespaceAware(true);
// Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° содСрТимого подписываСмого Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° Π½Π° основС
            // установлСнных Ρ„Π»Π°Π³Π°ΠΌΠΈ ΠΏΡ€Π°Π²ΠΈΠ» ΠΈΠ· массива Π±Π°ΠΉΡ‚ΠΎΠ² data            DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
           Document doc = documentBuilder.parse(new ByteArrayInputStream(data));
            /*
             * Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΡƒΠ·Π»Π° подписи  Π² Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹ΠΉ XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚
             */
            // Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ подписи (Π“ΠžΠ‘Π’ Π  34.10-2001)
            final String signMethod = XMLDSIG_MORE_GOSTR34102001_GOSTR3411;
            // Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹ΠΉ ΠΏΡ€ΠΈ подписи (Π“ΠžΠ‘Π’ Π  34.11-94)
            final String digestMethod = XMLDSIG_MORE_GOSTR3411;
            final String canonicalizationMethod = CANONICALIZATION_METHOD;
            String[][] filters = {{XPath2FilterContainer.SUBTRACT, DS_SIGNATURE}};
            String sigId = SIG_ID;
                // инициализация ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° формирования ЭЦП Π² соотвСтствии с
                // Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ Π“ΠžΠ‘Π’ Π  34.10-2001
                XMLSignature sig = new XMLSignature(doc, "", signMethod, canonicalizationMethod);
                // ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ ΡƒΠ·Π»Π° подписи
                sig.setId(sigId);
                // ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠ³ΠΎ ΡƒΠ·Π»Π° XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
                Element anElement = null;
                if (xmlElementName == null) {
                    anElement = doc.getDocumentElement();
                } else {
                    NodeList nodeList = doc.getElementsByTagName(xmlElementName);
                    anElement = (Element) nodeList.item(0);
                }
                // = doc.getElementById("#AppData");
                // Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π² ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠΉ ΡƒΠ·Π΅Π» XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° ΡƒΠ·Π»Π° подписи
                if (anElement != null) {
                    anElement.appendChild(sig.getElement());
                } else {
                    throw new SignatureProcessorException(COULD_NOT_FIND_XML_ELEMENT_NAME + xmlElementName);
                }
                /*
                 * ΠžΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΏΡ€Π°Π²ΠΈΠ» Ρ€Π°Π±ΠΎΡ‚Ρ‹ с XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π² ΡƒΠ·Π΅Π» подписи этих
                 * ΠΏΡ€Π°Π²ΠΈΠ»
                 */
                // созданиС ΡƒΠ·Π»Π° ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠΉ  ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌΠΎΠ³ΠΎ
                // XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
                Transforms transforms = new Transforms(doc);
                // Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π² ΡƒΠ·Π΅Π» ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Π°Π½ΠΈΠΉ ΠΏΡ€Π°Π²ΠΈΠ» Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ
                // transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
                transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
                transforms.addTransform(SmevTransformSpi.ALGORITHM_URN);
                // Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π² ΡƒΠ·Π΅Π» подписи ссылок (ΡƒΠ·Π»Π° ),
                // ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‰ΠΈΡ… ΠΏΡ€Π°Π²ΠΈΠ»Π° Ρ€Π°Π±ΠΎΡ‚Ρ‹ с
                // XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ (обрабатываСтся Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ с Π·Π°Π΄Π°Π½Π½Ρ‹ΠΌΠΈ Π²
                // ΡƒΠ·Π»Π΅  ΠΏΡ€Π°Π²ΠΈΠ»Π°ΠΌΠΈ
                // ΠΈ Π·Π°Π΄Π°Π½Π½Ρ‹ΠΌ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ Ρ…Π΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ)
                sig.addDocument(xmlElementID == null ? "" : GRID + xmlElementID, transforms, digestMethod);
                /*
                 * Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ подписи всСго содСрТимого XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° Π½Π° основС Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π°,
                 * Π·Π°Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΡ€Π°Π²ΠΈΠ» ΠΈ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ²
                 */
                // созданиС Π²Π½ΡƒΡ‚Ρ€ΠΈ ΡƒΠ·Π»Π° подписи ΡƒΠ·Π»Π°  ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎΠ±
                // ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΎΠΌ ΠΊΠ»ΡŽΡ‡Π΅ Π½Π° основС
                // сСртификата
                sig.addKeyInfo(x509Cert);
                // созданиС подписи XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
                sig.sign(privateKey);
            // ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΡ‚ΠΎΠΊΠ°, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ осущСствляСтся запись подписанного
            // XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°
            bais = new ByteArrayOutputStream();
            // инициализация ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° копирования содСрТимого XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° Π²
            // ΠΏΠΎΡ‚ΠΎΠΊ
            TransformerFactory tf = TransformerFactory.newInstance();
            // созданиС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° копирования содСрТимого XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° Π² ΠΏΠΎΡ‚ΠΎΠΊ
            Transformer trans = tf.newTransformer();
            // ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ содСрТимого XML-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π° Π² ΠΏΠΎΡ‚ΠΎΠΊ
            trans.transform(new DOMSource(doc), new StreamResult(bais));
            bais.close();
        } catch (TransformationException e) {
            throw new SignatureProcessorException("TransformationException " + XML_SIGNATURE_ERROR + e.getMessage());
        } catch (XMLSignatureException e) {
            throw new SignatureProcessorException("XMLSignatureException " + XML_SIGNATURE_ERROR + e.getMessage());
        } catch (TransformerException e) {
            throw new SignatureProcessorException("TransformerException " + XML_SIGNATURE_ERROR + e.getMessage());
        } catch (IOException e) {
            throw new SignatureProcessorException("IOException " + XML_SIGNATURE_ERROR + e.getMessage());
        } catch (XMLSecurityException e) {
            throw new SignatureProcessorException("XMLSecurityException " + XML_SIGNATURE_ERROR + e.getMessage());
        } catch (SAXException e) {
            throw new SignatureProcessorException("SAXException " + XML_SIGNATURE_ERROR + e.getMessage());
        } catch (ParserConfigurationException e) {
            throw new SignatureProcessorException(
                    "ParserConfigurationException " + XML_SIGNATURE_ERROR + e.getMessage());
        }
        return bais.toByteArray();

The main parameters here are:

byte[] data, // XML сообщСниС Π² Π²ΠΈΠ΄Π΅ массива Π±Π°ΠΉΡ‚ΠΎΠ²
String xmlElementName, // имя элСмСнта Π² XML вмСстС с прСфиксом, Π² ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ слСдуСт Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ подпись, для Π‘ΠœΠ­Π’-3 Π² ΠΎΠ±Ρ‰Π΅ΠΌ случаС "ns2:CallerInformationSystemSignature"
String xmlElementID // ID элСмСнта Π² XML (Ссли присутствуСт) вмСстС с прСфиксом, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ слСдуСт ΠΏΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ подпись, для Π‘ΠœΠ­Π’-3 Π² ΠΎΠ±Ρ‰Π΅ΠΌ случаС "SIGNED_BY_CONSUMER"
X509Certificate certificate // сСртификат ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ подписи
PrivateKey privateKey // Π·Π°ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ подписи


Verification of the signature of the CMEE 3 message


The signature verification code is as follows:

private static final QName QNAME_SIGNATURE = new QName("http://www.w3.org/2000/09/xmldsig#", "Signature", "ds");
    private static final String SIGNATURE_NOT_FOUND = "Signature not found!";
    private static final String SIGNATURE_NOT_VALID = "Signature not valid";
    private static final String SMEV_SIGNATURE_PASSED_CORE_VALIDATION = "SmevSignature passed core validation";
    private static final String VERIFY_SIGNATURE_ON_XML_IO_EXCEPTION = "Verify signature on XML IOException: ";
    private static final String VERIFY_SIGNATURE_ON_XML_PARSER_CONFIGURATION_EXCEPTION = "Verify signature on XML ParserConfigurationException: ";
    private static final String VERIFY_SIGNATURE_ON_XML_SAX_EXCEPTION = "Verify signature on XML SAXException: ";
    private static final String VERIFY_SIGNATURE_ON_XML_XML_SIGNATURE_EXCEPTION = "Verify signature on XML XMLSignatureException: ";
    private static final String VERIFY_SIGNATURE_ON_XML_XML_SECURITY_EXCEPTION = "Verify signature on XML XMLSecurityException: ";
    private static final String ID = "Id";
   boolean coreValidity = true;
        try {
            DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
            bf.setNamespaceAware(true);
            DocumentBuilder b = bf.newDocumentBuilder();
            Document doc = b.parse(new InputSource(new ByteArrayInputStream(signedXmlData)));
            NodeList sigs = doc.getElementsByTagNameNS(QNAME_SIGNATURE.getNamespaceURI(), QNAME_SIGNATURE.getLocalPart());
            org.apache.xml.security.signature.XMLSignature sig = null;
            sigSearch: {
                for (int i = 0; i < sigs.getLength(); i++) {
                    Element sigElement = (Element) sigs.item(i);
                    String sigId = sigElement.getAttribute(ID);
                    if (sigId != null) {
                        sig = new org.apache.xml.security.signature.XMLSignature(sigElement, "");
                        break sigSearch;
                    }
                }
                throw new XMLSignatureVerificationException(SIGNATURE_NOT_FOUND);
            }
            org.apache.xml.security.keys.KeyInfo ki = (org.apache.xml.security.keys.KeyInfo) sig.getKeyInfo();
            X509Certificate certificate = ki.getX509Certificate();
            if (!sig.checkSignatureValue(certificate.getPublicKey())) {
                coreValidity = false;
                LOGGER.log(Level.INFO, SIGNATURE_NOT_VALID);
            } else {
                LOGGER.log(Level.INFO, String.format(SMEV_SIGNATURE_PASSED_CORE_VALIDATION));
            }
        } catch (IOException e) {
            throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_IO_EXCEPTION + ExceptionUtils.getStackTrace(e));
        } catch (ParserConfigurationException e) {
            throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_PARSER_CONFIGURATION_EXCEPTION + ExceptionUtils.getStackTrace(e));
        } catch (SAXException e) {
            throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_SAX_EXCEPTION + ExceptionUtils.getStackTrace(e));
        } catch (org.apache.xml.security.signature.XMLSignatureException e) {
            throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_XML_SIGNATURE_EXCEPTION + ExceptionUtils.getStackTrace(e));
        } catch (XMLSecurityException e) {
            throw new XMLSignatureVerificationException(VERIFY_SIGNATURE_ON_XML_XML_SECURITY_EXCEPTION + ExceptionUtils.getStackTrace(e));
        }
        return coreValidity;


Problems. Hash does not match


Attention!



For debugging, an example of the SMEV 3 envelope SendRequestRequestNoAttach.xml
was used. The ds: Signature element was removed from it in order to sign the message again and verify it with the original.

Despite the fact that the signature method and transformation SmevTransformSpi, taken from the Guidelines, were worked out, the output was a signed document, the signature of which was interpreted as online verification on the SMEV 3 portal as

EP-OV is not confirmed: Error checking the ES: The integrity of the ES is violated

Why

e76oVeYGapFDE+PV6glsj0XDjLHydLMd0cSkFPY8fWk=

did not match the original example:

/jXl70XwnttJB5sSokwh8SaVHwo2gjgILSu0qBaLUAo==

To diagnose the causes, a new XMLEventWriter was added to the SmevTransformSpi class in the process method.

ByteArrayOutputStream baos = new ByteArrayOutputStream();
XMLEventWriter bdst =
outputFactory.get().createXMLEventWriter(baos, ENCODING_UTF_8); 

for parallel analysis of all stages of transformation.

The normalized XML element you want to sign on looked like this:

db0486d0-3c08-11e5-95e2-d4c9eff07b77Π’785Π•Π‘57Π“Π˜Π‘Π”Π” Π Π€GIBDDΠ—Π°Π³ΡƒΡ€ΡΠΊΠΈΠΉΠΠ½Π΄Ρ€Π΅ΠΉΠŸΠ΅Ρ‚Ρ€ΠΎΠ²ΠΈΡ‡

The search for a solution showed that, firstly, the CryptoPro forum , a normalized document may look really different and accordingly its hash will be different and possibly correct.

Secondly, he brought it to GitHub , where the SmevTransformSpi class of the older version was posted.

The old version of the transformation class produced the following normalized document:

db0486d0-3c08-11e5-95e2-d4c9eff07b77Π’785Π•Π‘57Π“Π˜Π‘Π”Π” Π Π€GIBDDΠ—Π°Π³ΡƒΡ€ΡΠΊΠΈΠΉΠΠ½Π΄Ρ€Π΅ΠΉΠŸΠ΅Ρ‚Ρ€ΠΎΠ²ΠΈΡ‡

With it, the hash began to match, and the signature was successfully validated.

A comparison of the versions of the SmevTransformSpi class showed that in addition to the additional logging and diagnostic functions added in the new implementation in debug mode:

if (logger.isDebugEnabled()) {
                debugStream = new DebugOutputStream(argDst);
                dst = outputFactory.get().createXMLEventWriter(debugStream, ENCODING_UTF_8);
            } else {
                dst = outputFactory.get().createXMLEventWriter(argDst, ENCODING_UTF_8);
            }

The class from the Guidelines does not contain the required line, or contains a typo:



Missing line:

prefixMappingStack.pop();

that removes the first object from the stack with prefixes

  Stack> prefixMappingStack = new Stack>();

, which led to the incorrect operation of SmevTransformSpi.

Adding this line to the new version of SmevTransformSpi.java solved the problem.

A working transformation class and an envelope with a signature can be viewed at github.com/VBurmistrov/Smev3

results


The signing of the SMEV 3 envelopes is successful.

Messages are checked on the portal of the e-Government Government Services



And in the native application:

Read Next