openssl函数库使用

有门课程要求使用openssl函数库,实现以下要求:

  1. 用MD5生成一个消息(字符串)的消息摘要
  2. 生成RSA密钥对,并用私钥对消息摘要进行签名
  3. 把生成的签名转换成BASE64编码

在此整理一下openssl函数库的使用及基本思想

环境:ubuntu、g++
按照实验要求,是要用openssl的函数库
参阅官方参考文档:openssl官方参考文档

编程语言选择

选用C++

用MD5生成一个消息的消息摘要

首先MD5是一个散列函数,针对任意长输入会产生128位(16字节)的散列值
可以用来确保信息的完整性
参阅openssl官方文档:MD5参考文档
所用函数如下:

  • int MD5_Init(MD5_CTX *c);
    先初始化一个MD5的结构,类型是MD5_CTX
  • int 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;
    }