【问题标题】:OpenSSL RSA signature verification: hash and padding?OpenSSL RSA 签名验证:哈希和填充?
【发布时间】:2013-04-19 21:42:26
【问题描述】:

我正在尝试编写代码来验证一些 RSA 签名。签名是使用 OpenSSL 命令行工具生成的,使用此命令行的等效项:

openssl dgst -sha1 -sign private_key_file.pem < binary_data_file > sig

我正在尝试使用libtomcrypt 进行验证:

https://www.libtom.net/

这里是libtomcrypt中RSA验证函数的调用签名:

int rsa_verify_hash_ex(
    const unsigned char *sig, unsigned long siglen,  // signature to verify
    const unsigned char *hash, unsigned long hashlen,  // hash value to check against sig
    int padding,  // defined constant value, see below
    int hash_idx,  // identifies which hash algorithm, see below
    unsigned long saltlen,  // specify salt length, see below
    int *stat,  // output parameter, returns whether verify succeeded or not
    rsa_key *key);  // RSA public key to use for verify

如果该函数运行没有错误,则返回 0,否则返回错误代码。如果运行无误,stat 输出参数指示签名是否验证。

大多数参数看起来很简单:传入要检查的签名、用于比较的哈希值以及用于检查的 RSA 密钥。 hash_idxlibtomcrypt 包含的示例代码中很清楚;它是受支持的哈希算法表的索引,我可以找到与此代码 sn-p 一起使用的正确值:hash_idx = find_hash("sha1")

但我想知道 paddingsaltlen 的值。 padding 并没有让我太担心,因为只有两个可能的值,我可以同时尝试它们。但是我应该为saltlen 传递什么?

用于 RSA 验证的 OpenSSL 函数的 OpenSSL 文档未显示 saltlen 参数。 openssl dgst 的手册页(即 man dgst 的结果)没有讨论盐。

所以我的问题:

  • 如何确定要使用的正确盐长度?
  • OpenSSL 的dgst 命令是否在输入中插入任何额外的内容,例如:(stdin)=

(我通过搜索 StackOverflow 找到了 (stdin)= 的东西:Why are the RSA-SHA256 signatures I generate with OpenSSL and Java different?

  • libtomcrypt 也有一个名为 pkcs_1_pss_decode() 的函数,它被记录为“解码 PSS 编码的签名块”。有没有可能这是我需要调用的函数?

感谢您能给我的任何帮助。

编辑:感谢以下来自@Jonathan Ben-Avraham 的帮助,我今天能够完成这项工作。我的问题的答案分别是:

  • 使用长度 0 作为盐,完全没有盐。
  • 不,OpenSSL 没有插入任何额外内容,例如 (stdin)=
  • 我需要调用rsa_verify_hash_ex(),并且需要将padding 参数指定为LTC_LTC_PKCS_1_V1_5

【问题讨论】:

    标签: c openssl rsa libtomcrypt


    【解决方案1】:

    无盐:

    首先,生成数据的二进制 SHA1 哈希:

    openssl dgst -sha1 -binary -out hash1 some_data_file
    

    这是一个 SHA1 哈希或摘要。文件some_data_file 没有附加盐。 openssl dgst -sha1 本身不加盐。请注意,输出文件只是一个没有加盐的 20 字节 SHA1 哈希。如果有盐,则哈希必须包含它,可能在保存 SHA1 哈希的最后 20 个字节之前添加。

    接下来,使用您的私钥签署 SHA1 哈希文件 hash1

    openssl pkeyutl -sign -in hash1 -inkey privkey.pem -pkeyopt digest:sha1 -out sig1
    

    现在用openssl dgst签名some_data_file

    openssl dgst -sha1 -sign privkey.pem < some_data_file > sig2
    

    最后,比较两个签名:

    diff sig1 sig2
    

    并且您应该看到它们是相同的。这告诉我们,在不加盐的情况下对文件的原始 SHA1 哈希进行签名与使用openssl dgst -sha1 -sign 命令对文件进行签名是一样的,所以一定是openssl dgst -sha1 -sign 命令在生成其 SHA1 时也没有使用任何盐sig2 的哈希值。

    另请注意,使用已弃用的 rsautl 无法获得相同的结果:

    openssl rsautl -sign -in hash1 -inkey privkey.pem -out sig1
    

    而不是openssl pkeyutl,因为openssl rsautl -sign 没有按照例如定义在 RSASSA-PKCS1-v1_5 中的要求对 DigestInfo 进行 ASN.1 编码。 RFC3447 第 9.2 节第 2 步。(这就是为什么你需要 -pkeyopt digest:,即使 pkeyutl -sign 本身不做任何散列。)有关详细信息,请参阅 this SE post

    【讨论】:

    • 感谢您的出色回答。你给了我工具,让我自己看看没有盐。我现在有libtomcrypt 代码成功验证了签名! +1 并接受回答。
    • @steveha:谢谢!您可能还希望看到稍后有关rsautl 的帖子:stackoverflow.com/questions/9951559/…
    • 不,rsautl -sign 不“包含加密文本”。问题是rsautl 没有按照例如要求对 OID 和摘要值 (DigestInfo) 进行 ASN.1 编码。 rfc3447 第 9.2 节第 2 步。事实上,如果您在没有 -pkeyopt digest:$name 的情况下执行 pkeyutl -sign,它也会这样做是错误的。在您对 #9951559 的回答中,您是对的。
    • @dave_thompson_085 谢谢,请对我的帖子进行编辑以澄清这一点。
    【解决方案2】:

    需要强调的一点:一定要传递 hash 而不是实际数据。这让我迷惑了一段时间。这是一个有效的 sn-p(但使用 sha256):

    void
    verify_tomcrypt(unsigned char *keyblob, size_t klen,
                    unsigned char *payload, size_t dlen,
                    unsigned char *signature, size_t slen)
    {
        rsa_key key;
        int stat;
        unsigned long len;
        unsigned char digest2[SHA256_DIGEST_LENGTH];
    
        ltc_mp = ltm_desc;
        register_hash(&sha256_desc);
    
        /* try reading the key */
        if (rsa_import(keyblob, klen, &key) != CRYPT_OK) {
            printf("Error reading key\n");
            exit(-1);
        }
    
        int hash_idx = find_hash("sha256");
        if (hash_idx == -1) {
            printf("LTC_SHA256 not found...?\n");
            exit(-1);
        }
        len = sizeof(digest2);
        if (hash_memory(hash_idx, payload, dlen, digest2, &len) != CRYPT_OK) {
            printf("sha256 fails...?\n");
            exit(-1);
        }
    
        if (rsa_verify_hash_ex(signature, slen, digest2, sizeof(digest2), LTC_LTC_PKCS_1_V1_5, hash_idx, 0, &stat, &key) == CRYPT_OK) {
            if (stat == 1)
                printf("Tomcrypt: Signature OK!\n");
            else
                printf("Tomcrypt: Signature NOK?\n");
        } else {
            printf("Tomcrypt: Signature error\n");
        }
    }
    

    【讨论】:

    • @Behrouz.M 可能是您的字符串散列和您正在比较的字符串的字节大小不同。这是我有一段时间的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 2021-02-04
    • 2012-01-16
    相关资源
    最近更新 更多