I am integrating XADES-EPES digital signature in PHP on the UBL standard, I have read a lot about it and in several repositories they do the signing in different ways, well, what goes inside the DiguestValue and SignatureValue , finally leaving me with the doubt that : information really goes into those elements? .
To verify my progress and arrive at the correct signature, I am relying on this validator https://tools.chilkat.io/xmlDsigVerify.cshtml and I have opened a repository where I share my PHP code as far as I am advanced: https:// gitlab.com/siegroupmx/xmlsinger
I go to the matter....
>> How are DigestValues generated for References within SignedInfo ??
My procedure for this is simply take the Node (for example KeyInfo) canonicalized to C14N and then pass it through an Algorithm (sha1, sha256, etc...), then convert it to Base64 and put the result over DigestValue , my process is it correct ???
Simple and quick example:
Needless to say, in the example below I am skipping the process where I create the Singature tag and its inner elements.
<?php
const XMLDSIGNS= 'http://www.w3.org/2000/09/xmldsig#';
$xmlSigned= 'mixml.xml';
$xml= new DOMDocument();
$xml->preserveWhiteSpace= FALSE;
$xml->formatOutput= true;
$xml->load($xmlSigned);
$xp= new DOMXPath($xml);
$xp->registerNamespace('secdsig', self::XMLDSIGNS);
$keyinfo= $xp->query('.//secdsig:Signature/secdsig:KeyInfo');
$hash= hash("sha256", $keyinfo->item(0)->C14N(), true);
$base64= base64_encode($hash);
echo $base64; # imprime LcOJdpKtT7sgjbblkmGyYrSUslXMlw0TyCc8SOUBJQM=
?>
And in the XML:
<ds:Reference URI="#xmldsig-a837805e-9235-a8bb-7ab4-688282608259-keyinfo">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>LcOJdpKtT7sgjbblkmGyYrSUslXMlw0TyCc8SOUBJQM=</ds:DigestValue>
</ds:Reference>
....
....
<ds:KeyInfo Id="xmldsig-a837805e-9235-a8bb-7ab4-688282608259-keyinfo">
... todas las etiquetas internas....
</ds:KeyInfo>
>> How is the SignatureValue generated???
In my procedure I take the SignedInfo node canonicalized to C14N, sign them with openssl_sign() and convert the result to Base64 and put it inside the SignatureValue. Is this correct what I do ???
Quick example:
<?php
const XMLDSIGNS= 'http://www.w3.org/2000/09/xmldsig#';
$xmlOut= 'mixml.xml';
$xml= new DOMDocument();
$xml->preserveWhiteSpace= FALSE;
$xml->formatOutput= true;
$xml->load($xmlOut);
$xp= new DOMXPath($xml);
$xp->registerNamespace('secdsig', self::XMLDSIGNS);
$si= $xp->query('.//secdsig:Signature/secdsig:SignedInfo'); # signedinfo
openssl_sign($si->item(0)->C14N(), $signature, $this->privateKey, OPENSSL_ALGO_SHA256);
$dato= base64_encode($signature); # esto va en SignatureValue
$sv= $xp->query('.//secdsig:Signature/secdsig:SignatureValue'); # signaturevalue
$sv->item(0)->nodeValue= $dato;
?>
And the SignatureValue inside the XML is:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-bfcdf0f0-2655-b952-9c85-77493d9f241f">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference Id="xmldsig-bfcdf0f0-2655-b952-9c85-77493d9f241f-ref0" URI="">
<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/xmlenc#sha256"/>
<ds:DigestValue>KWOvQ3tWaygQXkkvfI6s9UO0azjQgc0YGp9JANDXYis=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#xmldsig-a837805e-9235-a8bb-7ab4-688282608259-keyinfo">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>LcOJdpKtT7sgjbblkmGyYrSUslXMlw0TyCc8SOUBJQM=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="xmldsig-bfcdf0f0-2655-b952-9c85-77493d9f241f-sigvalue">
ZGcaOhKK3Uzp64T11LHFKAy1eOkKeMpvKNNAeBFapJxbyCr8iQDkGQ2B+VQFYLWZnDneK6Cuc6uDtUw14q4mlYoarlIRVvLlsiQK0OmFrdWEM4sd62WPP2N0GpUOOh/I/BjggsqNV1OOWG2xutZNbdh4maJzxEbcLuU8SrdPuU6q38bTAw/QJyQ5F4eiRr++28sR9G3yV0nTzkZXR5QNPScdrbwrMtH/vkBx0FfV8ErzubwKyPdvQSbugmLMk8hd7Rnn5SRg8JaxQhCC5TDPC3KOmj5Nh1zHmzATZaRAbP0XNoYsDLnQtmteo5/RL2NHGvHYbP0teUF43nHXu/KAVg==
</ds:SignatureValue>
>> CONCLUSION
The validator tells me that the Signature is wrong, and that the signature of the references is also wrong :(
Link to an example https://gitlab.com/siegroupmx/xmlsinger/blob/master/examples/example_out.xml
Certificate: https://gitlab.com/siegroupmx/xmlsinger/blob/master/tmp/certificado.pem
Signature: https://gitlab.com/siegroupmx/xmlsinger/blob/master/tmp/firma.pem
Key: legal_person_evidence
After 4 days that seemed "eternal" I finally solved my problem.
The process in how the DigestValue of the References and the SignatureValue were made are totally correct, the problem was in the Canonization.
At the time of loading the signed XML, extracting a node and generating the reference was generating an incorrect opening which set canonicalization by default to "preserve spaces" which resulted in a different hash than expected, since when declaring the Transform for each Reference, a regular C14N was indicated, but not a C14N-WithComments.
Before:
Correct:
This simple detail allowed me to generate the Xades-Epes Signature without problems, now all the tools mark "pass/valid" the signature of the references as the SignatureValue.
Cheers !