首页 文章

RSA登录node.js并在C中验证

提问于
浏览
12

我正在努力保护我从node.js服务器发送到C应用程序的消息 .

从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')

这部分工作正常,因为我能够在C端生成完全相同的哈希 .

然后,我正在调用node-rsa的encrypt函数来生成缓冲区

const signature = rsa.encrypt(hash)

我尝试了各种编码,但由于数据是通过websocket(MsgPack打包)发送的,因此二进制格式是一个不错的选择

在C方面,我第一次读取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));

读取密钥没有错误,之后,我正在计算消息中的散列,从unsigned char缓冲区生成一个std :: string

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错误错误:0406506C:lib(4):func(101):reason(108)

我尝试了各种编码和填充模式但总是出错 .

1 回答

  • 2

    (假设您想使用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;
    }
    

    一些(随机)笔记:

    • 以上代码只是nodejs和openssl之间RSA签名/验证的工作概念验证 . 请不要指望它是一个安全的实现,具有正确的错误处理,安全密钥长度,防止定时/侧通道攻击等 .

    • signed string / buffer可以任意长,因为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-----
    

相关问题