// PKCS7Sign.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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 main(int argc, char* 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<
//验证数字签名,BOC-CA.cer 为含有公钥的证书,最简单的验证是由p12转化为pem,如xxx.pem
if(PKCS7_VerifySign("BOC-CA.cer",plainText,cipherText))
std::cout<<"Verify OK!"<
else
std::cout<<"Verify Failed!"<
//释放签名字符串(缓存)
free(cipherText);
//输入任意字符继续
getchar();
return 0;
}
/*
关于OpenSSL的补充参考资料
[1]convert a PKCS 12 cert to PEM format .
http://blog.csdn.net/immcss/article/details/4443319
*/