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

    关于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;
}

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

转载于:https://my.oschina.net/u/2445210/blog/856882

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值