简体   繁体   English

OpenSSL RSA 签名验证:散列和填充?

[英]OpenSSL RSA signature verification: hash and padding?

I am trying to write code to verify some RSA signatures.我正在尝试编写代码来验证一些 RSA 签名。 The signatures were made using the OpenSSL command-line tool, using the equivalent of this command line:签名是使用 OpenSSL 命令行工具生成的,使用等效于以下命令行:

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

I am trying to use libtomcrypt to do the verify:我正在尝试使用libtomcrypt进行验证:

https://www.libtom.net/ https://www.libtom.net/

Here is the calling signature of the RSA verification function in libtomcrypt :这是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

This function returns a 0 if it operates without error, otherwise returns an error code.如果该函数运行没有错误,则返回 0,否则返回错误代码。 If it operates without error, the stat output parameter indicates whether the signature verified.如果运行无误,则stat输出参数指示签名是否经过验证。

Most of the arguments seem straightforward: pass in the signature to check, the hash value to use to compare it, and the RSA key to use for the check.大多数参数看起来很简单:传入要检查的签名、用于比较的哈希值以及用于检查的 RSA 密钥。 hash_idx is clear from the example code included with libtomcrypt ; hash_idx是从包含在示例代码明确libtomcrypt ; it is an index into a table of supported hash algorithms, and I can find the correct value to use with this code snippet: hash_idx = find_hash("sha1")它是支持的哈希算法表的索引,我可以找到与此代码片段一起使用的正确值: hash_idx = find_hash("sha1")

But I'm wondering about the padding and saltlen values.但我想知道paddingsaltlen值。 padding doesn't worry me too much, as there are only two possible values, and I can just try them both. padding不会让我太担心,因为只有两个可能的值,我可以同时尝试它们。 But what should I pass for saltlen ?但是我应该为saltlen传递什么?

The OpenSSL documentation for the OpenSSL functions for RSA verify don't show a saltlen parameter.用于 RSA 验证的 OpenSSL 函数的 OpenSSL 文档未显示saltlen参数。 The man page for openssl dgst (ie the result of man dgst ) does not discuss salt. openssl dgst的手册页(即man dgst的结果)没有讨论盐。

So my questions:所以我的问题:

  • How can I determine the correct salt length to use?如何确定要使用的正确盐长度?
  • Does OpenSSL's dgst command insert any extra stuff in the input, such as: (stdin)= OpenSSL 的dgst命令是否在输入中插入任何额外的东西,例如: (stdin)=

(I found that (stdin)= thing by searching StackOverflow: Why are the RSA-SHA256 signatures I generate with OpenSSL and Java different? ) (我通过搜索 StackOverflow 发现(stdin)=东西: 为什么我用 OpenSSL 和 Java 生成的 RSA-SHA256 签名不同?

  • libtomcrypt also has a function called pkcs_1_pss_decode() which is documented to "decode a PSS encoded signature block". libtomcrypt还有一个名为pkcs_1_pss_decode()的函数,它被记录为“解码 PSS 编码的签名块”。 Is there any chance that this is the function I need to call?有没有可能这是我需要调用的函数?

Thanks for any help you can give me.感谢你给与我的帮助。

EDIT: thanks to the help below, from @Jonathan Ben-Avraham, I was able to get this working today.编辑:感谢以下来自@Jonathan Ben-Avraham 的帮助,我今天能够完成这项工作。 The answers to my questions are, respectively:我的问题的答案分别是:

  • Use length 0 for the salt, no salt at all.使用长度 0 作为盐,根本没有盐。
  • No, OpenSSL did not insert anything extra such as (stdin)=不,OpenSSL 没有插入任何额外的东西,例如(stdin)=
  • I needed to call rsa_verify_hash_ex() , and I needed to specify the padding argument as LTC_LTC_PKCS_1_V1_5 .我需要调用rsa_verify_hash_ex() ,并且需要将padding参数指定为LTC_LTC_PKCS_1_V1_5

No salt:无盐:

First, generate a binary SHA1 hash of your data:首先,生成数据的二进制 SHA1 哈希:

openssl dgst -sha1 -binary -out hash1 some_data_file

This is an SHA1 hash or digest.这是一个 SHA1 哈希或摘要。 There is no salt prependended to the file some_data_file .文件some_data_file没有预先添加盐。 The openssl dgst -sha1 itself does not add salt. openssl dgst -sha1本身不加盐。 Note that the output file is just a 20 byte SHA1 hash with no salt.请注意,输出文件只是一个没有加盐的 20 字节 SHA1 哈希。 If there were salt, the hash would have to include it, probably prepended before the last 20 bytes that hold the SHA1 hash.如果有盐,散列必须包含它,可能在保存 SHA1 散列的最后 20 个字节之前。

Next, sign the SHA1 hash file hash1 with your private key:接下来,使用您的私钥对 SHA1 哈希文件hash1进行签名:

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

Now sign the some_data_file with openssl dgst :现在签署some_data_fileopenssl dgst

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

Finally, compare the two signatures:最后,比较两个签名:

diff sig1 sig2

and you should see that they are the same.你应该看到它们是一样的。 This tells us that signing the raw SHA1 hash of a file with no salt is the same as using the openssl dgst -sha1 -sign command to sign the file, so it must be that the openssl dgst -sha1 -sign command also did not use any salt when generating its SHA1 hash for sig2 .这告诉我们,对文件的原始 SHA1 哈希进行签名而没有使用 salt 与使用openssl dgst -sha1 -sign命令对文件进行签名是一样的,所以一定是openssl dgst -sha1 -sign命令也没有使用为sig2生成其 SHA1 哈希时的任何盐。

Note also that you cannot achieve the same result using the deprecated rsautl :另请注意,使用已弃用的rsautl无法获得相同的结果:

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

instead of openssl pkeyutl , because openssl rsautl -sign does not do the ASN.1 encoding of DigestInfo as required by RSASSA-PKCS1-v1_5 defined in eg RFC3447 section 9.2 step 2. (This is why you need -pkeyopt digest: even though pkeyutl -sign itself doesn't do any hashing.) See this SE post for details.而不是openssl pkeyutl ,因为openssl rsautl -sign不会按照 RFC3447 第 9.2 步第 2 节中定义的 RSASSA-PKCS1-v1_5 要求对 DigestInfo 进行 ASN.1 编码。(这就是为什么您需要-pkeyopt digest:即使pkeyutl -sign本身不做任何散列。)有关详细信息,请参阅此 SE 帖子

One thing to emphasize: Be sure to pass the hash instead of the actual data.需要强调的一件事:确保传递散列而不是实际数据。 That threw me off for some time.这让我失望了一段时间。 Here's a snippet that works (but use sha256):这是一个有效的片段(但使用 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");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM