有门课程要求使用openssl函数库,实现以下要求:
- 用MD5生成一个消息(字符串)的消息摘要
- 生成RSA密钥对,并用私钥对消息摘要进行签名
- 把生成的签名转换成BASE64编码
在此整理一下openssl函数库的使用及基本思想
环境:ubuntu、g++
按照实验要求,是要用openssl的函数库
参阅官方参考文档:openssl官方参考文档
编程语言选择
选用C++
用MD5生成一个消息的消息摘要
首先MD5是一个散列函数,针对任意长输入会产生128位(16字节)的散列值
可以用来确保信息的完整性
参阅openssl官方文档:MD5参考文档
所用函数如下:
int MD5_Init(MD5_CTX *c);
先初始化一个MD5的结构,类型是MD5_CTXint MD5_Update(MD5_CTX *c, const void *data, unsigned long len);
此时对消息进行重复的hash操作int MD5_Final(unsigned char *md, MD5_CTX *c);
将结果存储至md里
此时注意,MD5输出的是ascii码,所以如果不处理的话输出的是乱码,可以将其转化为16进制的字符串,如下cout<<hex<<setw(2)<<setfill('0')<<(int)md[i];
至此消息摘要即可形成生成RSA密钥对
RSA是公钥密码体系,需要生成公钥和私钥对
查阅官方文档发现有生成函数,名为RSA_generate_key_ex(新的)和RSA_generate_key(旧的)
选用RSA_generate_key_ex函数:int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
bits指得是密钥长度,一般取1024,e是一个加密指数,是一个奇数,通常取65537,cb为NULL时会自动去调用一个随机生成素数的方法
此时密钥对在rsa中,那如何分别提取公钥和私钥,并将其保存为pem文件呢?
这时候参阅文档时发现有:int PEM_write_bio_RSAPrivateKey(BIO *bp, RSA *x, const EVP_CIPHER *enc,unsigned char *kstr, int klen,pem_password_cb *cb, void *u);
int PEM_write_bio_PUBKEY(BIO *bp, EVP_PKEY *x);
这俩函数可以实现将公私钥写入pem文件,还可以给私钥设置密码
当然如果不习惯用bio(openssl封装的io)的话可以用不使用io的方法,具体参见:
PEM_write_bio_PrivateKey
至此生成密钥对并将其保存为pem文件完成利用私钥对消息摘要签名
RSA私钥可以用于数字签名,而公钥可以用作验证
查阅文档,有RSA_sign方法:int RSA_sign(int type, const unsigned char *m, unsigned int m_len,unsigned char *sigret, unsigned int *siglen, RSA *rsa);
发现这个正好就是对消息摘要进行签名的函数
type根据摘要生成采用的函数来选择,这里使用NID_md5,m就是摘要信息,sigret就是签名后的信息,此处的rsa为私钥
还有一个问题是如何从文件中获取到私钥:
可以采用BIO和PEM_read_bio_RSAPrivateKey函数结合的方法
至此摘要签名完成将签名使用base64编码
base64是一种编码方式,有关base64的编码查看文档如下:
base64编码文档
Openssl下的base64是结合其BIO的
b64=BIO_new(BIO_f_base64());在输入输出流中编码
至此签名的base64编码完成验证
为了验证上述操作是否正确,需要逆过程检查base64解码
与编码方法类似,openssl中的base64解码也是与BIO结合的
b64=BIO_new(BIO_f_base64());签名验证
用RSA私钥签名,验证需使用其对应公钥
参照文档,与sign相对的使用RSA_verify函数
函数返回1,证明是被正确签名注意事项
编译程序时,需加上链接库 -l crypto代码
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#include <iostream>
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <string.h>
#include <openssl/md5.h>
#include <sstream>
#include <iomanip>
#include <fstream>
using namespace std;
void generateMesDigest(char* filePath){
string message="";
ifstream in(filePath);
string temp="";
while(getline(in,temp)){
message+=temp;
}
unsigned char md[MD5_DIGEST_LENGTH];
MD5_CTX md5;
MD5_Init(&md5);
MD5_Update(&md5,message.c_str(),message.length());
MD5_Final(md,&md5);
stringstream digest;
for(int i=0;i<MD5_DIGEST_LENGTH;i++){
digest<<hex<<setw(2)<<setfill('0')<<(int)md[i];
}
in.close();
ofstream out("digest.txt");
out<<digest.str();
out.close();
cout<<"digest success!"<<endl;
}
void generateRSAKeys(){
RSA *rsa=RSA_new();
BIGNUM *e=BN_new();
BN_set_word(e,65537);
RSA_generate_key_ex(rsa,1024,e,NULL);
BIO *out;
out=BIO_new_file("priv.pem","w");
int temp=PEM_write_bio_RSAPrivateKey(out,rsa,NULL,NULL,0,NULL,NULL);
BIO_flush(out);
BIO_free(out);
out=BIO_new_file("pub.pem","w");
temp=PEM_write_bio_RSAPublicKey(out,rsa);
BIO_flush(out);
BIO_free(out);
BN_free(e);
RSA_free(rsa);
}
void signFile(char *filePath){
string signText;
unsigned int siglen;
//open the digest file
string digest="";
ifstream in(filePath);
string temp="";
while(getline(in,temp)){
digest+=temp;
}
char *signBuffer;
RSA *priKey=RSA_new();
BIO *priIO=BIO_new_file("priv.pem","rb");
priKey=PEM_read_bio_RSAPrivateKey(priIO,&priKey,NULL,NULL);
signBuffer=(char*)malloc(RSA_size(priKey));
int mark=RSA_sign(NID_md5,(const unsigned char*)digest.c_str(),digest.length(),(unsigned char *)signBuffer,&siglen,priKey);
if(mark==1){
signText=string(signBuffer);
}else{
cout<<"signature fail";
return;
}
ofstream out("signature.txt");
out<<signText;
cout<<"signature success"<<endl;
free(signBuffer);
RSA_free(priKey);
BIO_free(priIO);
}
void base64Encode(char *filePath){
FILE *f=fopen("base64.txt","w+");
//open the signatureFile
string signText="";
ifstream in(filePath);
string temp="";
while(getline(in,temp)){
signText+=temp;
}
BIO *bio,*b64;
b64=BIO_new(BIO_f_base64());
bio=BIO_new_fp(f,BIO_NOCLOSE);
BIO_push(b64,bio);
BIO_write(b64,signText.c_str(),signText.length());
BIO_flush(b64);
BIO_free_all(b64);
}
int main(){
char filePath[50];
cout<<"Please input the route of a file:"<<endl;
cin>>filePath;
generateMesDigest(filePath);
generateRSAKeys();
char filePath1[50];
cout<<"PLease input the file to be signed:"<<endl;
cin>>filePath1;
signFile(filePath1);
cout<<"Please input the signature file:";
char filePath2[50];
cin>>filePath2;
base64Encode(filePath2);
}验证代码
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#include <iostream>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <fstream>
using namespace std;
void base64Decode(char *filePath){
FILE *f1=fopen(filePath,"r");
FILE *f2=fopen("base64-decode.txt","w+");
BIO *bio,*b64,*bio_out;
char inbuf[512];
int inlen;
b64=BIO_new(BIO_f_base64());
bio=BIO_new_fp(f1,BIO_NOCLOSE);
bio_out=BIO_new_fp(f2,BIO_NOCLOSE);
BIO_push(b64,bio);
while((inlen=BIO_read(b64,inbuf,512))>0){
BIO_write(bio_out,inbuf,inlen);
}
BIO_flush(bio_out);
BIO_free_all(b64);
cout<<"decode success!"<<endl;
}
void signVerify(char *filePath1,char *filePath2){
string sign="";
ifstream in1(filePath1);
string temp1="";
while(getline(in1,temp1)){
sign+=temp1;
}
in1.close();
string digest="";
ifstream in2(filePath2);
string temp2;
while(getline(in2,temp2)){
digest+=temp2;
}
in2.close();
//load the pub key
RSA *pubKey=RSA_new();
BIO *pubIO=BIO_new_file("pub.pem","rb");
pubKey=PEM_read_bio_RSAPublicKey(pubIO,&pubKey,NULL,NULL);
if(RSA_verify(NID_md5,(const unsigned char *)digest.c_str(),(unsigned int)digest.length(),(unsigned char *)sign.c_str(),(unsigned int)sign.length(),pubKey)){
cout<<"verify success!"<<endl;
}else{
cout<<"verify fail!"<<endl;
}
RSA_free(pubKey);
BIO_free(pubIO);
}
int main(){
char filePath[50];
cout<<"Please input the file to decode:"<<endl;
cin>>filePath;
base64Decode(filePath);
char filePath1[50];
char filePath2[50];
cout<<"Please input the file to verify and the digest file:"<<endl;
cin>>filePath1;
cout<<"the digest file:"<<endl;
cin>>filePath2;
signVerify(filePath1,filePath2);
return 0;
}