CSP介紹、以及使用CryptoAPI枚舉CSP並獲取其屬性

CSP,全名爲“加密服務提供者(Cryptographic Service Provider)”,是微軟定義的一套密碼服務API。目前常用的密碼規範或者標準有3套:CSP,PKCS#11和國密標準。前兩者主要是爲RSA算法提供服務,當然PKCS#11最新的擴展也開始支持ECC算法。而國家密碼管理制定的國密標準,主要提供SM2(實際上也是ECC)服務,當然國密標準同時支持RSA,不過大多數情況下RSA的應用還是使用CSP和PKCS#11來實現。


一、CSP爲一個獨立的密鑰服務模塊

CSP可以是軟件,比如Windows自帶的“Microsoft Base Cryptographic Provider v1.0”和“Microsoft Enhanced Cryptographic Provider v1.0”。

CSP也可以是硬件設備,通常是USBKey,比如飛天誠信等廠商生產的。


二、一個CSP對應一個密鑰容器

CSP沒有設備(Key)的概念,這點和PKCS11以及國密規範都不一樣。一個CSP直接對應一個密鑰容器。通過CSP名和容器名直接定位密鑰模塊,如果不指定容器名,則是定位缺省的容器(一般情況下爲第一個容器)。所以對CSP來說,最好容器名要求唯一,一般是使用GUID來作爲容器名的。

如果同一個CSP有多個設備,在需要確定使用哪個設備時(比如新建容器),CSP會彈出選擇框,根據設備的SN來選擇使用哪個設備。


三、一個密鑰容器可以包含一對簽名密鑰、一對加密鑰、一個簽名證書以及一個加密證書

通常CSP的一個密鑰容器只包含一對密鑰對和對應的證書,但是理論上可以把簽名密鑰對和加密密鑰對放在同一個容器,然後通過AT_SIGNATURE和AT_KEYEXCHANGE來查找密鑰。


四、枚舉系統中的CSP

系統中的CSP,都在註冊表:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider目錄下,我們可以通過API:CryptEnumProviders()來枚舉想要的CSP,如下面代碼所示:

void CTestCSPDlg::EnumCSP()
{
	DWORD dwIndex = 0;
	DWORD dwType = 0;
	DWORD dwNameLen = 0;
	CComboBox* pCSPList = (CComboBox*)GetDlgItem(IDC_COMBO_CSPLIST);
	pCSPList->ResetContent();

	while (CryptEnumProviders(dwIndex, NULL, 0, &dwType, NULL, &dwNameLen))
	{	
		DWORD dwItem = 0;
		TCHAR * pName = new TCHAR[dwNameLen + 1 ];
		if (CryptEnumProviders(dwIndex++, NULL, 0, &dwType, pName, &dwNameLen))
		{
			dwItem = pCSPList->AddString(pName);
			pCSPList->SetItemData(dwItem, dwType);
		}
		delete []pName;
	}
	pCSPList->SetCurSel(0);
	OnCbnSelchangeComboCsplist();
}

五、獲取CSP屬性

得到CSP句柄之後,可以通過API:CryptGetProvParam()獲取CSP的屬性,比如該CSP具有的容器名、實現類型、支持算法等等。

比如下面的代碼爲獲取當前CSP使用的容器名:

	//獲取CSP容器名稱
	dwParamLen = 2048;
	memset(btParamData, 0, 2048);
	pList->InsertItem(dwIndex, _T("PP_CONTAINER"), 0);
	pList->SetItemText(dwIndex, 1, _T("密鑰容器名稱"));
	if (CryptGetProvParam(hProv, PP_CONTAINER, btParamData, &dwParamLen, 0))
	{
		TCHAR *tcValue = NULL;
#ifdef UNICODE
		tcValue = A2W((char*)btParamData);
#else
		tcValue = (char*)btParamData;
#endif
		pList->SetItemText(dwIndex, 2, tcValue);
	}
	else
	{
		pList->SetItemText(dwIndex, 2, _T("Failed!"));
	}

下面的代碼枚舉CSP所有的容器名:

	//獲取CSP所有的容器名稱
	dwParamLen = 2048;
	memset(btParamData, 0, 2048);
	pList->InsertItem(dwIndex, _T("PP_ENUMCONTAINERS"), 0);
	pList->SetItemText(dwIndex, 1, _T("所有容器名"));
	if (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, btParamData, &dwParamLen, CRYPT_FIRST))
	{
		CString strContianers;
		TCHAR *tcValue = NULL;
#ifdef UNICODE
		tcValue = A2W((char*)btParamData);
#else
		tcValue = btParamData;
#endif
		strContianers += tcValue;
		
		dwParamLen = 2048;
		memset(btParamData, 0, 2048);
		while (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, btParamData, &dwParamLen, CRYPT_NEXT))
		{
#ifdef UNICODE
			tcValue = A2W((char*)btParamData);
#else
			tcValue = btParamData;
#endif
			strContianers += _T("/");
			strContianers += tcValue;
		}
		pList->SetItemText(dwIndex, 2, strContianers);
	}
	else
	{
		pList->SetItemText(dwIndex, 2, _T("Failed!"));
	}


下面的代碼獲取CSP的所支持的算法:

	//獲取CSP所支持的算法信息
	dwParamLen = 2048;
	memset(btParamData, 0, 2048);
	pList->InsertItem(dwIndex, _T("PP_ENUMALGS"), 0);
	pList->SetItemText(dwIndex, 1, _T("支持的算法信息"));
	if (CryptGetProvParam(hProv, PP_ENUMALGS, btParamData, &dwParamLen, CRYPT_FIRST))
	{
		CString strAlgs;
		PROV_ENUMALGS* alg = (PROV_ENUMALGS*)btParamData;
		TCHAR *tcValue = NULL;
#ifdef UNICODE
		tcValue = A2W(alg->szName);
#else
		tcValue = alg->szName;
#endif
		strAlgs += tcValue;
		
		dwParamLen = 2048;
		memset(btParamData, 0, 2048);
		while (CryptGetProvParam(hProv, PP_ENUMALGS, btParamData, &dwParamLen, CRYPT_NEXT))
		{
			alg = (PROV_ENUMALGS*)btParamData;
#ifdef UNICODE
			tcValue = A2W(alg->szName);
#else
			tcValue = alg->szName;
#endif
			strAlgs += _T("/");
			strAlgs += tcValue;
		}
		pList->SetItemText(dwIndex, 2, strAlgs);
	}
	else
	{
		pList->SetItemText(dwIndex, 2, _T("Failed!"));
	}

等等。


如果需要詳細代碼,請下載本人枚舉CSP的例子,下載連接爲:枚舉CSP並獲取屬性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章