Suggest a new feature: adding smart card support to windows svn client in version 1.5
Hi:
Maybe someone have posted this suggestion before, and the Linux build now can support smart card thanks to Joe Orton's greate work(http://svn.haxx.se/users/archive-2008-04/0377.shtml).
I think it is not difficult to implement this feature on windows. Last month, we made an effort to implement smart card authentication by using Microsoft's CSP and openSSL library. We found that it is simple to manipulate the smart card by CSP API. Microsoft's CSP is a software interface standard. Almost all the smart card manufacturers support this standard and provide the software realization(dll). The CSP standard allows us to use the public-private key pair to encrypt,decrypt data and sign message digest while know nothing about the hardware details. All the CSPs are registered in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider. This page(http://msdn.microsoft.com/en-us/library/aa380245%28VS.85%29.aspx) describes the CSP in detail.
Most smart card manufacturers also provide a standards-based PKCS#11 library. PKCS#11(http://www.rsa.com/rsalabs/node.asp?id=2133) is a cryptographic token interface standard developed by RSA laboratories. It is the counterpart of Microsoft's CSP. PuTTY, openVPN and some other software on windows platform use PKCS#11 instead of CSP. The specifications of the PKCS#11 standard are described in ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-11/v2-20/pkcs-11v2-20.pdf.
The following are several typical examples of using CSP API. (without error handling)
All the CSP APIs are declared in header file wincrypt.h.
1. Enumerate all the certificates stored in USB KEY:
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
HCRYPTPROV hCryptProv = NULL;
CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
HCERTSTORE hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, hCryptProv, CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
// just access to the publicly available information, the CSP would not ask for a PIN
PCCERT_CONTEXT pCertContext = NULL;
while (1)
{
pCertContext = CertEnumCertificatesInStore (hCertStore, pCertContext);
if (!pCertContext) break;
//suppose the maximum length of all the fileds doesn't exceed 512
const int iMaxFldLen = 512;
char szSubjectName[iMaxFldLen]={0}, szIssuerName[iMaxFldLen]={0};
// get common name,
CertGetNameString (pCertContext, CERT_NAME_ATTR_TYPE, NULL, szOID_COMMON_NAME, szSubjectName, iMaxFldLen);
// get issuer name
CertGetNameString (pCertContext, CERT_NAME_ATTR_TYPE, CERT_NAME_ISSUER_FLAG,
szOID_COMMON_NAME, szIssuerName, iMaxFldLen);
printf ("%s %s\n", szSubjectName, szIssuerName);
// get the CSP name and key container name
char szContName[iMaxFldLen]={0}, szProvName[iMaxFldLen]={0}, pvData[iMaxFldLen]={0};
DWORD cbData = iMaxFldLen;
CertGetCertificateContextProperty (pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pvData, &cbData);
CRYPT_KEY_PROV_INFO* pProvInfo = (CRYPT_KEY_PROV_INFO *)pvData;
sprintf (szContName, "%S", pProvInfo->pwszContainerName);
sprintf (szProvName, "%S", pProvInfo->pwszProvName);
printf ("%s %s\n", szContName, szProvName);
// get other fields of the X509 certificate
// ...
}
CryptReleaseContext (hCryptProv, 0); // release the handle
2. Using private key stored in the USB KEY to sign the digest of message
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
// the container name and CSP name can be retrieved in example 1
// char szContName[] = {"61aecdd2-65b4-4919-a56c-0b2f5f7833d1"};
// char szProvName[] = {"SafeSign CSP Version 1.0"};
char szMsg[] = {"something"};
DWORD dwMsgLen = strlen(szMsg);
HCRYPTPROV hCryptProv = NULL;
CryptAcquireContext (&hCryptProv, szContName, szProvName, PROV_RSA_FULL, 0);
HCRYPTHASH hHash = NULL;
CryptCreateHash (hCryptProv, CALG_MD5, 0, 0, &hHash);
CryptHashData (hHash, (BYTE*)szMsg, dwMsgLen, 0);
DWORD dwSignLen = 0;
CryptSignHash (hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSignLen);
char* szSignBuf = (char*) malloc(dwSignLen*sizeof(char));
if (!CryptSignHash (hHash, AT_SIGNATURE, NULL, 0, (BYTE*)szSignBuf, &dwSignLen))
{
printf ("CryptSignHash failed\n");
}
else
{
printf ("CryptSignHash done\n");
}
free (szSignBuf);
CryptReleaseContext (hCryptProv, 0); // release the handle
3. Using private key to decrypt the ciphertext encrypted by the public key
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
// the container name and CSP name can be retrieved in example 1
// char szContName[] = {"61aecdd2-65b4-4919-a56c-0b2f5f7833d1"};
// char szProvName[] = {"SafeSign CSP Version 1.0"};
char pEncryptedData[] = {"top secret encrypted by public key"};
DWORD dwEncryptDataLen = strlen(pEncryptedData);
HCRYPTPROV hCryptProv = NULL;
CryptAcquireContext (&hCryptProv, szContName, szProvName, PROV_RSA_FULL, 0);
HCRYPTKEY hUserKey = NULL;
BOOL bRet= CryptGetUserKey (hCryptProv, AT_KEYEXCHANGE, &hUserKey);
// pEncryptedData point to the ciphertext encrypted by the public key and
// dwEncryptDataLen is the length of the ciphertext
if (!CryptDecrypt(hUserKey, 0, true, 0, (BYTE*)pEncryptedData, &dwEncryptDataLen))
{
// CryptDecrypt() will failed if the pEncryptedData was NOT
// encrypted by the corresponding public key
printf ("CryptDecrypt failed\n");
}
else
{
printf ("CryptDecrypt done\n");
}
// now pEncryptedData point to plaintext and
// dwEncryptDataLen is the length of the plaintext
CryptReleaseContext (hCryptProv, 0);
Thanks
Setven Zhang
Received on 2008-06-11 12:07:07 CEST