RSA введите node.js и проверьте в С++

Я работаю над тем, чтобы защитить сообщение, отправленное с сервера node.js, в приложение на С++.

Из node.js я создал пару ключей.

Я использую node -rsa для чтения открытого ключа на стороне node.js(https://github.com/rzcoder/node-rsa)

var rsa =  new nodeRSA(publicKeyBuffer  ,{encryptionScheme :'pkcs1'})

Поскольку мое сообщение может быть длинным, я вычисляю salted sha256 сообщения перед вызовом шифрования.

const hash = crypto.createHash('sha256').update(message + config.signSalt).digest('hex')

эта часть работает нормально, потому что я могу генерировать точный хэш на стороне С++.

тогда я вызываю функцию шифрования node -rsa для создания буфера

const signature = rsa.encrypt(hash)

Я пробовал различную кодировку, но поскольку данные отправляются через websocket (+ MsgPack упаковка), двоичный формат является хорошим вариантом

На стороне С++ я читаю сначала ключ для char []

const char keyStr[] = "-----BEGIN RSA PRIVATE KEY-----\n" ..........

BIO* bio = BIO_new_mem_buf(keyStr, (int)strlen(keyStr)); // -1: assume string is null terminated

m_rsaPrivKey = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);

if (!m_rsaPrivKey)
        LogOutSys("ERROR: Could not load PRIVATE KEY! PEM_write_bio_RSAPrivateKey FAILED: %s\n", ERR_error_string(ERR_get_error(), NULL));

ключ читается без ошибок, после этого я вычисляю хэш из сообщения, создавая std::string из unsigned char buffer

std::string hash = sha256(msg.c_str());

std::string signatureStr(signature.begin(), signature.end());

char *decrypt;

int decryptLen;

decrypt = new char[RSA_size(m_rsaPrivKey)];

decryptLen = RSA_private_decrypt((int)msg.size() + 1, (unsigned char*)msg.c_str(), (unsigned char*)decrypt, m_rsaPrivKey, RSA_NO_PADDING /* RSA_PKCS1_OAEP_PADDING */ );

if (decryptLen == -1)
{
    char errStr[130];
    ERR_error_string(ERR_get_error(), errStr);
    LogOutSys("Rsa::decrypt - Error decrypting string ssl error %s", errStr);
}

for (int i = 0; i < decryptLen; i++)
{
    decryptData.push_back(decrypt[i]);
}

delete decrypt;

дешифрование завершилось ошибкой со следующей ошибкой

Rsa:: decrypt - Ошибка дешифрования строки ssl error error: 0406506C: lib (4): func (101): reason (108)

Я пробовал различный режим кодирования и заполнения, но всегда получал ошибку.

Ответ 1

(Предполагая, что вы хотите подписывать с помощью закрытого ключа на стороне nodejs и проверять использование открытого ключа на стороне openssl)

Чтобы подписать строку Test с помощью приватного ключа образца, используйте, например:

NodeRSA=require('node-rsa');
key = new NodeRSA(null, {signingScheme: 'pkcs1-sha256'});
key.importKey('-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI2GkqoVj1l59MQh\nI/ZswgdnNG3o5XSmyeGgNdmTgQQ6cWCJcscCS6d3+nWFl3Xe7SxzKxo7pHMHTeJU\nGTZpLzW7fk5Y/ISWIr2Qsswpm8JUVOAUQVU/qwZYPr0ACCDQLGLaVByKFgvKnf5p\npkdroM63AFakn5YlCP+WM4ASuZyvAgMBAAECgYAUsBNIYZZu0fEBqoaDQyqpwmBb\noKvJ/YeNP8ofX/yADbr9DZqFlMRSWqt1+m1FgazRzpQCZa2IUw0DhJ+a4I1R6E30\nw7ZVWdVvWtkA70YGMaqB618fMR6SpTmzVGUjzQqk7Zim+uQVugTXEimC6/7sa7em\nLtVdjXuvOFOCVEeXwQJBAO/bN2q2u8YTBy9q4A34KoeeM8NX8zV3bRVhrB4eVcT0\ngpNNCrvjo2g5qKQs1fmLmjylSBihus0RjjJZTwxsffkCQQCXDRhh83OCtLfDEztO\nObu5BvVFcli76VEdw4EqzJtrddG77B43ggYYyJFxOuJHz+33oM4GtnHEiHkV9sRS\n47nnAkBcu8qPLZsnl4+9m3qIrBv1Vwr4SXa0gznffGXJNz096rLZNH4j6nzw/Ong\nn50S4BB/xf871rucMV9iw/i1+vQxAkEAjDBDKOVhlzVSN2Jp8Df02cxzZnixkfUA\nq7b+8lHjDODUPqztfmbWcbn0Ajq8OBnqqaA8lk5NWDGw74mOu79OkQJARDwRop7c\nfkd39rY/+an50uj2L4UJv1Jb+Yal5c0u37ACRnp+0n6qlcL0pj98UfW9H+oYFqwT\nKXzq4lptzgfQIg==\n-----END PRIVATE KEY-----\n', 'pkcs8');
signature=key.sign('Test', 'hex');

Предоставление шестнадцатеричной кодированной сигнатуры:

'45f0c0672c8c07ecfe318b8ffa425c169ed2458ac6f0e4f1ffe0bcebec38cc3e59311858ed443d45f0dc81935ee0c490fa452b2f427d59a2c43fdd69f71f2b46d9e39072cb517b4afba5d5c66b26e14ca8a2900650b923fd77271deba84103d42c1f81619825d4987eeabc05401b0bc35bee08c59843aa94a535d2fb032b681b'

Чтобы проверить эту подпись с помощью openssl, используйте, например:

#include <stdlib.h>
#include <stdio.h>

#include <openssl/pem.h>
#include <openssl/evp.h>

int main(void) {

    // Read public key from memory
    EVP_PKEY *pubKey=NULL;
    {
        BIO * pubKeyPemBio = BIO_new_mem_buf("-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNhpKqFY9ZefTEISP2bMIHZzRt\n6OV0psnhoDXZk4EEOnFgiXLHAkund/p1hZd13u0scysaO6RzB03iVBk2aS81u35O\nWPyEliK9kLLMKZvCVFTgFEFVP6sGWD69AAgg0Cxi2lQcihYLyp3+aaZHa6DOtwBW\npJ+WJQj/ljOAErmcrwIDAQAB\n-----END PUBLIC KEY-----", -1);
        if(pubKeyPemBio==NULL) { printf("Error at line %i\n", __LINE__); return 0; }
        pubKey=PEM_read_bio_PUBKEY(pubKeyPemBio, &pubKey, NULL, NULL);
        BIO_free(pubKeyPemBio);
        if(pubKey==NULL) { printf("Error at line %i\n", __LINE__); return 0; }
    }

    // Verify signature
    {
        EVP_MD_CTX *ctx = EVP_MD_CTX_create();
        if(ctx==NULL) { printf("Error at line %i\n", __LINE__); return 0; }

        int ret=EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubKey);
        if(ret!=1) { printf("Error at line %i\n", __LINE__); return 0; }

        ret=EVP_DigestVerifyUpdate(ctx, "Test", 4);
        if(ret!=1) { printf("Error at line %i\n", __LINE__); return 0; }

        ret=EVP_DigestVerifyFinal(ctx, "\x45\xf0\xc0\x67\x2c\x8c\x07\xec\xfe\x31\x8b\x8f\xfa\x42\x5c\x16\x9e\xd2\x45\x8a\xc6\xf0\xe4\xf1\xff\xe0\xbc\xeb\xec\x38\xcc\x3e\x59\x31\x18\x58\xed\x44\x3d\x45\xf0\xdc\x81\x93\x5e\xe0\xc4\x90\xfa\x45\x2b\x2f\x42\x7d\x59\xa2\xc4\x3f\xdd\x69\xf7\x1f\x2b\x46\xd9\xe3\x90\x72\xcb\x51\x7b\x4a\xfb\xa5\xd5\xc6\x6b\x26\xe1\x4c\xa8\xa2\x90\x06\x50\xb9\x23\xfd\x77\x27\x1d\xeb\xa8\x41\x03\xd4\x2c\x1f\x81\x61\x98\x25\xd4\x98\x7e\xea\xbc\x05\x40\x1b\x0b\xc3\x5b\xee\x08\xc5\x98\x43\xaa\x94\xa5\x35\xd2\xfb\x03\x2b\x68\x1b", 128);
        if(ret!=1) { printf("Error at line %i\n", __LINE__); return 0; }
        EVP_MD_CTX_destroy(ctx);
    }
    EVP_PKEY_free(pubKey);

    printf("All OK!\n");
    return EXIT_SUCCESS;
}

Некоторые (случайные) заметки:

  • приведенный выше код - это просто доказательство концепции RSA для подтверждения/проверки между nodejs и openssl. Не ожидайте, что это будет безопасная реализация с правильной обработкой ошибок, безопасностью ключевых длин, защитой от сбоев времени/побочных каналов и т.д.

  • подписанная строка/буфер может быть сколь угодно длинной, поскольку хеширование выполняется с помощью node -rsa

  • закодированный открытый/закрытый ключ может быть получен в nodejs с использованием, например, key.exportKey('pkcs8-public')/key.exportKey('pkcs8')

  • выберите кодировку, которая подходит вам (я использовал строки и шестнадцатеричные строки, поскольку они четко читаемы). Вероятно, вы хотите использовать буферы

Удачи!

Отказ от ответственности: я не специалист по криптографии, поэтому, пожалуйста, подтвердите мои мысли.


Используется закрытый ключ:

-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI2GkqoVj1l59MQh
I/ZswgdnNG3o5XSmyeGgNdmTgQQ6cWCJcscCS6d3+nWFl3Xe7SxzKxo7pHMHTeJU
GTZpLzW7fk5Y/ISWIr2Qsswpm8JUVOAUQVU/qwZYPr0ACCDQLGLaVByKFgvKnf5p
pkdroM63AFakn5YlCP+WM4ASuZyvAgMBAAECgYAUsBNIYZZu0fEBqoaDQyqpwmBb
oKvJ/YeNP8ofX/yADbr9DZqFlMRSWqt1+m1FgazRzpQCZa2IUw0DhJ+a4I1R6E30
w7ZVWdVvWtkA70YGMaqB618fMR6SpTmzVGUjzQqk7Zim+uQVugTXEimC6/7sa7em
LtVdjXuvOFOCVEeXwQJBAO/bN2q2u8YTBy9q4A34KoeeM8NX8zV3bRVhrB4eVcT0
gpNNCrvjo2g5qKQs1fmLmjylSBihus0RjjJZTwxsffkCQQCXDRhh83OCtLfDEztO
Obu5BvVFcli76VEdw4EqzJtrddG77B43ggYYyJFxOuJHz+33oM4GtnHEiHkV9sRS
47nnAkBcu8qPLZsnl4+9m3qIrBv1Vwr4SXa0gznffGXJNz096rLZNH4j6nzw/Ong
n50S4BB/xf871rucMV9iw/i1+vQxAkEAjDBDKOVhlzVSN2Jp8Df02cxzZnixkfUA
q7b+8lHjDODUPqztfmbWcbn0Ajq8OBnqqaA8lk5NWDGw74mOu79OkQJARDwRop7c
fkd39rY/+an50uj2L4UJv1Jb+Yal5c0u37ACRnp+0n6qlcL0pj98UfW9H+oYFqwT
KXzq4lptzgfQIg==
-----END PRIVATE KEY-----

С помощью соответствующего открытого ключа:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNhpKqFY9ZefTEISP2bMIHZzRt
6OV0psnhoDXZk4EEOnFgiXLHAkund/p1hZd13u0scysaO6RzB03iVBk2aS81u35O
WPyEliK9kLLMKZvCVFTgFEFVP6sGWD69AAgg0Cxi2lQcihYLyp3+aaZHa6DOtwBW
pJ+WJQj/ljOAErmcrwIDAQAB
-----END PUBLIC KEY-----