【问题标题】:How to compute RSA-SHA1(sha1WithRSAEncryption) value with OpenSSL如何使用 OpenSSL 计算 RSA-SHA1(sha1WithRSAEncryption) 值
【发布时间】:2012-06-14 22:04:55
【问题描述】:

我对 RSA-SHA1 感到困惑,我认为它是 RSA_private_encrypt(SHA1(message))。 但我无法获得正确的签名值。 有什么问题吗?

【问题讨论】:

    标签: cryptography openssl rsa digital-signature xml-signature


    【解决方案1】:

    我认为您在错误的 OpenSSL 抽象级别上工作;您可能应该使用rsa.h 声明的函数RSA_sign()RSA_verify(),它们旨在用于PKCS#1 兼容的签名。

    【讨论】:

    • 感谢您的回答。我仍然不明白 RSA_sign() 还有什么期望哈希和 RSA 加密功能?这两个还不够吗?
    • 它还处理 PKCS#1 填充和 ASN.1 编码;如果您只是在没有填充的情况下对内容进行签名,则可能会导致 RSA 的弱点,并且编码是为了与其他程序可靠地互操作。
    • 我使用了 RSA_sign(NID_SHA1,...),但结果与使用 xmlsec(xml 签名 API) 得到的结果不同。我想知道 RSA_sign(NID_SHA1,...) 是否在做 RSASSA-PKCS1-v1_5?
    • 有人可以在我的帖子stackoverflow.com/questions/17167254/… 上帮助我。我想在 iOS 中使用带有 PKCS1 填充的 privateKey 加密。
    【解决方案2】:

    是的,PKCS#1 加密和 PKCS#1 签名是 different。在加密情况下(您尝试过的那个),输入消息在取幂之前被简单地填充。

    另一方面,PKCS#1 签名将首先计算表单的 ASN.1 DER 结构

    DigestInfo ::= SEQUENCE {
        digestAlgorithm AlgorithmIdentifier,
        digest OCTET STRING
    }
    

    然后再次填充以形成编码消息 EM

    EM = 0x00 || 0x01 || PS || 0x00 || T
    

    其中 PS 是足够长度的 0xff 填充字符串。如果您重现此 EM 并使用 RSA_private_encrypt,那么您将获得正确的 PKCS#1 v1.5 签名编码,与使用 RSA_sign 获得的相同,甚至更好,使用通用的 EVP_PKEY_sign

    这里有一个 Ruby 的小演示:

    require 'openssl'
    require 'pp'
    
    data = "test"
    digest = OpenSSL::Digest::SHA256.new
    hash = digest.digest("test")
    key = OpenSSL::PKey::RSA.generate 512
    
    signed = key.sign(digest, data)
    dec_signed = key.public_decrypt(signed)
    
    p hash
    pp OpenSSL::ASN1.decode dec_signed
    

    SHA-256 哈希打印如下:

    "\x9F\x86\xD0\x81\x88L}e\x9A/..."
    

    dec_signed 是用公钥再次解密RSA_sign 的结果——这让我们准确地返回了 RSA 函数的输入,去掉了填充,事实证明,这正是 DigestInfo 结构上面提到的:

     #<OpenSSL::ASN1::Sequence:0x007f60dc36b250
     @infinite_length=false,
     @tag=16,
     @tag_class=:UNIVERSAL,
     @tagging=nil,
     @value=
      [#<OpenSSL::ASN1::Sequence:0x007f60dc36b318
        @infinite_length=false,
        @tag=16,
        @tag_class=:UNIVERSAL,
        @tagging=nil,
        @value=
         [#<OpenSSL::ASN1::ObjectId:0x007f60dc36b390
           @infinite_length=false,
           @tag=6,
           @tag_class=:UNIVERSAL,
           @tagging=nil,
           @value="SHA256">,
          #<OpenSSL::ASN1::Null:0x007f60dc36b340
           @infinite_length=false,
           @tag=5,
           @tag_class=:UNIVERSAL,
           @tagging=nil,
           @value=nil>]>,
       #<OpenSSL::ASN1::OctetString:0x007f60dc36b2a0
        @infinite_length=false,
        @tag=4,
        @tag_class=:UNIVERSAL,
        @tagging=nil,
        @value="\x9F\x86\xD0\x81\x88L}e\x9A/...">]>
    

    如您所见,DigestInfodigest 字段的值与我们自己计算的 SHA-256 哈希值相同。

    【讨论】:

    • 感谢您的详细解答。我已经使用带有 NID_sha1 的 RSA_sign 获得了正确的签名(NID_sha1WithRSA 是错误的),并且我认为我可以通过修改 OpenSSL 的 RSA_sign 函数来获得作为 RSA_private_encrypt 输入的编码值。再次非常感谢您!
    • 对不起,另一个问题,我怎样才能用 OpenSSL 获得 EM?我追踪了 RSA_sign() 并发现 i=RSA_private_encrypt(i,s,sigret,rsa,RSA_PKCS1_PADDING);这最终导致了一个函数 rsa_priv_enc ,我在其他地方找不到。谢谢
    • 还有,RSA_PKCS1_PADDING 在这里做什么?
    • RSA_PKCS1_PADDING 将 SHAXWithRSA 签名格式切换为确定性且更兼容的 PKCS1 格式(默认情况下,例如在验证签名时)来自JAVA)。另一种格式在填充中有一些随机性 - 使相同数据的每个签名都不同 - 这使得它无法用于(例如)fingerprinting Soap-XML 消息的有效负载。
    猜你喜欢
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    相关资源
    最近更新 更多