OpenSSL: 简单易上手的RSA加解密

这篇文章是继 OpenSSL: 用VS2017创建C工程OpenSSL: 完成VS2017的配置 两篇文章的第3篇,前两篇分别跟大家分享了如何在 visual studio2017 中创建C工程,配置OpenSSL的开发环境以及使用 openssl 开源库实现base64的编解码。

本篇跟大家分享如何使用 openssl 开源库实现 RSA 的加密和解密,不过这一篇是对简单的数据进行加解密,下一篇继续跟大家分享复杂一点的 RSA 加解密过程以及实例代码。

2019 即将被画上句号,自己还有很多愿望没有实现。一拿起书就感觉困,一拿起手机觉得黑夜里阳光明媚,无药可救了呀!

获取公、私钥

precompiled-openssl 获取的压缩包中,解压可以在 bin64或者bin(32位操作系统使用) 目录下面找到 openssl.exe 文件,双击打开即可。

生成私钥

执行下面代码,如下:

genrsa -out rsa_private_key.pem 1024

此时在 bin64 目录下会生成 rsa_private_key.pem 文件,即私钥文件;

将 RSA 私钥转换成 PKCS8 格式,执行如下代码:

pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt

生成公钥

执行下面代码,如下:

rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

此时在 bin64 目录下会生成 rsa_public_key.pem 文件,即公钥文件;

用VS Code打开这两个文件,可以看到具体内容,在我的机器上生成的公钥:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrPgCMJW17JN2DW7tZFk/FB6pU
pLvLOo6G/EuND8XZptffXbyiY2VscMRhP+kKVeaLO9HuEYR3Zl78x8oR6prytstc
/MueersWDxh4iGSHsZXGxA41hXrXLRElrSTRc43ea18o0zMxZoVZiR2JFt7QcgM+
T6eOrvj59MhXv9O46QIDAQAB
-----END PUBLIC KEY-----

私钥,如下:

-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKs+AIwlbXsk3YNb
u1kWT8UHqlSku8s6job8S40Pxdmm199dvKJjZWxwxGE/6QpV5os70e4RhHdmXvzH
yhHqmvK2y1z8y556uxYPGHiIZIexlcbEDjWFetctESWtJNFzjd5rXyjTMzFmhVmJ
HYkW3tByAz5Pp46u+Pn0yFe/07jpAgMBAAECgYBj1YH8MtXhNVzveEuBZMCc3hsv
vdq+YSU3DV/+nXN7sQmp77xJ8CjxT80t5VS38dy2z+lUImJYOhamyNPGHkC2y84V
7i5+e6ScQve1gnwHqRKGBjtSCaYOqm9rTDECCTT1oMU26sfYznWlJqMrkJp1jWn7
aAwr+3FcX2XhD74ZAQJBAN34Y6fmHLRPv21MsdgGqUjKgyFvJfLUmtFFgb6sLEWc
k22J3BAFAcNCTLYHFZwMhL/nwaw9/7rIUJD+lcl6n3cCQQDFfrN14qKC3GJfoBZ8
k9S6F7Ss514DDPzIuenbafhoUjZDVcjLw9EmYZQjpfsQ3WdNICUKRrDHZay1Pz+s
YkKfAkB+OKfaquS5t/t/2LPsxuTuipIEqiKnMjSTOfYsidVnBEFlcZZc2awF76aV
f/PO1+OJCO2910ebXBtMSCi++GbDAkEAmc7zNPwsVH4OnyquWJdJNSUBMSd/sCCN
PkaMOrVtINHmMMq+dvMqEBoupRS/U4Ma0JYYQsiLJL+qof2AOWDNQQJAcquLGHLT
eGDDLluHo+kkIGwZi4aK/fDoylZ0NCEtYyMtShQ3JmllST9kmb9NJX2gMsejsirc
H6ObxqZPbka6UA==
-----END PRIVATE KEY-----

紧接着我会使用这对公私钥进行编码。

RSA 实战

RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

1973年,在英国政府通讯总部工作的数学家克利福德·柯克斯(Clifford Cocks)在一个内部文件中提出了一个相同的算法,但他的发现被列入机密,一直到1997年才被发表。

对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式解破。到目前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。

1983年麻省理工学院在美国为RSA算法申请了专利。这个专利2000年9月21日失效。由于该算法在申请专利前就已经被发表了,在世界上大多数其它地区这个专利权不被承认。

新建头文件 mzc_rsa.h ,声明几个函数,如下:

#pragmaonce

/* 公钥解密 */
int public_key_decrypt(unsigned char *enc_data, int data_len, unsigned char *key, unsigned char *decrypted);

/* 私钥加密 */
int private_key_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted);

/* 公钥加密 */
int public_key_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted);

/* 私钥解密 */
int private_key_decrypt(unsigned char *enc_data, int data_len, unsigned char *key, unsigned char *decrypted);

对应的实现文件 mzc_rsa.c ,代码如下:

#include"mzc_rsa.h"

#include<openssl/pem.h>
#include<openssl/ssl.h>
#include<openssl/rsa.h>
#include<openssl/evp.h>
#include<openssl/bio.h>
#include<openssl/err.h>
#include<openssl/buffer.h>

#include<stdio.h>

const int PADDING = RSA_PKCS1_PADDING;

RSA *createRSA(unsigned char *key, int public_token)
{
    RSA *rsa = NULL;
    BIO *keybio;
    keybio = BIO_new_mem_buf(key, -1);
    if (keybio == NULL)
    {
        printf("Failed to create key BIO");
        return 0;
    }
    if (public_token)
    {
        rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
    }
    else
    {
        rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
    }
    if (rsa == NULL)
    {
        printf("Failed to create RSA");
    }

    return rsa;
}

int public_key_decrypt(unsigned char *enc_data, int data_len, unsigned char *key, unsigned char *decrypted)
{
    RSA *rsa = createRSA(key, 1);
    int result = RSA_public_decrypt(data_len, enc_data, decrypted, rsa, PADDING);
    return result;
}

int private_key_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted)
{
    RSA *rsa = createRSA(key, 0);
    int result = RSA_private_encrypt(data_len, data, encrypted, rsa, PADDING);
    return result;
}

int public_key_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted)
{
    RSA *rsa = createRSA(key, 1);
    int result = RSA_public_encrypt(data_len, data, encrypted, rsa, PADDING);
    return result;
}

int private_key_decrypt(unsigned char *enc_data, int data_len, unsigned char *key, unsigned char *decrypted)
{
    RSA *rsa = createRSA(key, 0);
    int result = RSA_private_decrypt(data_len, enc_data, decrypted, rsa, PADDING);
    return result;
}

注意这里使用的 RSA 对齐模式是 RSA_PKCS1_PADDING

在测试的主函数(main)中,使用明文 www.veryitman.com 作为示例进行加密和解密操作,我在下面只列出关键实现。具体代码可以去 Github 下载, 注意 clone rsa-feature 这个分支。

/* 公钥加密, 私钥解密 */
int main()
{
    //原始数据为字符串:www.veryitman.com
    unsigned char plainText[] = "www.veryitman.com";

    unsigned char publicKey[] = "-----BEGIN PUBLIC KEY-----\n"
        "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrPgCMJW17JN2DW7tZFk/FB6pU\n"
        "pLvLOo6G/EuND8XZptffXbyiY2VscMRhP+kKVeaLO9HuEYR3Zl78x8oR6prytstc\n"
        "/MueersWDxh4iGSHsZXGxA41hXrXLRElrSTRc43ea18o0zMxZoVZiR2JFt7QcgM+\n"
        "T6eOrvj59MhXv9O46QIDAQAB\n"
        "-----END PUBLIC KEY-----\n";

    unsigned char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n"
        "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKs+AIwlbXsk3YNb\n"
        "u1kWT8UHqlSku8s6job8S40Pxdmm199dvKJjZWxwxGE/6QpV5os70e4RhHdmXvzH\n"
        "yhHqmvK2y1z8y556uxYPGHiIZIexlcbEDjWFetctESWtJNFzjd5rXyjTMzFmhVmJ\n"
        "HYkW3tByAz5Pp46u+Pn0yFe/07jpAgMBAAECgYBj1YH8MtXhNVzveEuBZMCc3hsv\n"
        "vdq+YSU3DV/+nXN7sQmp77xJ8CjxT80t5VS38dy2z+lUImJYOhamyNPGHkC2y84V\n"
        "7i5+e6ScQve1gnwHqRKGBjtSCaYOqm9rTDECCTT1oMU26sfYznWlJqMrkJp1jWn7\n"
        "aAwr+3FcX2XhD74ZAQJBAN34Y6fmHLRPv21MsdgGqUjKgyFvJfLUmtFFgb6sLEWc\n"
        "k22J3BAFAcNCTLYHFZwMhL/nwaw9/7rIUJD+lcl6n3cCQQDFfrN14qKC3GJfoBZ8\n"
        "k9S6F7Ss514DDPzIuenbafhoUjZDVcjLw9EmYZQjpfsQ3WdNICUKRrDHZay1Pz+s\n"
        "YkKfAkB+OKfaquS5t/t/2LPsxuTuipIEqiKnMjSTOfYsidVnBEFlcZZc2awF76aV\n"
        "f/PO1+OJCO2910ebXBtMSCi++GbDAkEAmc7zNPwsVH4OnyquWJdJNSUBMSd/sCCN\n"
        "PkaMOrVtINHmMMq+dvMqEBoupRS/U4Ma0JYYQsiLJL+qof2AOWDNQQJAcquLGHLT\n"
        "eGDDLluHo+kkIGwZi4aK/fDoylZ0NCEtYyMtShQ3JmllST9kmb9NJX2gMsejsirc\n"
        "H6ObxqZPbka6UA==\n"
        "-----END RSA PRIVATE KEY-----\n";

    unsigned char encrypted_str[128];
    unsigned char decrypted_str[128];

    // 需要初始化,否则解密出来的字符串会有多余的乱码
    memset(encrypted_str, '\0', sizeof(encrypted_str));
    memset(decrypted_str, '\0', sizeof(decrypted_str));

    size_t len = strlen((const char *)plainText);
    printf("Encrypted length =%d\n", len);

    // 公钥加密
    int encrypted_length = public_key_encrypt(plainText, len, publicKey, encrypted_str);
    if (encrypted_length == -1)
    {
        printf("Private Encrypt failed\n");
        exit(0);
    }

    // 私钥解密
    int decrypted_length = private_key_decrypt(encrypted_str, encrypted_length, privateKey, decrypted_str);
    if (decrypted_length == -1)
    {
        printf("Public Decrypt failed\n");
        exit(0);
    }

    printf("Decrypted Text =%s\n", decrypted_str);
    printf("Decrypted Length =%d\n", decrypted_length);
    
    return 0;
}

今天到此为止,累了,睡觉了:stuck_out_tongue_winking_eye:。

活着不是靠泪水搏取同情,而是靠汗水获得掌声~

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章