C-C++ Source: getVerifyingCertContext.cpp

 

[This sample code uses features that were implemented in MSXML 5.0. XML digital signatures are not supported in MXSML 6.0 and later.]

#import <msxml5.dll>
using namespace MSXML2;

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

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

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

VARIANT_BOOL LoadXML(_bstr_t sigFile)
{
   if (!objectsAreInitialized) {
      printf("Must initialize objects before loading signature.\n");
      return VARIANT_FALSE;
   }

   // Load signature document from file:
   if (xmldoc->load(sigFile) == VARIANT_FALSE) {
      printf("Can't load %s\n", (LPCSTR)sigFile);
      return VARIANT_FALSE;
   }

   xmldoc->setProperty("SelectionNamespaces", DSIGNS);

   // Set the signature property to a <ds:Signature> DOM node.
   xmldsig->signature = xmldoc->selectSingleNode(".//ds:Signature");

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

   return VARIANT_TRUE;
}


// Test if the certificate of a key (pKey) is valid.
// In this simple example, the certificate is verified 
// if a trust chain can be built up without errors. In 
// a production application, more elaborate verification 
// criteria may be necessary. For more information, see 
// the CryptoAPI documentation.

VARIANT_BOOL IsCertificateValid(IXMLDSigKeyExPtr pKey)
{
   if (pKey == NULL) {
      printf("invalid key object.\n");
      return VARIANT_FALSE;
   }

   // Retrieve the ceritificate from the verifying key.
   PCCERT_CONTEXT pCert=NULL;
   pCert = (PCCERT_CONTEXT)pKey->getVerifyingCertificateContext();
   if (pCert == NULL) {
      printf ("Can't get verifying certificate context\n");
      return VARIANT_FALSE;
   }

   // Use CryptoAPI to verify the certificate.
    CERT_CHAIN_ENGINE_CONFIG chainConfig;
    PCCERT_CHAIN_CONTEXT     pChainContext;
    CERT_CHAIN_PARA          chainPara;
 
    HCERTCHAINENGINE         hChainEngine=NULL;  // Use the default chain engine.[HCCE_CURRENT_USER]
 
   // Initialize chainPara:
    chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
    chainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
    chainPara.RequestedUsage.Usage.cUsageIdentifier =0;
    chainPara.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
 
   // Initialize chainConfig:
    chainConfig.cbSize = sizeof(CERT_CHAIN_ENGINE_CONFIG);
    chainConfig.hRestrictedRoot           = NULL;
    chainConfig.hRestrictedTrust          = NULL;
    chainConfig.hRestrictedOther          = NULL;
    chainConfig.cAdditionalStore          = 0;
    chainConfig.rghAdditionalStore        = NULL;
    chainConfig.dwFlags                   = CERT_CHAIN_CACHE_END_CERT;
    chainConfig.dwUrlRetrievalTimeout     = 0 ;
    chainConfig.MaximumCachedCertificates = 0 ;
    chainConfig.CycleDetectionModulus     = 0;
 
   // Creates a certificate chain engine to build a certificate trust chain.
    if( !CertCreateCertificateChainEngine(&chainConfig, &hChainEngine) )
   {
      printf("Can't create certificate chain engine. Error Code= %d\n", GetLastError());
      return VARIANT_FALSE;
   }

   // Build a certificate chain from the chain engine.
    if( !CertGetCertificateChain(hChainEngine,          // chain engine handle
                                 pCert,                 // Pointer to the end certificate.
                                 NULL,                  // Use the default time.
                                 NULL,                  // Search no additional stores.
                                 &chainPara,            // Use AND logic, and enhanced key usage as indicated in the ChainPara data structure.
                                 CERT_CHAIN_REVOCATION_CHECK_END_CERT,
                                 NULL,                  // Currently reserved.
                                 &pChainContext))       // Return a pointer to the chain created.       
   {
      printf("Failed to complete the trust chain of the certificate. ");
      printf("Error code = %d\n", GetLastError());
      CertFreeCertificateChain(pChainContext);
      CertFreeCertificateChainEngine(hChainEngine);
      CertFreeCertificateContext(pCert);
      return VARIANT_FALSE;
   }
 
   // Verification successful.
    CertFreeCertificateChain(pChainContext); 
    CertFreeCertificateChainEngine(hChainEngine);
   CertFreeCertificateContext(pCert);

   return VARIANT_TRUE;
}

VARIANT_BOOL VerifyXML(XMLDSIG_WRITEKEYINFO fWriteKeyInfo)
{
   IXMLDOMNodePtr pKeyInfo, pNode;
   IXMLDSigKeyPtr pKey, pKeyOut;

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

   switch (fWriteKeyInfo & CERTIFICATES) 
   {
      case CERTIFICATES:
         pKeyInfo = xmldoc->selectSingleNode(
                  ".//ds:KeyInfo/ds:X509Data");
         break;
      case KEYVALUE:
         pKeyInfo = xmldoc->selectSingleNode(
                  ".//ds:KeyInfo/ds:KeyValue");
         break;
   }

   if (pKeyInfo == NULL) {
      printf("Invalid <ds:KeyInfo>\n");
      return VARIANT_FALSE;
   }

   pKey = xmldsig->createKeyFromNode(pKeyInfo);
   if (pKey== NULL) {
      printf("Invalid key from <ds:KeyInfo>\n");
      return VARIANT_FALSE;
   }

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

   printf("Signature verified on the data.\n\n");

   if ( (fWriteKeyInfo & CERTIFICATES) == CERTIFICATES) {
      if (IsCertificateValid(pKeyOut)) {
         printf("certificate used is valid.\n");
      }
   }

   return VARIANT_TRUE;
}

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;
}

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);
   }

   printf("Verifying signature.\n\n");
   if (VARIANT_TRUE == LoadXML(INFILE)) {
      VerifyXML(CERTIFICATES);
   }

   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 "getVerifyingCertContextProj" 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 getVerifyingCertContextProj files. From the File menu, select New.

  6. On the Files tab of the New dialog box, highlight C++ Source File. Then type "signature_signed.rsa.cert.xml" in the File name text box.

  7. Click OK.

  8. Copy the XML signature template file from Resource: signature_signed.rsa.cert.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 (getVerifyingCertContext.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 getVerifyingCertContextProj.exe from the Build menu.

  13. Execute the sample application by selecting !Execute getVerifyingCertContextProj.exe from the Build menu.

  14. Verify that your output is the same as that listed in the Output topic.