作者:ARM-WinCE
在WinCE下如果想导入X509证书及私钥,需要用到微软的CryptoAPI函数集,这是微软提供的专门用来支持PKI相关功能的模块。相信有些人知道在Linux下用的是Openssl,我会在后面介绍基于WinCE和Linux不同的平台,如何彼此进行证书验证以及加解密。今天这里先介绍一下证书和私钥的导入。
WinCE支持CryptoAPI函数,但是和Windows环境相比还是有些区别,主要是对一些Hash算法,加解密算法支持的不是很好。在WinCE下调用CryptoAPI函数实现的功能在Windows环境下运行没什么问题,但是反之,就不一定了。
下面先介绍一下证书及私钥导入的相关函数:
1. HCERTSTORE WINAPI CertOpenStore(LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType, HCRYPTPROV hCryptProv, DWORD dwFlags, const void* pvPara)
该函数用于打开一个证书库。
lpszStoreProvider: 证书库提供者,可以是一个文件,也可以来自一个注册表中的键值,或者是系统证书库等。
dwMsgAndCertEncodingType: 没有被使用,设置为0
hCryptProv: 密钥提供者,一般设置为NULL表示使用默认的提供者
dwFlags: 打开证书库的标记位,描述如何打开一个证书库
pvPara: 依赖于第一个参数证书提供者,说明具体的证书提供者
该函数如果调用成功,将会返回打开证书库的句柄。
2. BOOL WINAPI CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags)
该函数用于关闭一个证书库。
hCertStore: 要关闭的证书库的句柄
dwFlags: 标记位,表示如何关闭证书库
该函数调用成功,将返回TRUE
3. PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext)
该函数用于枚举证书库中的X509证书,该函数可以在循环中反复被调用从而枚举每一个X509证书。
hCertStore: 已打开证书库的句柄
pPrevCertContext: 指向上一个被找到的X509证书,如果该参数为NULL,表示第一次被调用
该函数调用成功,将返回被枚举的X509证书结构信息
这里说一下PCCERT_CONTEXT,也就是该函数的返回值,它是一个指针指向CERT_CONTEXT,该结构用于描述X509证书信息,其中包括证书的类型,编码后的证书数据,大小,以及具体X509证书内部的详细信息,比如加密算法,Public key等。
4. BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext)
该函数用于释放一个证书结构。
PCCERT_CONTEXT: 要被释放的X509证书结构的指针
该函数调用成功,返回TRUE。
5. BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, const BYTE* pbCertEncoded, DWORD cbCertEncoded, DWORD dwAddDisposition, PCCERT_CONTEXT* ppCertContext)
该函数用于添加一个X509证书到证书库当中
hCertStore: 已打开证书库的句柄
dwCertEncodingType: 证书的编码类型,这里只能是X509_ASN_ENCODING,表示X509经过ASN.1编码后的证书
pbCertEncoded: 指向要添加的证书数据
cbCertEncoded: 证书的大小
dwAddDisposition: 添加证书标记位,描述添加方法
ppCertContext: 返回经过解码后的证书,一般设置为NULL,如果不为NULL,该结构要通过CertFreeCertificateContext函数释放
该函数调用成功,返回TRUE。
6. BOOLEAN CRYPTFUNC CryptAcquireContext(HCRYPTPROV* phProv, LPCTSTR pszContainer, LPCTSTR pszProvider, DWORD dwProvType, DWORD dwFlags)
该函数用于获得一个可用的密钥容器。
phProv: 返回一个密钥容器提供者的句柄
pszContainer: 密钥容器的名字,如果该参数为NULL,一个默认的秘钥容器名将被使用,如果最后一个参数类型为CYRPT_VERIFYCONTEXT,则该参数必须为NULL
pszProvider: 密钥容器提供者的名字,为NULL,表示使用一个默认的秘钥容器提供者
dwProvType: 要获得的提供者的类型
dwFlags: 密钥容器标记位,描述如何使用密钥容器
该函数调用成功,返回TRUE。
7. BOOL WINAPI CryptImportKey( HCRYPTPROV hProv, BYTE* pbData, DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY* phKey)
该函数用于导入密钥到密钥容器中。
hProv: 密钥容器的句柄
pbData: 密钥数据,必须是密钥BLOB格式
dwDataLen: 密钥数据长度
hPubKey: 一个公钥的句柄,如果导入的密钥是经过签名或者加密的,需要该公钥进行验证或者解密。如果导入的密钥没有加密,该参数为0
dwFlags: 导入标记位,一般为CRYPT_EXPORTABLE
phKey: 返回导入密钥的句柄
该函数调用成功,返回TRUE。
8. CryptExportPublicKeyInfo(HCRYPTPROV hCryptProv, DWORD dwKeySpec, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, DWORD *pcbInfo)
该函数用于导出公钥信息。
hCryptProv: 密钥容器的句柄
dwKeySpec: 密钥的类型,可以是AT_KEYEXCHANGE或者AT_SIGNATURE
dwCertEncodingType: 编码类型,应该是X509_ASN_ENCODING
pInfo: 指向导出的公钥信息
pcbInfo: 作为输入指向Buffer的大小,作为输出表示到处公钥信息的长度
9. PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore, DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType, const void* pvFindPara, PCCERT_CONTEXT pPrevCertContext)
该函数用于在证书库中查找一个X509证书。
hCertStore: 已打开的证书库的句柄
dwCertEncodingType: 证书的类型,只能是X509_ASN_ENCODING
dwFindFlags: 没被使用,设置为0
dwFindType: 指定查找方法,设置为NULL表示返回下一个证书,也可以设置根据发行者信息,证书的ID,public key以及签名等信息来查找
pvFindPara: 指向查找的信息
pPrevCertContext: 指向最后一次调用该函数返回的证书信息,一般设置为NULL
该函数调用成功,将返回找到的证书的信息结构。如果没有找到证书,将返回NULL。
10. BOOL CRYPTFUNC CryptDestroyKey(HCRYPTKEY hKey)
该函数用于释放一个被导入的密钥的句柄。
hKey: 曾经被导入到密钥容器中的密钥句柄
函数调用成功,返回TRUE。
11. BOOL WINAPI CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
该函数用于释放一个以获得的密钥容器。
hProv: 密钥容器的句柄
dwFlags: 没被使用,设置为0
该函数调用成功,返回TRUE。
看完上面的介绍,但是感觉不知如何入手。我第一次使用CryptoAPI就是这种感觉,看完MSDN以后,还是不知道怎么用。幸好WinCE的控制面板中提供了证书和私钥导入的工具,同时也提供了源代码,源代码在” D:\WINCE600\PUBLIC\WCESHELLFE\OAK\CTLPNL\CPLMAIN”下面的certcpl.cpp文件中。我在看过源代码后,将部分移植到我的应用中用于证书和私钥的导入,下面结合代码介绍一下函数的使用。
//初始化函数,打开证书库
DWORD Cert_Init(void)
{
DWORD fRet = FALSE;
HANDLE hCaStore;
PCCERT_CONTEXT pCaCert;
WCHAR szName[512];
//加载Crypt32.dll库
g_hCrypt32 = LoadLibraryW(L"crypt32.dll");
g_CaPubKeySize = 0;
if (g_hCrypt32)
{
pCertOpenStore = (PFNCERTOPENSTORE)GetProcAddressW(g_hCrypt32, L"CertOpenStore");
pCertAddEncodedCertificateToStore = (PFNCERTADDENCODEDCERTIFICATETOSTORE)GetProcAddressW(g_hCrypt32, L"CertAddEncodedCertificateToStore");
pCertCloseStore = (PFNCERTCLOSESTORE)GetProcAddressW(g_hCrypt32, L"CertCloseStore");
pCryptExportPublicKeyInfo = (PFNCRYPTEXPORTPUBLICKEYINFO)GetProcAddressW(g_hCrypt32, L"CryptExportPublicKeyInfo");
pCertFindCertificateInStore = (PFNCERTFINDCERTIFICATEINSTORE)GetProcAddressW(g_hCrypt32, L"CertFindCertificateInStore");
pCertSetCertificateContextProperty = (PFNCERTSETCERTIFICATECONTEXTPROPERTY)GetProcAddressW(g_hCrypt32, L"CertSetCertificateContextProperty");
pCertFreeCertificateContext = (PFNCERTFREECERTIFICATECONTEXT)GetProcAddressW(g_hCrypt32, L"CertFreeCertificateContext");
pCryptAcquireCertificatePrivateKey = (PFNCRYPTACQUIRECERTIFICATEPRIVATEKEY)GetProcAddressW(g_hCrypt32, L"CryptAcquireCertificatePrivateKey");
pCryptDecodeObjectEx = (PFNCRYPTDECODEOBJECTEX)GetProcAddressW(g_hCrypt32, L"CryptDecodeObjectEx");
pCryptImportPublicKeyInfoEx = (PFNCRYPTIMPORTPUBLICKEYINFOEX)GetProcAddressW(g_hCrypt32, L"CryptImportPublicKeyInfoEx");
pCertEnumCertificatesInStore = (PFNCERTENUMCERTIFICATESINSTORE)GetProcAddressW(g_hCrypt32, L"CertEnumCertificatesInStore");
// 打开证书库
hStore = pCertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_MAXIMUM_ALLOWED_FLAG, TEXT("My"));
if (hStore != NULL)
{
fRet = TRUE;
}
}
return fRet;
}
//导入X509证书,lpszFileName为证书的路径
DWORD Cert_ImportCert(LPTSTR lpszFileName)
{
DWORD dwSize, dwRealSize;
PBYTE pbFile = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
BOOL fRet = FALSE;
// 打开证书文件
hFile = CreateFile(lpszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile)
{ // 获得证书文件大小
dwSize = GetFileSize(hFile, NULL);
if (dwSize != 0xFFFFFFFF)
{ // 分配内存
pbFile = (PBYTE)LocalAlloc(LPTR, dwSize);
if (pbFile)
{ // 读入证书
if (ReadFile(hFile, pbFile, dwSize, &dwRealSize, NULL) == TRUE)
{
// 添加一个DER格式的X509证书到证书库
fRet = pCertAddEncodedCertificateToStore(hStore, X509_ASN_ENCODING,
pbFile, dwRealSize, CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES,0);
if (fRet == TRUE)
{
printf("Import Certificate OK.\r\n");
}
else if (fRet = CRYPT_E_EXISTS)
{
printf("Certificate exists.\r\n");
fRet = TRUE;
}
else
{
printf("Import Certificate Error.\r\n");
}
}
}
}
}
if (hFile)
{
CloseHandle(hFile);
}
if (pbFile)
{
LocalFree(pbFile);
}
return fRet;
}
// 获得私钥中的密码,该函数会被Cert_ImportPrvtKey函数调用
static BOOL GetPasswordKey(
IN HCRYPTPROV hProv,
IN ALG_ID Algid,
IN PBYTE pbPswd,
IN DWORD dwPswdLen,
IN BYTE *pbSalt,
IN DWORD cbSalt,
OUT HCRYPTKEY *phEncryptKey
)
{
BOOL fResult;
BYTE *pbPassword;
DWORD cbPassword;
HCRYPTHASH hHash = 0;
HCRYPTKEY hEncryptKey = 0;
pbPassword = pbPswd;
cbPassword = dwPswdLen;
if (cbPassword)
{
if (!CryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash))
goto ErrorReturn;
if (cbSalt) {
if (!CryptHashData(hHash, pbSalt, cbSalt, 0))
goto ErrorReturn;
}
if (!CryptHashData(hHash, pbPassword, cbPassword, 0))
goto ErrorReturn;
if (!CryptDeriveKey(hProv, Algid, hHash, 0, &hEncryptKey))
goto ErrorReturn;
}
fResult = TRUE;
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
if (hEncryptKey)
{
CryptDestroyKey(hEncryptKey);
hEncryptKey = 0;
}
CommonReturn:
if (hHash)
CryptDestroyHash(hHash);
*phEncryptKey = hEncryptKey;
return fResult;
}
//导入PVK格式的私钥并在证书库中查找和它匹配的证书,lpFileName为私钥文件路径,pbPassword为私钥密码,dwPswdLen为密码长度
DWORD Cert_ImportPrvtKey(LPTSTR lpFileName, BYTE* pbPassword, DWORD dwPswdLen)
{
BOOL fRet = TRUE;
DWORD dwSize, dwRealSize, cbPvk, cbData;
PBYTE pbFile = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
HCRYPTPROV hCryptProv = NULL;
PCERT_PUBLIC_KEY_INFO pcertpubkeyinfo = NULL;
BYTE *pbEncryptData = NULL;
BYTE *pbPvk = NULL;
CRYPT_KEY_PROV_INFO keyProvInfo;
HCRYPTKEY hKey = 0;
PCCERT_CONTEXT pCert = NULL;
HCRYPTHASH hHash = 0;
HCRYPTKEY hDecryptKey = 0;
// 打开私钥文件
hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile)
{
dwSize = GetFileSize(hFile, NULL);
if (dwSize != 0xFFFFFFFF)
{
pbFile = (PBYTE)LocalAlloc(LPTR, dwSize);
if (pbFile)
{
if (ReadFile(hFile, pbFile, dwSize, &dwRealSize, NULL) == TRUE)
{
memcpy(szKeyContainer,TEXT("CERT"), sizeof(TEXT("CERT")));
// 检查密钥容器是否存在
if (CryptAcquireContext(&hCryptProv, szKeyContainer, NULL, CSP_ALGORITHM, 0) == FALSE)
{ //不存在,创建新的密钥容器
if (CryptAcquireContext(&hCryptProv, szKeyContainer, NULL, CSP_ALGORITHM, CRYPT_NEWKEYSET) == FALSE)
{
goto PvkErr;
}
}
// Check the Provider
if (!hCryptProv)
{
goto PvkErr;
}
#if 1
<p class