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

14 篇文章 1 订阅
// 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
*/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kagula086

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值