// XMLSecPerf.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

WinCAPICryptoX509      *X509;             // the X509 public key
WinCAPICryptoProvider  *cryptoProv;       // the default MSCryptoProvider

struct Config_t
{
  TCHAR idAttributeNS[512];               // ID attributes namespace in W3C xml soap digitally signed doc
  TCHAR idAttributeName[512];             // ID attributes name in W3C xml soap digitally signed doc
  TCHAR signerName[512];                  // Name of the certificate signer (used to find pub key in key store)
  TCHAR storeName[512];                   // Store name for the public certificate (eg "MY" or "ROOT")
};


Config_t config = 
{ 
  _T("http://schemas.xmlsoap.org/ws/2002/07/utility"),
  _T("Id"),
  _T("SIGNERNAME"),
  _T("fwsrv\\Root")
};


BOOL CreateX509()
{
  //
  // load the public key from the certificate store.  
  // install the key as a service certificate using mmc->certificates->services certificate, 
  // browse for the "microsoft firewall" and install the cert in the Trusted Root Authority.
  // For example the "microsoft firewall" uses the storeName L"fwsrv\Root" 
  //
  HCERTSTORE     certStore = NULL;
  PCCERT_CONTEXT certContext = NULL; 
  DWORD          certOpenFlags = CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_SERVICES | CERT_STORE_READONLY_FLAG;

  // open the store .. fail if the store does not exist, look in the services store, open for readonly
  certStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, certOpenFlags, config.storeName);
  if(certStore == NULL)
  {
    _com_error e(GetLastError());
    
    TCHAR err[1024];
    _stprintf(err, _T("Unable to open certificate store for %s, %s\n"), config.storeName, e.ErrorMessage());
    OutputDebugString(err);
    return FALSE;
  }

  // find signer's certificate
  certContext = CertFindCertificateInStore(certStore, (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING), 0, CERT_FIND_SUBJECT_STR, config.signerName , NULL);
  CertCloseStore(certStore, 0); // close the store regardless
  if(certContext == NULL)
  {  
    _com_error e(GetLastError());

    TCHAR err[1024];
    _stprintf(err, _T("Unable to find certificate in store for %s, %s, %s\n"), config.storeName, config.signerName, e.ErrorMessage());
    OutputDebugString(err);
    return FALSE;
  }
 
  try
  {
    if(X509 != NULL)  // if we have already been created .. then destroy and re-create
      delete X509;    // X509 destructor takes care of CertFreeCertificateContext()
    X509 = new WinCAPICryptoX509(certContext, cryptoProv->getProviderRSA(), cryptoProv->getProviderDSS()); 
  }
  catch(XSECCryptoException &e) 
  {
    TCHAR err[1024];
    _stprintf(err, _T("An error occured in the XML-Security-C Crypto routines %s\n"), e.getMsg());
    OutputDebugString(err);
  
    delete X509;
    return FALSE;
  }

  return TRUE;
}

BOOL ValidateSig(char *buffer, DWORD bufferLen)
{
  //
  // parse the XML document, load in the signature field and attempt to validate it using a copy of the
  // X509 public key we create earlier from the certificate store.
  //
  BOOL valid = FALSE;
  try
  {
    XercesDOMParser parser;
    parser.setDoNamespaces(true);
    parser.setCreateEntityReferenceNodes(true);
    parser.setDoSchema(true);
 
    MemBufInputSource *memIS = new MemBufInputSource((const XMLByte *)buffer, bufferLen, "XSECMem", false);
    parser.parse(*memIS);
    delete memIS;   // destroy as soon as possible

    if(parser.getErrorCount() > 0) 
    {
      TCHAR err[1024];
      _stprintf(err, _T("Error parsing input document, %s, %d\n"), buffer, bufferLen);
      OutputDebugString(err);
      return FALSE;
    } 
 
    // Now create a signature object to validate the document
    XSECProvider prov;
    DSIGSignature *sig = prov.newSignatureFromDOM(parser.getDocument());

    sig->registerIdAttributeName(config.idAttributeName);
    sig->registerIdAttributeNameNS(config.idAttributeNS, config.idAttributeName);
    sig->load();  

    XSECCryptoKey *clone = X509->clonePublicKey(); 
    sig->setSigningKey(clone);

    prov.releaseSignature(sig);
    return TRUE;
  
    if(sig->verify()) 
    {
      valid = TRUE;  // the only way to set this is if this fn() is successfull
    }
    else 
    {
      char *err = XMLString::transcode(sig->getErrMsgs());
 
      TCHAR str[1024];
      _stprintf(str, _T("Error parsing [or] validating document, %s\n"), err);
      OutputDebugString(str);

      XSEC_RELEASE_XMLCH(err);
    }
  }
  catch(XSECException &e) // signature related errors
  {
    char *err = XMLString::transcode(e.getMsg());

    TCHAR str[1024];
    _stprintf(str, _T("An error occured during signature load %s"), err);
    OutputDebugString(str);
  
    XSEC_RELEASE_XMLCH(err);
  }
  catch(const XMLException &e) // xml related parsing errors
  {
    char *err = XMLString::transcode(e.getMessage());

    TCHAR str[1024];
    _stprintf(str, _T("An error occured during xerces parsing and loading of xml %s"), err);
    OutputDebugString(str);

    XSEC_RELEASE_XMLCH(err);
  }
 
  return valid;
}


int _tmain(int argc, _TCHAR* argv[])
{
  // startup xerces & xsec for signature validation
  // because parts of the Apache signature library are not thread safe - all initialisation
  // now takes place inside the constructor - rather than the preferred Init() method.
  // NEVER destroy the cryptoProv object... it is ownned by XSEC and is killed vie XSECPlatformUtils::Terminate()
  // 
  try 
  {
    // initialize xerces xml parser and the Apache XML-Security library
    XMLPlatformUtils::Initialize();
    XSECPlatformUtils::Initialise();  

    cryptoProv = new WinCAPICryptoProvider(NULL, NULL, 0);
    XSECPlatformUtils::SetCryptoProvider(cryptoProv);       // this is set "globally" 
  }
  catch(const XMLException &e)
  {
    TCHAR err[1024];
    _stprintf(err, _T("Error during initialisation of Xerces. Error code is \n%s"), e.getMessage());
    OutputDebugString(err);
    exit(1);
  }
  catch(XSECException &e)
  {  
    TCHAR err[1024];
    _stprintf(err, _T("Error during initialisation of XSec \n%s"), e.getMsg());
    OutputDebugString(err);
    exit(1);
  }

  CreateX509();

  // load a previously signed document
  FILE *fp = fopen("raw.txt", "rb");
  fseek(fp, 0, SEEK_END); 
  DWORD bufferLength = ftell(fp);
  rewind(fp);
  char *buffer = (char *)malloc(bufferLength);
  fread(buffer, bufferLength, 1, fp);
  fclose(fp);

  while( 1 )
    ValidateSig(buffer, bufferLength);

  // shutdown xerces & xsec for signature validation
  XMLPlatformUtils::Terminate();
  XSECPlatformUtils::Terminate();

  return 0;
}

