【问题标题】:RSA_public_decrypt fails with a -1RSA_public_decrypt 失败并返回 -1
【发布时间】:2019-12-25 09:28:52
【问题描述】:

我有一个小型 react-native 应用程序,它使用 RSA 私钥对消息进行签名。这是对消息进行签名的代码段(在本例中为 Hello World

import { RSA } from 'react-native-rsa-native'

RSA.generateKeys(2048).then(keys => {
    RSA.signWithAlgorithm('Hello World', keys.private, RSA.SHA512withRSA).then(message => {
        console.log(message)
        // "TfAgdPqm+CPxAqxPdV1jfGQvZAZ+uQ/sAlaHCK0ZvHKnRhHnKj+I2PHZhHhM5tJ14hgPd9H48Pe5\nbPFXj0KTk/Ty8YOcd7h98STwmJyNzlqM03Get42q9fD//ofyFZfQrm3R2OZjgOlQ5myepqko8+nk\nobuawPNVsi2Eu+N6PBBAW86hX9pRfKP3gkrOCK8ae1k9O9ysOXucSguaV1++WHkv30O2mN2rsEAT\nATi6SFxYry6n9G+6gIxOyWF9X8qleg3dJgURAPwpD1SWnTOb2hZChpYOWnIreqO9hGQlTgMVzDx3\nncdGpuJ4EatW4ETBO9ox+s7OeqI4TQxt0kH09Q==\n"
    })
})

现在,我正在尝试使用 OpenSSL 的 RSA_public_decrypt 函数验证(然后使用相应的公钥解密)来自 C 程序的签名。这是带有公钥和嵌入其中的加密签名的 C 代码。

#include <stdio.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>


unsigned char key[] = "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAmz6Z6jk2qQ0kPUNCcxTz/dJmUEdVWDWeOeTKS2jwSd5vlwqPHH8Y\nC5BVCuDJ2EKRmB3k18bIDrBqqG9mguqimCY5FA2E5FyIWMOswrnvgzmmyO0RFLUK\nBNUhQ2A3fegL3TNwU8S9bWZV4Bb4zgGIq51QVL8r9yCNd2TC5Y0PHvT36ZbP1W/K\nAvfSsJZYarbVz3QhcEnSXcnllK8AADqMp6jX6zf6vpIEDaf+gs3wg6ubaq7Du81I\nc7iMHfxNL6Uv803T0LKJLOiDXsZZB4K8AKLy86ZsXIkU8NHb2fX4bFvncBZraWGD\nreEDlMNMfwxX6UJR/scnj9R/spiV+9XJqwIDAQAB\n-----END RSA PUBLIC KEY-----\n";


//digest for "Hello World"
unsigned char encrypted[] = "TfAgdPqm+CPxAqxPdV1jfGQvZAZ+uQ/sAlaHCK0ZvHKnRhHnKj+I2PHZhHhM5tJ14hgPd9H48Pe5\nbPFXj0KTk/Ty8YOcd7h98STwmJyNzlqM03Get42q9fD//ofyFZfQrm3R2OZjgOlQ5myepqko8+nk\nobuawPNVsi2Eu+N6PBBAW86hX9pRfKP3gkrOCK8ae1k9O9ysOXucSguaV1++WHkv30O2mN2rsEAT\nATi6SFxYry6n9G+6gIxOyWF9X8qleg3dJgURAPwpD1SWnTOb2hZChpYOWnIreqO9hGQlTgMVzDx3\nncdGpuJ4EatW4ETBO9ox+s7OeqI4TQxt0kH09Q==\n";


unsigned char decoded_encrypted[4096] = {0}, decrypted[4096] = {0};


int main() {

    RSA *rsa = RSA_new();
    BIO *keybio = BIO_new_mem_buf((void *) key, -1);
    if (keybio == NULL) {
        printf("Failed to create key BIO\n");
        return -1;
    }
    rsa = PEM_read_bio_RSAPublicKey(keybio, &rsa, NULL, NULL);
    if (rsa == NULL) {
        printf("Failed to create RSA\n");
        return -1;
    }

    int result = EVP_DecodeBlock((unsigned char *) decoded_encrypted, (const unsigned char *) encrypted, strlen((const char *) encrypted));
    printf("\n----------%s(%d)------------\n", decoded_encrypted, result);



    // result = RSA_public_decrypt(strlen((const char *) decoded_encrypted), decoded_encrypted, decrypted, rsa, RSA_PKCS1_PADDING);
    // printf("\n----------%s(%d)------------\n", decrypted, result);
    // ERR_print_errors(keybio);



    // const unsigned char m[] = "Hello World";
    // result = RSA_verify(NID_sha1, (const unsigned char *) m, strlen((const char *) m), (unsigned char *) encrypted, strlen((const char *) encrypted), rsa);
    // printf("\n----------(%d)------------\n", result);


    RSA_free(rsa);
    BIO_free(keybio);

    return 0;
}

但解密(验证)没有发生,因为 RSA_public_decrypt(RSA_verify) 失败并返回 -1(0) 值。任何帮助表示赞赏。

【问题讨论】:

  • (1) 每当 libcrypto 例程返回错误指示 look at the errror queue (aka error stack) 时(对于 libssl sometimes,您需要在 ERR_* 之前使用 SSL_get_error)。 (2) 您的“加密”是 base64 编码的。您可以使用 EVP_DecodeBlock 将实际(二进制/原始)数据“解密”(您说得对,实际上是签名验证而不是加密,但由于历史原因,我们坚持使用该名称)
  • RSASSA-PKCS1-v1_5 为带有摘要算法标识符 ID 的数据的哈希 H 加上前缀,在本例中为 SHA512 的 0x3051300d060960864801650304020305000440RSA_public_decrypt 不会删除此前缀,因此它会返回 ID || H。使用专用功能进行签名/验证更容易,例如RSA_verify,如果成功则简单地返回1
  • 我已经尝试了你的两种方法(正如你在代码中看到的修改)。但似乎没有一个工作。 EVP_DecodeBlock 以 -1 失败。 RSA_public_decrypt 已经失败,RSA_verify 也是如此(返回值为 0)。

标签: authentication encryption openssl rsa


【解决方案1】:

有几个小缺陷:对于EVP_DecodeBlock,必须删除换行符 (\n)。请注意,Internet 上有各种 Base64 实现,其中一些处理带有和不带换行符的 Base64 编码字符串,例如Base64 BIO 过滤器BIO_f_base64 默认考虑换行符(可以使用标志BIO_FLAGS_BASE64_NO_NL 更改)。在此示例中,为简单起见,EVP_DecodeBlock 没有 换行符的签名结合使用。以下关于RSA_public_decrypt 的代码适用于我的机器:

RSA* rsa = RSA_new();
...
// RSA_public_decrypt
// SHA512-ID: 3051300d060960864801650304020305000440
const unsigned char encrypted[] = "TfAgdPqm+CPxAqxPdV1jfGQvZAZ+uQ/sAlaHCK0ZvHKnRhHnKj+I2PHZhHhM5tJ14hgPd9H48Pe5bPFXj0KTk/Ty8YOcd7h98STwmJyNzlqM03Get42q9fD//ofyFZfQrm3R2OZjgOlQ5myepqko8+nkobuawPNVsi2Eu+N6PBBAW86hX9pRfKP3gkrOCK8ae1k9O9ysOXucSguaV1++WHkv30O2mN2rsEATATi6SFxYry6n9G+6gIxOyWF9X8qleg3dJgURAPwpD1SWnTOb2hZChpYOWnIreqO9hGQlTgMVzDx3ncdGpuJ4EatW4ETBO9ox+s7OeqI4TQxt0kH09Q==";

unsigned char decoded[258]; // length(encrypted) / 4 * 3, possibly padded with 0, see EVP_DecodeBlock
EVP_DecodeBlock(decoded, encrypted, strlen((const char*)encrypted));

unsigned char decrypted[19 + 64]; // length(hash-ID) + length(hash)
int decryptedLength = RSA_public_decrypt(RSA_size(rsa), decoded, decrypted, rsa, RSA_PKCS1_PADDING); // RSA_NO_PADDING reveals the padding (in conjunction with 256 bytes buffer) 

for (int i = 0; i < decryptedLength; i++)
    printf("%02x", decrypted[i]);

对于RSA_verify,必须使用数据的哈希值(而不是数据本身)。此外,必须应用 NID_sha512(而不是 NID_sha1),因为在 react-native 代码中使用的是 SHA512 而不是 SHA1。在上面的代码中加入如下sn-p:

// RSA_verify
const unsigned char hash[64] = { 0x2c, 0x74, 0xfd, 0x17, 0xed, 0xaf, 0xd8, 0x0e, 0x84, 0x47, 0xb0, 0xd4, 0x67, 0x41, 0xee, 0x24, 0x3b, 0x7e, 0xb7, 0x4d, 0xd2, 0x14, 0x9a, 0x0a, 0xb1, 0xb9, 0x24, 0x6f, 0xb3, 0x03, 0x82, 0xf2, 0x7e, 0x85, 0x3d, 0x85, 0x85, 0x71, 0x9e, 0x0e, 0x67, 0xcb, 0xda, 0x0d, 0xaa, 0x8f, 0x51, 0x67, 0x10, 0x64, 0x61, 0x5d, 0x64, 0x5a, 0xe2, 0x7a, 0xcb, 0x15, 0xbf, 0xb1, 0x44, 0x7f, 0x45, 0x9b };
int verified = RSA_verify(NID_sha512, hash, sizeof(hash), decoded, RSA_size(rsa), rsa);

printf("\n%d\n", verified);

在这两个 sn-ps 中,仅实现了与说明各自过程相关的步骤,即跳过了诸如删除换行符、确定哈希等不太重要的步骤。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    相关资源
    最近更新 更多