The implementation of the normalization algorithm for XML SMEV 3?

According to methodical recommendations on work with the SIEC when signing the XML fragments to the electronic signature in the form of XMLDSig, be sure to use the normalization urn://smev-gov-ru/xmldsig/transform.

In the normalization algorithm I absolutely do not understand the items related to namespace'AMI, namely:
  1. To remove the namespace prefix, which at current levels are declared, but not used.
  2. Check that the current element namespace is declared, or above the tree or the current element. If not declared, to declare in the current element.
  3. Namespace prefix for elements and attributes should be replaced by automatically generated. Generated the prefix consists of the literal "ns", and a sequence number generated prefix in the part treated as an XML fragment, starting with one. When generating prefixes should be eliminated for their redundancy.


This is an example of the correct xml obtained after normalization.

The source XML*:

<ns:senderprovidedrequestdata id="SIGNED_BY_CONSUMER" xmlns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" xmlns:ns="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" xmlns:ns2="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1">
<ns:messageid>db0486d0-3c08-11e5-95e2-d4c9eff07b77</ns:messageid>
<ns2:messageprimarycontent>
 <ns1:breachrequest xmlns:ns1="urn://x-artefacts-gibdd-gov-ru/breach/root/1.0" xmlns:ns2="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0" xmlns:ns3="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1" id="PERSONAL_SIGNATURE">
<ns1:requestedinformation>
<ns2:regpointnum>Т785ЕС57</ns2:regpointnum>
</ns1:requestedinformation>
<ns1:governance>
 <ns2:name>traffic police of the Russian Federation</ns2:name>
<ns2:code>GIBDD</ns2:code>
<ns2:officialperson>
<ns3:familyname>Zagurskii</ns3:familyname>
<ns3:firstname>Andrew</ns3:firstname>
<ns3:patronymic>Petrovich</ns3:patronymic>
</ns2:officialperson>
</ns1:governance>
</ns1:breachrequest>
</ns2:messageprimarycontent>
<ns:testmessage>
</ns:testmessage></ns:senderprovidedrequestdata>


After normalization*:

<ns1:senderprovidedrequestdata xmlns:ns1="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/1.1" id="SIGNED_BY_CONSUMER">
<ns1:messageid>db0486d0-3c08-11e5-95e2-d4c9eff07b77</ns1:messageid>
 <ns2:messageprimarycontent xmlns:ns2="urn://x-artefacts-smev-gov-ru/services/message-exchange/types/basic/1.1">
 <ns3:breachrequest xmlns:ns3="urn://x-artefacts-gibdd-gov-ru/breach/root/1.0" id="PERSONAL_SIGNATURE">
<ns3:requestedinformation>
 <ns4:regpointnum xmlns:ns4="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">Т785ЕС57</ns4:regpointnum>
</ns3:requestedinformation>
<ns3:governance>
 <ns5:name xmlns:ns5="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">Russian traffic police</ns5:name>
 <ns6:code xmlns:ns6="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">GIBDD</ns6:code>
 <ns7:officialperson xmlns:ns7="urn://x-artefacts-gibdd-gov-ru/breach/commons/1.0">
 <ns8:familyname xmlns:ns8="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1">Zagurskii</ns8:familyname>
 <ns9:firstname xmlns:ns9="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1">Andrew</ns9:firstname>
 <ns10:patronymic xmlns:ns10="urn://x-artefacts-smev-gov-ru/supplementary/commons/1.0.1">Petrovich</ns10:patronymic>
</ns7:officialperson>
</ns3:governance>
</ns3:breachrequest>
</ns2:messageprimarycontent>
<ns1:testmessage>
</ns1:testmessage></ns1:senderprovidedrequestdata>


* Newlines and indentation added for easy viewing.

An exemplary implementation of the (model if it is unknown) of the algorithm presented in the methodological recommendations on the Java Apache Santuario.

Has anyone implemented the translation algorithm in PHP?

Or at least was able to delve into those rules, according to which there should be a transformation? Not the language of GOST-like documentation, and human?
June 8th 19 at 17:03
1 answer
June 8th 19 at 17:05
Solution
For future searchers: here's a monster I got (PHP 7.1). It passes all tests from Mr 3.5, but maybe some nuances are not considered.
<?php
$in = new DOMDocument();
$in--->load($argv[1]);

$out = new XMLWriter();
$out->openMemory();

$index = 0;
$stack = [null, [$in->documentElement, []]];
while (count($stack)) {
 $item = array_pop($stack);
 if ($item === null) {
$out->text(");
$out->endElement();
continue;
}
 [$node, $nsList] = $item;
 if ($node->nodeType == XML_ELEMENT_NODE) {
 // Let the magic begin! ;)
 // The element itself.
 if ($node->namespaceURI !== null) {
 $thisLevel = false;
 if (($nsList[$node->namespaceURI] ?? null) === null) {
 $thisLevel = true;
 $nsList[$node->namespaceURI] = 'ns' . ++$index;
}
$out->startElement("{$nsList[$node->namespaceURI]}:{$node->localName}");
 if ($thisLevel) {
 $out->writeAttribute("xmlns:{$nsList[$node->namespaceURI]}", $node->namespaceURI);
}
}
 else {
$out->startElement($node->localName);
}
 // Attributes.
 $attrs = iterator_to_array($node->attributes);
 usort($attrs, function($a, $b) {
 if ($a->namespaceURI !== null && $b->namespaceURI === null) return -1;
 else if ($a->namespaceURI === null && $b->namespaceURI !== null) return 1;
 else return strcmp($a->namespaceURI, $b->namespaceURI) ?: strcmp($a->localName, $b->localName);
});
 foreach ($attrs as $attr) {
 if ($attr->namespaceURI !== null && ($nsList[$attr->namespaceURI] ?? null) === null) {
 $nsList[$attr->namespaceURI] = 'ns' . ++$index;
 $out->writeAttribute("xmlns:{$nsList[$attr->namespaceURI]}", $attr->namespaceURI);
}
}
 foreach ($attrs as $attr) {
 if ($attr->namespaceURI !== null) {
 $out->writeAttribute("{$nsList[$attr->namespaceURI]}:{$attr->localName}", $attr->nodeValue);
}
 else {
 $out->writeAttribute($attr->localName, $attr->nodeValue);
}
}
}
 else if ($node->nodeType == XML_TEXT_NODE && strlen(trim($node->nodeValue))) {
$out->text($node->nodeValue);
}

 if ($node->lastChild !== null) {
 $stack[] = null;
 for ($node = $node->lastChild; $node !== null; $node = $node->previousSibling) {
 $stack[] = [$node, $nsList];
}
}
}

echo $out->outputMemory(), "\n";

?>
I note the decision of your work, though not tested. - Kamron12 commented on June 8th 19 at 17:08
and thank you. He tried to make friends with SMEV and PHP, now plugging just the same with the caption, transformation smelovsky that's done, now gostovskaya cryptography need to understand.

But, if I may, I have another question. I was under the impression that after this transformation, initially requested the canonization of a special role does not play. Do not share your experience in this matter? - cade commented on June 8th 19 at 17:11
We to multiple types of information СМЭВ3 have successfully connected, so ready to share the experience.

In the file "a List of typical errors returned to the participant when working in SIEC 3.0" listed the following algorithm for obtaining the signature:

"Check the signature algorithm. The General sequence should be (for example SendRequest):
1. Canonicalization site content SenderProvidedRequestData;
2. Normalization;
3. The calculation of the hash;
4. Formation of ES-S:
a. write the contents of the hash in CallerInformationSystemSignature\Signature\SignedInfo\DigestValue
b. canonicalization, normalization of element CallerInformationSystemSignature\Signature\SignedInfo
c. calculate the hash of the element CallerInformationSystemSignature\Signature\SignedInfo
d. the hash signature CallerInformationSystemSignature\Signature\SignedInfo
e. record the signature value in CallerInformationSystemSignature\Signature\SignatureValue
f. data entry certificate in CallerInformationSystemSignature\Signature\KeyInfo\X509Data\X509Certificate"


However, I can say that it is not entirely true. In any case, forming the signature on this algorithm I consistently received error SignatureVerificationFaultException.

By gugleniya and experimentation I got the following working algorithm:

1. Canonicalization node SenderProvidedRequestData
$item = DOMDocument::getElementsByTagName('SenderProvidedRequestData')->item(0);
$canonData = $item->C14N(true);


2. SMEV-tranformative
$transformed = DOMManager::smevTransform($canonData);


3. Get the hash
$digestValue = CryptoClient::getHash($transformed);


4. The entry hash in CallerInformationSystemSignature\Signature\SignedInfo\DigestValue

5. Canonicalization node SignedInfo (Attention! Only canonicalization without transformation)
$item = DOMDocument::getElementsByTagName('SignedInfo')->item(0);
$canonSignedInfo = $item->C14N(true);


6. Obtaining signatures:
$signatureValue = CryptoClient::getSignature($canonSignedInfo);

7. Record the signature value in CallerInformationSystemSignature\Signature\SignatureValue

This algorithm was painted by some kind person on the website blog.gin.su. However, it is not currently available. - Kamron12 commented on June 8th 19 at 17:14

Find more questions by tags PHPXMLXSD