OpenSSL RSA签名验证:哈希和填充?

时间:2013-04-25 20:31:20

标签: c openssl rsa libtomcrypt

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

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

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

http://www.libtom.org/

以下是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_idx中包含的示例代码清楚地显示了libtomcrypt;它是支持的哈希算法表的索引,我可以找到与此代码段一起使用的正确值: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

2 个答案:

答案 0 :(得分:7)

没有盐:

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

openssl dgst -sha1 -binary -out hash1 some_data_file

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

接下来,使用您的私钥对SHA1哈希文件hash1进行签名:

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

现在使用some_data_file

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

最后,比较两个签名:

diff sig1 sig2

你应该看到它们是一样的。这告诉我们,对没有salt的文件的原始SHA1哈希进行签名与使用openssl dgst -sha1 -sign命令对文件进行签名相同,因此openssl dgst -sha1 -sign命令也必须使用任何盐为sig2生成SHA1哈希时。

另请注意,使用已弃用的rsautl

无法获得相同的结果
openssl rsautl -sign -in hash1 -inkey privkey.pem -out sig1

而不是openssl pkeyutl,显然是因为openssl rsautl -sign在输出中包含加密文本以及签名。有关详细信息,请参阅this SE post

答案 1 :(得分:3)

需要强调的一点是:确保传递哈希而不是实际数据。那让我离开了一段时间。这是一个有效的代码片段(但使用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");
    }
}