Security Certificates Treatment with CryptoAPI

Environment: VC6

Introduction

A few months ago, I needed to program a complete installation for a VPN access (IPSEC\L2TP) 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 files\Microsoft Visual Studio\VC98\Include 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


Comments

  • Private keyID

    Posted by DSkrypka on 12/14/2005 08:29am

    hello, How in the provider CSD on KeyID to receive a descriptor of the closed key from key pair certificate?! After successful completion PFXImportCertStore of the function Thank

    Reply
  • Excellent article

    Posted by vijay_visana on 08/24/2004 06:31am

    I am also developing same kind of application. A VPN Client for Windows supporting 98 to 2003. Please write some more articles regarding vpn support and certificate as there are almost no such articles on web.

    Reply
  • any good reference for CryptoAPI? books please

    Posted by Legacy on 10/07/2003 12:00am

    Originally posted by: Helo

    thanks for yr good article, current I am working on CryptoAPI, do you have any idea what book I should turn to? Thanks ahead.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds