Microsoft XML Core Services (MSXML) 5.0 for Microsoft Office - Digital Signatures

C/C++ Source: KeyFromCertContext.cpp

#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#import <msxml5.dll>
using namespace MSXML2;

#define DSIGNS "xmlns:ds='http://www.w3.org/2000/09/xmldsig#'"
#define SIG_IN "signature_template.rsa.xml"

IXMLDOMDocument3Ptr xmldoc = NULL;
IXMLDigitalSignatureExPtr xmldsig = NULL;
VARIANT_BOOL objectsAreInitialized = VARIANT_FALSE;

// Get the first certifcate context from the "MY" system store.
PCCERT_CONTEXT GetCertContext()
{
   HCERTSTORE hCertStore=NULL;
   PCCERT_CONTEXT  pCert=NULL;
   #define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
   
   hCertStore = CertOpenSystemStore(0, "MY");
   if (!hCertStore) {
      printf("failed to open store\n");
      return NULL;
   }

   pCert = CertFindCertificateInStore(
      hCertStore, 
      MY_ENCODING_TYPE,
      0,
      CERT_FIND_ANY,
      NULL,
      NULL);
   if (!pCert) {
      printf("Failed to find certificate in store\n");
      return NULL;
   }

    CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);
   return pCert;
}

// Load an XML file (a signature template), sigFile, as a DOM object.
VARIANT_BOOL LoadXML(_bstr_t sigFile)
{
   if (!objectsAreInitialized) {
      printf("Must initialize objects before loading signature.\n");
      return VARIANT_FALSE;
   }

   if (xmldoc->load(sigFile) == VARIANT_FALSE) {
      printf("Can't load %s\n", (LPCSTR)sigFile);
      return VARIANT_FALSE;
   }

   xmldoc->setProperty("SelectionNamespaces", DSIGNS);
   xmldsig->signature = xmldoc->selectSingleNode(".//ds:Signature");

   if (xmldsig->signature == NULL) {
      printf("Failed to set the signature property.\n");
      return VARIANT_FALSE;
   }
   return VARIANT_TRUE;
}

// Sign the XML data embedded in the signature template.
VARIANT_BOOL SignXML()
{
   IXMLDSigKeyPtr pKey, pKeyOut;

   if (xmldsig->signature == NULL) 
   {
      printf("Invalid signature template.\n");
      return VARIANT_FALSE;
   }

   PCCERT_CONTEXT pCertContext = GetCertContext();
   pKey = xmldsig->createKeyFromCertContext((void*)pCertContext);

   if (pKey== NULL) {
      printf("Invalid key from CertContext\n");
      return VARIANT_FALSE;
   }

   pKeyOut = xmldsig->sign(pKey, KEYVALUE); 
   if (pKeyOut== NULL) {
      printf("Invalid signature.\n");
      return VARIANT_FALSE;
   }
   CertFreeCertificateContext(pCertContext);

   printf("The data referenced in the signature template was signed ");
   printf("successfully.\nResultant signature:\n\n%s\n",(LPCSTR)xmldoc->xml);
   return VARIANT_TRUE;
}

// Helper function that creates and initiates required DOM objects:
VARIANT_BOOL initObjects()
{
   if (FAILED(xmldsig.CreateInstance(__uuidof(MXDigitalSignature50)) )) {
      printf("Installation of msxml5 is required to run this app.\n");
      return VARIANT_FALSE;
   }

   if (FAILED(xmldoc.CreateInstance(__uuidof(DOMDocument50)) )) {
      printf("Installation of msxml5 is required to run this app.\n");
      return VARIANT_FALSE;
   }
   xmldoc->async = VARIANT_FALSE;
   xmldoc->validateOnParse = VARIANT_FALSE;
   xmldoc->preserveWhiteSpace = VARIANT_TRUE;
   xmldoc->resolveExternals = VARIANT_FALSE;

   objectsAreInitialized = VARIANT_TRUE;

   return VARIANT_TRUE;
}

// Helper function that releases objects of the application scope:
void cleanObjects()
{
   if (xmldoc) xmldoc.Release();
   if (xmldsig) xmldsig.Release();
}


void main() 
{
   if ( CoInitialize(NULL) == E_FAIL) {
      printf("can't initialize COM Lib\n");
      exit(-1);
   }

   if (!initObjects()) {
      cleanObjects();
      exit(-1);
   }

   if(VARIANT_TRUE == LoadXML(SIG_IN)) {
      if (VARIANT_TRUE != SignXML()) {
         printf("exit with failure.\n");
      }
   }

   cleanObjects();
   CoUninitialize();
}

Try It!

  1. Ensure that you have completed all the procedures in Getting Started with XML Digital Signatures.
  2. Start Visual C++.
  3. From the File menu, select New. On the Projects tab of the New dialog box that appears, select Win32 Console Application in the left pane. Then type "keyFromCertContextProj" in the Project name field. For the project Location field, either accept the default setting or choose another location. Click OK.
  4. The Win32 Console Application property page will appear. For the type of Console Application, select An empty project and click Finish. When the New Project Information box displays, click OK.
  5. Select FileView on the project browser, and highlight keyFromCertContextProj files. From the File menu, select New.
  6. On the Files tab of the New dialog box, highlight C++ Source File. Then type "signature_template.rsa.xml" in the File name text box.
  7. Click OK.
  8. Copy the XML data file from Resource: signature_template.rsa.xml, and paste it into the text file you just created.
    Note   You can also copy the file into the project's main directory using Windows Explorer (or a command prompt).
  9. Repeat steps 5-8 for the C++ file above (keyFromCertContext.cpp).
  10. From the Project menu, click Settings..., then click the Link tab.
  11. In Object/library modules, type "crypt32.lib". Insert this text either before or after the existing string that lists all objects and modules for the current project. Then click OK.
  12. Build the sample by selecting Build keyFromCertContextProj.exe from the Build menu.
  13. Execute the sample application by selecting !Execute keyFromCertContextProj.exe from the Build menu.
  14. Verify that your output is similar to that listed in the Output topic.