OpenSSL入门-RSA综合

PKCS#1加密填充

格式:

1
EB=00+BT+PS+00+D

EB位转化后十六进制表示的数据块,如密钥为1024位时EB长度为128字节。BT为处理模式,公钥操作为02,私钥操作为00或01,当D以00开头时BT为01,不以00开头时为00。PS为填充字节,填充数量为$密钥字节长-3-明文字节长$,最小长度为8,当BT为00时填充用00,BT为01时填充用FF,BT为02时随机非零填充。

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
#include <iostream>
#include <stdlib.h>
#include <time.h>
int rsaEncDataPaddingPkcs1(unsigned char* in, int ilen, unsigned char* eb, int olen) {
int i;
unsigned char byteRand;
if (ilen > (olen - 11))
return -1;
srand(time(NULL));
eb[0] = 0x0;
eb[1] = 0x2; //加密用的是公钥
for (i = 2; i < (olen - ilen - 1); i++) {
do
{
byteRand = rand();
} while (byteRand == 0); //BT = 02 时,随机填充,但不能为 00
eb[i] = byteRand;
}
eb[i++] = 0x0; //明文前是00
memcpy(eb + i, in, ilen); //实际明文
return 0;
}
void PrintBuf(unsigned char* buf, int len) {
int i;
for (i = 0; i < len; i++) {
printf("%02x ", (unsigned char)buf[i]);
if (i % 16 == 15)
putchar('\n');
}
putchar('\n');
}
int main() {
int i, lenN = 1024 / 8;
unsigned char* pRsaPaddingBuf = (unsigned char*) new char[lenN]; //根据1024位的密钥长度来分配空间
unsigned char plain[] = "abc";
int ret = rsaEncDataPaddingPkcs1(plain, sizeof(plain), pRsaPaddingBuf, lenN);
if (ret != 0)
std::cout << "rsaEncDataPaddingPkcs1 failed:" << ret;
PrintBuf(pRsaPaddingBuf, lenN);
}

OpenSSL命令行实现

方法如下:

1
2
3
4
5
6
openssl genrsa -out rsa_private_key.pem 1024 #生成私钥
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform pem -nocrypt -out rsa_private_pkcs8.pem #转为PKCS#8格式
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem #从私钥提取公钥
openssl rsa -in rsa_private_key.pem -text -out private.txt #提取p、q和模数 分别为prime1、prime2和modulus
openssl rsautl -encrypt -in plain.txt -inkey rsa_public_key.pem -public-out enfile.dat #用公钥加密
openssl rsautl -decrypt -inkey rsa_private_key.pem -in enfile.dat -out plainheck.txt #用私钥解密

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
using namespace std;
inline int gcd(int a, int b) {
int t;
while (b) {
t = a;
a = b;
b = t % b;
}
return a;
}
bool prime_w(int a, int b) {
if (gcd(a, b) == 1)
return true;
else
return false;
}
inline int mod_inverse(int a, int r) {
int b = 1;
while (((a * b) % r) != 1) {
b++;
if (b < 0) {
printf("error ,function can't find b ,and now b is negative number");
return -1;
}
}
return b;
}
inline bool prime(int i) {
if (i <= 1)
return false;
for (int j = 2; j < i; j++)
if (i % j == 0)
return false;
return true;
}
void secret_key(int* p, int* q) {
int s = time(0);
srand(s);
do {
*p = rand() % 50 + 1;
} while (!prime(*p));
do {
*q = rand() % 50 + 1;
} while (p == q || !prime(*q));
}
int getRand_e(int r) {
int e = 2;
while (e<1 || e>r || !prime_w(e, r)) {
e++;
if (e < 0) {
printf("error ,function can't find e ,and now e is negative number");
return -1;
}
}
return e;
}
int rsa(int a, int b, int c) {
int aa = a, r = 1;
b = b + 1;
while (b != 1) {
r = r * aa;
r = r % c;
b--;
}
return r;
}
int getlen(char* str) {
int i = 0;
while (str[i] != '\0') {
i++;
if (i < 0)return -1;
}
return i;
}
int main(int argc, char** argv) {
FILE* fp;
fp = fopen("text.txt", "w");
for (int i = 2; i <= 65535; i++)
if (prime(i))
fprintf(fp, "%d ", i);
fclose(fp);
int p, q, N, r, e, d;
p = 0, q = 0, N = 0, e = 0, d = 0;
secret_key(&p, &q);
N = p * q;
r = (p - 1) * (q - 1);
e = getRand_e(r);
d = mod_inverse(e, r);
cout << "N:" << N << '\n' << "p:" << p << '\n' << "q:" << q << '\n' << "r:" << r << '\n' << "e:" << e << '\n' << "d:" << d << '\n';
char mingwen, jiemi;
int miwen;
char mingwenStr[1024], jiemiStr[1024];
int mingwenStrlen;
int* miwenBuff;
cout << "\n\n输入明文:";
cin >> mingwenStr;
mingwenStrlen = getlen(mingwenStr);
miwenBuff = (int*)malloc(sizeof(int) * mingwenStrlen);
for (int i = 0; i < mingwenStrlen; i++)
miwenBuff[i] = rsa((int)mingwenStr[i], e, N);
for (int i = 0; i < mingwenStrlen; i++)
jiemiStr[i] = rsa(miwenBuff[i], d, N);
jiemiStr[mingwenStrlen] = '\0';
cout << "明文:" << mingwenStr << '\n' << "明文长度:" << mingwenStrlen << '\n';
cout << "密文:";
for (int i = 0; i < mingwenStrlen; i++)
cout << miwenBuff[i] << " ";
cout << '\n';
cout << "解密:" << jiemiStr << '\n';
return 0;
}

OpenSSL中RSA结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct rsa_st{
int pad;
long version;
const RSA_METHOD* meth;
ENGINE* engine;
BIGNUM* n;
BIGNUM* e;
BIGNUM* d;
BIGNUM* p;
BIGNUM* q;
BIGNUM* dmp1;
BIGNUM* dmq1;
BIGNUM* iqmp;
CRYPTO_EX_DATA ex_data;
int references;
int flags;
BN_MONT_CTX* _method_mod_n;
BN_MONT_CTX* _method_mod_p;
BN_MONT_CTX* _method_mod_q;
char* bignum_data;
BN_BLINDING* blinding;
BN_BLINDING* mt_blinding;
};
typedef struct rsa_st RSA;

rsa_new初始化一个RSA结构,用rsa_free释放一个RSA结构:

1
2
RSA* rsa_new(void);
void rsa_free(RSA* rsa);

RSA_generate_key产生一个模为num位的密钥对:

1
2
3
4
5
6
7
#include <openssl/rsa.h>
RSA* RSA_generate_key(
int num, //模数的位数
unsigned long e, //公钥指数
void (*callback)(int,int,void*), //回调函数
void* cb_arg //回调函数参数
);

RSA_public_encrypt进行公钥加密:

1
2
3
4
5
6
7
8
#include <openssl/rsa.h>
int RSA_public_encrypt(
int flen,//明文长度
const unsigned char* from, //明文缓冲区
unsigned char* to, //密文缓冲区 被0填充到RSA_size(rsa)
RSA* rsa,
int padding //填充模式
);

填充模式type取值有:

填充模式 描述 flen可用最大值
RSA_PKCS1_PADDING 用PKCS#1 v1.5 RSA_size(rsa)-11
RSA_PKCS1_OAEP_PADDING 用PKCS#1 v2.0,推荐这个 RSA_size(rsa)-42
RSA_SSLV23_PADDING PKSC#1 v1.5,专用于支持SSL3 RSA_size(rsa)
RSA_NO_PADDING 不填充,不安全

成功则返回密文长度,即RSA_size(rsa),出错返回-1,可用ERR_get_error获得错误码。

RSA_private_decrypt进行私钥解密:

1
2
3
4
5
6
7
8
#include <openssl/rsa.h>
int RSA_private_decrypt(
int flen, //密文长度
const unsigned char* from, //密文缓冲区
unsigned char* to, //明文缓冲区
RSA* rsa,
int padding //填充模式 同上
); //返回值同上

OpenSSL的EVP与非EVP实现方法:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include<openssl/pem.h>
#include<openssl/err.h>
#include <openssl/bio.h>
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
#define RSA_KEY_LENGTH 1024
static const char rnd_seed[] = "string to make the random number generator initialized";
#define PRIVATE_KEY_FILE "d:\\test\\rsapriv.key"
#define PUBLIC_KEY_FILE "d:\\test\\rsapub.key"
#define RSA_PRIKEY_PSW "123"
// 生成公钥文件和私钥文件,私钥文件带密码
int generate_key_files(const char* pub_keyfile, const char* pri_keyfile, const unsigned char* passwd, int passwd_len) {
RSA* rsa = NULL;
RAND_seed(rnd_seed, sizeof(rnd_seed));
rsa = RSA_generate_key(RSA_KEY_LENGTH, RSA_F4, NULL, NULL);
if (rsa == NULL) {
printf("RSA_generate_key error!\n");
return -1;
}
// 开始生成公钥文件
BIO* bp = BIO_new(BIO_s_file());
if (NULL == bp) {
printf("generate_key bio file new error!\n");
return -1;
}
if (BIO_write_filename(bp, (void*)pub_keyfile) <= 0) {
printf("BIO_write_filename error!\n");
return -1;
}
if (PEM_write_bio_RSAPublicKey(bp, rsa) != 1) {
printf("PEM_write_bio_RSAPublicKey error!\n");
return -1;
}
// 公钥文件生成成功,释放资源
//printf("Create public key ok!\n");
BIO_free_all(bp);
// 生成私钥文件
bp = BIO_new_file(pri_keyfile, "w+");
if (NULL == bp) {
printf("generate_key bio file new error2!\n");
return -1;
}
if (PEM_write_bio_RSAPrivateKey(bp, rsa, EVP_des_ede3_ofb(), (unsigned char*)passwd, passwd_len, NULL, NULL) != 1) {
printf("PEM_write_bio_RSAPublicKey error!\n");
return -1;
}
// 释放资源
BIO_free_all(bp);
RSA_free(rsa);
return 0;
}
// 打开私钥文件,返回EVP_PKEY结构的指针
EVP_PKEY* open_private_key(const char* keyfile, const unsigned char* passwd) {
EVP_PKEY* key = NULL;
RSA* rsa = RSA_new();
OpenSSL_add_all_algorithms();
BIO* bp = NULL;
bp = BIO_new_file(keyfile, "rb");
if (NULL == bp) {
printf("open_private_key bio file new error!\n");
return NULL;
}
rsa = PEM_read_bio_RSAPrivateKey(bp, &rsa, NULL, (void*)passwd);
if (rsa == NULL) {
printf("open_private_key failed to PEM_read_bio_RSAPrivateKey!\n");
BIO_free(bp);
RSA_free(rsa);
return NULL;
}
key = EVP_PKEY_new();
if (NULL == key) {
printf("open_private_key EVP_PKEY_new failed\n");
RSA_free(rsa);
return NULL;
}
EVP_PKEY_assign_RSA(key, rsa);
return key;
}
// 打开公钥文件,返回EVP_PKEY结构的指针
EVP_PKEY* open_public_key(const char* keyfile) {
EVP_PKEY* key = NULL;
RSA* rsa = NULL;
OpenSSL_add_all_algorithms();
BIO* bp = BIO_new(BIO_s_file());;
BIO_read_filename(bp, keyfile);
if (NULL == bp) {
printf("open_public_key bio file new error!\n");
return NULL;
}
rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);//读取PKCS#1的公钥
if (rsa == NULL) {
printf("open_public_key failed to PEM_read_bio_RSAPublicKey!\n");
BIO_free(bp);
RSA_free(rsa);
return NULL;
}
key = EVP_PKEY_new();
if (NULL == key) {
printf("open_public_key EVP_PKEY_new failed\n");
RSA_free(rsa);
return NULL;
}
EVP_PKEY_assign_RSA(key, rsa);
return key;
}
// 使用密钥加密,这种封装格式只适用公钥加密,私钥解密,这里key必须是公钥
int rsa_key_encrypt(EVP_PKEY* key, const unsigned char* orig_data, size_t orig_data_len, unsigned char* enc_data, size_t& enc_data_len) {
EVP_PKEY_CTX* ctx = NULL;
OpenSSL_add_all_ciphers();
ctx = EVP_PKEY_CTX_new(key, NULL);
if (NULL == ctx) {
printf("ras_pubkey_encryptfailed to open ctx.\n");
EVP_PKEY_free(key);
return -1;
}
if (EVP_PKEY_encrypt_init(ctx) <= 0) {
printf("ras_pubkey_encryptfailed to EVP_PKEY_encrypt_init.\n");
EVP_PKEY_free(key);
return -1;
}
if (EVP_PKEY_encrypt(ctx, enc_data, &enc_data_len, orig_data, orig_data_len) <= 0) {
printf("ras_pubkey_encryptfailed to EVP_PKEY_encrypt.\n");
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(key);
return -1;
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(key);
return 0;
}
// 使用密钥解密,这种封装格式只适用公钥加密,私钥解密,这里key必须是私钥
int rsa_key_decrypt(EVP_PKEY* key, const unsigned char* enc_data, size_t enc_data_len, unsigned char* orig_data, size_t& orig_data_len, const unsigned char* passwd) {
EVP_PKEY_CTX* ctx = NULL;
OpenSSL_add_all_ciphers();
ctx = EVP_PKEY_CTX_new(key, NULL);
if (NULL == ctx) {
printf("ras_prikey_decryptfailed to open ctx.\n");
EVP_PKEY_free(key);
return -1;
}
if (EVP_PKEY_decrypt_init(ctx) <= 0) {
printf("ras_prikey_decryptfailed to EVP_PKEY_decrypt_init.\n");
EVP_PKEY_free(key);
return -1;
}
if (EVP_PKEY_decrypt(ctx, orig_data, &orig_data_len, enc_data, enc_data_len) <= 0) {
printf("ras_prikey_decryptfailed to EVP_PKEY_decrypt.\n");
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(key);
return -1;
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(key);
return 0;
}
void PrintBuf(unsigned char* buf, int len) { //打印字节缓冲区函数
int i;
for (i = 0; i < len; i++) {
printf("%02x ", (unsigned char)buf[i]);
if (i % 16 == 15)
putchar('\n');
}
putchar('\n');
}
int NoEvpRsa() {
// 产生RSA密钥对
RSA* rsaKey = RSA_generate_key(1024, 65537, NULL, NULL);
int keySize = RSA_size(rsaKey);
char fData[] = "Jeep car";
printf("明文:%s\n", fData);
char tData[128];
int flen = strlen(fData);
int ret = RSA_public_encrypt(flen, (unsigned char*)fData, (unsigned char*)tData, rsaKey, RSA_PKCS1_PADDING);
//ret = 128
puts("密文:");
PrintBuf((unsigned char*)tData, ret);
ret = RSA_private_decrypt(128, (unsigned char*)tData, (unsigned char*)fData, rsaKey, RSA_PKCS1_PADDING);
if (ret != -1)
printf("解密结果:%s\n", fData);
RSA_free(rsaKey);
return 0;
}
int main(int argc, char** argv) {
char origin_text[] = "hello world!";
char enc_text[512] = "";
char dec_text[512] = "";
size_t enc_len = 512;
size_t dec_len = 512;
printf("明文:%s\n", origin_text);
// 生成公钥和私钥文件
generate_key_files(PUBLIC_KEY_FILE, PRIVATE_KEY_FILE, (const unsigned char*)RSA_PRIKEY_PSW, strlen(RSA_PRIKEY_PSW));
EVP_PKEY* pub_key = open_public_key(PUBLIC_KEY_FILE);
EVP_PKEY* pri_key = open_private_key(PRIVATE_KEY_FILE, (const unsigned char*)RSA_PRIKEY_PSW);
rsa_key_encrypt(pub_key, (const unsigned char*)&origin_text, sizeof(origin_text), (unsigned char*)enc_text, enc_len);
puts("密文:");
PrintBuf((unsigned char*)enc_text, enc_len);
rsa_key_decrypt(pri_key, (const unsigned char*)enc_text, enc_len, (unsigned char*)dec_text, dec_len, (const unsigned char*)RSA_PRIKEY_PSW);
printf("解密结果:%s\n", dec_text);
puts("---------------No Evp RSA-------------");
NoEvpRsa();
return 0;
}

签名与验签

命令行法:

1
2
3
4
openssl genrsa -out prikey.pem
openssl rsa -in prikey.pem -pubout -out pubkey.pem
openssl dgst -sign prikey.pem -sha1 -out sha1_rsa_file.sign file.txt #签名
openssl dgst -verify pubkey.pem -sha1 -signature sha1_rsa_file.sign file.txt #验签

RSA_sign使用私钥对指定摘要算法的摘要结果进行签名:

1
2
3
4
5
6
7
8
int RSA_sign(
int type, //摘要算法 NID_md5、NID_sha、NID_sha1、NID_md5_sha1
const unsigned char* m, //摘要值
unsigned int m_length, //摘要长度
unsigned char* sigret, //签名结果
unsigned int* siglen, //签名结果长度
RSA* rsa
); //成功1 失败0

当type为NID_md5_sha1时,摘要长度应为36,16字节MD5和20字节SHA1。

RSA_verify验证签名是否与消息摘要匹配:

1
2
3
4
5
6
7
8
int RSA_verify(
int type, //摘要算法
const unsigned char* m, //摘要值
unsigned int m_length, //摘要长度
const unsigned char* sigbuf, //签名
unsigned int siglen, //签名长度
RSA* rsa //公钥
); //成功1 失败0

实现为:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include <iostream>
#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 <stdio.h>
#include<fstream>
using namespace std;
int padding = RSA_PKCS1_PADDING;
char publicKey[] = "-----BEGIN PUBLIC KEY-----\n"\
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJY\n"\
"ozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+\n"\
"vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQAp\n"\
"fc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68\n"\
"i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoV\n"\
"PpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUy\n"\
"wQIDAQAB\n"\
"-----END PUBLIC KEY-----\n";
char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n"\
"MIIEowIBAAKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hy\n"\
"vGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9\n"\
"Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9\n"\
"yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6l\n"\
"WCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5q\n"\
"gLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQABAoIBADhg1u1Mv1hAAlX8\n"\
"omz1Gn2f4AAW2aos2cM5UDCNw1SYmj+9SRIkaxjRsE/C4o9sw1oxrg1/z6kajV0e\n"\
"N/t008FdlVKHXAIYWF93JMoVvIpMmT8jft6AN/y3NMpivgt2inmmEJZYNioFJKZG\n"\
"X+/vKYvsVISZm2fw8NfnKvAQK55yu+GRWBZGOeS9K+LbYvOwcrjKhHz66m4bedKd\n"\
"gVAix6NE5iwmjNXktSQlJMCjbtdNXg/xo1/G4kG2p/MO1HLcKfe1N5FgBiXj3Qjl\n"\
"vgvjJZkh1as2KTgaPOBqZaP03738VnYg23ISyvfT/teArVGtxrmFP7939EvJFKpF\n"\
"1wTxuDkCgYEA7t0DR37zt+dEJy+5vm7zSmN97VenwQJFWMiulkHGa0yU3lLasxxu\n"\
"m0oUtndIjenIvSx6t3Y+agK2F3EPbb0AZ5wZ1p1IXs4vktgeQwSSBdqcM8LZFDvZ\n"\
"uPboQnJoRdIkd62XnP5ekIEIBAfOp8v2wFpSfE7nNH2u4CpAXNSF9HsCgYEA2l8D\n"\
"JrDE5m9Kkn+J4l+AdGfeBL1igPF3DnuPoV67BpgiaAgI4h25UJzXiDKKoa706S0D\n"\
"4XB74zOLX11MaGPMIdhlG+SgeQfNoC5lE4ZWXNyESJH1SVgRGT9nBC2vtL6bxCVV\n"\
"WBkTeC5D6c/QXcai6yw6OYyNNdp0uznKURe1xvMCgYBVYYcEjWqMuAvyferFGV+5\n"\
"nWqr5gM+yJMFM2bEqupD/HHSLoeiMm2O8KIKvwSeRYzNohKTdZ7FwgZYxr8fGMoG\n"\
"PxQ1VK9DxCvZL4tRpVaU5Rmknud9hg9DQG6xIbgIDR+f79sb8QjYWmcFGc1SyWOA\n"\
"SkjlykZ2yt4xnqi3BfiD9QKBgGqLgRYXmXp1QoVIBRaWUi55nzHg1XbkWZqPXvz1\n"\
"I3uMLv1jLjJlHk3euKqTPmC05HoApKwSHeA0/gOBmg404xyAYJTDcCidTg6hlF96\n"\
"ZBja3xApZuxqM62F6dV4FQqzFX0WWhWp5n301N33r0qR6FumMKJzmVJ1TA8tmzEF\n"\
"yINRAoGBAJqioYs8rK6eXzA8ywYLjqTLu/yQSLBn/4ta36K8DyCoLNlNxSuox+A5\n"\
"w6z2vEfRVQDq4Hm4vBzjdi3QfYLNkTiTqLcvgWZ+eX44ogXtdTDO7c+GeMKWz4XX\n"\
"uJSUVL5+CVjKLjZEJ6Qc2WZLl94xSwL71E41H4YciVnSCQxVc4Jw\n"\
"-----END RSA PRIVATE KEY-----\n";
//把字符串写成public.pem文件
int createPublicFile(char* file, const string& pubstr) {
if (pubstr.empty()) {
printf("public key read error\n");
return (-1);
}
int len = pubstr.length();
string tmp = pubstr;
for (int i = 64; i < len; i += 64) {
if (tmp[i] != '\n')
tmp.insert(i, "\n");
i++;
}
tmp.insert(0, "-----BEGIN PUBLIC KEY-----\n");
tmp.append("\n-----END PUBLIC KEY-----\n");
//写文件
ofstream fout(file);
fout << tmp.c_str();
return (0);
}
//把字符串写成private.pem文件
int createPrivateFile(char* file, const string& pristr) {
if (pristr.empty()) {
printf("public key read error\n");
return (-1);
}
int len = pristr.length();
string tmp = pristr;
for (int i = 64; i < len; i += 64) {
if (tmp[i] != '\n')
tmp.insert(i, "\n");
i++;
}
tmp.insert(0, "-----BEGIN RSA PRIVATE KEY-----\n");
tmp.append("-----END RSA PRIVATE KEY-----\n");
//写文件
ofstream fout(file);
fout << tmp.c_str();
return (0);
}
//读取密钥
RSA* createRSA(unsigned char* key, int publi) {
RSA* rsa = NULL;
BIO* keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio == NULL) {
printf("Failed to create key BIO\n");
return 0;
}
if (publi)
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL); //从内存读取PEM格式公钥内容
else
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL); //从内存读取PEM格式私钥内容
if (rsa == NULL)
printf("Failed to create RSA\n");
return rsa;
}
//公钥加密,私钥解密
int public_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_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;
}
int public_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_sign(const unsigned char* in_str, unsigned int in_str_len, unsigned char* outret, unsigned int* outlen, unsigned char* key) {
RSA* rsa = createRSA(key, 0);
unsigned char md[20] = "";
SHA1(in_str, in_str_len, md);
int result = RSA_sign(NID_sha1, md, 20, outret, outlen, rsa);
if (result != 1) {
printf("sign error\n");
return -1;
}
return result;
}
int public_verify(const unsigned char* in_str, unsigned int in_len, unsigned char* outret, unsigned int outlen, unsigned char* key) {
RSA* rsa = createRSA(key, 1);
unsigned char md[20] = "";
SHA1(in_str, in_len, md);
int result = RSA_verify(NID_sha1, md, 20, outret, outlen, rsa);
if (result != 1) {
printf("verify error\n");
return -1;
}
return result;
}
int main() {
char plainText[2048 / 8] = "hello";//key length : 2048
printf("create pem file\n");
string strPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChNr0TmflORv9C62+tSAYhyj4DwB6fyOHqttddq8Y+R+8cIGT7EKuqSRuUUuLVBN6IIjd14UkxxtjHqrDxPWZz9WfX0LB2lTmnSdkg9Q10IfP9ZrVCW8Pe5vJ7gt5iQ4lOebdqR47+ef9E7oE+eJFQhxSYGGy/FnKjBkadJQtwPQIDAQAB";
int file_ret = createPublicFile((char*)"public_test.pem", strPublicKey);
unsigned char encrypted[4098] = {};
unsigned char decrypted[4098] = {};
unsigned char signret[4098] = {};
unsigned int siglen;
printf("source data=[%s]\n", plainText);
printf("public encrytpt ----private decrypt\n");
int encrypted_length = public_encrypt((unsigned char*)plainText, strlen(plainText), (unsigned char*)publicKey, encrypted);
if (encrypted_length == -1) {
printf("encrypted error \n");
exit(0);
}
printf("Encrypted length =%d\n", encrypted_length);
int decrypted_length = private_decrypt((unsigned char*)encrypted, encrypted_length, (unsigned char*)privateKey, decrypted);
if (decrypted_length == -1) {
printf("decrypted error \n");
exit(0);
}
printf("DecryptedText =%s\n", decrypted);
printf("DecryptedLength =%d\n", decrypted_length);
printf("\nprivate sign ----public verify\n");
int ret = private_sign((const unsigned char*)plainText, strlen(plainText), signret, &siglen, (unsigned char*)privateKey);
printf("sign ret =[%d]\n", ret);
ret = public_verify((const unsigned char*)plainText, strlen(plainText), signret, siglen, (unsigned char*)publicKey);
if (ret == 1)
printf("verify OK,ret =[%d]\n", ret);
else
printf("verify failed,ret =[%d]\n", ret);
return (0);
}

EVP法

EVP_SignInit_ex进行签名初始化,设置摘要算法和摘要算法引擎等:

1
2
3
4
5
6
#define EVP_SignInit_ex(a,b,c) EVP_DigestInit_ex(a,b,c)
int EVP_DigestInit_ex(
EVP_MD_CTX* ctx,
const EVP_MD* type, //摘要算法
ENGINE* impl //算法引擎 NULL默认引擎
); //成功1 失败0

参数type可以为如下算法:

1
2
3
4
5
6
7
EVP_md2();
EVP_md4();
EVP_md5();
EVP_sha();
EVP_sha1();
EVP_dss();
EVP_mdc2();

签名函数为EVP_SignUpdate

1
2
3
4
5
6
7
#include <openssl/evp.h>
#define EVP_SignUpdate(a,b,c) EVP_DigestUpdate(a,b,c)
int EVP_DigestUpdate(
EVP_MD_CTX* ctx,
const void* d, //源数据缓冲区
size_t cnt //源数据长度 单位字节
); //成功1 失败0

签名结束函数为EVP_SignFinal

1
2
3
4
5
6
int EVP_SignFinal(
EVP_MD_CTX* ctx,
unsigned char* md, //签名结果
unsigned int* s, //签名结果长度
EVP_PKEY* pkey //私钥
); //成功1 失败0

验签初始化函数为EVP_VerifyInit_ex

1
#define EVP_VerifyInit_ex(a,b,c) EVP_DigestInit_ex(a,b,c)

验签函数为EVP_VerifyUpdate

1
#define EVP_VerifyUpdate(a,b,c) EVP_DigestUpdate(a,b,c)

验签结束函数为EVP_VerifyFinal

1
2
3
4
5
6
int EVP_VerifyFinal(
EVP_MD_CTX* ctx,
const unsigned char* sigbuf, //签名值
unsigned int siglen, //签名值长度
EVP_PKEY* pkey //公钥
); //成功1 失败0

使用ctx后必须用EVP_MD_CTX_cleanup释放内存。

实现为:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
void SignAndVerify() {
unsigned char sign_value[1024]; //保存签名值的数组
unsigned int sign_len; //签名值长度
EVP_MD_CTX* mdctx= EVP_MD_CTX_new(); //摘要算法上下文变量
char mess1[] = "I love China!"; //待签名的消息
RSA* rsa = NULL; //RSA结构体变量
EVP_PKEY* evpKey = NULL; //EVP KEY结构体变量
int i;
printf("正在产生RSA密钥...");
rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL); //产生一个1024位的RSA密钥
if (rsa == NULL) {
printf("gen rsa err\n");
return;
}
printf(" 成功.\n");
evpKey = EVP_PKEY_new(); //新建一个EVP_PKEY变量
if (evpKey == NULL) {
printf("EVP_PKEY_new err\n");
RSA_free(rsa);
return;
}
if (EVP_PKEY_set1_RSA(evpKey, rsa) != 1) { //保存RSA结构体到EVP_PKEY结构体
printf("EVP_PKEY_set1_RSA err\n");
RSA_free(rsa);
EVP_PKEY_free(evpKey);
return;
}
//以下是计算签名代码
EVP_MD_CTX_init(mdctx); //初始化摘要上下文
if (!EVP_SignInit_ex(mdctx, EVP_md5(), NULL)) { //签名初始化,设置摘要算法,本例为MD5
printf("err\n");
EVP_PKEY_free(evpKey);
RSA_free(rsa);
return;
}
if (!EVP_SignUpdate(mdctx, mess1, strlen(mess1))) { //计算签名(摘要)Update
printf("err\n");
EVP_PKEY_free(evpKey);
RSA_free(rsa);
return;
}
if (!EVP_SignFinal(mdctx, sign_value, &sign_len, evpKey)) {//签名输出
printf("err\n");
EVP_PKEY_free(evpKey);
RSA_free(rsa);
return;
}
printf("消息\"%s\"的签名值是: \n", mess1);
for (i = 0; i < sign_len; i++) {
if (i % 16 == 0)
printf("\n%08xH: ", i);
printf("%02x ", sign_value[i]);
}
printf("\n");
//EVP_MD_CTX_cleanup(&mdctx);
printf("\n正在验证签名...\n");
//以下是验证签名代码
EVP_MD_CTX_init(mdctx); //初始化摘要上下文
if (!EVP_VerifyInit_ex(mdctx, EVP_md5(), NULL)) { //验证初始化,设置摘要算法,一定要和签名一致。
printf("EVP_VerifyInit_ex err\n");
EVP_PKEY_free(evpKey);
RSA_free(rsa);
return;
}
if (!EVP_VerifyUpdate(mdctx, mess1, strlen(mess1))) { //验证签名(摘要)Update
printf("err\n");
EVP_PKEY_free(evpKey);
RSA_free(rsa);
return;
}
if (!EVP_VerifyFinal(mdctx, sign_value, sign_len, evpKey)) {//验证签名
printf("verify err\n");
EVP_PKEY_free(evpKey);
RSA_free(rsa);
return;
}
else
printf("验证签名正确.\n");
//释放内存
EVP_PKEY_free(evpKey);
RSA_free(rsa);
// EVP_MD_CTX_cleanup(&mdctx);
return;
}
int main() {
OpenSSL_add_all_algorithms();
SignAndVerify();
return 0;
}