发布于 

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 文件,双击打开即可。

生成私钥

执行下面代码,如下:

1
genrsa -out rsa_private_key.pem 1024

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

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

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

生成公钥

执行下面代码,如下:

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

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

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

1
2
3
4
5
6
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrPgCMJW17JN2DW7tZFk/FB6pU
pLvLOo6G/EuND8XZptffXbyiY2VscMRhP+kKVeaLO9HuEYR3Zl78x8oR6prytstc
/MueersWDxh4iGSHsZXGxA41hXrXLRElrSTRc43ea18o0zMxZoVZiR2JFt7QcgM+
T6eOrvj59MhXv9O46QIDAQAB
-----END PUBLIC KEY-----

私钥,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----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,声明几个函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#pragma once

/* 公钥解密 */
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,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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 这个分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* 公钥加密, 私钥解密 */
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;
}

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


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


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

veryitman