文档章节

C语言:基于OpenSSL-RSA实现RSA非对称加解密

ZZ-D-NEMO
 ZZ-D-NEMO
发布于 2017/03/11 17:43
字数 1427
阅读 1340
收藏 0

    关于OpenSSL的介绍和安装在此不多赘述,可以在网上找到很多相关资料,各位感兴趣可以去了解下(自觉对OpenSSL开源库只是初级使用阶段,也就不在此“秀下限”了),直接进入主题,本篇源码基于OpenSSL中RSA实现,除此之外还可以基于EVP实现。

  • 示例代码:【RSA-1024bit-PKCS1Padding|x509公钥加密|pkcs12私钥解密】

****************************************************************************************

"crtdef.h":

#ifndef __CRTDEF_H
#define __CRTDEF_H

/* x509公钥文件 */
#define PUBKEY  "/home/nemo/etc/rsa/public.cer"

/* pkcs12私钥文件及密码 */
#define PRIKEY  "/home/nemo/etc/rsa/private_111111.pfx"
#define PRIPWD  "111111"

/* 校验错误函数宏 */
#define CHECK( func ) {             \
    if( EXIT_FAILURE == (func) ){   \
        exit( EXIT_FAILURE );       \
    }                               \
}

#endif

****************************************************************************************

"rsa.c":

/* 算法:RSA-1024bit-PKCS1Padding
 * 公钥加密:x509结构(.cer)
 * 私钥解密:pkcs12结构(.pfx)+密码 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
#include <openssl/err.h>
#include "crtdef.h"

/* 公钥证书加密
 * RSA(1024位)/PKCS1Padding算法
 * 明文最大长度:128-11=117字节
 * 密文长度:128字节
 * 返回:成功则返回密文长度,失败则返回"EXIT_FAILURE" */
static int encryptRsa( char *plainData, char *cipherData, int plainDataSize )
{
    int nResult = 0;
    EVP_PKEY *pkey = NULL;
    X509 *cert = NULL;
    BIO *bn = NULL;
    RSA *pubkey = NULL;

    /* 01:加载所有的运算法则 */
    OpenSSL_add_all_algorithms();

    /* 02:解析cer公钥证书 */
    /* 2.1.打开证书文件 */
    bn = BIO_new_file( PUBKEY, "r" );
    if( NULL == bn ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "BIO_new_file() Failed!\n" );
        goto _err;
    }
    /* 2.2.读取X509结构 */
    cert = PEM_read_bio_X509( bn, NULL, NULL, NULL );
    if( NULL == cert ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "PEM_read_bio_X509() Failed!\n" );
        goto _err;
    }
    /* 2.3.分解X509结构得到EVP_PKEY */
    #if 0
    cert = d2i_X509_bio( bn, NULL );                    /* DER格式 */
    #endif
    cert = PEM_read_bio_X509( bn, NULL, NULL, NULL );   /* PEM格式 */
    if( NULL == pkey ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "X509_get_pubkey() Failed!\n" );
        ERR_print_errors_fp( stderr );
        goto _err;
    }

    /* 03:EVP_PKEY转换成RSA的KEY */
    pubkey = EVP_PKEY_get1_RSA( pkey );
    if( NULL == pubkey ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "EVP_PKEY_get1_RSA() Failed!\n" );
        ERR_print_errors_fp( stderr );
        goto _err;
    }

    /* 04:公钥加密:RSA_PKCS1_PADDING */
    nResult = RSA_public_encrypt( plainDataSize, (unsigned char *)plainData,
        (unsigned char *)cipherData, pubkey, RSA_PKCS1_PADDING );
    if( nResult < 0 ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "RSA_public_encrypt() Failed!\n" );
        ERR_print_errors_fp( stderr );
        goto _err;
    }

_err:
    /* 释放BIO */
    if( bn != NULL ){
        BIO_free( bn );
        bn = NULL;
    }

    /* 释放X509 */
    if( cert != NULL ){
        X509_free( cert );
        cert = NULL;
    }

    /* 释放EVP_PKEY */
    if( pkey != NULL ){
        EVP_PKEY_free( pkey );
        pkey = NULL;
    }

    /* 释放RSA */
    if( pubkey != NULL ){
        RSA_free( pubkey );
        pubkey = NULL;
    }

    /* 清空加载的内容 */
    EVP_cleanup();      /* for EVP */

    return nResult;
}

/* 私钥证书解密
 * RSA(1024位)/PKCS1Padding算法
 * 明文最大长度:128-11=117字节
 * 密文长度(固定):128字节
 * 返回:成功则返回明文长度,失败则返回"EXIT_FAILURE" */
static int decryptRsa( char *cipherData, char *plainData, int cipherDataSize )
{
    int nResult = 0;
    EVP_PKEY *pkey = NULL;
    X509 *cert = NULL;
    PKCS12 *p12 = NULL;
    RSA *prikey = NULL;
    FILE *fp = NULL;

    /* 01:加载所有的运算法则 */
    OpenSSL_add_all_algorithms();

    /* 02:解析pfx私钥证书 */
    /* 2.1.打开pfx文件 */
    if( NULL == ( fp = fopen( PRIKEY, "rb" ) ) ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "fopen() Failed!\n" );
        goto _err;
    }
    /* 2.2.读取PKCS12结构 */
    p12 = d2i_PKCS12_fp( fp, NULL );
    if( NULL == p12 ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "d2i_PKCS12_fp() Failed!\n" );
        goto _err;
    }
    /* 2.3.分解PKCS12结构得到EVP_PKEY */
    if( !PKCS12_parse( p12, PRIPWD, &pkey, &cert, NULL ) ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "PKCS12_parse() Failed!\n" );
        ERR_print_errors_fp( stderr );
        goto _err;
    }

    /* 03:EVP_PKEY转换成RSA的KEY */
    prikey = EVP_PKEY_get1_RSA( pkey );
    if( NULL == prikey ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "EVP_PKEY_get1_RSA() Failed!\n" );
        ERR_print_errors_fp( stderr );
        goto _err;
    }

    /* 04:私钥解密:RSA_PKCS1_PADDING */
    nResult = RSA_private_decrypt( cipherDataSize, (unsigned char *)cipherData,
        (unsigned char *)plainData, prikey, RSA_PKCS1_PADDING );
    if( nResult < 0 ){
        nResult = EXIT_FAILURE;
        fprintf( stderr, "RSA_private_decrypt() Failed!\n" );
        ERR_print_errors_fp( stderr );
        goto _err;
    }

_err:
    /* 关闭证书文件 */
    if( fp != NULL ){
        fclose( fp );
        fp = NULL;
    }

    /* 释放PKCS12 */
    if( p12 != NULL ){
        PKCS12_free( p12 );
        p12 = NULL;
    }

    /* 释放X509 */
    if( cert != NULL ){
        X509_free( cert );
        cert = NULL;
    }

    /* 释放EVP_PKEY */
    if( pkey != NULL ){
        EVP_PKEY_free( pkey );
        pkey = NULL;
    }

    /* 释放RSA */
    if( prikey != NULL ){
        RSA_free( prikey );
        prikey = NULL;
    }

    /* 清空加载的内容 */
    EVP_cleanup();      /* for EVP */

    return nResult;
}

/* 测试程序
 * 01:公钥加密
 * 02:针对加密好的密文使用私钥解密
 * 目标:解密出的明文和最初的明文一致证明功能实现
 * 命令行参数:最初的明文 */
int main( int argc, char *argv[] )
{
    int i = 0;
    int plainDataSize = 0;
    int cipherDataSize = 0;
    char plainData[128 + 1] = {0};
    char cipherData[128 + 1] = {0};

    /* 依赖命令行参数输入 */
    if( 1 == argc ){
        fprintf( stderr, "Usage:[%s <buf>]\n", argv[0] );
        exit( EXIT_FAILURE );
    }

    printf( "【初始明文】[%s]\n", argv[1] );

    /* 01:公钥加密 */
    CHECK( cipherDataSize = encryptRsa( argv[1], cipherData, strlen( argv[1] ) ) );
    /* LOOP:鉴于密文极有可能存在不可打印字符故而转十六进制输出 */
    printf( "【加密密文】[" );
    for( i = 0; i < cipherDataSize; i ++ )
    {
        printf( "%02X", cipherData[i] );
    }
    printf( "]\n" );

    /* 02:私钥解密 */
    CHECK( plainDataSize = decryptRsa( cipherData, plainData, cipherDataSize ) );
    printf( "【解密明文】[%s]\n", plainData );

    exit( EXIT_SUCCESS );
}

****************************************************************************************

"Makefile":

#执行文件
bin=irsa

#目标文件
objects=rsa.o

#连接规则
$(bin):$(objects)
 gcc -o ~/bin/$(bin) $(objects) -lssl -lcrypto -ldl

#清理对象
.PHONY:clean
clean:
 -rm ~/bin/$(bin) $(objects)

****************************************************************************************

  • 验证效果:

  • 【P.S.】说明:
  1. 鉴于RSA非对称加解密算法明文长度有限,故而通常应用场景:通过对称加密算法(例如DES)加密明文,而通过RSA加密对称加密算法密钥,两者组合即数字信封;(以上示例代码是存粹的RSA裸加密解密)
  2. "Makefile"中"gcc"和"-rm"行首是'Tab'键入的水平制表符'\t',非此会导致"make"失败;(因为编辑器的缘故无法准确表示)
  3. 关于公钥,若无公钥文件而是公钥明文串,则可以直接从中截取出模数和指数构建RSA结构,示例代码如下:

****************************************************************************************

模数 跳过公钥明文开头的7字节(十六进制即14字节),截取256字节十六进制(以1024bit为例)
指数 公钥明文末尾3字节(十六进制即6字节)

****************************************************************************************

char *moduleHex = "xxxxxxxxx";  /* 模数16进制[256Bytes] */
char *expandHex = "xxxxxxxxx";  /* 指数16进制 */
BIGNUM *bnn = NULL; /* 模数-大数 */
BIGNUM *bne = NULL; /* 指数-大数 */
RSA *pubkey = NULL;

/* 生成大数 */
bnn = BN_new();
bne = BN_new();
BN_hex2bn( &bnn, moduleHex );
BN_hex2bn( &bne, expandHex );
if( NULL == bnn || NULL == bne ){
    fprintf( stderr, "BN_new() Failed!" );
    ERR_print_errors_fp( stderr );
    exit( EXIT_FAILURE );
}

/* 生成公钥 */
pubkey = RSA_new();
if( NULL == pubkey ){
    fprintf( stderr, "RSA_new() Failed!" );
    ERR_print_errors_fp( stderr );
    exit( EXIT_FAILURE );
}
pubkey->n = bnn;
pubkey->e = bne;

/* 释放大数 */
if( bnn != NULL ){
    BN_free( bnn );
    bnn = NULL;
}
if( bne != NULL ){
    BN_free( bne );
    bne = NULL;
}

/* 释放公钥 */
if( pubkey != NULL ){
    RSA_free( pubkey );
    pubkey = NULL;
}

****************************************************************************************

© 著作权归作者所有

ZZ-D-NEMO
粉丝 0
博文 5
码字总数 3822
作品 0
顺义
程序员
私信 提问
SSH加密原理、RSA非对称加密算法学习与理解

首先声明一下,这里所说的SSH,并不是Java传统的三大框架,而是一种建立在应用层和传输层基础上的安全外壳协议,熟悉Linux的朋友经常使用到一个SSH Secure ShellCilent的工具,本文也是基于此...

Realfighter
2015/03/18
29.3K
0
浅析RSA公钥密码以及使用Java自带API实现RSA的密钥生成和加解密

RSA是目前最流行的非对称密码,目前广泛应用在数字签名,数字证书上。 那么什么是非对称密码呢?就是给明文加密的密钥和给密文解密的密钥是不一样的。其中,对外暴露的是公钥,自己保留的是私...

Lunqi
2015/07/15
1K
1
大型网站的HTTPS实践(一)——HTTPS协议和原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/g2V13ah/article/details/83189718 前言 百度于2015年上线了全站HTTPS的安全搜索,默认会将HTTP请求跳转成HTT...

AIOps智能运维
2018/10/19
0
0
打造属于你的加密Helper类

摘要 在我们软件系统设计中,数据的安全性是我们考虑的重中之重,特别像银行系统的设计账户和密码都需进行加密处理。这时我们可以使用加密算法对数据进行加密处理,这就是我们今天要介绍的主...

长平狐
2012/06/11
210
0
浅谈android数据存储加密

写在开头 CSDN:http://blog.csdn.net/sayfromwen 掘金:https://juejin.im/user/59b09eb2518825241e2255ea 在移动端的开发中,数据安全的问题一直是大家备受关注的,数据加密技术也受到了大...

亭子happy
2018/10/15
34
0

没有更多内容

加载失败,请刷新页面

加载更多

浅谈FlyWeight享元模式

一、前言 享元(FlyWeight)模式顾名思义,即是轻量级,原因就是享元,共享元素,这里的元素指的是对象。如何共享对象,那就是在检测对象产生的时候,如果产生的是同一个对象,那么直接使用已...

青衣霓裳
17分钟前
3
0
Python学习10.14:Python set集合详解

Python 中的集合,和数学中的集合概念一样,用来保存不重复的元素,即集合中的元素都是唯一的,互不相同。 从形式上看,和字典类似,Python 集合会将所有元素放在一对大括号 {} 中,相邻元素...

太空堡垒185
17分钟前
5
0
好程序员大数据教程分享Scala系列之文件以及正则表达式

好程序员大数据教程分享Scala系列之文件以及正则表达式 1 读取行 导入scala.io.Source后,即可引用Source中的方法读取文件信息。 import scala.io.Source object FileDemo extends App{ val ...

好程序员官网
18分钟前
4
0
75.nosql memcached与安装及查看状态

21.1 nosql介绍 21.2 memrcached介绍 21.3 安装memcached 21.4 查看memcachedq状态 21.1 nosql介绍 什么是NoSQL: 1.非关系型数据库就是NoSQL,关系型数据库代表MySQL 也是一种数据库,来存储...

oschina130111
19分钟前
3
0
玩转阿里云 Terraform(二):Terraform 的几个关键概念

上一篇《玩转阿里云Terraform(一):Terraform 是什么》介绍了 Terraform 的基本定义和特点之后,本文将着重介绍几个Terraform中的关键概念。 Terraform 关键概念 在使用Terraform的过程中,通...

阿里云官方博客
20分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部