|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Microsoft Windows, Copyright (C) Microsoft Corporation, 2000
File: SmartCard.cpp
Content: Implementation of helper routines for accessing certificates in smart card. Functions in this module require that the Smart Card Base Component v1.1 to be installed.
History: 12-06-2001 dsie created
------------------------------------------------------------------------------*/
#include "StdAfx.h"
#include "CAPICOM.h"
#include "SmartCard.h"
//
// typedefs for SCardXXX APIs.
//
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDESTABLISHCONTEXT) ( IN DWORD dwScope, IN LPCVOID pvReserved1, IN LPCVOID pvReserved2, OUT LPSCARDCONTEXT phContext);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDLISTREADERSA) ( IN SCARDCONTEXT hContext, IN LPCSTR mszGroups, OUT LPSTR mszReaders, IN OUT LPDWORD pcchReaders);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDGETSTATUSCHANGEA) ( IN SCARDCONTEXT hContext, IN DWORD dwTimeout, IN OUT LPSCARD_READERSTATE_A rgReaderStates, IN DWORD cReaders);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDLISTCARDSA) ( IN SCARDCONTEXT hContext, IN LPCBYTE pbAtr, IN LPCGUID rgquidInterfaces, IN DWORD cguidInterfaceCount, OUT LPSTR mszCards, IN OUT LPDWORD pcchCards);
typedef WINSCARDAPI LONG (WINAPI* PFNSCARDGETCARDTYPEPROVIDERNAMEA) ( IN SCARDCONTEXT hContext, IN LPCSTR szCardName, IN DWORD dwProviderId, OUT LPSTR szProvider, IN OUT LPDWORD pcchProvider);
typedef WINSCARDAPI LONG (WINAPI* PFNSCARDFREEMEMORY) ( IN SCARDCONTEXT hContext, IN LPVOID pvMem);
typedef WINSCARDAPI LONG (WINAPI * PFNSCARDRELEASECONTEXT) ( IN SCARDCONTEXT hContext);
//
// Function pointer to SCardXXX APIs.
//
static PFNSCARDESTABLISHCONTEXT pfnSCardEstablishContext = NULL; static PFNSCARDLISTREADERSA pfnSCardListReadersA = NULL; static PFNSCARDGETSTATUSCHANGEA pfnSCardGetStatusChangeA = NULL; static PFNSCARDLISTCARDSA pfnSCardListCardsA = NULL; static PFNSCARDGETCARDTYPEPROVIDERNAMEA pfnSCardGetCardTypeProviderNameA = NULL; static PFNSCARDFREEMEMORY pfnSCardFreeMemory = NULL; static PFNSCARDRELEASECONTEXT pfnSCardReleaseContext = NULL;
////////////////////////////////////////////////////////////////////////////////
//
// Local functions.
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : AddCert
Synopsis : Add the specified certificate to the specified store.
Parameter: - IN LPCSTR szCSPName CSP name string. - IN LPCSTR szContainerName Key container name string. - IN DWORD dwKeySpec
AT_KEYEXCHANGZE or AT_SIGNATURE. - IN LPBYTE pbEncodedCert
Pointer to encoded cert data to be added.
- IN DWORD cbEncodedCert
Length of encoded cert data.
- IN HCERTSTORE hCertStore Handle of cert store where the cert will be added.
Remarks :
------------------------------------------------------------------------------*/
static HRESULT AddCert (IN LPCSTR szCSPName, IN LPCSTR szContainerName, IN DWORD dwKeySpec, IN LPBYTE pbEncodedCert, IN DWORD cbEncodedCert, IN HCERTSTORE hCertStore) { HRESULT hr = S_OK; PCCERT_CONTEXT pCertContext = NULL; CComBSTR bstrCSPName; CComBSTR bstrContainerName; CRYPT_KEY_PROV_INFO KeyProvInfo;
DebugTrace("Entering AddCert().\n");
//
// Sanity check.
//
ATLASSERT(szCSPName); ATLASSERT(szContainerName); ATLASSERT(pbEncodedCert); ATLASSERT(cbEncodedCert); ATLASSERT(hCertStore);
//
// Create certificate context for the specified certificate.
//
if (!(pCertContext = ::CertCreateCertificateContext(CAPICOM_ASN_ENCODING, pbEncodedCert, cbEncodedCert))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertCreateCertificateContext() failed.\n", hr); goto ErrorExit; }
//
// Convert strings to UNICODE.
//
if (!(bstrCSPName = szCSPName)) { hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: bstrCSPName = szCSPName failed.\n", hr); goto ErrorExit; } if (!(bstrContainerName = szContainerName)) { hr = E_OUTOFMEMORY;
DebugTrace("Error [%#x]: bstrContainerName = szContainerName failed.\n", hr); goto ErrorExit; }
//
// Add the CSP & key container info. This is used by CAPI to load the
// CSP and find the keyset when the user indicates this certificate.
//
::ZeroMemory((LPVOID) &KeyProvInfo, sizeof(CRYPT_KEY_PROV_INFO)); KeyProvInfo.pwszContainerName = (LPWSTR) bstrContainerName; KeyProvInfo.pwszProvName = (LPWSTR) bstrCSPName; KeyProvInfo.dwProvType = PROV_RSA_FULL; KeyProvInfo.dwFlags = 0; KeyProvInfo.dwKeySpec = dwKeySpec;
if (!::CertSetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, 0, (const void *) &KeyProvInfo)) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertSetCertificateContextProperty() failed.\n", hr); goto ErrorExit; }
//
// Put the cert in the store!
//
if (!::CertAddCertificateContextToStore(hCertStore, pCertContext, CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, // or CERT_STORE_ADD_NEW
NULL)) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertCreateCertificateContext() failed.\n", hr); goto ErrorExit; } CommonExit: //
// Free resources.
//
if (pCertContext != NULL) { CertFreeCertificateContext(pCertContext); } DebugTrace("Leaving AddCert().\n");
return hr;
ErrorExit: //
// Sanity check.
//
ATLASSERT(FAILED(hr));
goto CommonExit; }
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : GetCert
Synopsis : Get the cert from the specified CSP for the specified key type.
Parameter: - IN HCRYPTPROV hCryptProv
Crypto context returned by CryptAcquireContext().
- IN DWORD dwKeySpec
AT_KEYEXCHANGZE or AT_SIGNATURE.
- OUT LPBYTE * ppbEncodedCert
Ponter to pointer to encoded cert data. Upon success, the buffer is automatically allocated and must be later freed by CoTaskMemFree().
- OUT DWORD * pcbEncodedCert
Pointer to encoded cert data length. Upon success, receive the length of the encoded cert data.
Remarks :
------------------------------------------------------------------------------*/
static HRESULT GetCert (IN HCRYPTPROV hCryptProv, IN DWORD dwKeySpec, OUT LPBYTE * ppbEncodedCert, OUT DWORD * pcbEncodedCert) { HRESULT hr = S_OK; HCRYPTKEY hCryptKey = NULL; LPBYTE pbEncodedCert = NULL; DWORD cbEncodedCert = 0;
DebugTrace("Entering GetCert().\n");
//
// Sanity check.
//
ATLASSERT(hCryptProv); ATLASSERT(ppbEncodedCert); ATLASSERT(pcbEncodedCert);
//
// Get key handle.
//
if (!::CryptGetUserKey(hCryptProv, dwKeySpec, &hCryptKey)) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptGetUserKey() failed.\n", hr); goto ErrorExit; }
//
// Query certificate data length.
//
if (!::CryptGetKeyParam(hCryptKey, KP_CERTIFICATE, NULL, // NULL to query certificate data length
&cbEncodedCert, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptGetKeyParam() failed.\n", hr); goto ErrorExit; }
//
// Allocate memory for certificate data.
//
if (!(pbEncodedCert = (LPBYTE) ::CoTaskMemAlloc(cbEncodedCert))) { hr = E_OUTOFMEMORY; DebugTrace("Error [%#x]: CoTaskMemAlloc() failed.\n", hr); goto ErrorExit; }
//
// Now read the certificate data.
//
if (!::CryptGetKeyParam(hCryptKey, KP_CERTIFICATE, pbEncodedCert, &cbEncodedCert, 0)) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptGetKeyParam() failed.\n", hr); goto ErrorExit; }
//
// Return encoded cert to caller.
//
*ppbEncodedCert = pbEncodedCert; *pcbEncodedCert = cbEncodedCert;
CommonExit: //
// Free resources.
//
if (hCryptKey) { ::CryptDestroyKey(hCryptKey); }
DebugTrace("Leaving GetCert().\n");
return hr;
ErrorExit: //
// Sanity check.
//
ATLASSERT(FAILED(hr));
//
// Free resources.
//
if (pbEncodedCert) { ::CoTaskMemFree((LPVOID) pbEncodedCert); }
goto CommonExit; }
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : PropCert
Synopsis : Propagate the digital certificate associated with the specified CSP and container name to the soecified store.
Parameter: - IN LPCSTR szCSPName
Pointer to Crypto Service Provider name string. - IN LPCTSTR szContainerName
Pointer to key container name string.
- IN HCERTSTORE hCertStore
Handle of store to add the certificate to. Remarks :
------------------------------------------------------------------------------*/
static HRESULT PropCert (IN LPCSTR szCSPName, IN LPCSTR szContainerName, IN HCERTSTORE hCertStore) { HRESULT hr = S_OK; HCRYPTPROV hCryptProv = NULL; DWORD rgdwKeys[] = {AT_KEYEXCHANGE, AT_SIGNATURE}; DWORD cbEncodedCert = 0; LPBYTE pbEncodedCert = NULL; DWORD i;
DebugTrace("Entering PropCert().\n");
//
// Sanity check.
//
ATLASSERT(szCSPName); ATLASSERT(szContainerName); ATLASSERT(hCertStore);
//
// Obtain the crypto context.
//
// CRYPT_SILENT forces the CSP to raise no UI. The fully qualified
// container name indicates which reader to connect to, so the
// user should not be prompted to insert or select a card.
//
if (!::CryptAcquireContextA(&hCryptProv, szContainerName, szCSPName, PROV_RSA_FULL, CRYPT_SILENT)) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptAcquireContextA() failed.\n", hr); goto ErrorExit; }
//
// For each key pair found in the smart card, store the corresponding
// digital certificate to the specified store.
//
for (i = 0; i < ARRAYSIZE(rgdwKeys); i++) { //
// Get the certificate data.
//
if (FAILED(hr = ::GetCert(hCryptProv, rgdwKeys[i], &pbEncodedCert, &cbEncodedCert))) { if (HRESULT_FROM_WIN32(NTE_NO_KEY) == hr) { //
// We are OK if there is no key of such type.
//
hr = S_OK; continue; }
DebugTrace("Error [%#x]: GetCert() failed.\n", hr); goto ErrorExit; } //
// Add the certificate to the specified store.
//
if (FAILED(hr = ::AddCert(szCSPName, szContainerName, rgdwKeys[i], pbEncodedCert, cbEncodedCert, hCertStore))) { DebugTrace("Error [%#x]: AddCert() failed.\n", hr); goto ErrorExit; } //
// Free resources.
//
if (pbEncodedCert) { ::CoTaskMemFree((LPVOID) pbEncodedCert), pbEncodedCert = NULL; } }
CommonExit: //
// Free resources.
//
if (pbEncodedCert) { ::CoTaskMemFree((LPVOID) pbEncodedCert); }
if (hCryptProv) { ::CryptReleaseContext(hCryptProv, 0); } DebugTrace("Leaving PropCert().\n");
return hr;
ErrorExit: //
// Sanity check.
//
ATLASSERT(FAILED(hr));
goto CommonExit; }
////////////////////////////////////////////////////////////////////////////////
//
// Exported functions.
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : LoadFromSmartCard
Synopsis : Load all certificates from all smart card readers. Parameter: HCERTSTORE hCertStore - Certificate store handle of store to receive all the certificates. Remark :
------------------------------------------------------------------------------*/
HRESULT LoadFromSmartCard (HCERTSTORE hCertStore) { HRESULT hr = S_OK; LONG lResult = 0; DWORD dwNumReaders = 0; DWORD dwAutoAllocate = SCARD_AUTOALLOCATE; SCARDCONTEXT hContext = NULL; LPSTR szReaderName = NULL; LPSTR mszReaderNames = NULL; LPSTR szCardName = NULL; LPSTR szCSPName = NULL; LPSTR szContainerName = NULL; HMODULE hWinSCardDll = NULL; LPSCARD_READERSTATE lpReaderStates = NULL;
DebugTrace("Entering LoadFromSmartCard().\n"); //
// Sanity check.
//
ATLASSERT(hCertStore);
//
// Load WinSCard.dll.
//
if (!(hWinSCardDll = ::LoadLibrary("WinSCard.dll"))) { hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: Smart Card Base Component (WinSCard.dll) not installed.\n", hr); goto ErrorExit; }
//
// Load all SCard APIs used.
//
if (!(pfnSCardEstablishContext = (PFNSCARDESTABLISHCONTEXT) ::GetProcAddress(hWinSCardDll, "SCardEstablishContext"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardEstablishContext.\n", hr); goto ErrorExit; }
if (!(pfnSCardListReadersA = (PFNSCARDLISTREADERSA) ::GetProcAddress(hWinSCardDll, "SCardListReadersA"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardListReadersA.\n", hr); goto ErrorExit; } if (!(pfnSCardGetStatusChangeA = (PFNSCARDGETSTATUSCHANGEA) ::GetProcAddress(hWinSCardDll, "SCardGetStatusChangeA"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardStatusChangeA.\n", hr); goto ErrorExit; } if (!(pfnSCardListCardsA = (PFNSCARDLISTCARDSA) ::GetProcAddress(hWinSCardDll, "SCardListCardsA"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardListCardsA.\n", hr); goto ErrorExit; } if (!(pfnSCardGetCardTypeProviderNameA = (PFNSCARDGETCARDTYPEPROVIDERNAMEA) ::GetProcAddress(hWinSCardDll, "SCardGetCardTypeProviderNameA"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardGetCardTypeProviderNameA.\n", hr); goto ErrorExit; }
if (!(pfnSCardFreeMemory = (PFNSCARDFREEMEMORY) ::GetProcAddress(hWinSCardDll, "SCardFreeMemory"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardFreeMemory.\n", hr); goto ErrorExit; }
if (!(pfnSCardReleaseContext = (PFNSCARDRELEASECONTEXT) ::GetProcAddress(hWinSCardDll, "SCardReleaseContext"))) { hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetProcAddress() failed for SCardReleaseContext.\n", hr); goto ErrorExit; }
//
// Establish context with the resource manager.
//
if (SCARD_S_SUCCESS != (lResult = pfnSCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext))) { hr = HRESULT_FROM_WIN32(lResult);
DebugTrace("Error [%#x]: SCardEstablishContext() failed.\n", hr); goto ErrorExit; }
//
// Get the list of all reader(s).
// Note: The buffer is automatically allocated and must be freed
// by SCardFreeMemory().
//
if (SCARD_S_SUCCESS != (lResult = pfnSCardListReadersA(hContext, NULL, (LPSTR) &mszReaderNames, &dwAutoAllocate))) { hr = HRESULT_FROM_WIN32(lResult); DebugTrace("Error [%#x]: SCardListReadersA() failed.\n", hr); goto ErrorExit; }
//
// Count number of readers.
//
for (dwNumReaders = 0, szReaderName = mszReaderNames; *szReaderName; dwNumReaders++) { szReaderName += ::strlen(szReaderName) + 1; }
//
// Nothing to do if no reader.
//
if (0 < dwNumReaders) { DWORD i;
//
// Allocate memory for SCARD_READERSTATE array.
//
if (!(lpReaderStates = (LPSCARD_READERSTATE) ::CoTaskMemAlloc(dwNumReaders * sizeof(SCARD_READERSTATE)))) { hr = E_OUTOFMEMORY; DebugTrace("Error [%#x]: CoTaskMemAlloc() failed.\n", hr); goto ErrorExit; } //
// Prepare state array.
//
::ZeroMemory((LPVOID) lpReaderStates, dwNumReaders * sizeof(SCARD_READERSTATE)); for (i = 0, szReaderName = mszReaderNames; i < dwNumReaders; i++) { lpReaderStates[i].szReader = (LPCSTR) szReaderName; lpReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE; szReaderName += ::strlen(szReaderName) + 1; } //
// Initialize card status.
//
if (SCARD_S_SUCCESS != (lResult = pfnSCardGetStatusChangeA(hContext, INFINITE, lpReaderStates, dwNumReaders))) { hr = HRESULT_FROM_WIN32(lResult); DebugTrace("Error [%#x]: SCardGetStatusChangeA() failed.\n", hr); goto ErrorExit; } //
// For each card found, find the proper CSP and propagate the
// certificate(s) to the specified store.
//
for (i = 0; i < dwNumReaders; i++) { //
// Card in this reader?
//
if (!(lpReaderStates[i].dwEventState & SCARD_STATE_PRESENT)) { //
// No card in this reader.
//
continue; } //
// Get card name.
//
dwAutoAllocate = SCARD_AUTOALLOCATE; if (SCARD_S_SUCCESS != (lResult = pfnSCardListCardsA(hContext, lpReaderStates[i].rgbAtr, NULL, 0, (LPSTR) &szCardName, &dwAutoAllocate))) { hr = HRESULT_FROM_WIN32(lResult); DebugTrace("Error [%#x]: SCardListCardsA() failed.\n", hr); goto ErrorExit; } //
// Get card's CSP name.
//
dwAutoAllocate = SCARD_AUTOALLOCATE; if (SCARD_S_SUCCESS != (lResult = pfnSCardGetCardTypeProviderNameA(hContext, szCardName, SCARD_PROVIDER_CSP, (LPSTR) &szCSPName, &dwAutoAllocate))) { hr = HRESULT_FROM_WIN32(lResult); DebugTrace("Error [%#x]: SCardGetCardTypeProviderNameA() failed.\n", hr); goto ErrorExit; } //
// Prepare fully qualified container name.
//
if (!(szContainerName = (LPSTR) ::CoTaskMemAlloc(sizeof("\\\\.\\") + 1 + ::strlen(lpReaderStates[i].szReader)))) { hr = E_OUTOFMEMORY; DebugTrace("Error [%#x]: CoTaskMemAlloc() failed.\n", hr); goto ErrorExit; } wsprintfA(szContainerName, "\\\\.\\%s\\", lpReaderStates[i].szReader); //
// Propagate the cert.
//
if (FAILED(hr = ::PropCert(szCSPName, szContainerName, hCertStore))) { DebugTrace("Error [%#x]: PropCert() failed.\n", hr); goto ErrorExit; }
//
// Free resources.
//
if (szContainerName) { ::CoTaskMemFree((LPVOID) szContainerName), szContainerName = NULL; } if (szCSPName) { pfnSCardFreeMemory(hContext, (LPVOID) szCSPName), szCSPName = NULL; } if (szCardName) { pfnSCardFreeMemory(hContext, (LPVOID) szCardName), szCardName = NULL; } } }
CommonExit: //
// Free resource.
//
if (szContainerName) { ::CoTaskMemFree((LPVOID) szContainerName); } if (szCSPName) { pfnSCardFreeMemory(hContext, (LPVOID) szCSPName); } if (szCardName) { pfnSCardFreeMemory(hContext, (LPVOID) szCardName); } if (lpReaderStates) { ::CoTaskMemFree((LPVOID) lpReaderStates); } if (mszReaderNames) { pfnSCardFreeMemory(hContext, (LPVOID) mszReaderNames); } if (hContext) { pfnSCardReleaseContext(hContext); } if (hWinSCardDll) { ::FreeLibrary(hWinSCardDll); }
DebugTrace("Leaving LoadFromSmartCard().\n");
return hr;
ErrorExit: //
// Sanity check.
//
ATLASSERT(FAILED(hr));
goto CommonExit; }
|