您的位置:首页 > 运维架构

关于openssl几个API的一点小收获

2016-07-24 11:00 211 查看
    今天心血来潮突然想搞搞openssl了,趁着端午小假,刚好有空可以鼓捣孤岛自己喜欢的东西,出去东奔西跑的实在太造孽了,还是宅起来给自己充充电吧。下载openssl最新代码1.0.1g,修复了“心血漏洞”那个版本。编译安装那些小儿科的东西就不再浪费笔墨了,如果出现头文件或者库文件之类的错误,请在本人博客里寻找相关文章,应该主要集中在动态库那几篇博文。反正我在自己虚拟机里安装的时候是妥妥滴。

    因为我主要对非对称加密的RSA算法比较感兴趣,网上最多的就是这么用的:

   生成私钥文件(其中已经包含了公钥):
[root@localhost release]#openssl genrsa -out plainPrv.key 1024
   

   然后再从这个私钥文件里将公钥提取出来,保存到文件里:
[root@localhost release]#openssl rsa -in plainPrv.key -pubout -out plainPub.key
    RSA一般有两种应用场景:

   1、公钥加密、私钥解密:这是数据安全通信领域最常见情形;

   2、私钥加密、公钥解密:这主要用于数字签名。

   两种方式,一通百通,本文只看第一种场景。

  关于测试代码,网上到处都是,也都基本能用,我就先不摘抄了。大家问的最多的问题就是在读取公钥文件时,PEM_read_RSA_PUBKEY()函数和PEM_read_RSAPublicKEY()的疑惑。为什么读取私钥文件用的PEM_read_RSAPrivateKey(),针对上述openssl命令生成的公钥文件,在读取其内容时用对称的PEM_read_RSAPublicKEY()接口却会报错,必须要用PEM_read_RSA_PUBKEY()才可以。

   其实,我们要是看看一两个文件内容就明白了:
[root@localhost release]# cat plainPrv.key

-----BEGIN RSA PRIVATE KEY-----

MIICXgIBAAKBgQDlGVxzTDVhnC16SW+D0WG8hvm1wztmr0vBh2VK6CU7k90mdrCx

4by1URcZ6iS6KxomxSqWmK9g2C0iRd8xx7OyykBHKttjIx5Diq3wLXDP2qU4mjSI

vHP3MwgrOR2Jwa4mNr1veM2c1pyXn5xIfZs2IFnicMugj+0sXik1pLWwIwIDAQAB

AoGBAKoB5OomfmJ92/2oKxmdsjKN0xY/13++y6/EgrVQifipJG5bm4mVI01F7Ket

ai3AuHpWy+DPUy3BndSWFyfAsyatULiK3cJnIZumxmWP8G9odfO1pH/KcZB2Vi61

HcbioDuJRCcF3jpbGMun3lCwkdG/qVfsFmOElbzSbNMDbwkJAkEA/K9mOSKrP+lu

6bsIuD6/n2XQkz8XE2lPuPwKhVLX+ljXqRyxJZH0n+2EC8pUi694Q2Zhgn0uPdEl

KCYtlBaLXQJBAOgawH01Xc0r63+XVif6rLZfwJGBAP8921e2dRDFYhYLP3riflY8

xvFQsh4n7kbAXt4xZ3pDA/J1INnE01Rk8X8CQCmzyOslDZ4+qE9qzsWZlYZ5BzNF

9kj92GpvLk1SntJyVyVR1uqcbAL48BICEnH7Q53cB7vBbSBGpBs8Mcl+7wECQQCF

Dbjkze/sys2ggd+44WGa1n8sqhgpOYuA1656I7ybyGzmg+pKg2LEOS8yTE+yrVp0

4ztfggVEO1LOo59F1Ov/AkEApfUtgKHB4YCPy70syFaQoAWjiaxOWq/FLM7FBntP

ikz1X7gNsRkb4I/be15ZN8E/2Z0Q95FOpsgqw76Bi4Yynw==

-----END RSA PRIVATE KEY-----

[root@localhost release]# cat plainPub.key

-----BEGIN PUBLIC KEY-----

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlGVxzTDVhnC16SW+D0WG8hvm1

wztmr0vBh2VK6CU7k90mdrCx4by1URcZ6iS6KxomxSqWmK9g2C0iRd8xx7OyykBH

KttjIx5Diq3wLXDP2qU4mjSIvHP3MwgrOR2Jwa4mNr1veM2c1pyXn5xIfZs2IFni

cMugj+0sXik1pLWwIwIDAQAB

-----END PUBLIC KEY-----
    当我们在用PEM_read_RSAPublicKEY()读取公钥文件plainPub.key时报的错误是酱紫滴:
3077879432:error:0906D06C:lib(9):func(109):reason(108):pem_lib.c:698:Expecting: RSA PUBLIC KEY
    所以我就天真地将公钥文件头和尾分别改成“-----BEGIN RSA PUBLIC KEY-----”和“-----BEGIN RSA PUBLIC KEY-----”,理想很丰满,显示很骨感。实践证明openssl是不能那么轻易就被忽悠过去的。没办法,查看openssl源码发现,提取公钥文件时除了-pubout参数可以设置外,还有有个参数叫做-RSAPublicKey_out,但是命令行提示和man手册里居然没有任何提及。幸好我还会读C代码,所以提取公钥时我改用下面的命令:
[root@localhost release]#openssl rsa -in plainPrv.key -RSAPublicKey_out -out plainPub.key
   这样做完的结果是,首先公钥文件的内容有点变化:
[root@localhost test_openssl]# cat plainPub2.key

-----BEGIN RSA PUBLIC KEY-----

MIGJAoGBAOUZXHNMNWGcLXpJb4PRYbyG+bXDO2avS8GHZUroJTuT3SZ2sLHhvLVR

FxnqJLorGibFKpaYr2DYLSJF3zHHs7LKQEcq22MjHkOKrfAtcM/apTiaNIi8c/cz

CCs5HYnBriY2vW94zZzWnJefnEh9mzYgWeJwy6CP7SxeKTWktbAjAgMBAAE=

-----END RSA PUBLIC KEY-----

[root@localhost release]# cat plainPub.key

-----BEGIN PUBLIC KEY-----

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlGVxzTDVhnC16SW+D0WG8hvm1

wztmr0vBh2VK6CU7k90mdrCx4by1URcZ6iS6KxomxSqWmK9g2C0iRd8xx7OyykBH

KttjIx5Diq3wLXDP2qU4mjSIvHP3MwgrOR2Jwa4mNr1veM2c1pyXn5xIfZs2IFni

cMugj+0sXik1pLWwIwIDAQAB

-----END PUBLIC KEY-----

   其次,当我再用PEM_read_RSAPublicKEY()接口来读取公钥文件plainPub2.key时,居然成功了。说明RSA PUBLIC KEY和PUBLIC KEY的两种公钥文件其存储方式是不一样的,PEM_read_RSAPublicKEY()只能读取RSA PUBLIC KEY形式的公钥文件;而PEM_read_RSA_PUBKEY()只能读取PUBLIC KEY格式的公钥文件。由于本人密码学基础较薄弱,现在还不能说出两者的区别,请各位见谅,还望密码方面的大牛们予以点拨。演示代码如下:

点击(此处)折叠或打开

/* filename: tmp.c

*/

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<openssl/rsa.h>

#include<openssl/pem.h>

#include<openssl/err.h>

void hexprint(char *str,int len)

{

    int i=0;

    for(i=0;i<len;i++){

        printf("%s%02x%s",((i%16==0?"|":"")),*((unsigned char*)str+i),(((i+1)%16==0)?"|\n":" "));

    }

    if(i%16!=0)

        printf("|\n");

}

static int do_operation(RSA* rsa_ctx,char *instr,char* path_key,int inlen,char** outstr,int type)

{

    if(rsa_ctx == NULL || instr == NULL || path_key == NULL)

    {

        perror("input elems error,please check them!");

        return -1;

    }

    int rsa_len,num;

    rsa_len=RSA_size(rsa_ctx);

    *outstr=(unsigned char *)malloc(rsa_len+1);

    memset(*outstr,0,rsa_len+1);

    switch(type){

        case 1: //pub enc

        if(inlen == 0){

            perror("input str len is zero!");

            goto err;

        }

        num = RSA_public_encrypt(inlen,(unsigned char *)instr,(unsigned char*)*outstr,rsa_ctx,RSA_PKCS1_OAEP_PADDING);

        break;

        case 2: //prv dec

        num = RSA_private_decrypt(inlen,(unsigned char *)instr,(unsigned char*)*outstr,rsa_ctx,RSA_PKCS1_OAEP_PADDING);        

        default:

        break;

    }

    if(num == -1)

    {

        printf("Got error on enc/dec!\n");

err:

        free(*outstr);

        *outstr = NULL;

        num = -1;

    }

    return num;

}

int rsa_pub_encrypt(char *str,char *path_key,char** outstr){

    RSA *p_rsa;

    FILE *file;

    int flen,rsa_len,num;

    if((file=fopen(path_key,"r"))==NULL){

        perror("open key file error");

        return -1;

    }

#ifdef RSAPUBKEY

    if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){

#else

    if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){

#endif

        ERR_print_errors_fp(stdout);

        return -1;

    }

    num = do_operation(p_rsa,str,path_key,strlen(str),outstr,1);

    RSA_free(p_rsa);

    fclose(file);

    return num;

}

int rsa_prv_decrypt(char *str,char *path_key,int inlen,char** outstr){    

    RSA *p_rsa;

    FILE *file;

    int rsa_len,num;

        

    if((file=fopen(path_key,"r"))==NULL){

        perror("open key file error");

        return -1;

    }

    if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){

        ERR_print_errors_fp(stdout);

        return -1;

    }    

    num = do_operation(p_rsa,str,path_key,inlen,outstr,2);

    RSA_free(p_rsa);

    fclose(file);

    return num;

}

int main(int argc,char** argv){

    char *ptr_en,*ptr_de;

    int len;

   

    printf("source is :%s\n",argv[1]);

    len=rsa_pub_encrypt(argv[1],argv[2],&ptr_en);

    printf("pubkey encrypt:\n");

    hexprint(ptr_en,len);

    

    rsa_prv_decrypt(ptr_en,argv[3],len,&ptr_de);

    printf("prvkey decrypt:%s\n",ptr_de==NULL?"NULL":ptr_de);

    

    if(ptr_en!=NULL){

        free(ptr_en);

    }

    if(ptr_de!=NULL){

        free(ptr_de);

    }

    

    return 0;

}

    如果开启RSAPUBKEY宏,则用PEM_read_RSA_PUBKEY()来读取公钥文件;否则用PEM_read_RSAPublicKey()读取:
[root@localhost release]#gcc -o pub rsatest.c -lcrypto -g -DRSAPUBKEY

[root@localhost release]#gcc -o nopub rsatest.c -lcrypto -g 
   测试结果如下:



   实际应用中,出于安全考虑我们一般会对私钥文件加密。我们可以用如下的方式来重新生成经3DES加密后私钥文件:
[root@localhost release]#openssl genrsa -des3 -out cipherPrv.key 1024
   这样生成的私钥文件使用3DES加过密的,看看内容就晓得和之前的有什么不同了。头部多了一些信息:Proc-Type和DEK-Info,猜想这肯定是某种加密信息(这TM不废话么),但是我看不懂,现阶段“会用”是首要问题:



   上述加密私钥文件的口令是123456,分别提取RSA PUBLIC KEY和PUBLIC KEY格式的公钥文件:
[root@localhost release]# openssl rsa -in cipherPrv.key -pubout -out cipherPub.key
[root@localhost release]# openssl rsa -in cipherPrv.key -RSAPublicKey_out -out cipherPub2.key
   在代码中我们需要通过下面的方式来读取经3DES加密处理后的私钥文件:

点击(此处)折叠或打开

RSA* getPRV(char *path_key_fullname,char* pwd)

{

    RSA *rsaK=RSA_new();

    OpenSSL_add_all_algorithms();

    BIO *BP = BIO_new_file(path_key_fullname,"rb");

    if(NULL == BP)

        return NULL;

    rsaK=PEM_read_bio_RSAPrivateKey(BP,NULL,NULL,pwd);

    return rsaK;

}

    然后将tmp.c中第85行从:

   if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){

   替换成:

   if((p_rsa=getPRV(path_key,"123456"))==NULL){

   重新编译,运行后结果如下:



   关于openssl有很多值得学习的地方,空了再慢慢研究。


<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>

阅读(14329) | 评论(2) | 转发(2) |

3
上一篇:经典排序算法归纳笔记(4)

下一篇:Linux 内核通知链随笔【中】

相关热门文章
MyBatis 入门(五)--分页查询(...

关于内核模块的挂载后的最终虚...

关于__init、__initdata和__ex...

关于内核模块挂载出现“no sym...

关于U-Boot源码顶层Makefile...

linux 常见服务端口

xmanager 2.0 for linux配置

【ROOTFS搭建】busybox的httpd...

openwrt中luci学习笔记

什么是shell

linux dhcp peizhi roc

关于Unix文件的软链接

求教这个命令什么意思,我是新...

sed -e "/grep/d" 是什么意思...

谁能够帮我解决LINUX 2.6 10...

给主人留下些什么吧!~~





4608963392015-12-23 14:05:21
太感谢了,解决了大问题。PEM_read_RSA_PUBKEY 不知道这个地方为什么这么用,有啥研究成果木有
回复 | 举报





popfeng2014-11-25 17:05:41
太棒了,谢谢分享~


回复 | 举报

评论热议
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: