0

I am currently trying to follow this tutorial on "Signing an XML Document". So far I have been able to run the c14n algorithm and calculate the appropriate DigestValue.

However, I cannot seem to get the correct value for the SignatureValue field. I am currently calculating the digest value for the SignedInfo tag correctly as it matches the author's calculation but the problem that I cannot seem to solve is that signing the hash results in a different string that the expected one.

My current script looks like this:

<?php
$c14n = '<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
        <DigestValue>UWuYTYug10J1k5hKfonxthgrAR8=</DigestValue>
      </Reference>
    </SignedInfo>';

echo sha1($c14n).PHP_EOL.PHP_EOL; // a25a06d339d68b625cd7383a932357889956a54e OK !!

$data = sha1($c14n); // sha1($c14n,true) wont work either

$pkeyid = openssl_pkey_get_private("file://key.txt",'password'); // PK

// compute signature
openssl_sign($data, $signature, $pkeyid);

// free the key from memory
openssl_free_key($pkeyid);

// Result should be: TSQUoVrQ0kg1eiltNwIhKPr ...
echo base64_encode($signature); // Wont match!
?>

I am not sure whether the raw version or the hex one should be signed. Either way, the values obtained do not match, so there must be something else. I used openssl and came up with another different result, so I am very confused.

$ cat signedinfo.txt 
<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
      <Reference URI="">
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
        <DigestValue>UWuYTYug10J1k5hKfonxthgrAR8=</DigestValue>
      </Reference>
    </SignedInfo>
$ openssl dgst -sha1 -sign key.txt -out sign-ID.bin signedinfo.txt
$ php -r 'echo base64_encode(file_get_contents("sign-ID.bin"));'
bFGI6KjCXtu1mAPFzvfyPkhZU5/CsYIR2IIQ0/n+6zZYqVAuIqjtgrc+xQXOQhf5RTcVR2zr/teWSSjmQvaHzFn4NhXGxvEMng1Kqg04XpQS41OLgAjaNMRW5iU4cRwFW1VMPAiHkjZggDkDgG8pSn6zdQh0yUEgo+86zJI6kbQ=

I have been able to check that openssl generates a diferent digest than the expected (a25a06d339d68b625cd7383a932357889956a54e) if run from the terminal. I have double checked, triple checked and made sure that the file contents where exactly like the $c14n variable.

What exactly am I doing wrong?

Notes: I did check the XML that the author provided on this website and it seems to be okay. The private key used is the same as the one on the How-To.

wtf8_decode
  • 452
  • 6
  • 19

2 Answers2

3

I know it's a very old post, but I just stumbled over it because I am having a similar issue.

I went through the same tutorial and from what I understand, you must not calculate the sha1 for the canonicalized string, but insert it directly in the openssl_sign command. Sticking to your first example (php), it therefore should say:

$c14n = '<SignedInfo... </SignedInfo>';
// Do not use sha1 here
//$data = sha1($c14n); // sha1($c14n,true) wont work either

$pkeyid = openssl_pkey_get_private("file://key.txt",'password'); // PK

// compute signature
// openssl_sign($data, $signature, $pkeyid);   // your initial command
openssl_sign($c14n, $signature, $pkeyid, 'OPENSSL_ALGO_SHA1');   // modified version

// free the key from memory
openssl_free_key($pkeyid);

// Result should be: TSQUoVrQ0kg1eiltNwIhKPr ...
echo base64_encode($signature); // Should match now  ;-)
Sebastian
  • 109
  • 6
0

I am using the following:

1) Get a .pem file openssl pkcs12 -in certificate.pfx -nocerts -out privatekey.pem -nodes Then in PHP 2)

$certificateNode = $doc3
        ->getElementsByTagName('X509Certificate')
        ->item(0);

$content = $certificateNode->C14N(FALSE, TRUE); // <-- no exclusive, with comments

$actualDigest = base64_encode(hash('SHA1', $content, true)); 
$fp = fopen('privatekey.pem', "r");
$priv_key = fread($fp, 8192);
fclose($fp);
$passphrase = 'the pasphrase';
$res = openssl_get_privatekey($priv_key,$passphrase);
openssl_private_encrypt($actualDigest, $crypttext, $res);
$signatureValueBase64 = base64_encode($crypttext);

I hope this will help you. jrcscarrillo@gmail.com

  • Juan, thanks for the answer. It still does not seem to work. The returned value is D8U622bvnhiEXBTfcD90... which is not the right one. I am computing the digest value for the SignedInfo tag correctly but can not get the signing step working. – wtf8_decode Oct 30 '14 at 10:56
  • Converting the string to Base64 should be the last step. $actualDigest shouldn't be Base64 encoded ! – canolucas Dec 16 '14 at 14:48