绘声绘彩的生活志

彩动力、彩信之星的诞生地

CAPICOM在VC6下的使用

最近几天在弄数字证书的一些东西,windows平台下在非net平台下的原生编程,用的比较多的是微软提供的CAPICOM组件,从网上找了个例子,MS CAPICOM Samples
可惜在VC6下遇到了很多问题,究其原因,其代码一是采用了高版本vs,二是对于BSTR的处理上稍有不完善之处,没有考虑到CAPICOM_ENCODE_BASE64和CAPICOM_ENCODE_BINARY的差别,在使用CAPICOM_ENCODE_BINARY时,原版本会在采用证书解密(CertDecryptData)时,提示“[0x80093102]:COM:ASN1 异常的数据结尾。”(对应英文提示:“ASN1 bad tag value met”)
下面的代码是我修订过的版本:

// examCAPI.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <afxdisp.h>
// 加载 CAPICOM 组件
#import "capicom.dll"
using namespace CAPICOM;
 
#define CAPICOM_MY_STORE	_T("MY")
 
//=========================================================
// 工具方法
//=========================================================
 
// 暂停
void pause()
{
    _tprintf(_T("\nPress ENTER key to continue \n"));
    getchar();
}
 
// 错误报告
void CancelByError(HRESULT errCode, TCHAR* errStr)
{
    _tprintf(_T("\nFAULT:\n"));
    _tprintf(_T("An error occurred in running the program. \n"));
    _tprintf(_T("%s \n"), errStr);
    throw errCode;
}
 
// 字节反序
void reverse(BYTE* data, int nLen)
{
    for(int ii=0; ii < nLen/2; ii++)
    {
        BYTE c = data[ii];
        data[ii] = data[nLen -ii -1];
        data[nLen -ii -1] = c;
    }
}
 
// 输出文件
void writeFile(const char* sFileName, BYTE* data, DWORD nSize)
{
    FILE* fp = fopen(sFileName, "wb");
    if(fp == NULL)
    {
        CancelByError(0, _T("Open file for write failed!"));
    }
 
    if(fwrite(data, 1, nSize, fp) != nSize)
    {
        fclose(fp);
        CancelByError(0, _T("Write to file failed!"));
    }
 
    fclose(fp);
    printf("Write %d bytes to file '%s'! \n", nSize, sFileName);
}
 
// 读取文件(data = NULL for get file size ONLY)
void readFile(const char* sFileName, BYTE* data, DWORD & nSize)
{
    nSize = 0;
    FILE* fp = fopen(sFileName, "rb");
    if(fp == NULL)
    {
        CancelByError(0, _T("Open file for read failed!"));
    }
 
    fseek(fp, 0, SEEK_END);
    nSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
 
    if(data != NULL)
    {
        if(fread(data, 1, nSize, fp) != nSize)
        {
            fclose(fp);
            CancelByError(0, _T("Read from file failed!"));
        }
        printf("Read %d bytes from file '%s'! \n", nSize, sFileName);
    }
    fclose(fp);
}
 
// 显示HEX码
void showData(BYTE* data, DWORD nSize)
{
    printf("\n****\n");
    for(DWORD ii=0; ii < nSize; ii++)
    {
        printf("%02x ", data[ii]);
        if((ii+1) % 16 ==0) printf("\n");
    }
    printf("\n**** %d bytes\n", nSize);
}
 
// 准备数据(Auto new outData)
void prepareData(BYTE* inData, DWORD inSize, LPCSTR inFileName, BYTE* &outData, DWORD& outSize)
{
    if(inData == NULL && inFileName != NULL)
    {
        // Read from file
        readFile(inFileName, NULL, outSize);
        if(outSize != 0)
        {
            outData = (BYTE*) new char[outSize +1];
            memset(outData, 0, outSize +1);
            if(outData == NULL)    CancelByError(0, _T("Not enough memory. \n"));
            readFile(inFileName, outData, outSize);
        }
    }
    else
    {
        // Read from buffer
        outSize = inSize;
        outData = (BYTE*) new char[outSize];
        if(outData == NULL)    CancelByError(0, _T("Not enough memory. \n"));
        memcpy(outData, inData, outSize);
    }
}
 
// 获取BSTR的长度及字节值
void getBSTRByte(BSTR& bstr, BYTE* (&data), DWORD& nSize)
{
    data = (BYTE*) bstr;
    nSize = *((DWORD*)bstr - 1);
}
 
//=========================================================
// 编码与解码
//=========================================================
 
// BASE64编码
void Base64Encode(BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
 
    // 注意:
    // 原始数据是二进制值,应使用原值,以避免转换为宽字符串参数时导致失真。
    // SysAllocStringByteLen 会禁用多字节到宽字节的转换,因而可以保证内容无损地转换为宽字符串。
    _bstr_t orgStr;
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
    orgStr.Assign(orgBStr);
    SysFreeString(orgBStr);
    if(pbOrgData != NULL) 
		delete [] pbOrgData;
 
    // 创建 Utilities 对象
    IUtilitiesPtr pUtils(__uuidof(Utilities));
    try
    {
        // 编码
        _bstr_t encStr = pUtils->Base64Encode(orgStr);
 
        // 输出
        // 注意:编码输出转换为多字节不会导致失真,直接转换。
        char* pEncStr = encStr;
        printf("[%s]\n", pEncStr);
        if(outFileName != NULL)
        {
            writeFile(outFileName, (BYTE*)pEncStr, strlen(pEncStr));
        }
    }
    catch(_com_error e) 
    {
        // 转换宽字符错误信息为多字节错误信息串
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pUtils.Release();
}
 
// BASE64解码
void Base64Decode(BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备编码数据
    DWORD cbEncData = 0;
    BYTE* pbEncData = NULL;
    prepareData(inData, inSize, inFileName, pbEncData, cbEncData);
 
    // 创建 Utilities 对象
    IUtilitiesPtr pUtils(__uuidof(Utilities));
    try
    {
        // 解码
        // 注意:编码数据转换为宽字符参数不会导致失真,因此直接转换。
        BSTR orgStr = pUtils->Base64Decode((char*)pbEncData);
 
        // 从解码输出的宽字符串中获取原始数据
        DWORD cbOrgData = 0;
        BYTE* pbOrgData = NULL;
 
        getBSTRByte(orgStr, pbOrgData, cbOrgData);
 
        // 输出
        showData(pbOrgData, cbOrgData);
        if(outFileName != NULL)
        {
            writeFile(outFileName, pbOrgData, cbOrgData);
        }
    }
    catch(_com_error e) 
    {
        // 转换宽字符错误信息为多字节错误信息串
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    if(pbEncData != NULL) delete [] pbEncData;
    pUtils.Release();
}
 
//=========================================================
// 数字摘要
//=========================================================
 
// SHA1
void hashSHA1(BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
 
    _bstr_t orgStr;
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
    orgStr.Assign(orgBStr);
    SysFreeString(orgBStr);
    if(pbOrgData != NULL) delete [] pbOrgData;
 
    // 创建 HashData 对象
    IHashedDataPtr pHash(__uuidof(HashedData));
    try
    {
        // 计算数字摘要
        pHash->Algorithm = CAPICOM_HASH_ALGORITHM_SHA1;
        pHash->Hash(orgStr);
 
        _bstr_t outStr = pHash->Value;
        const char* encStr = (char*) outStr;
        printf("%s\n", encStr);
        if(outFileName != NULL)
        {
            writeFile(outFileName, (BYTE*)encStr, strlen(encStr));
        }
    }
    catch(_com_error e) 
    {
        // 转换宽字符错误信息为多字节错误信息串
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT hr)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), hr);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pHash.Release();
}
 
//===============================================================
// 对称加密
//     CAPICOM 加密使用非PKCS#7标准的格式,只能使用 CAPICOM 解密。
//===============================================================
 
// DES 加密
void DESEncrypt(BYTE* key, BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
 
    // 转换为宽字符串参数
    _bstr_t orgStr;
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
    orgStr.Assign(orgBStr);
    SysFreeString(orgBStr);
    if(pbOrgData != NULL) delete [] pbOrgData;
 
    // 创建加密对象
    IEncryptedDataPtr pEncryptedData(__uuidof(EncryptedData));
    try
    {
        // 加密
        pEncryptedData->Content = orgStr;
        pEncryptedData->SetSecret((char*)key, CAPICOM_SECRET_PASSWORD);
        pEncryptedData->Algorithm->Name = CAPICOM::CAPICOM_ENCRYPTION_ALGORITHM_DES;
        _bstr_t encStr = pEncryptedData->Encrypt(CAPICOM_ENCODE_BASE64);
 
        // 输出加密结果
        char* pEncStr = encStr;
        printf("%s", pEncStr);
        if(outFileName != NULL)
        {
            writeFile(outFileName, (BYTE*) pEncStr, strlen(pEncStr));
        }
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT retCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), retCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pEncryptedData.Release();
}
 
// DES 解密
void DESDecrypt(BYTE* key, BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备数据
    DWORD cbEncData = 0;
    BYTE* pbEncData = NULL;
    prepareData(inData, inSize, inFileName, pbEncData, cbEncData);
 
    // 创建加密对象
    IEncryptedDataPtr pEncryptedData(__uuidof(EncryptedData));
    try
    {
        // 解密
        pEncryptedData->SetSecret((char*)key, CAPICOM_SECRET_PASSWORD);
        pEncryptedData->Algorithm->Name = CAPICOM::CAPICOM_ENCRYPTION_ALGORITHM_DES;
        pEncryptedData->Decrypt((char*)pbEncData);
 
        // 获取原始数据
        BSTR orgStr = pEncryptedData->Content;
        BYTE* orgData = NULL;
        DWORD orgSize = 0;
        getBSTRByte(orgStr, orgData, orgSize);
 
        showData(orgData, orgSize);
        if(outFileName != NULL)
        {
            writeFile(outFileName, orgData, orgSize);
        }
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    if(pbEncData != NULL) delete [] pbEncData;
    pEncryptedData.Release();
}
 
// DES 加密(bin)
void DESEncryptBin(BYTE* key, BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
 
    // 转换为宽字符参数
    _bstr_t orgStr;
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
    orgStr.Assign(orgBStr);
    SysFreeString(orgBStr);
    if(pbOrgData != NULL) delete [] pbOrgData;
 
    // 创建加密对象
    IEncryptedDataPtr pEncryptedData(__uuidof(EncryptedData));
    try
    {
        // 加密
        pEncryptedData->Content = orgStr;
        pEncryptedData->SetSecret((char*)key, CAPICOM_SECRET_PASSWORD);
        pEncryptedData->Algorithm->Name = CAPICOM::CAPICOM_ENCRYPTION_ALGORITHM_DES;
        BSTR encStr = pEncryptedData->Encrypt(CAPICOM_ENCODE_BINARY);
 
        // 获取加密数据
        DWORD cbEncData = 0;
        BYTE* pbEncData = NULL;
        getBSTRByte(encStr, pbEncData, cbEncData);
 
        // 输出
        showData(pbEncData, cbEncData);
        if(outFileName != NULL)
        {
            writeFile(outFileName, pbEncData, cbEncData);
        }
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT retCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), retCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pEncryptedData.Release();
}
 
// DES 解密(bin)
void DESDecryptBin(BYTE* key, BYTE* inData, int inSize, LPCSTR inFileName = NULL, LPCSTR outFileName = NULL)
{
    // 准备数据
    DWORD cbEncData = 0;
    BYTE* pbEncData = NULL;
    prepareData(inData, inSize, inFileName, pbEncData, cbEncData);
 
    // 转换为宽字符参数
    _bstr_t encStr;
    BSTR encBStr = SysAllocStringByteLen((LPCSTR)pbEncData, cbEncData);
    encStr.Assign(encBStr);
    SysFreeString(encBStr);
    if(pbEncData != NULL) delete [] pbEncData;
 
    // 创建加密对象
    IEncryptedDataPtr pEncryptedData(__uuidof(EncryptedData));
    try
    {
        // 解密
        pEncryptedData->SetSecret((char*)key, CAPICOM_SECRET_PASSWORD);
        pEncryptedData->Algorithm->Name = CAPICOM::CAPICOM_ENCRYPTION_ALGORITHM_DES;
        pEncryptedData->Decrypt(encStr);
 
        // 获取原始数据
        BSTR orgStr = pEncryptedData->Content;
        BYTE* orgData = NULL;
        DWORD orgSize = 0;
        getBSTRByte(orgStr, orgData, orgSize);
 
        showData(orgData, orgSize);
        if(outFileName != NULL)
        {
            writeFile(outFileName, orgData, orgSize);
        }
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pEncryptedData.Release();
}
 
//=========================================================
// 数字证书
//=========================================================
 
// 显式证书信息
void viewCertInfo(ICertificatePtr pCert)
{
    COleDateTime timeFrom(pCert->ValidFromDate);
    COleDateTime timeTo(pCert->ValidToDate);
    _tprintf(_T("     Subject: %s\n"), pCert->SubjectName);
    _tprintf(_T("   SubjectCN: %s\n"), pCert->GetInfo(CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME));
    _tprintf(_T("     Issuser: %s\n"), pCert->IssuerName);
    _tprintf(_T("SerialNumber: %s\n"), pCert->SerialNumber);
    _tprintf(_T("   ValidDate: %s TO %s \n"), timeFrom.Format("%Y-%m-%d %H:%M:%S"), timeTo.Format("%Y-%m-%d %H:%M:%S"));
 
    // Check Certificate
    ICertificateStatusPtr pStatus = pCert->IsValid();
    pStatus->CheckFlag = CAPICOM_CHECK_TIME_VALIDITY;
    _tprintf(_T("  Check-Time: %s\n"), (pStatus->Result) ? L"Yes" : L"NO");
    pStatus->CheckFlag = CAPICOM_CHECK_SIGNATURE_VALIDITY;
    _tprintf(_T("  Check-Sign: %s\n"), (pStatus->Result) ? L"Yes" : L"NO");
    pStatus->CheckFlag = CAPICOM_CHECK_TRUSTED_ROOT;
    _tprintf(_T("    Check-CA: %s\n"), (pStatus->Result) ? L"Yes" : L"NO");
    pStatus->CheckFlag =  CAPICOM_CHECK_COMPLETE_CHAIN;
    _tprintf(_T(" Check-Chain: %s\n"), (pStatus->Result) ? L"Yes" : L"NO");
}
 
// 系统证书库
void viewSystemCertStore(LPCTSTR sCertName)
{
    // 创建证书库对象
    IStorePtr pStore(__uuidof(Store));
    try
    {
        HRESULT retCode = pStore->Open(CAPICOM_CURRENT_USER_STORE, sCertName, CAPICOM_STORE_OPEN_READ_ONLY);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not open certStore! "));
        }
 
        ICertificatesPtr pCerts = pStore->GetCertificates();
        if(pCerts != NULL)
        {
            _tprintf(_T("======== L I S T  C E R T  I N  S T O R E ============\n"));
            for(int ii=0; ii<pCerts->Count; ii++)
            {
                _variant_t p = pCerts->GetItem(ii+1);
                ICertificatePtr pCert = p.pdispVal;
                if(pCert != NULL)
                {
                    viewCertInfo(pCert);
                    _tprintf(_T("----------------------------------------------------\n"));
                }
            }
            _tprintf(_T("======== %d Certs ============\n"), pCerts->Count);
        }
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pStore.Release();
}
 
// 文件证书库(crt/p7b)
void viewCrtCertStore(LPCSTR strCertFileName)
{
    IStore2Ptr pStore(__uuidof(Store));
    try
    {
        // 访问证书库
        HRESULT retCode = pStore->Open(CAPICOM_MEMORY_STORE, strCertFileName, CAPICOM_STORE_OPEN_READ_WRITE);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not open memory certStore! "));
        }
 
        // 加载证书文件到证书库
        retCode = pStore->Load((char*)strCertFileName, "", CAPICOM_KEY_STORAGE_DEFAULT);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not load certFile! "));
        }
 
        // 列出证书
        ICertificatesPtr pCerts = pStore->GetCertificates();
        if(pCerts != NULL)
        {
            _tprintf(_T("======== L I S T  C E R T  I N  S T O R E ============\n"));
            for(int ii=0; ii<pCerts->Count; ii++)
            {
                _variant_t p = pCerts->GetItem(ii+1);
                ICertificatePtr pCert = p.pdispVal;
                if(pCert != NULL)
                {
                    viewCertInfo(pCert);
                    _tprintf(_T("----------------------------------------------------\n"));
                }
            }
            _tprintf(_T("======== %d Certs ============\n"), pCerts->Count);
        }
 
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pStore.Release();
}
 
// 文件证书库(pfx)
void viewPfxCertStore(LPCSTR sPfxFileName, LPCSTR sPfxPwd)
{
    // 创建证书对象
    ICertificate2Ptr pCert(__uuidof(Certificate));
    try
    {
        HRESULT retCode = pCert->Load(sPfxFileName, sPfxPwd, CAPICOM_KEY_STORAGE_DEFAULT, CAPICOM_CURRENT_USER_KEY);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not open certStore! "));
        }
 
        viewCertInfo(pCert);
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pCert.Release();
}
 
//=========================================================
// 数字签名
//=========================================================
 
// 数字签名
void callSignedData(BYTE* inData, int inSize, LPCSTR inFileName = NULL, BOOL bDetached = FALSE, LPCSTR outFileName = NULL)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
 
    _bstr_t orgStr;
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
    orgStr.Assign(orgBStr);
 
    if(pbOrgData != NULL) delete [] pbOrgData;
    SysFreeString(orgBStr);
 
    // 创建对象
    IStorePtr pStore(__uuidof(Store));                    // 证书库对象
    ISignerPtr pSigner(__uuidof(Signer));                // 签名者对象
    ISignedDataPtr pSignedData(__uuidof(SignedData));    // 签名数据对象
 
    try
    {
        // 设置原始数据
        pSignedData->Content = orgStr;
 
        // 访问证书库
        HRESULT retCode = pStore->Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_READ_ONLY);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not open certStore! "));
        }
 
        // 设置签名者证书
        ICertificates2Ptr pSignerCerts = ((ICertificates2Ptr) pStore->Certificates)->Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, L"彩动力", FALSE);
        if(pSignerCerts == NULL || pSignerCerts->Count < 1)
        {
            CancelByError(0, _T("Can not find signer certificate! "));
        }
 
        pSigner->Certificate = (ICertificatePtr) pSignerCerts->GetItem(1).pdispVal;
        viewCertInfo(pSigner->Certificate);
 
        // 签名者签名
        _bstr_t strSignedStr = pSignedData->Sign(pSigner, bDetached, CAPICOM_ENCODE_BASE64);
 
        /* =============== 多签名者签名 ========================================
 
        ISignerPtr pCoSigner(__uuidof(Signer));                // 协助签名者对象
 
        // 设置协助签名者证书
        ICertificates2Ptr pCoSignerCerts = ((ICertificates2Ptr) pStore->Certificates)->Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, L"041@3BNKICNSZXXX@test9@00000001", FALSE);
        if(pCoSignerCerts == NULL || pCoSignerCerts->Count < 1)
        {
            CancelByError(0, _T("Can not find CoSigner certificate! "));
        }
 
        pCoSigner->Certificate = (ICertificatePtr) pCoSignerCerts->GetItem(1).pdispVal;
        viewCertInfo(pCoSigner->Certificate);
 
        // 协助签名者签名
        strSignedStr = pSignedData->CoSign(pCoSigner, CAPICOM_ENCODE_BASE64);
 
        ========================================================================= */
 
        // 输出
        _tprintf(_T("%s\n"), strSignedStr);
        char* pOutStr = strSignedStr;
        if(outFileName != NULL)
        {
            writeFile(outFileName, (BYTE*)pOutStr, strlen((char*)pOutStr));
        }
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    pStore.Release();
    pSigner.Release();
    pSignedData.Release();
}
 
// 核验带原文的数字签名
void VerifySignedData(LPCSTR inFileName)
{
    // 准备签名数据
    DWORD cbSigData = 0;
    BYTE* pbSigData = NULL;
    prepareData(NULL, 0, inFileName, pbSigData, cbSigData);
 
    // 创建签名数据对象
    ISignedDataPtr pSignedData(__uuidof(SignedData));
 
    try
    {
        // 校验数字签名
        HRESULT retCode = pSignedData->Verify((char*)pbSigData, FALSE, CAPICOM_VERIFY_SIGNATURE_ONLY);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("校验数字签名失败!"));
        }
 
        // 获取原文
        BYTE* orgStr  = NULL;
        DWORD orgSize = 0;
		BSTR content = pSignedData->Content;
        getBSTRByte(content, orgStr, orgSize);
 
        printf("%s\n", orgStr);
 
        // 显式签名者证书信息
        _tprintf(_T("======== L I S T  C E R T  I N  S T O R E ============\n"));
        for(int ii=0; ii<pSignedData->Certificates->Count; ii++)
        {
            viewCertInfo(pSignedData->Certificates->GetItem(ii+1));
            _tprintf(_T("----------------------------------------------------\n"));
        }
        _tprintf(_T("======== %d Certs ============\n"), pSignedData->Certificates->Count);
    }
    catch(_com_error e) 
    {
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    if(pbSigData != NULL) delete [] pbSigData;
    pSignedData.Release();
}
 
// 核验无原文的数字签名
void VerifyDetachedSignedData(BYTE* inData, int inSize, LPCSTR inFileName, LPCSTR outFileName)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
 
    // 准备签名数据
    DWORD cbSigData = 0;
    BYTE* pbSigData = NULL;
    prepareData(NULL, 0, outFileName, pbSigData, cbSigData);
 
    // 创建签名者对象
    ISignerPtr pSigner(__uuidof(Signer));
 
    // 创建签名数据对象
    ISignedDataPtr pSignedData(__uuidof(SignedData));
 
    try
    {
        // 设置原始数据
        _bstr_t inStr;
        inStr.Assign(orgBStr);
        pSignedData->Content = inStr;
 
        // 校验数字签名
        HRESULT retCode = pSignedData->Verify((char*)pbSigData, TRUE, CAPICOM_VERIFY_SIGNATURE_ONLY);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("校验数字签名失败!"));
        }
 
        // 显式签名者证书信息
        _tprintf(_T("======== L I S T  C E R T  I N  S T O R E ============\n"));
        for(int ii=0; ii<pSignedData->Certificates->Count; ii++)
        {
            viewCertInfo(pSignedData->Certificates->GetItem(ii+1));
            _tprintf(_T("----------------------------------------------------\n"));
        }
        _tprintf(_T("======== %d Certs ============\n"), pSignedData->Certificates->Count);
    }
    catch(_com_error e) 
    {
        // 转换宽字符错误信息为多字节错误信息串
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    if(pbOrgData != NULL) delete [] pbOrgData;
    if(pbSigData != NULL) delete [] pbSigData;
    SysFreeString(orgBStr);
    pSigner.Release();
    pSignedData.Release();
}
 
//=========================================================
// 数字信封(证书加密与解密)
//=========================================================
 
// 证书加密
void CertEncryptData(BYTE* inData, int inSize, LPCSTR inFileName, LPCSTR outFileName)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(inData, inSize, inFileName, pbOrgData, cbOrgData);
    BSTR orgBStr = SysAllocStringByteLen((LPCSTR)pbOrgData, cbOrgData);
 
    // 创建证书库对象
    IStorePtr pStore(__uuidof(Store));
 
    // 创建签名数据对象
    IEnvelopedDataPtr pEnvelopedData(__uuidof(EnvelopedData));
 
    try
    {
        // 设置原始数据
        _bstr_t inStr;
        inStr.Assign(orgBStr);
        pEnvelopedData->Content = inStr;
 
        // 设置加密算法
        pEnvelopedData->Algorithm->Name = CAPICOM_ENCRYPTION_ALGORITHM_3DES;
        pEnvelopedData->Algorithm->KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM;
 
        // 设置接收者证书(公钥)
 
        // 1.访问证书库
        HRESULT retCode = pStore->Open(CAPICOM_CURRENT_USER_STORE, CAPICOM_MY_STORE, CAPICOM_STORE_OPEN_READ_ONLY);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not open certStore! "));
        }
 
        // 2.获取接收者证书
        ICertificates2Ptr pReceiverCerts = ((ICertificates2Ptr) pStore->Certificates)->Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, L"彩动力", FALSE);
        if(pReceiverCerts == NULL || pReceiverCerts->Count < 1)
        {
            CancelByError(0, _T("Can not find receiver certificate! "));
        }
        pEnvelopedData->Recipients->Add((ICertificatePtr) pReceiverCerts->GetItem(1).pdispVal);
 
        // 证书加密
        _bstr_t strCryptedStr = pEnvelopedData->Encrypt(CAPICOM_ENCODE_BINARY);
 
        // 获取密文数据
#if 0
        BYTE* encStr  = (BYTE *)(char *)strCryptedStr;
        DWORD encSize = strCryptedStr.length();
#else
        BYTE* encStr;
        DWORD encSize;
        BSTR  encBStr = strCryptedStr;
        getBSTRByte(encBStr, encStr, encSize);
        SysFreeString(encBStr);
#endif
        // 输出
        showData(encStr, encSize);
        if(outFileName != NULL)
        {
            writeFile(outFileName, encStr, encSize);
        }
    }
    catch(_com_error e) 
    {
        // 转换宽字符错误信息为多字节错误信息串
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    if(pbOrgData != NULL) delete [] pbOrgData;
    SysFreeString(orgBStr);
    pStore.Release();
    pEnvelopedData.Release();
}
 
// 证书解密
void CertDecryptData(LPCSTR inFileName, LPCSTR outFileName)
{
    // 准备原始数据
    DWORD cbOrgData = 0;
    BYTE* pbOrgData = NULL;
    prepareData(NULL, 0, inFileName, pbOrgData, cbOrgData);
 
    // 创建签名数据对象
    IEnvelopedDataPtr pEnvelopedData(__uuidof(EnvelopedData));
 
    try
    {
        // 证书解密
		BSTR inBSTR = SysAllocStringByteLen((char *)pbOrgData, cbOrgData);
		_bstr_t inStr = inBSTR;
		const char *buf = inStr;
        HRESULT retCode = pEnvelopedData->Decrypt(inStr);
        if(FAILED(retCode))
        {
            CancelByError(retCode, _T("Can not decrypt data! "));
        }
 
        // 获取原文数据
        BSTR orgBStr  = pEnvelopedData->Content;
        BYTE* orgStr  = NULL;
        DWORD orgSize = 0;
        getBSTRByte(orgBStr, orgStr, orgSize);
        SysFreeString(orgBStr);
 
        // 输出
        showData(orgStr, orgSize);
        if(outFileName != NULL)
        {
            writeFile(outFileName, orgStr, orgSize);
        }
    }
    catch(_com_error e) 
    {
        // 转换宽字符错误信息为多字节错误信息串
        _bstr_t strErrMesg(e.ErrorMessage());
        printf("[%#x]:COM:%s.\n", e.Error(), (char*)strErrMesg);
    }
    catch (HRESULT errCode)
    {
        _tprintf(_T("[%#x]:CAPICOM error. \n"), errCode);
    }
    catch(...)
    {
        _tprintf(_T("Unknown error. \n"));
    }
 
    // 清理
    if(pbOrgData != NULL) delete [] pbOrgData;
    pEnvelopedData.Release();
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(0);
 
#ifdef _TEST_BASE64
    // BASE64
    Base64Encode((BYTE*)"123456789012345678", 15, NULL, "c:\\li.base64");
    Base64Decode(NULL, 0, "c:\\li.base64", "c:\\li.org");
#endif
 
#ifdef _TEST_HASH
    // HASH
    hashSHA1((BYTE*)"1234567890", 10, NULL, "c:\\li.sha1");
    hashSHA1(NULL, 0, "c:\\li.txt", "c:\\li.sha1");
#endif
 
#ifdef _TEST_ENDECRYPT
    // Encrypt & Decrypt
    DESEncrypt((BYTE*)"12345678", (BYTE*)"1234567890\x00 12345", 15, NULL, "c:\\li.des");
    DESDecrypt((BYTE*)"12345678", NULL, 0, "c:\\li.des", "c:\\1.org");
    DESEncryptBin((BYTE*)"12345678", (BYTE*)"123456789012345", 15, NULL, "c:\\li.des");
    DESDecryptBin((BYTE*)"12345678", NULL, 0, "c:\\li.des", "c:\\1.org");
#endif
 
#ifdef _TEST_VIEWSTORE
    // CertStore & Cert
    viewSystemCertStore("MY");                // CAPICOM_MY_STORE
    viewSystemCertStore("CA");                // CAPICOM_CA_STORE
    viewSystemCertStore("ROOT");            // CAPICOM_ROOT_STORE
    viewSystemCertStore("AddressBook");    // CAPICOM_OTHER_STORE
#endif
 
#ifdef _TEST_VIEWFILE
    viewCrtCertStore("c:\\temp\\ca\\test.p7b");
    viewCrtCertStore("c:\\temp\\ca\\test.cer");
    viewPfxCertStore("c:\\temp\\ca\\test.pfx", "123456");
#endif
 
#ifdef _TEST_SIGNDATA
    // TACHED SignData
    callSignedData((BYTE*)"123456789012345", 15, NULL, FALSE, "c:\\li.tch.sig");
    VerifySignedData("c:\\li.tch.sig");
#endif
 
#ifdef _TEST_DETACHEDSIGNDATA
    // DETACHED SignData
    callSignedData((BYTE*)"1234567890", 10, NULL, TRUE, "c:\\li.detch.sig");
    VerifyDetachedSignedData((BYTE*) "1234567890", 10, NULL, "c:\\li.detch.sig");
#endif
 
    // Cert Encrypt & Decrypt
    CertEncryptData((BYTE*)"1234567890", 10, NULL, "c:\\li.enc");
    CertDecryptData("c:\\li.enc", "c:\\li.org");
 
    CoUninitialize();
    pause();
    return 0;
}
 
/**================================================================================
About BSTR
 
BSTR是"Basic STRing"的简称,是微软在COM/OLE中定义的标准字符串数据类型。
定义在wtypes.h文件中:
typedef wchar_t WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR __RPC_FAR * BSTR;
 
COM字符(wchar_t)使用16bit,因此可以支持各种code pages,包括Unicode。
Windows系统可以简单理解为OLECHAR使用的就是Unicode 。
OLECHAR串与单字节字符串很类似,是一个以NULL(\x0\x0)结尾的buffer。
 
使用以NULL结尾字符串在COM component间传递不太方便,
因此,标准的BSTR是一个有长度前缀和NULL结束符的OLECHAR数组。
BSTR的前4字节是一个表示字符串长度的前缀,其值是不含NULL的字符串字节数。
 
BSTR和OLECHAR的交换可以使用COM提供了两个BSTR分配用的API:
SysAllocString / SysReallocString。
函数返回的指针指向BSTR的第一个字符,而不是BSTR在内存的第一个字节,即去掉前缀。
 
使用BSTR一定要注意释放该对象。SysFreeString()
 
_bstr_t是C++对BSTR的封装,VC中是为了兼容BSTR类型而增加的,也为实现LPCSTR与BSTR转换。
 
==================================================
 
BSTR转换成char*
 
方法一,使用ConvertBSTRToString。
 
#pragma comment(lib, 'comsupp.lib')
 
BSTR bstrText = ::SysAllocString(L"Test");
char* p = _com_util::ConvertBSTRToString(bstrText);
SysFreeString(bstrText); // 用完释放
delete[] lpszText2;
 
方法二,使用_bstr_t的赋值运算符重载。
 
_bstr_t b = bstrText;
char* lpszText2 = b;
 
-------------------------
 
char*转换成BSTR
 
方法一,使用SysAllocString等API函数。
 
BSTR bstrText = ::SysAllocString(L'Test');
BSTR bstrText = ::SysAllocStringLen(L'Test',4);
BSTR bstrText = ::SysAllocStringByteLen('Test',4);
 
方法二,使用COleVariant或_variant_t。
 
COleVariant strVar('This is a test');
_variant_t strVar('This is a test');
BSTR bstrText = strVar.bstrVal;
 
方法三,使用_bstr_t,这是一种最简单的方法。
 
BSTR bstrText = _bstr_t('This is a test');
 
方法四,使用CComBSTR。例如:
 
BSTR bstrText = CComBSTR('This is a test');
 
或
 
CComBSTR bstr('This is a test');
BSTR bstrText = bstr.m_str;
 
方法五,使用ConvertStringToBSTR。例如:
 
char* lpszText = 'Test';
BSTR bstrText = _com_util::ConvertStringToBSTR(lpszText);
 
===================================================================================**/

Mac OSX下SO_NOSIGPIPE的怪异表现(二)

为了测试进程间通讯的多种方式,尝试了使用unix domain。其使用很简单,等价于传统的tcp/udp通讯,不同之处在于使用了本地文件节点而不占用本地端口号。
下面是一个使用ipc unix domain通讯的server例子。

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
 
#define UNIX_DOMAIN "/var/tmp/LinuxLessons.domain"
 
int main(int argc, const char *argv[])
{
	int iret;
	unsigned long ul = 1;
	unsigned int ui = 1;
	struct sockaddr_un address;
	int hsocket = socket(PF_UNIX, SOCK_STREAM, 0);
	if (hsocket == -1) {
		perror("socket PF_UNIX create failure:");
		return EXIT_FAILURE;
	}
	unlink(UNIX_DOMAIN);
	address.sun_family = AF_UNIX;
	strncpy(address.sun_path, UNIX_DOMAIN, sizeof(address.sun_path)-1);
	iret = bind(hsocket, (const struct sockaddr *)&address, (socklen_t)sizeof(address));
	if(iret)
	{
		perror("socket PF_UNIX bind failure:");
		close(hsocket);
	}
 
	ioctl(hsocket, FIONBIO, &ul);
	iret = listen(hsocket, 255);
	if (iret==-1)
	{
		perror("listen socket %d error:");
		unlink(UNIX_DOMAIN);
		return EXIT_FAILURE;
	}
 
	for (;;)
	{
		fd_set set;
		struct timeval timeout;
		FD_ZERO(&set);
		FD_SET(hsocket, &set);
		memset(&timeout, 0x00, sizeof(timeout));
		timeout.tv_usec = 500000;
		iret = select(hsocket+1, &set, NULL, NULL, &timeout);
		switch(iret)
		{
		case -1:
			perror("Select error");
			break;
		case 0:
			printf(".");
			fflush(stdout);
			continue;
		default:
			if(FD_ISSET(hsocket, &set))
			{
				struct sockaddr_un in_address;
				socklen_t len;
				int hclientsocket = accept(hsocket, (struct sockaddr *) &in_address, &len);
				if(hclientsocket<0)
				{
					perror("accept client connect error:");
					break;
				}
#ifdef __APPLE__
				iret = setsockopt(hclientsocket, SOL_SOCKET, SO_NOSIGPIPE, &ui, (socklen_t)sizeof(int));
				if(iret==-1)
				{
					perror("setsockopt SO_NOSIGPIPE:");
				}else{
					printf("setsockopt SO_NOSIGPIPE ok!\n");
				}
#endif
				iret = send(hclientsocket, "hello unix domain!", sizeof("hello unix domain!") - 1,
						MSG_DONTWAIT |
#ifdef __APPLE__
								SO_NOSIGPIPE
#else
								MSG_NOSIGNAL
#endif
						);
				if(iret==-1)
				{
					perror("send data error");
				}else if(iret==0){
					printf("connection maybe down!\n");
				}else{
					printf("welcome msg sent, %d bytes!\n", iret);
				}
			}else{
				printf("fd is not in set!\n");
			}
		}
	}
	unlink(UNIX_DOMAIN);
	close(hsocket);
	return EXIT_SUCCESS;
}

大致的运行目的是启动后,向通过unix domain连接过来的客户端发送一个欢迎信息。遇到的奇诡之处在于:
1)鉴于mac os是基于freeBSD内核,遵守POSIX标准,我直接在macbook上编写并且运行了这个程序,且mac下send函数MSG_NOSIGNAL对应为SO_NOSIGPIPE,目的是不接收由于Client异常断开造成的Broken pipe信号。
2)实际测试SO_NOSIGPIPE在mac下(OS X Yosemite 10.10.2)send中无效;
3)使用setsockopt后有效。
即,在mac下总会提示”Broken pipe: 13″,然后程序退出。同期:
1)Linux下程序运行正常,pipe信号成功被屏蔽
2)mac下tcp socket程序正常,pipe信号成功被屏蔽。
结论:
mac(OS X)下对于unix domain,只支持setsockopt对SO_NOSIGPIPE的设置,算是OSX的BUG么?

Mac OSX下SO_NOSIGPIPE的怪异表现

在linux下为了避免网络出错引起程序退出,我们一般采用MSG_NOSIGNAL来避免系统发送singal。这种错误一般发送在网络断开,但是程序仍然发送数据时,在接收时,没有必要使用。但是在linux下,使用此参数,也不会引起不好的结果,下面是我常用的代码:

    isent = send(socket_fd, szRecvbuff, irecv, MSG_DONTWAIT|
    #if (defined(__APPLE__) && defined(__MACH__))
                                 SO_NOSIGPIPE
    #else
                                 MSG_NOSIGNAL
    #endif
    );
    //recv同

Mac OSX,底层融合了FreeBSD,兼容大部分的POSIX函数,绝大多数的Linux代码可以很少的修改即可使用,在我写的一个TCP测试server中,也是用了上述的代码:

    irecv = recv(socket_fd, szRecvbuff, sizeof(szRecvbuff), MSG_DONTWAIT|
    #if (defined(__APPLE__) && defined(__MACH__))
                                 SO_NOSIGPIPE
    #else
                                 MSG_NOSIGNAL
    #endif
    );

程序表现出来的症状是:
1)recv循环接收到相同的数据,即recv执行后,不会清除底层socket的缓冲区指针,在使用select时,一直提示有读数据,在不使用select循环recv时,永远不会出现0以下结果。直到
2)socket在下次accept时出现段错误。
但同期,使用linux,并无此现象。首先,程序中使用到了没有必要的多余参数,其次,是否说明MacOSX下对于Socket的处理有值得商榷的地方哪?存疑中。 使用下面的方法来设置socket,正常:

    int optval = 1;
    setsockopt(new_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, sizeof(int));

不同编译器下union的size

 
#include <stdio.h>   
#pragma pack( push, 1 )
union type
{
    unsigned char udpdatatype;
    struct {
        unsigned int version:4;
        unsigned int reserved_sign:2;
        unsigned int needack_sign:1;
        unsigned int resend_sign :1;
    } protocol;
};
 
#pragma pack( pop )
 
int main(int argc, const char *argv[])
{
    printf("sizeof: %u.\n", sizeof(union type));
    return 0;
}

试着在gcc和VC中运行下,看看sizeof是多少,修改struct中的unsigned int 为unsigned char再看看:

1)unsigned int 情况下,sizeof在gcc下为1,VC下为4
2)unsigned char情况下,sizeof在gcc下为1,VC下也为1