【问题标题】:Calculating SignatureValue from SignedInfo从 SignedInfo 计算 SignatureValue
【发布时间】:2014-10-29 13:58:32
【问题描述】:

我目前正在尝试关注this tutorial 的“签署 XML 文档”。到目前为止,我已经能够运行 c14n 算法并计算出合适的DigestValue

但是,我似乎无法获得 SignatureValue 字段的正确值。我目前正在正确计算 SignedInfo 标记的摘要值,因为它与作者的计算相匹配,但我似乎无法解决的问题是,对哈希进行签名会导致与预期不同的字符串。

我当前的脚本如下所示:

<?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!
?>

我不确定应该对原始版本还是十六进制版本进行签名。无论哪种方式,获得的值都不匹配,所以一定有别的东西。我用了openssl,得到了另一个不同的结果,所以我很困惑。

$ 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=

如果从终端运行,我已经能够检查 openssl 生成的摘要是否与预期的 (a25a06d339d68b625cd7383a932357889956a54e) 不同。我已经仔细检查,三重检查并确保文件内容与$c14n 变量完全相同。

我到底做错了什么?

注意事项: 我确实检查了作者在this website 上提供的 XML,似乎没问题。 private key used 与操作指南中的相同。

【问题讨论】:

    标签: openssl digest xml-signature


    【解决方案1】:

    我知道这是一篇很老的帖子,但我只是偶然发现了它,因为我遇到了类似的问题。

    我完成了相同的教程,据我了解,您不能计算规范化字符串的 sha1,而是直接将其插入 openssl_sign 命令。坚持你的第一个例子(php),因此它应该说:

    $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  ;-)
    

    【讨论】:

      【解决方案2】:

      我正在使用以下内容:

      1) 获取 .pem 文件 openssl pkcs12 -in certificate.pfx -nocerts -out privatekey.pem -nodes 然后在 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);
      

      我希望这会对你有所帮助。 jrcscarrilo@gmail.com

      【讨论】:

      • 胡安,谢谢你的回答。它似乎仍然不起作用。返回值是 D8U622bvnhiEXBTfcD90... 这不是正确的。我正在正确计算 SignedInfo 标记的摘要值,但无法使签名步骤正常工作。
      • 将字符串转换为 Base64 应该是最后一步。 $actualDigest 不应该是 Base64 编码的!
      猜你喜欢
      • 2021-04-01
      • 1970-01-01
      • 2021-07-25
      • 1970-01-01
      • 2023-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-12
      相关资源
      最近更新 更多