HOWTO: Read SPC Files Dumped by Certificate Enrollment Control

ID: Q193076

The information in this article applies to:

SUMMARY

You can get information for an issued certificate without implementing a custom exit module. The Certificate Enrollment Control object has a property called SPCFileName. By setting SPCFileName, you tell the object to dump the base64 encoded PKCS #7 certificate to a file in binary PKCS #7 format. You can decode this file by using CryptoAPI to obtain information about the certificate.

NOTE: This information is only relevant to Certificate Server 1.0.

MORE INFORMATION

The following sample code demonstrates how to get a CERT_CONTEXT structure from the SPC file. You can use the CryptoAPI Certificate Helper Functions to retrieve the certificate information.

Sample Code

   // Decode SPC.exe.
   #define _WIN32_WINNT  0x0400

   #include <windows.h>
   #include <wincrypt.h>
   #include <stdio.h>
   #include <tchar.h>

   #define MY_ENCODE_TYPE (CRYPT_ASN_ENCODING | PKCS7_ASN_ENCODING)
   #define BUFFERSIZE 40

   void _cdecl _tmain(int argc, TCHAR *argv[])
   {
      HCRYPTMSG hCryptMsg = NULL;
      PCCERT_CONTEXT  pCertContext = NULL;
      BYTE *bPKCS7 = NULL;
      BYTE *pbCertificate = NULL;
      HANDLE hFile = INVALID_HANDLE_VALUE;
      DWORD CertCount, dwIndex, dwResult;
      DWORD dwNumRead;
      BOOL bResult;

      if (argc != 2)
      {
         _tprintf(TEXT("Usage: DecodeSPC <SPC file>\n"));
         return;
      }

      __try {

         // Open SPC file.
         hFile = CreateFile(argv[1], GENERIC_READ, 0,
                            NULL, OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL, NULL);
         if (hFile == INVALID_HANDLE_VALUE)
         {
            _tprintf(TEXT("CreateFile failed with %d\n"), GetLastError());
            __leave;
         }

         // Get file size.
         dwResult = GetFileSize(hFile, NULL);
         if (dwResult == 0xFFFFFFFF)
         {
            _tprintf(TEXT("GetFileSize failed with %d\n"), GetLastError());
            __leave;
         }

         // Allocate PKCS7 buffer.
         bPKCS7 = (BYTE *)HeapAlloc(GetProcessHeap(), 0, dwResult);
         if (bPKCS7 == NULL)
         {
            _tprintf(TEXT("HeapAlloc failed to allocate PKCS7 buffer\n"));
            __leave;
         }

         // Read SPC file.
         bResult = ReadFile(hFile, bPKCS7, dwResult, &dwNumRead, NULL);
         if (!bResult)
         {
            _tprintf(TEXT("ReadFile failed with %d\n"), GetLastError());
            __leave;
         }

         // Open message for decoding.
         hCryptMsg = CryptMsgOpenToDecode(MY_ENCODE_TYPE, 0, 0,
                                          0, NULL, NULL);
         if (hCryptMsg == NULL)
         {
            _tprintf(TEXT("CryptMsgOpenToDecode failed with %x\n"),
                     GetLastError());
            __leave;
         }

         // Update the contents of the message.
         bResult = CryptMsgUpdate(hCryptMsg, bPKCS7, dwResult, TRUE);
         if (!bResult)
         {
            _tprintf(TEXT("CryptMsgUpdate failed with %x\n"),
                     GetLastError());
            __leave;
         }

         // Get Number of Certificates in message.
         // There should be two certificates:
         // the first certificate is the issuer certificate,
         // and the second is the subject certificate.
         dwNumRead = sizeof(DWORD);
         bResult = CryptMsgGetParam(hCryptMsg, CMSG_CERT_COUNT_PARAM , 0,
                                    &CertCount, &dwNumRead);
         if (!bResult)
         {
            _tprintf(TEXT("CryptMsgGetParam failed with %x\n"),
                          GetLastError());
            __leave;
         }

         _tprintf(TEXT("Certificate Count : %d\n"), CertCount);

         // Enumerate through certificates.
         for (dwIndex = 0; dwIndex < CertCount; dwIndex++)
         {
            _tprintf(TEXT("\n\nCertificate #%d\n"), dwIndex+1);

            // Get encoded certificate length.
            dwNumRead = 0;
            bResult = CryptMsgGetParam(hCryptMsg, CMSG_CERT_PARAM, dwIndex,
                                       NULL, &dwNumRead);
            if (!bResult)
            {
               _tprintf(TEXT("CryptMsgGetParam with %x\n"),
                        GetLastError());
               __leave;
            }

            // Allocate buffer for encoded certificate.
            pbCertificate = (BYTE *)HeapAlloc(GetProcessHeap(), 0,
                                              dwNumRead);
            if (pbCertificate == NULL)
            {
               _tprintf(
                  TEXT("Failed to allocate memory for certificate\n"));
               __leave
            }

            // Get encoded certificate.
            bResult = CryptMsgGetParam(hCryptMsg, CMSG_CERT_PARAM,
                                      dwIndex, pbCertificate, &dwNumRead);
            if (!bResult)
            {
               _tprintf(TEXT("CryptMsgGetParam failed with %x\n"),
                        GetLastError());
               __leave;
            }

            // Create certificate context from encoded certificate bytes.
            pCertContext = CertCreateCertificateContext(
                                        X509_ASN_ENCODING,
                                        pbCertificate, dwNumRead);
            if (pCertContext == NULL)
            {
               _tprintf(
                  TEXT("CertCreateCertificateContext failed with %x\n"),
                  GetLastError());
               __leave;
            }

            // 
            // Get certificate information from pCertContext.
            // 

            // Free memory for next iteration.
            CertFreeCertificateContext(pCertContext);
            pCertContext = NULL;

            HeapFree(GetProcessHeap, 0, pbCertificate);
            pbCertificate = NULL;
         }
      }
      __finally {

         // Clean up.
         if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
         if (bPKCS7 != NULL) HeapFree(GetProcessHeap(), 0, bPKCS7);
         if (hCryptMsg != NULL) CryptMsgClose(hCryptMsg);
         if (pbCertificate != NULL) LocalFree(GetProcessHeap(),
                                              0, pbCertificate);
         if (pCertContext != NULL)
             CertFreeCertificateContext(pCertContext);
      }
   }

REFERENCES

Platform SDK documentation: Certificate Helper Functions.

Additional query words:

Keywords          : kbAPI kbCrypt kbKernBase kbSDKPlatform kbCodeSam 
Issue type        : kbhowto

Last Reviewed: September 26, 1998