Security Certificates Treatment with CryptoAPI

Environment: VC6

Introduction

A few months ago, I needed to program a complete installation for a VPN access (IPSECL2TP) for clients with Microsoft Windows systems (98, ME, 2000, and XP). This included RAS access (creating the access, dialing control, and so forth), IPSec and L2TP aspects (enabling ipsec and l2tp, security policies, and so on), registry operations, and certificate treatment.

The most difficult point was the certificate treatment because there is not too much information related to that. Finally, I wrote a software (using CryptoAPI) that adds any certificate to any store in any Microsoft operating system (Windows 98 or higher).

Project

One of the greatest issues about this is the versions of the libraries and headers used. So, the first thing to do was to update the SDK and get the lastest files. Anyway, the updated files that I finally used (cypt32.lib, wincrypt.h, and BasesTD.h) are in the zip archive with the project files; so, if you want to compile the project, first you have to rename the original files in the VC6 directory (C:Program filesMicrosoft Visual StudioVC98Include and Lib) to, for example, “crypt32_old.lib, wincrypt_old.h, basestd_old.h” and saving this one there.

For my project, I had to import two certificates. One was the CA certificate (public and binary); the other one was the personal certificate, which was encrypted with a password that only the owner of the certificate knew (both of them were OpenSSL certificates). I am going to explain the second one:

To add the personal certificate to the personal system store, I wrote the ‘ImportPerCert’ function that imports the certificate indicated by ‘m_pathPer’ (which is a mapped variable from an edit text box) to the Personal system store. This function returns 1 if everything has gone okay or 0 in other cases.

How does this work? First, we make a ‘Blob’—a structure that holds an array of bytes; in our case, containing the view of the certificate. After that, we check that it is a valid certificate with the ‘PFXIsPFXBlob’ function. If so, we use the ‘PFXImportCertStore’ function, which returns a handle to the store of the certificate decrypted with the password. Now, we get the handle to the store where we want to put the certificate by using the ‘CertOpenStore’ function and specifying the personal store (L”My”). Whenever certificates exist in the store (the ‘CertEnumCertificatesInStore’ function), we import them with ‘CertAddCertificateContextToStore’.

This was my main goal, but once you have this info, you can do whatever you want with these certificates. For example, if we want to get the date when the certificate will be invalid, we can do:


PCERT_INFO pinfo;
pinfo = pctx->pCertInfo;
SYSTEMTIME date;
FileTimeToSystemTime(&(pinfo->NotAfter), &date);

where ‘pctx’ is the context of the certificate returned by the ‘CertEnumCertificatesInStore’ function.

Finally, if the operating system is Windows 98 (or SE, or ME), you have to substitute the instructions where indicated in the code.

You can see the ‘ImportPerCert’ function below:


int CCertImportDlg::ImportPerCert()
{
HANDLE hfile = INVALID_HANDLE_VALUE;
HANDLE hsection = 0;
void* pfx = 0;
HCERTSTORE pfxStore = 0;
HCERTSTORE hFileStore = 0;
HCERTSTORE myStore = 0;
PCCERT_CONTEXT pctx = 0;
bool recognizedPFX;

UpdateData(true);

// get the handle to the file…
hfile = CreateFile(m_pathPer, FILE_READ_DATA, FILE_SHARE_READ,
0, OPEN_EXISTING, 0, 0);

// FOR WINDOWS 98
// hfile = CreateFile(ruta , GENERIC_READ, FILE_SHARE_READ, 0,
// OPEN_EXISTING, 0, 0);

if (INVALID_HANDLE_VALUE == hfile)
{
AfxMessageBox(“Certificate not found. Check that the path
indicated is correct.”, MB_ICONERROR);
return 0;
}

// now, we create a file mapping object for that file
hsection = CreateFileMapping(hfile, 0, PAGE_READONLY, 0, 0, 0);

if (!hsection)
{
AfxMessageBox(“Error in ‘CreateFileMapping'”, MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx, pfxStore,
myStore);
return 0;
}

// we map a view of the file that we previously opened into pfx
pfx = MapViewOfFile(hsection, FILE_MAP_READ, 0, 0, 0);

if (!pfx)
{
AfxMessageBox(“Error in ‘MapViewOfFile'”, MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx, pfxStore,
myStore);
return 0;

}

// and with that view we make a blob …
CRYPT_DATA_BLOB blob;
blob.cbData = GetFileSize(hfile, 0);
blob.pbData = (BYTE*)pfx;
recognizedPFX = false;

try
{
// … and after we try to decode that blob to see whether
// pfx is recognized…

if (PFXIsPFXBlob(&blob))
recognizedPFX = true;
}

catch (…)
{
}

if (!recognizedPFX)
{
AfxMessageBox(“This certificate is not valid.”, MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx, pfxStore,
myStore);
return 0;
}

wchar_t password[128];

// We get the password that the user typed in the edit box…
GetPassword(password);

// We import the certificate store from the blob. I needed it
// to be exportable…

pfxStore = PFXImportCertStore(&blob, password,
CRYPT_MACHINE_KEYSET |
CRYPT_EXPORTABLE);

// FOR WINDOWS 98, the flag CRYPT_MACHINE_KEYSET is not valid
// pfxStore = PFXImportCertStore(&blob, password,
// CRYPT_EXPORTABLE);

if (!pfxStore)
{
AfxMessageBox(“Error in PFXImportCertStore. Probably password
incorrect.”, MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx, pfxStore,
myStore);
return 0;
}

// and we open the certificate store where we want to save
// the certificate
// I chose “My” for the personal store…

myStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0,
CERT_STORE_OPEN_EXISTING_FLAG |
CERT_SYSTEM_STORE_LOCAL_MACHINE, L”MY”);

// FOR WINDOWS 98
// myStore = CertOpenSystemStore(NULL, L”MY”);

if (!myStore)
{
AfxMessageBox(“Error in ‘CertOpenSystemStore'”, MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx, pfxStore,
myStore);
return 0;
}

// while certificates exist in the pfxStore store….
while (0 != (pctx = CertEnumCertificatesInStore(pfxStore, pctx)))
{

wchar_t name[128];

// we get the friendly name of the certificate…
if (!CertGetNameString(pctx, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
0, 0, name, sizeof name / sizeof *name))
{
AfxMessageBox(“Error in ‘CertGetNameString'”, MB_ICONERROR);
}

// and we try to add it to the store gotten before with
// CertOpenStore

if (!CertAddCertificateContextToStore(myStore, pctx,
CERT_STORE_ADD_NEW, 0))
{
DWORD err = GetLastError();

// Maybe there’s another equal certificate already added
if (CRYPT_E_EXISTS == err)
{
if(AfxMessageBox(“An equivalent previous personal
certificate already exists. Overwrite?
(Yes/No)”, MB_YESNO) == IDYES)
{
if (!CertAddCertificateContextToStore(myStore, pctx,
CERT_STORE_ADD_REPLACE_EXISTING, 0))
{
AfxMessageBox(“Error in
‘CertAddCertificateContextToStore'”,
MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx,
pfxStore, myStore);
return 0;
}
}
}
else
{
AfxMessageBox(“Error in
‘CertAddCertificateContextToStore'”,
MB_ICONERROR);
FreeHandles(hfile, hsection, hFileStore, pfx, pctx,
pfxStore, myStore);
return 0;
}
}
}

return 1;
}

Downloads


Download demo project – 9 Kb


Download source – 153 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read