【问题标题】:How to load a DSA Key from KeyInfo using openSSL如何使用 openSSL 从 KeyInfo 加载 DSA 密钥
【发布时间】:2014-11-29 00:18:21
【问题描述】:

我正在尝试使用 openSSL 验证 XML 数字签名。当我实际使用 EVP_VerifyFinal 时,我得到错误代码 0D07209B (ASN1_get_object:too long)。他就是我从证书中加载 KeyInfo 的方式:

<KeyInfo>
 <KeyValue>
  <DSAKeyValue>
    <P>/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFw==</P>
    <Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q>
    <G>Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==</G>
    <Y>butK4tBy8dwSJFjTRpTvmYZYnsDGO4CzMVgcD8EQ2UJrQZd0ZapQI/Ea2DZzQBTFjjdnNkFuNOtjVI615lhiBQ==</Y>
  </DSAKeyValue>
 </KeyValue>
</KeyInfo>

我的(伪代码)加载 PDSA 的方法:

 pDSA = DSA_new();
 pDSA.p = BN_bin2bn(base64Decode(text of P element));
 pDSA.q = BN_bin2bn(base64Decode(text of Q element));
 pDSA.g = BN_bin2bn(base64Decode(text of G element));
 pDSA.pub_key = BN_bin2bn(base64Decode(text of Y element));

在这之后使用 BN_bn2hex 可以得到值:

p: FCA682CE8E12CABA26EFCCF7110E526DB078B05EDECBCD1EB4A208F3AE1617AE01F35B91A47E6DF63413C5E12ED0899BCD132ACD50D99151BDC43EE737592E17
q: 962EDDCC369CBA8EBB260EE6B6A126D9346E38C5
g: 678471B27A9CF44EE91A49C5147DB1A9AAF244F05A434D6486931D2D14271B9E35030B71FD73DA179069B32E2935630E1C2062354D0DA20A6C416E50BE794CA4
pub_key: 6EEB4AE2D072F1DC122458D34694EF9986589EC0C63B80B331581C0FC110D9426B41977465AA5023F11AD836734014C58E376736416E34EB63548EB5E6586205

验证签名:

  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
      <Reference URI="">
        <Transforms>
         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
       </Transforms>
       <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>      
       <DigestValue>uooqbWYa5VCqcJCbuymBKqm17vY=</DigestValue>
      </Reference>
    </SignedInfo>    
    <SignatureValue>L4h4TFDj5rDKCHm0D+aH2LeSfAMV2t0V5S91Afu6U0NlfpjxdTXRUA==</SignatureValue> 
    <KeyInfo>
     <KeyValue>
      <DSAKeyValue>         
       <P>/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFw=</P> 
       <Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q> 
       <G>Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==</G>
       <Y>POKkQtGuazaz6OEWi91P4C9z1tREpeP9f7L3piRD/3TiNFzvt0BmYzNO0CoPSjEVTXYKIRo/+HXK6MhBRk2eUw=</Y>
     </DSAKeyValue>
    </KeyValue>
   </KeyInfo>
  </Signature>
</Envelope>

这是我验证签名的方式:

  pkey = EVP_PKEY_new;
  EVP_PKEY_set1_DSA(pkey, pdsa)
  EVP_MD_CTX_init(ctx);
  EVP_VerifyInit(@ctx, EVP_sha1)
  EVP_VerifyUpdate(@ctx, digest of SignedInfo);
  bytes = unbase64(text of SignatureValue)
  EVP_VerifyFinal(@ctx, btes, length(bytes)}, pKey);

验证最终返回以下错误:

0D07207B:asn1 encoding routines:ASN1_get_object:header too long)
0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header)
0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error)

所以要么我错误地加载了证书或签名值。根据规范,签名值是“签名值由两个八位字节流连接的 base64 编码组成,这两个八位字节流分别由值 r 和 s 按该顺序的八位字节编码产生” - 但我在任何地方都找不到openSSL 文档应该将什么传递给 EVP_VerifyInit 以获得 DSA 签名的签名值。据我所知,我在一封电子邮件存档中找到了一个指向 RFC 3279 的参考,它似乎指定了相同的格式(Dss-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }

【问题讨论】:

    标签: openssl digital-signature dsa


    【解决方案1】:

    是的,您实际上已经回答了自己的问题。您应该将接收到的签名分成两部分(XML-DSig 指定的两个 20 字节值),然后创建 ASN.1 签名。它确实包含以下 ASN.1 DER 结构:

    Dss-Sig-Value ::= SEQUENCE {
        r INTEGER,
        s INTEGER
    }
    

    DER 是一个“标签、长度、值”的结构。现在一个序列有标签编码30,然后是两个编码整数的DER长度,r和s是INTEGER值,用标签02编码,然后是的长度有符号大端整数的最短表示

    因为编程 ASN.1 充其量是棘手的,而最坏的情况是完全易受攻击(OpenSSL 1.0.1 之前的所有版本都有内存损坏错误,旧的 Windows NT 身份验证完全被破坏)我将展示如何创建它使用伪代码的结构:

    • 将 base64 转换为字节(无符号字符,其中 40 个)
    • 将前 20 个字节用于rData,第二个字节用于sData(或者更好,分成两半)
    • 去除rDatasData 的所有前导(最左边)00 值字节
    • 如果前导字节为80 或以上,则在rData 前加上00 字节和sData
    • 创建一个结构 30 LL { 02 LL { rData } 02 LL { sData } },其中 LL 是大括号之间结构的字节大小

    我会留给您执行编码。请注意,只要 LL 的值绝不是 128 或更大(DSA 应该是这种情况),这将起作用。

    顺便说一句,您可能想切换到 ECDSA;请注意,它可能需要与上述相同的结构。另请注意,我看不到摘要的输入是否正确,但它至少应该解决 ASN.1 错误。


    最后你应该得到this structure作为你当前的签名(注意这个有点无聊,因为它不包含00的任何初始值并且它不包含任何等于或更高的初始字节也超过 128)。

    【讨论】:

    • 真是太好了,谢谢。我找不到我应该使用哪种 asn.1 编码,我非常感谢您为此所做的工作。我现在可以验证签名了。但是我有一个后续问题 - 当我使用 EVP_MD_CTX_init(ctx)、EVP_SignInit(ctx, EVP_sha256)、EVP_SignUpdate(@ctx, bytes of digest)、EVP_SignFinal(ctx, sig, len, pKey) 签名时,我得到一个 len 70,这是一个 asn.1 结构,包含 2 个 32 字节密钥,而不是 20 字节密钥。但它们应该只有 20 个字节?
    • 可能取决于使用的密钥。 20 字节对应于参数 q 的 160 位值。您现在可能正在使用 2048 / 256 位的密钥。很长一段时间以来,只有 1024 / 160 位大小的 DSA 密钥被使用,而 XML 规范似乎仍然存在于那个时代:)
    猜你喜欢
    • 2013-12-02
    • 2019-05-03
    • 1970-01-01
    • 2013-05-07
    • 2017-05-16
    • 1970-01-01
    • 2020-03-15
    • 2015-11-12
    • 2012-04-07
    相关资源
    最近更新 更多