调用OpenSSL实现数字签名功能例程(二)

// PKCS7Sign.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>   
#include <openssl/md5.h> 

#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/ssl.h>

#pragma comment(lib, "libeay32.lib")  
#pragma comment(lib, "ssleay32.lib")  

/*
PKCS7Sign.cpp
Auth:Kagula
功能:调用OpenSSL实现数字签名功能例程(二)
环境:VS2008+SP1,OpenSSL1.0.1
*/

/*
功能:初始化OpenSSL
*/
void InitOpenSSL()
{
 CRYPTO_malloc_init();
 /* Just load the crypto library error strings,
 * SSL_load_error_strings() loads the crypto AND the SSL ones */
 /* SSL_load_error_strings();*/
 ERR_load_crypto_strings();
 OpenSSL_add_all_algorithms();
 OpenSSL_add_all_ciphers();
 OpenSSL_add_all_digests();
}

/*
功能:对length长度的input指向的内存块进行BASE64编码
入口:
const void *input           指向内存块的指针
int length                  内存块的有效长度
返回:
char *                      返回字符串指针,使用完毕后,必须用free函数释放。
*/
char *base64(const void *input, int length)
{
  BIO *bmem, *b64;
  BUF_MEM *bptr;

  b64 = BIO_new(BIO_f_base64());
  bmem = BIO_new(BIO_s_mem());
  b64 = BIO_push(b64, bmem);
  BIO_write(b64, input, length);
  BIO_flush(b64);
  BIO_get_mem_ptr(b64, &bptr);

  char *buff = (char *)malloc(bptr->length);
  memcpy(buff, bptr->data, bptr->length-1);
  buff[bptr->length-1] = 0;

  BIO_free_all(b64);

  return buff;
}

/*
功能:base64解码
入口:
char *inputBase64  BASE64编码的签名
void *retBuf       缓存大小
返回:
void *retBuf       解码后数据存放在这块内存中
int *retBufLen     解码后数据的长度
*/
void *decodeBase64(char *inputBase64, void *retBuf,int *retBufLen)
{
 BIO *b64, *bmem;
 
 b64 = BIO_new(BIO_f_base64());
 bmem = BIO_new_mem_buf(inputBase64, strlen((const char *)inputBase64));
 bmem = BIO_push(b64, bmem);
 int err=0;
 int i=0;
 do{
  err = BIO_read(bmem, (void *)( (char *)retBuf+i++), 1);
 }while( err==1 && i<*retBufLen );
 BIO_free_all(bmem);

 *retBufLen = --i;
 
 return retBuf;
}

 

/*
功能:对明文进行签名
入口:
char*certFile    证书(例如:xxx.pfx)
char* pwd        证书的密码
char* plainText  待签名的字符串
int flag         签名方式
出口:
char *           签名后的数据以BASE64形式返回
                 使用完毕后,必须用free函数释放。
*/

char * PKCS7_GetSign(char*certFile,char* pwd, char* plainText,int flag)
{
 //取PKCS12對象
 FILE* fp;
 if (!(fp = fopen(certFile, "rb")))
 {
  fprintf(stderr, "Error opening file %s\n", certFile);       
  return NULL;    
 }   
 PKCS12 *p12= d2i_PKCS12_fp(fp, NULL); 
 fclose (fp);   
 if (!p12) {     
  fprintf(stderr, "Error reading PKCS#12 file\n");  
  ERR_print_errors_fp(stderr); 
  return NULL;  
 }
 
 //取pkey對象、X509證書、證書鏈
 EVP_PKEY *pkey=NULL;    
 X509 *x509=NULL;
 STACK_OF(X509) *ca = NULL;
 if (!PKCS12_parse(p12, pwd, &pkey, &x509, &ca)) {        
  fprintf(stderr, "Error parsing PKCS#12 file\n");      
  ERR_print_errors_fp(stderr);
  return NULL;
 }
 PKCS12_free(p12);

 //明文轉為BIO對象
 //《vc++网络安全编程范例(14)-openssl bio编程 》   http://www.2cto.com/kf/201112/115018.html
 BIO *bio = BIO_new(BIO_s_mem()); 
 BIO_puts(bio,plainText);

 //數字簽名
 //PKCS7_NOCHAIN:签名中不包含证书链,第三个参数为NULL值的话,可不加这个FLAG标记
 //PKCS7_NOSMIMECAP:签名不需要支持SMIME
 PKCS7* pkcs7 = PKCS7_sign(x509,pkey, ca,bio, flag);
 if(pkcs7==NULL)
 {
  ERR_print_errors_fp(stderr);
  return NULL;
 }

 //共有两种编码,一种是ASN1,另一种是DER编码。
 //取數據簽名(DER格式)
 //openssl学习笔记之pkcs7-data内容类型的编码解码
 //http://ipedo.i.sohu.com/blog/view/114822358.htm
 //入口:pkcs7对象
 //出口:der对象
 unsigned char *der;
 unsigned char *derTmp;
 unsigned long derlen;
 derlen = i2d_PKCS7(pkcs7,NULL);
 der = (unsigned char *) malloc(derlen);
 memset(der,0,derlen);
 derTmp = der;
    i2d_PKCS7(pkcs7,&derTmp);

 //DER转BASE64
 return base64(der,derlen);
}

/*
功能:验证签名
入口:
char*certFile    证书(含匙)
char* plainText  明文
char* cipherText 签名
出口:
bool true  签名验证成功
bool false 验证失败
*/
bool PKCS7_VerifySign(char*certFile,char* plainText,char* cipherText )
{
 /* Get X509 */
 FILE* fp = fopen (certFile, "r");
 if (fp == NULL)
  return false;
 X509* x509 = PEM_read_X509(fp, NULL, NULL, NULL);
 fclose (fp);

 if (x509 == NULL) {
  ERR_print_errors_fp (stderr);
  return false;
 }

 //BASE64解码
 unsigned char *retBuf[1024*8];
 int retBufLen = sizeof(retBuf);
 memset(retBuf,0,sizeof(retBuf));
 decodeBase64(cipherText,(void *)retBuf,&retBufLen);

 //从签名中取PKCS7对象
 BIO* vin = BIO_new_mem_buf(retBuf,retBufLen);
 PKCS7 *p7 = d2i_PKCS7_bio(vin,NULL);


 //取STACK_OF(X509)对象
 STACK_OF(X509) *stack=sk_X509_new_null();//X509_STORE_new()
 sk_X509_push(stack,x509);


 //明码数据转为BIO
 BIO *bio = BIO_new(BIO_s_mem()); 
 BIO_puts(bio,plainText);

 //验证签名
 int err = PKCS7_verify(p7, stack, NULL,bio, NULL, PKCS7_NOVERIFY);

 if (err != 1) {
  ERR_print_errors_fp (stderr);
  return false;
 }

 return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
 char certFile[] = "demo.pfx";
 char plainText[]= "Hello,World!";

 InitOpenSSL();

 //數字簽名
 //PKCS7_NOCHAIN:签名中不包含证书链
 //PKCS7_NOSMIMECAP:签名不需要支持SMIME
 char * cipherText = PKCS7_GetSign(certFile,"11111111",plainText,PKCS7_DETACHED|PKCS7_NOSMIMECAP);

 //打印出BASE64编码后的签名
 std::cout<<cipherText<<std::endl;

 //验证数字签名
 if(PKCS7_VerifySign("BOC-CA.cer",plainText,cipherText))
  std::cout<<"Verify OK!"<<std::endl;
 else
  std::cout<<"Verify Failed!"<<std::endl;
 
 //释放签名字符串(缓存)
 free(cipherText);

 //输入任意字符继续
 getchar();
 return 0;
}

/*
关于OpenSSL的补充参考资料
[1]convert a PKCS 12 cert to PEM format .
http://blog.csdn.net/immcss/article/details/4443319
*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值