如何使用openSSL从KeyInfo加载DSA密钥

如何使用openSSL从KeyInfo加载DSA密钥,openssl,digital-signature,dsa,Openssl,Digital Signature,Dsa,我正在尝试使用openSSL验证XML数字签名。当我实际使用EVP_VerifyFinal时,我得到了错误代码0D07209B(ASN1_get_对象:太长)。他是我如何从证书加载密钥信息的: <KeyInfo> <KeyValue> <DSAKeyValue> <P>/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1ku

我正在尝试使用openSSL验证XML数字签名。当我实际使用EVP_VerifyFinal时,我得到了错误代码0D07209B(ASN1_get_对象:太长)。他是我如何从证书加载密钥信息的:

<KeyInfo>
 <KeyValue>
  <DSAKeyValue>
    <P>/KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFw==</P>
    <Q>li7dzDacuo67Jg7mtqEm2TRuOMU=</Q>
    <G>Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA==</G>
    <Y>butK4tBy8dwSJFjTRpTvmYZYnsDGO4CzMVgcD8EQ2UJrQZd0ZapQI/Ea2DZzQBTFjjdnNkFuNOtjVI615lhiBQ==</Y>
  </DSAKeyValue>
 </KeyValue>
</KeyInfo>
在这之后使用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);
验证final返回以下错误:

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文档中找不到DSA签名的签名值应传递给EVP_VerifyInit的内容。我在电子邮件存档中找到一个指向RFC 3279的引用,据我所知,它似乎指定了相同的格式(Dss Sig值::=SEQUENCE{r INTEGER,s INTEGER}

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

Dss-Sig-Value ::= SEQUENCE {
    r INTEGER,
    s INTEGER
}
DER是一种“标记、长度、值”结构。现在一个序列有标记编码
30
,然后是两个编码整数的DER长度,r和s是整数值,用标记
02
编码,然后是长度后跟有符号大端整数的最短表示

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

  • 将base64转换为字节(无符号字符,共40个)
  • 前20个字节用于
    rData
    ,第二个字节用于
    sData
    (或者更好,一分为二)
  • 去除
    rData
    sData的所有前导(最左侧)
    00
    值字节
  • 如果前导字节为
    80
    或以上,请将
    00
    字节前缀为
    rData
    an
    sData
  • 创建一个结构
    30 LL{02 LL{rData}02 LL{sData}
    ,其中LL是大括号之间结构的字节大小
我将让您执行编码。请注意,只要
ll
的值永远不是128或以上(DSA应该是这种情况),这将起作用

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



最后,您应该得到当前的签名(请注意,这个签名有点无聊,因为它不包含任何
00
的初始值,也不包含任何等于或大于128的初始字节).

那真的很好,谢谢。我找不到我应该使用哪种asn.1编码,我真的很感谢你为此所做的工作。我现在可以验证签名。但是我有一个后续问题-当我签名时,使用EVP_MD_CTX_init(CTX)、EVP_SignInit(CTX、EVP_sha256)、EVP_SignUpdate(@CTX,字节摘要)、EVP_SignFinal(ctx、sig、len、pKey),我得到一个70的len,这是一个asn.1结构,包含2个32字节的键,而不是20字节的键。但它们应该只有20字节?可能取决于使用的键。20字节与参数q的160位值有关。您现在可能使用2048/256位的键。很长一段时间以来,仅使用1024/160位大小的DSA键,XML规范似乎仍然存在于那个时代:)
Dss-Sig-Value ::= SEQUENCE {
    r INTEGER,
    s INTEGER
}