Berin,

I'm attaching Win32 SSL URI resolver class and test code.
I haven't still finished testing.

I have some doubts. I have noticed that XSECURIResolver
interface doesn't have send() method and resolveURI()
returns BinInputStream.
Without send() I'm not sure how we can use it in SOAP?

Best regards,
Milan


> -----Original Message-----
> From: Milan Tomic [mailto:[EMAIL PROTECTED] 
> Sent: Saturday, January 22, 2005 10:27 AM
> To: security-dev@xml.apache.org
> Subject: RE: XSECSOAPRequestorSimple::doRequest()
> 
> 
> 
>       OK, I will implement XSECURIResolver for the Windows SSL stream.
> 
> Greetings,
> Milan
> 
> 
> > -----Original Message-----
> > From: Berin Lautenbach [mailto:[EMAIL PROTECTED]
> > Sent: Saturday, January 22, 2005 9:58 AM
> > To: Milan Tomic
> > Subject: Re: XSECSOAPRequestorSimple::doRequest()
> > 
> > 
> > That sounds reasonable.  But some random thoughts :>.
> > 
> > The Java library has a register of URI resolvers.  I wonder
> > if we should 
> > do the same thing that are global to the library.  Thatway, 
> the SOAP 
> > class could simply call the resolver for the particular URI and get 
> > returned a class that would do the connection etc.  (Currently the 
> > caller needs to explicitly set the correct resolver for signature 
> > checking which is a real PITA)
> > 
> > So rather than modify XSECBinHTTPURIInputStream, you could simply
> > re-implement XSECURIResolver for the Windows SSL stream.  We 
> > can fix the 
> > SOAP resolver so you can just pass a resolver to it and that way it 
> > doesn't have to have anything about understanding certs - the 
> > only class 
> > that needs to is the resolver.
> > 
> > When we get fancy, we can great a reolver library class that
> > holds all 
> > instantiated resolvers, and the SOAP handler simply makes a 
> > call to the 
> > library that hands back the correct resolver for the URI.
> > 
> > Does that sound reasonable?
> > 
> > We should probably start putting this on security-dev so others can
> > comment as well!
> > 
> > Cheers,
> >     Berin
> > 
> > Milan Tomic wrote:
> > 
> > > Hi,
> > > 
> > >   After taking a look into XSEC code
> > > (xsec/utils/winutils/XSECBinHTTPURIInputStream) I have
> > realized that
> > > WinSock API was used instead of WinINET API. I have found some SSL
> > > examples in MS Platform SDK, and I'm working on 
> implementing it. My 
> > > aproach is that XSECBinHTTPURIInputStream class should have
> > > setClientCertificate(PCCERT_CONTEXT) method and this class 
> > should use
> > > it for SSL in case URL starts with "https". If it doesn't start it
> > > should act as it already is for "http" addresses. 
> > > XSECSOAPRequestorSimpleWin32 class should also have 
> > > setClientCertificate() method to be able to transfer 
> certificate to 
> > > the XSECBinHTTPURIInputStream class. XSECSOAPRequestorSimpleWin32 
> > > class user in ctor specify URL and s/he knows if it starts with 
> > > "https" and should somehow find proper cert (e.g. ask user 
> > to select
> > > it). I couldn't think of anything better...
> > > 
> > > Best regards,
> > > Milan
> 
/*
 * Copyright 2002-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * imitations under the License.
 */

/*
 * XSEC
 *
 * XSECURIResolverSSLWin32 := Virtual Interface class that takes a URI and
 *                    creates a binary input stream from it.
 *
 * Author(s): Milan Tomic
 *
 */

#ifndef XSECURIRESOLVERSSLWIN32_INCLUDE
#define XSECURIRESOLVERSSLWIN32_INCLUDE

#include <xsec/framework/XSECURIResolver.hpp>
#include <xsec/utils/XSECSafeBuffer.hpp>
#include <xercesc/util/XMLUri.hpp>

#define SECURITY_WIN32
#include <security.h>
#include <schannel.h>
#include <winsock.h>

/**
 * @ingroup pubsig
 */
/[EMAIL PROTECTED]/

/**
 * @brief URIResolver implementation class based on WinSock functions.
 *
 * This class provides Windows API implementation of the XSECURIResolver
 * class.
 *
 */

class DSIG_EXPORT XSECURIResolverSSLWin32 : public XSECURIResolver {

public:

  /**
         * \brief Protocol type
         */

  enum ProtocolType {

                PROTO_PCT1      = 1,
                PROTO_SSL2      = 2,
                PROTO_SSL3      = 3,
    PROTO_TLS1  = 4

        };

  /**
         * \brief Key exchange algorithm type
         */

  enum KeyExchType {

                KEY_EXCH_RSA = 1,
                KEY_EXCH_DH  = 2,

        };

        /** @name Constructors and Destructors */
        //@{

        /**
         * \brief Constructor
         *
         * @param baseURI Provide a URI that relative URIs can be
         * matched to.
         */

        XSECURIResolverSSLWin32();
        virtual ~XSECURIResolverSSLWin32();

        //@}

        /** @name Interface Methods */
        //@{

        /**
         * \brief Create a BYTE_STREAM from a URI.
         *
         * The resolver is required to take the input URI and
         * dereference it to an actual stream of octets.
         *
         * @param uri The string containing the URI to be de-referenced.
         * @returns The octet stream corresponding to the URI.
         */

        virtual XERCES_CPP_NAMESPACE_QUALIFIER BinInputStream * 
resolveURI(const XMLCh * uri);

        /**
         * \brief Clone the resolver to be installed in a new object.
         *
         * When URIResolvers are passed into signatures and other
         * objects, they are cloned and control of the original object
         * is left with the caller.
         *
         */

        virtual XSECURIResolver * clone(void);

        //@}

  /** @name XSECURIResolverSSLWin32 Specific Methods */
        //@{

  /**
         * \brief Assignes client SSL cert. Note that we DON'T own it.
         *
   *  @param pCertContext Certificate to be used for client
   *  authentication
         */

  void setClientCertificate(PCCERT_CONTEXT pCertContext);

  /**
         * \brief Assignes security protocol.
         *
   *  @param protocol Protocol to be used
         */

  void setProtocol(ProtocolType protocol);

  /**
         * \brief Assignes key exchange algorithm type
         *
   *  @param aiKeyExch key exchange algorithm to be used
         */

  void setKeyExchAlg(KeyExchType aiKeyExch);

  //@}

private:

  //Loads proper Windows SSL dll
        bool loadSecurityLibrary(void);

        //Unloads Windows SSL dll
  void unloadSecurityLibrary(void);

        //Create credentials using certificate provided
  SECURITY_STATUS createCredentials(PCredHandle phCreds);

  //Connects us to the remote server
  int connectToServer(LPSTR    pszServerName, // in
                      int      iPortNumber,   // in
                      SOCKET * pSocket);      // out

        //Disconnects us from remote server
  long disconnectFromServer(
                      SOCKET          Socket, 
                      PCredHandle     phCreds,
                      CtxtHandle *    phContext);

  //Performs SSL handshake
  SECURITY_STATUS performClientHandshake(
                      SOCKET          Socket,         // in
                      PCredHandle     phCreds,        // in
                      LPSTR           pszServerName,  // in
                      CtxtHandle *    phContext,      // out
                      SecBuffer *     pExtraData);    // out

        //Perform handshake loop
  SECURITY_STATUS clientHandshakeLoop(
                      SOCKET          Socket,         // in
                      PCredHandle     phCreds,        // in
                      CtxtHandle *    phContext,      // in, out
                      BOOL            fDoInitialRead, // in
                      SecBuffer *     pExtraData);    // out

  //Download data
  SECURITY_STATUS httpsGetData(
                      SOCKET          Socket,             // in
                      PCredHandle     phCreds,            // in
                      CtxtHandle *    phContext,          // in
                      LPSTR           hostName,           // in
                      LPSTR           path,               // in
                      LPSTR           query,              // in
                      LPSTR           fragment,           // in
                      unsigned short  portNumber,         // in
                      safeBuffer        &sb);               // in

  //Verify server certificate using CAPI
  DWORD verifyServerCertificate(
                      PCCERT_CONTEXT  pServerCert,
                      PSTR            pszServerName,
                      DWORD           dwCertFlags);

  //Get new client credentials
  void getNewClientCredentials(CredHandle *phCreds, CtxtHandle *phContext);

        SCHANNEL_CRED          m_SchannelCred;
  PSecurityFunctionTable m_pSSPI;
  HMODULE                m_hSecurity;     //SSL dll handle
  PCCERT_CONTEXT         m_pCertContext;  //Client certificate
  DWORD                  m_dwProtocol;
  ALG_ID                 m_aiKeyExch;

};

#endif /* XSECURIRESOLVERSSLWIN32_INCLUDE */
/*
 * Copyright 2002-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * imitations under the License.
 */

/*
 * XSEC
 *
 * XSECURIResolverSSLWin32 := Virtual Interface class that takes a URI and
 *                    creates a binary input stream from it.
 *
 * Author(s): Milan Tomic
 *
 */

#include <xsec/framework/XSECDefs.hpp>
#include <xsec/framework/win32/XSECURIResolverSSLWin32.hpp>
#include <xsec/framework/XSECError.hpp>

#include <xercesc/framework/MemBufInputSource.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/Janitor.hpp>

XERCES_CPP_NAMESPACE_USE

#include <wincrypt.h>
#include <wintrust.h>
#include <sspi.h>

#define IO_BUFFER_SIZE  0x10000

#define DLL_NAME TEXT("Secur32.dll")
#define NT4_DLL_NAME TEXT("Security.dll")

// 
--------------------------------------------------------------------------------
//           Constructors and Destructors
// 
--------------------------------------------------------------------------------

XSECURIResolverSSLWin32::XSECURIResolverSSLWin32() : m_aiKeyExch(CALG_RSA_KEYX),
                                                     m_dwProtocol(SP_PROT_SSL2) 
{

};

XSECURIResolverSSLWin32::~XSECURIResolverSSLWin32() {

}

// 
--------------------------------------------------------------------------------
//           Interface Methods
// 
--------------------------------------------------------------------------------

XERCES_CPP_NAMESPACE_QUALIFIER BinInputStream * 
XSECURIResolverSSLWin32::resolveURI(const XMLCh * uri) {

  XSEC_USING_XERCES(MemBufInputSource);
        XSEC_USING_XERCES(BinInputStream);

  const XMLUri urlSource(uri);

  MemBufInputSource                     * mbs = NULL;     // Use Xerces MemBuf 
Input source
        BinInputStream                          * is  = NULL;           // To 
handle the actual input

  safeBuffer sb;

  //
  // Pull all of the parts of the URL out of th urlSource object, and transcode 
them
  //   and transcode them back to ASCII.
  //
  const XMLCh*        hostName = urlSource.getHost();
  char*               hostNameAsCharStar = XMLString::transcode(hostName);
  ArrayJanitor<char>  janBuf1(hostNameAsCharStar);

  const XMLCh*        path = urlSource.getPath();
  char*               pathAsCharStar = XMLString::transcode(path);
  ArrayJanitor<char>  janBuf2(pathAsCharStar);

  const XMLCh*        fragment = urlSource.getFragment();
  char*               fragmentAsCharStar = 0;
  if (fragment)
      fragmentAsCharStar = XMLString::transcode(fragment);
  ArrayJanitor<char>  janBuf3(fragmentAsCharStar);

  const XMLCh*        query = urlSource.getQueryString();
  char*               queryAsCharStar = 0;
  if (query)
      queryAsCharStar = XMLString::transcode(query);
  ArrayJanitor<char>  janBuf4(queryAsCharStar);         

  unsigned short      portNumber = (unsigned short) urlSource.getPort();

  //
  // SSL code starts here
  //

  WSADATA WsaData;
  SOCKET  Socket = INVALID_SOCKET;

  CredHandle hClientCreds;
  CtxtHandle hContext;
  BOOL fCredsInitialized   = FALSE;
  BOOL fContextInitialized = FALSE;

  SecBuffer  ExtraData;
  SECURITY_STATUS Status;

  PCCERT_CONTEXT pRemoteCertContext = NULL;

  if(!loadSecurityLibrary())
  {
    goto cleanup;
  }

  //
  // Initialize the WinSock subsystem.
  //

  if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
  {
    goto cleanup;
  }

  //
  // Create credentials.
  //

  if(createCredentials(&hClientCreds))
  {
    goto cleanup;
  }
  fCredsInitialized = TRUE;

  //
  // Connect to server.
  //

  if(connectToServer(hostNameAsCharStar, portNumber, &Socket))
  {
    goto cleanup;
  }

  //
  // Perform handshake
  //

  if(performClientHandshake(Socket,
                            &hClientCreds,
                            hostNameAsCharStar,
                            &hContext,
                            &ExtraData))
  {
    goto cleanup;
  }
  fContextInitialized = TRUE;


  //
  // Authenticate server's credentials.
  //

  // Get server's certificate.
  Status = m_pSSPI->QueryContextAttributes(&hContext,
                                           SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                                           (PVOID)&pRemoteCertContext);
  if(Status != SEC_E_OK)
  {
    goto cleanup;
  }

  // Attempt to validate server certificate.
  Status = verifyServerCertificate(pRemoteCertContext,
                                   hostNameAsCharStar,
                                   0);
  if(Status)
  {
      // The server certificate did not validate correctly. At this
      // point, we cannot tell if we are connecting to the correct 
      // server, or if we are connecting to a "man in the middle" 
      // attack server.

      // It is therefore best if we abort the connection.

    goto cleanup;
  }

  // Free the server certificate context.
  CertFreeCertificateContext(pRemoteCertContext);
  pRemoteCertContext = NULL;

  //
  // Read file from server.
  //

  if(httpsGetData(Socket, 
                  &hClientCreds,
                  &hContext, 
                  hostNameAsCharStar,
                  pathAsCharStar,
                  queryAsCharStar,
                  fragmentAsCharStar,
                  portNumber,
                  sb))
  {
      goto cleanup;
  }

  //
  // Send a close_notify alert to the server and
  // close down the connection.
  //

  if(disconnectFromServer(Socket, &hClientCreds, &hContext))
  {
      goto cleanup;
  }
  fContextInitialized = FALSE;
  Socket = INVALID_SOCKET;

  //
  // SSL code ends here
  //

  mbs = new MemBufInputSource((const unsigned char*)sb.rawCharBuffer(), 
sb.sbRawBufferSize(), "XSECURIResolverSSLWin32");

  // makeStream can (and is quite likely to) throw an exception
        //Janitor<MemBufInputSource> j_mbs(mbs);

  try {
    is = mbs->makeStream();
  } catch (...) {
    delete mbs;
  }

cleanup:

  // Free the server certificate context.
  if(pRemoteCertContext)
  {
      CertFreeCertificateContext(pRemoteCertContext);
      pRemoteCertContext = NULL;
  }

  // Free SSPI context handle.
  if(fContextInitialized)
  {
      m_pSSPI->DeleteSecurityContext(&hContext);
      fContextInitialized = FALSE;
  }

  // Free SSPI credentials handle.
  if(fCredsInitialized)
  {
      m_pSSPI->FreeCredentialsHandle(&hClientCreds);
      fCredsInitialized = FALSE;
  }

  // Close socket.
  if(Socket != INVALID_SOCKET)
  {
      closesocket(Socket);
  }

  // Shutdown WinSock subsystem.
  WSACleanup();

  unloadSecurityLibrary();

  if (is != NULL) {

                return is;

  } else {

    throw XSECException(XSECException::ErrorOpeningURI,
                          "An error occurred in XSECURIResolverSSLWin32");
  }


}


XSECURIResolver * XSECURIResolverSSLWin32::clone(void) {

        XSECURIResolverSSLWin32 * ret = new XSECURIResolverSSLWin32();

        return ret;

}

// 
--------------------------------------------------------------------------------
//           Specific Methods
// 
--------------------------------------------------------------------------------

bool XSECURIResolverSSLWin32::loadSecurityLibrary(void) {

    INIT_SECURITY_INTERFACE         pInitSecurityInterface;
    OSVERSIONINFO VerInfo;

    //
    //  Find out which security DLL to use, depending on
    //  whether we are on Win2K, NT or Win9x
    //

    VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
    if (!GetVersionEx (&VerInfo))   
    {
        return FALSE;
    }

    if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT 
        && VerInfo.dwMajorVersion == 4)
    {
        m_hSecurity = LoadLibrary(NT4_DLL_NAME);
    }
    else if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
          VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
    {
        m_hSecurity = LoadLibrary(DLL_NAME);
    }
    else
    {
        return FALSE;
    }

    if(m_hSecurity == NULL)
    {
        return FALSE;
    }

    pInitSecurityInterface = (INIT_SECURITY_INTERFACE)GetProcAddress(
                                    m_hSecurity,
                                    "InitSecurityInterfaceA");
    
    if(pInitSecurityInterface == NULL)
    {
        return FALSE;
    }

    m_pSSPI = pInitSecurityInterface();

    if(m_pSSPI == NULL)
    {
        return FALSE;
    }

    return TRUE;
}

void XSECURIResolverSSLWin32::unloadSecurityLibrary(void) {

    FreeLibrary(m_hSecurity);
    m_hSecurity = NULL;

}

SECURITY_STATUS XSECURIResolverSSLWin32::createCredentials(PCredHandle phCreds) 
{

    TimeStamp       tsExpiry;
    SECURITY_STATUS Status;

    DWORD           cSupportedAlgs = 0;
    ALG_ID          rgbSupportedAlgs[16];

    //
    // Build Schannel credential structure. Currently, this sample only
    // specifies the protocol to be used (and optionally the certificate, 
    // of course). Real applications may wish to specify other parameters 
    // as well.
    //

    ZeroMemory(&m_SchannelCred, sizeof(m_SchannelCred));

    m_SchannelCred.dwVersion  = SCHANNEL_CRED_VERSION;

    m_SchannelCred.cCreds     = 1;
    m_SchannelCred.paCred     = &m_pCertContext;

    m_SchannelCred.grbitEnabledProtocols = m_dwProtocol;

    if(m_aiKeyExch)
    {
        rgbSupportedAlgs[cSupportedAlgs++] = m_aiKeyExch;
    }

    if(cSupportedAlgs)
    {
        m_SchannelCred.cSupportedAlgs    = cSupportedAlgs;
        m_SchannelCred.palgSupportedAlgs = rgbSupportedAlgs;
    }

    m_SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;

    // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because
    // this sample verifies the server certificate manually. 
    // Applications that expect to run on WinNT, Win9x, or WinME 
    // should specify this flag and also manually verify the server
    // certificate. Applications running on newer versions of Windows can
    // leave off this flag, in which case the InitializeSecurityContext
    // function will validate the server certificate automatically.
    m_SchannelCred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;

    //
    // Create an SSPI credential.
    //

    Status = m_pSSPI->AcquireCredentialsHandleA(
                        NULL,                   // Name of principal    
                        UNISP_NAME_A,           // Name of package
                        SECPKG_CRED_OUTBOUND,   // Flags indicating use
                        NULL,                   // Pointer to logon ID
                        &m_SchannelCred,        // Package specific data
                        NULL,                   // Pointer to GetKey() func
                        NULL,                   // Value to pass to GetKey()
                        phCreds,                // (out) Cred Handle
                        &tsExpiry);             // (out) Lifetime (optional)

    if(Status != SEC_E_OK)
    {
        //error
    }

    return Status;
}

int XSECURIResolverSSLWin32::connectToServer(
                              LPSTR    pszServerName, // in
                              int      iPortNumber,   // in
                              SOCKET * pSocket)       // out
{
    SOCKET Socket;
    struct sockaddr_in sin;
    struct hostent *hp;

    Socket = socket(PF_INET, SOCK_STREAM, 0);
    if(Socket == INVALID_SOCKET)
    {
        return WSAGetLastError();
    }


    sin.sin_family = AF_INET;
    sin.sin_port = htons((u_short)iPortNumber);

    if((hp = gethostbyname(pszServerName)) == NULL)
    {
      return WSAGetLastError();
    }
      else
    {
      memcpy(&sin.sin_addr, hp->h_addr, 4);
    }

    if(connect(Socket, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        closesocket(Socket);
        return WSAGetLastError();
    }

    *pSocket = Socket;

    return SEC_E_OK;
}

long XSECURIResolverSSLWin32::disconnectFromServer(
                                SOCKET          Socket, 
                                PCredHandle     phCreds,
                                CtxtHandle *    phContext)
{
    DWORD           dwType;
    const char *    pbMessage;
    DWORD           cbMessage;
    DWORD           cbData;

    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    DWORD           Status;

    //
    // Notify schannel that we are about to close the connection.
    //

    dwType = SCHANNEL_SHUTDOWN;

    OutBuffers[0].pvBuffer   = &dwType;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = sizeof(dwType);

    OutBuffer.cBuffers  = 1;
    OutBuffer.pBuffers  = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    Status = m_pSSPI->ApplyControlToken(phContext, &OutBuffer);

    if(FAILED(Status)) 
    {
        goto cleanup;
    }

    //
    // Build an SSL close notify message.
    //

    dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                  ISC_REQ_REPLAY_DETECT     |
                  ISC_REQ_CONFIDENTIALITY   |
                  ISC_RET_EXTENDED_ERROR    |
                  ISC_REQ_ALLOCATE_MEMORY   |
                  ISC_REQ_STREAM;

    OutBuffers[0].pvBuffer   = NULL;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = 0;

    OutBuffer.cBuffers  = 1;
    OutBuffer.pBuffers  = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    Status = m_pSSPI->InitializeSecurityContextA(
                    phCreds,
                    phContext,
                    NULL,
                    dwSSPIFlags,
                    0,
                    SECURITY_NATIVE_DREP,
                    NULL,
                    0,
                    phContext,
                    &OutBuffer,
                    &dwSSPIOutFlags,
                    &tsExpiry);

    if(FAILED(Status)) 
    {
        goto cleanup;
    }

    pbMessage = (const char *)OutBuffers[0].pvBuffer;
    cbMessage = OutBuffers[0].cbBuffer;


    //
    // Send the close notify message to the server.
    //

    if(pbMessage != NULL && cbMessage != 0)
    {
        cbData = send(Socket, pbMessage, cbMessage, 0);
        if(cbData == SOCKET_ERROR || cbData == 0)
        {
            Status = WSAGetLastError();
            goto cleanup;
        }

        // Free output buffer.
        m_pSSPI->FreeContextBuffer((void*)pbMessage);
    }
    

cleanup:

    // Free the security context.
    m_pSSPI->DeleteSecurityContext(phContext);

    // Close the socket.
    closesocket(Socket);

    return Status;
}

SECURITY_STATUS XSECURIResolverSSLWin32::performClientHandshake(
                          SOCKET          Socket,         // in
                          PCredHandle     phCreds,        // in
                          LPSTR           pszServerName,  // in
                          CtxtHandle *    phContext,      // out
                          SecBuffer *     pExtraData)     // out
{
    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    SECURITY_STATUS scRet;
    DWORD           cbData;

    dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                  ISC_REQ_REPLAY_DETECT     |
                  ISC_REQ_CONFIDENTIALITY   |
                  ISC_RET_EXTENDED_ERROR    |
                  ISC_REQ_ALLOCATE_MEMORY   |
                  ISC_REQ_STREAM;

    //
    //  Initiate a ClientHello message and generate a token.
    //

    OutBuffers[0].pvBuffer   = NULL;
    OutBuffers[0].BufferType = SECBUFFER_TOKEN;
    OutBuffers[0].cbBuffer   = 0;

    OutBuffer.cBuffers = 1;
    OutBuffer.pBuffers = OutBuffers;
    OutBuffer.ulVersion = SECBUFFER_VERSION;

    scRet = m_pSSPI->InitializeSecurityContextA(
                    phCreds,
                    NULL,
                    pszServerName,
                    dwSSPIFlags,
                    0,
                    SECURITY_NATIVE_DREP,
                    NULL,
                    0,
                    phContext,
                    &OutBuffer,
                    &dwSSPIOutFlags,
                    &tsExpiry);

    if(scRet != SEC_I_CONTINUE_NEEDED)
    {
        return scRet;
    }

    // Send response to server if there is one.
    if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
    {
        cbData = send(Socket,
                      (const char *)OutBuffers[0].pvBuffer,
                      OutBuffers[0].cbBuffer,
                      0);
        if(cbData == SOCKET_ERROR || cbData == 0)
        {
            m_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
            m_pSSPI->DeleteSecurityContext(phContext);
            return SEC_E_INTERNAL_ERROR;
        }

        // Free output buffer.
        m_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
        OutBuffers[0].pvBuffer = NULL;
    }


    return clientHandshakeLoop(Socket, phCreds, phContext, TRUE, pExtraData);
}

SECURITY_STATUS XSECURIResolverSSLWin32::clientHandshakeLoop(
                    SOCKET          Socket,         // in
                    PCredHandle     phCreds,        // in
                    CtxtHandle *    phContext,      // in, out
                    BOOL            fDoInitialRead, // in
                    SecBuffer *     pExtraData)     // out
{
    SecBufferDesc   InBuffer;
    SecBuffer       InBuffers[2];
    SecBufferDesc   OutBuffer;
    SecBuffer       OutBuffers[1];
    DWORD           dwSSPIFlags;
    DWORD           dwSSPIOutFlags;
    TimeStamp       tsExpiry;
    SECURITY_STATUS scRet;
    DWORD           cbData;

    char *          IoBuffer;
    DWORD           cbIoBuffer;
    BOOL            fDoRead;


    dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                  ISC_REQ_REPLAY_DETECT     |
                  ISC_REQ_CONFIDENTIALITY   |
                  ISC_RET_EXTENDED_ERROR    |
                  ISC_REQ_ALLOCATE_MEMORY   |
                  ISC_REQ_STREAM;

    //
    // Allocate data buffer.
    //

    IoBuffer = (char *)LocalAlloc(LMEM_FIXED, IO_BUFFER_SIZE);
    if(IoBuffer == NULL)
    {
        return SEC_E_INTERNAL_ERROR;
    }
    cbIoBuffer = 0;

    fDoRead = fDoInitialRead;


    // 
    // Loop until the handshake is finished or an error occurs.
    //

    scRet = SEC_I_CONTINUE_NEEDED;

    while(scRet == SEC_I_CONTINUE_NEEDED        ||
          scRet == SEC_E_INCOMPLETE_MESSAGE     ||
          scRet == SEC_I_INCOMPLETE_CREDENTIALS) 
   {

        //
        // Read data from server.
        //

        if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            if(fDoRead)
            {
                cbData = recv(Socket, 
                              IoBuffer + cbIoBuffer, 
                              IO_BUFFER_SIZE - cbIoBuffer, 
                              0);
                if(cbData == SOCKET_ERROR)
                {
                    scRet = SEC_E_INTERNAL_ERROR;
                    break;
                }
                else if(cbData == 0)
                {
                    scRet = SEC_E_INTERNAL_ERROR;
                    break;
                }

                cbIoBuffer += cbData;
            }
            else
            {
                fDoRead = TRUE;
            }
        }


        //
        // Set up the input buffers. Buffer 0 is used to pass in data
        // received from the server. Schannel will consume some or all
        // of this. Leftover data (if any) will be placed in buffer 1 and
        // given a buffer type of SECBUFFER_EXTRA.
        //

        InBuffers[0].pvBuffer   = IoBuffer;
        InBuffers[0].cbBuffer   = cbIoBuffer;
        InBuffers[0].BufferType = SECBUFFER_TOKEN;

        InBuffers[1].pvBuffer   = NULL;
        InBuffers[1].cbBuffer   = 0;
        InBuffers[1].BufferType = SECBUFFER_EMPTY;

        InBuffer.cBuffers       = 2;
        InBuffer.pBuffers       = InBuffers;
        InBuffer.ulVersion      = SECBUFFER_VERSION;

        //
        // Set up the output buffers. These are initialized to NULL
        // so as to make it less likely we'll attempt to free random
        // garbage later.
        //

        OutBuffers[0].pvBuffer  = NULL;
        OutBuffers[0].BufferType= SECBUFFER_TOKEN;
        OutBuffers[0].cbBuffer  = 0;

        OutBuffer.cBuffers      = 1;
        OutBuffer.pBuffers      = OutBuffers;
        OutBuffer.ulVersion     = SECBUFFER_VERSION;

        //
        // Call InitializeSecurityContext.
        //

        scRet = m_pSSPI->InitializeSecurityContextA(phCreds,
                                          phContext,
                                          NULL,
                                          dwSSPIFlags,
                                          0,
                                          SECURITY_NATIVE_DREP,
                                          &InBuffer,
                                          0,
                                          NULL,
                                          &OutBuffer,
                                          &dwSSPIOutFlags,
                                          &tsExpiry);

        //
        // If InitializeSecurityContext was successful (or if the error was 
        // one of the special extended ones), send the contends of the output
        // buffer to the server.
        //

        if(scRet == SEC_E_OK                ||
           scRet == SEC_I_CONTINUE_NEEDED   ||
           FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR))
        {
            if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL)
            {
                cbData = send(Socket,
                              (const char *)OutBuffers[0].pvBuffer,
                              OutBuffers[0].cbBuffer,
                              0);

                if(cbData == SOCKET_ERROR || cbData == 0)
                {
                    m_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
                    m_pSSPI->DeleteSecurityContext(phContext);
                    return SEC_E_INTERNAL_ERROR;
                }

                // Free output buffer.
                m_pSSPI->FreeContextBuffer(OutBuffers[0].pvBuffer);
                OutBuffers[0].pvBuffer = NULL;
            }
        }


        //
        // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
        // then we need to read more data from the server and try again.
        //

        if(scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            continue;
        }


        //
        // If InitializeSecurityContext returned SEC_E_OK, then the 
        // handshake completed successfully.
        //

        if(scRet == SEC_E_OK)
        {
            //
            // If the "extra" buffer contains data, this is encrypted 
application
            // protocol layer stuff. It needs to be saved. The application layer
            // will later decrypt it with DecryptMessage.
            //

            if(InBuffers[1].BufferType == SECBUFFER_EXTRA)
            {
                pExtraData->pvBuffer = LocalAlloc(LMEM_FIXED, 
                                                  InBuffers[1].cbBuffer);
                if(pExtraData->pvBuffer == NULL)
                {
                    return SEC_E_INTERNAL_ERROR;
                }

                MoveMemory(pExtraData->pvBuffer,
                           IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
                           InBuffers[1].cbBuffer);

                pExtraData->cbBuffer   = InBuffers[1].cbBuffer;
                pExtraData->BufferType = SECBUFFER_TOKEN;
            }
            else
            {
                pExtraData->pvBuffer   = NULL;
                pExtraData->cbBuffer   = 0;
                pExtraData->BufferType = SECBUFFER_EMPTY;
            }

            //
            // Bail out to quit
            //

            break;
        }


        //
        // Check for fatal error.
        //

        if(FAILED(scRet))
        {
            break;
        }


        //
        // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS,
        // then the server just requested client authentication. 
        //

        if(scRet == SEC_I_INCOMPLETE_CREDENTIALS)
        {
            //
            // Busted. The server has requested client authentication and
            // the credential we supplied didn't contain a client certificate.
            //

            // 
            // This function will read the list of trusted certificate
            // authorities ("issuers") that was received from the server
            // and attempt to find a suitable client certificate that
            // was issued by one of these. If this function is successful, 
            // then we will connect using the new certificate. Otherwise,
            // we will attempt to connect anonymously (using our current
            // credentials).
            //
            
            getNewClientCredentials(phCreds, phContext);

            // Go around again.
            fDoRead = FALSE;
            scRet = SEC_I_CONTINUE_NEEDED;
            continue;
        }


        //
        // Copy any leftover data from the "extra" buffer, and go around
        // again.
        //

        if ( InBuffers[1].BufferType == SECBUFFER_EXTRA )
        {
            MoveMemory(IoBuffer,
                       IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer),
                       InBuffers[1].cbBuffer);

            cbIoBuffer = InBuffers[1].cbBuffer;
        }
        else
        {
            cbIoBuffer = 0;
        }
    }

    // Delete the security context in the case of a fatal error.
    if(FAILED(scRet))
    {
        m_pSSPI->DeleteSecurityContext(phContext);
    }

    LocalFree(IoBuffer);

    return scRet;
}

SECURITY_STATUS XSECURIResolverSSLWin32::httpsGetData(
                  SOCKET          Socket,             // in
                  PCredHandle     phCreds,            // in
                  CtxtHandle *    phContext,          // in
                  LPSTR           hostName,           // in
                  LPSTR           path,               // in
                  LPSTR           query,              // in
                  LPSTR           fragment,           // in
                  unsigned short  portNumber,         // in
                  safeBuffer            &sb)                // in
{
    SecPkgContext_StreamSizes Sizes;
    SECURITY_STATUS scRet;
    SecBufferDesc   Message;
    SecBuffer       Buffers[4];
    SecBuffer *     pDataBuffer;
    SecBuffer *     pExtraBuffer;
    SecBuffer       ExtraBuffer;

    char *pbIoBuffer;
    DWORD cbIoBuffer;
    DWORD cbIoBufferLength;
    char *pbMessage;
    DWORD cbMessage;

    DWORD cbData;
    int   i;


    //
    // Read stream encryption properties.
    //

    scRet = m_pSSPI->QueryContextAttributes(phContext,
                                   SECPKG_ATTR_STREAM_SIZES,
                                   &Sizes);
    if(scRet != SEC_E_OK)
    {
        return scRet;
    }

    //
    // Allocate a working buffer. The plaintext sent to EncryptMessage
    // should never be more than 'Sizes.cbMaximumMessage', so a buffer 
    // size of this plus the header and trailer sizes should be safe enough.
    // 

    cbIoBufferLength = Sizes.cbHeader + 
                       Sizes.cbMaximumMessage +
                       Sizes.cbTrailer;

    pbIoBuffer = (char *)LocalAlloc(LMEM_FIXED, cbIoBufferLength);
    if(pbIoBuffer == NULL)
    {
        return SEC_E_INTERNAL_ERROR;
    }


    //
    // Build an HTTP request to send to the server.
    //

    // Remove the trailing backslash from the filename, should one exist.
    if(path && 
       strlen(path) > 1 && 
       path[strlen(path) - 1] == '/')
    {
        path[strlen(path)-1] = 0;
    }

    // Build the HTTP request offset into the data buffer by "header size"
    // bytes. This enables Schannel to perform the encryption in place,
    // which is a significant performance win.
    pbMessage = pbIoBuffer + Sizes.cbHeader;

    // Build HTTP request. Note that I'm assuming that this is less than
    // the maximum message size. If it weren't, it would have to be broken up.
    strcpy(pbMessage, "GET ");

    if (path != 0)
      strcat(pbMessage, path);

    if (query != 0)
    {
        // Tack on a ? before the fragment
        strcat(pbMessage,"?");
        strcat(pbMessage, query);
    }

    if (fragment != 0)
    {
        strcat(pbMessage, fragment);
    }
    strcat(pbMessage, " HTTP/1.0\r\n");

    strcat(pbMessage, "Host: ");
    strcat(pbMessage, hostName);

    if (portNumber != 80)
    {
        strcat(pbMessage, ":");
        int i = (int) strlen(pbMessage);
        _itoa(portNumber, pbMessage+i, 10);
    }

    cbMessage = (DWORD)strlen(pbMessage);

    //
    // Encrypt the HTTP request.
    //

    Buffers[0].pvBuffer     = pbIoBuffer;
    Buffers[0].cbBuffer     = Sizes.cbHeader;
    Buffers[0].BufferType   = SECBUFFER_STREAM_HEADER;

    Buffers[1].pvBuffer     = pbMessage;
    Buffers[1].cbBuffer     = cbMessage;
    Buffers[1].BufferType   = SECBUFFER_DATA;

    Buffers[2].pvBuffer     = pbMessage + cbMessage;
    Buffers[2].cbBuffer     = Sizes.cbTrailer;
    Buffers[2].BufferType   = SECBUFFER_STREAM_TRAILER;

    Buffers[3].BufferType   = SECBUFFER_EMPTY;

    Message.ulVersion       = SECBUFFER_VERSION;
    Message.cBuffers        = 4;
    Message.pBuffers        = Buffers;

    scRet = m_pSSPI->EncryptMessage(phContext, 0, &Message, 0);

    if(FAILED(scRet))
    {
        return scRet;
    }


    // 
    // Send the encrypted data to the server.
    //

    cbData = send(Socket,
                  pbIoBuffer,
                  Buffers[0].cbBuffer + Buffers[1].cbBuffer + 
Buffers[2].cbBuffer,
                  0);
    if(cbData == SOCKET_ERROR || cbData == 0)
    {
        m_pSSPI->DeleteSecurityContext(phContext);
        return SEC_E_INTERNAL_ERROR;
    }

    //printf("%d bytes of application data sent\n", cbData);

    //
    // Read data from server until done.
    //

    cbIoBuffer = 0;

    while(TRUE)
    {
        //
        // Read some data.
        //

        if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            cbData = recv(Socket, 
                          pbIoBuffer + cbIoBuffer, 
                          cbIoBufferLength - cbIoBuffer, 
                          0);
            if(cbData == SOCKET_ERROR)
            {
                scRet = SEC_E_INTERNAL_ERROR;
                break;
            }
            else if(cbData == 0)
            {
                // Server disconnected.
                if(cbIoBuffer)
                {
                    scRet = SEC_E_INTERNAL_ERROR;
                    return scRet;
                }
                else
                {
                    break;
                }
            }
            else
            {
                cbIoBuffer += cbData;
            }
        }

        // 
        // Attempt to decrypt the received data.
        //

        Buffers[0].pvBuffer     = pbIoBuffer;
        Buffers[0].cbBuffer     = cbIoBuffer;
        Buffers[0].BufferType   = SECBUFFER_DATA;

        Buffers[1].BufferType   = SECBUFFER_EMPTY;
        Buffers[2].BufferType   = SECBUFFER_EMPTY;
        Buffers[3].BufferType   = SECBUFFER_EMPTY;

        Message.ulVersion       = SECBUFFER_VERSION;
        Message.cBuffers        = 4;
        Message.pBuffers        = Buffers;

        scRet = m_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);

        if(scRet == SEC_E_INCOMPLETE_MESSAGE)
        {
            // The input buffer contains only a fragment of an
            // encrypted record. Loop around and read some more
            // data.
            continue;
        }

        // Server signalled end of session
        if(scRet == SEC_I_CONTEXT_EXPIRED)
            break;

        if( scRet != SEC_E_OK && 
            scRet != SEC_I_RENEGOTIATE && 
            scRet != SEC_I_CONTEXT_EXPIRED)
        {
            return scRet;
        }

        // Locate data and (optional) extra buffers.
        pDataBuffer  = NULL;
        pExtraBuffer = NULL;
        for(i = 1; i < 4; i++)
        {

            if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA)
            {
                pDataBuffer = &Buffers[i];
            }
            if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA)
            {
                pExtraBuffer = &Buffers[i];
            }
        }

        // Display or otherwise process the decrypted data.
        if(pDataBuffer)
        {
            sb.sbMemcpyIn(pDataBuffer->pvBuffer, pDataBuffer->cbBuffer);

        }

        // Move any "extra" data to the input buffer.
        if(pExtraBuffer)
        {
            MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, 
pExtraBuffer->cbBuffer);
            cbIoBuffer = pExtraBuffer->cbBuffer;
        }
        else
        {
            cbIoBuffer = 0;
        }

        if(scRet == SEC_I_RENEGOTIATE)
        {
            // The server wants to perform another handshake
            // sequence.

            scRet = clientHandshakeLoop(Socket, 
                                        phCreds, 
                                        phContext, 
                                        FALSE, 
                                        &ExtraBuffer);
            if(scRet != SEC_E_OK)
            {
                return scRet;
            }

            // Move any "extra" data to the input buffer.
            if(ExtraBuffer.pvBuffer)
            {
                MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, 
ExtraBuffer.cbBuffer);
                cbIoBuffer = ExtraBuffer.cbBuffer;
            }
        }
    }

    return SEC_E_OK;
}

DWORD XSECURIResolverSSLWin32::verifyServerCertificate(
                                PCCERT_CONTEXT  pServerCert,
                                PSTR            pszServerName,
                                DWORD           dwCertFlags)
{
    HTTPSPolicyCallbackData  polHttps;
    CERT_CHAIN_POLICY_PARA   PolicyPara;
    CERT_CHAIN_POLICY_STATUS PolicyStatus;
    CERT_CHAIN_PARA          ChainPara;
    PCCERT_CHAIN_CONTEXT     pChainContext = NULL;

    LPSTR rgszUsages[] = {  szOID_PKIX_KP_SERVER_AUTH,
                            szOID_SERVER_GATED_CRYPTO,
                            szOID_SGC_NETSCAPE };
    DWORD cUsages = sizeof(rgszUsages) / sizeof(LPSTR);

    PWSTR   pwszServerName = NULL;
    DWORD   cchServerName;
    DWORD   Status;

    if(pServerCert == NULL)
    {
        Status = SEC_E_WRONG_PRINCIPAL;
        goto cleanup;
    }


    //
    // Convert server name to unicode.
    //

    if(pszServerName == NULL || strlen(pszServerName) == 0)
    {
        Status = SEC_E_WRONG_PRINCIPAL;
        goto cleanup;
    }

    cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, NULL, 0);
    pwszServerName = (unsigned short *)LocalAlloc(LMEM_FIXED, cchServerName * 
sizeof(WCHAR));
    if(pwszServerName == NULL)
    {
        Status = SEC_E_INSUFFICIENT_MEMORY;
        goto cleanup;
    }
    cchServerName = MultiByteToWideChar(CP_ACP, 0, pszServerName, -1, 
pwszServerName, cchServerName);
    if(cchServerName == 0)
    {
        Status = SEC_E_WRONG_PRINCIPAL;
        goto cleanup;
    }


    //
    // Build certificate chain.
    //

    ZeroMemory(&ChainPara, sizeof(ChainPara));
    ChainPara.cbSize = sizeof(ChainPara);
    ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
    ChainPara.RequestedUsage.Usage.cUsageIdentifier     = cUsages;
    ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;

    if(!CertGetCertificateChain(
                            NULL,
                            pServerCert,
                            NULL,
                            pServerCert->hCertStore,
                            &ChainPara,
                            0,
                            NULL,
                            &pChainContext))
    {
        Status = GetLastError();
        goto cleanup;
    }


    //
    // Validate certificate chain.
    // 

    ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
    polHttps.cbStruct           = sizeof(HTTPSPolicyCallbackData);
    polHttps.dwAuthType         = AUTHTYPE_SERVER;
    polHttps.fdwChecks          = dwCertFlags;
    polHttps.pwszServerName     = pwszServerName;

    memset(&PolicyPara, 0, sizeof(PolicyPara));
    PolicyPara.cbSize            = sizeof(PolicyPara);
    PolicyPara.pvExtraPolicyPara = &polHttps;

    memset(&PolicyStatus, 0, sizeof(PolicyStatus));
    PolicyStatus.cbSize = sizeof(PolicyStatus);

    if(!CertVerifyCertificateChainPolicy(
                            CERT_CHAIN_POLICY_SSL,
                            pChainContext,
                            &PolicyPara,
                            &PolicyStatus))
    {
        Status = GetLastError();
        goto cleanup;
    }

    if(PolicyStatus.dwError)
    {
        Status = PolicyStatus.dwError;
        goto cleanup;
    }


    Status = SEC_E_OK;

cleanup:

    if(pChainContext)
    {
        CertFreeCertificateChain(pChainContext);
    }

    if(pwszServerName)
    {
        LocalFree(pwszServerName);
    }

    return Status;
}

void XSECURIResolverSSLWin32::getNewClientCredentials(CredHandle *phCreds, 
                                                      CtxtHandle *phContext) {

    CredHandle hCreds;
    SecPkgContext_IssuerListInfoEx IssuerListInfo;
    PCCERT_CHAIN_CONTEXT pChainContext;
    CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
    PCCERT_CONTEXT  pCertContext;
    TimeStamp       tsExpiry;
    SECURITY_STATUS Status;

    //
    // Read list of trusted issuers from schannel.
    //

    Status = m_pSSPI->QueryContextAttributes(phContext,
                                    SECPKG_ATTR_ISSUER_LIST_EX,
                                    (PVOID)&IssuerListInfo);
    if(Status != SEC_E_OK)
    {
        return;
    }

    //
    // Enumerate the client certificates.
    //

    ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));

    FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
    FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
    FindByIssuerPara.dwKeySpec = 0;
    FindByIssuerPara.cIssuer   = IssuerListInfo.cIssuers;
    FindByIssuerPara.rgIssuer  = IssuerListInfo.aIssuers;

    pChainContext = NULL;

    HCERTSTORE hMyCertStore = NULL;

    // Open the "MY" certificate store, which is where Internet Explorer
    // stores its client certificates.
    hMyCertStore = CertOpenSystemStore(0, "MY");

    if(!hMyCertStore)
    {
        return;
    }

    while(TRUE)
    {
        // Find a certificate chain.
        pChainContext = CertFindChainInStore(hMyCertStore,
                                             X509_ASN_ENCODING,
                                             0,
                                             CERT_CHAIN_FIND_BY_ISSUER,
                                             &FindByIssuerPara,
                                             pChainContext);
        if(pChainContext == NULL)
        {
            break;
        }

        // Get pointer to leaf certificate context.
        pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;

        // Create schannel credential.
        m_SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
        m_SchannelCred.cCreds = 1;
        m_SchannelCred.paCred = &pCertContext;

        Status = m_pSSPI->AcquireCredentialsHandleA(
                            NULL,                   // Name of principal
                            UNISP_NAME_A,           // Name of package
                            SECPKG_CRED_OUTBOUND,   // Flags indicating use
                            NULL,                   // Pointer to logon ID
                            &m_SchannelCred,        // Package specific data
                            NULL,                   // Pointer to GetKey() func
                            NULL,                   // Value to pass to GetKey()
                            &hCreds,                // (out) Cred Handle
                            &tsExpiry);             // (out) Lifetime (optional)

        if(Status != SEC_E_OK)
        {
            continue;
        }

        // Destroy the old credentials.
        m_pSSPI->FreeCredentialsHandle(phCreds);

        *phCreds = hCreds;

        //
        // As you can see, this sample code maintains a single credential
        // handle, replacing it as necessary. This is a little unusual.
        //
        // Many applications maintain a global credential handle that's
        // anonymous (that is, it doesn't contain a client certificate),
        // which is used to connect to all servers. If a particular server
        // should require client authentication, then a new credential 
        // is created for use when connecting to that server. The global
        // anonymous credential is retained for future connections to
        // other servers.
        //
        // Maintaining a single anonymous credential that's used whenever
        // possible is most efficient, since creating new credentials all
        // the time is rather expensive.
        //

        break;
    }

    // Close "MY" certificate store.
    if(hMyCertStore)
    {
        CertCloseStore(hMyCertStore, 0);
    }
}
    
void XSECURIResolverSSLWin32::setClientCertificate(PCCERT_CONTEXT pCertContext) 
{

  m_pCertContext = pCertContext;

}

void XSECURIResolverSSLWin32::setProtocol(ProtocolType protocol) {

  switch(protocol)
  {
      case PROTO_PCT1:
          m_dwProtocol = SP_PROT_PCT1;
          break;
      case PROTO_SSL2:
          m_dwProtocol = SP_PROT_SSL2;
          break;
      case PROTO_SSL3:
          m_dwProtocol = SP_PROT_SSL3;
          break;
      case PROTO_TLS1:
          m_dwProtocol = SP_PROT_TLS1;
          break;
      default:
          m_dwProtocol = SP_PROT_SSL2;
          break;
  }

}

void XSECURIResolverSSLWin32::setKeyExchAlg(KeyExchType aiKeyExch) {

  switch(aiKeyExch)
  {
      case KEY_EXCH_RSA:
          m_aiKeyExch = CALG_RSA_KEYX;
          break;
      case KEY_EXCH_DH:
          m_aiKeyExch = CALG_DH_EPHEM;
          break;
      default:
          m_aiKeyExch = CALG_RSA_KEYX;
          break;
  }

}

#include <xsec/utils/XSECPlatformUtils.hpp>
#include <xercesc/util/PlatformUtils.hpp>

#include <xsec/framework/XSECProvider.hpp>
#include <xsec/framework/Win32/XSECURIResolverSSLWin32.hpp>

#include <iostream>
#include <string>

using namespace std;

XSEC_USING_XERCES(XMLPlatformUtils);
XERCES_CPP_NAMESPACE_USE

void main()
{
  XMLPlatformUtils ::Initialize();
  XSECPlatformUtils::Initialise();

  {
    PCCERT_CONTEXT pCertContext = NULL;

    HCERTSTORE hMyCertStore = CertOpenSystemStore(0, "MY");

    if(!hMyCertStore)
    {
        cout << "**** Error returned by CertOpenSystemStore\n" << 
GetLastError();
        return;
    }

    pCertContext = CertFindCertificateInStore(hMyCertStore, 
                                              X509_ASN_ENCODING, 
                                              0,
                                              CERT_FIND_SUBJECT_STR_A,
                                              "bankpass_cript",
                                              NULL);

    if(pCertContext == NULL)
    {
        cout << "**** Error returned by CertFindCertificateInStore\n" << 
GetLastError();
        return;
    }

    XSECURIResolverSSLWin32 * res = new XSECURIResolverSSLWin32();
    res->setClientCertificate(pCertContext);
    BinInputStream * is = res->resolveURI(L"localhost:443/file.txt");

    unsigned int read = 1;
    XMLByte buf[4096];
    char    txt[4096];

    while (read) {
      read = is->readBytes(buf, 4096);
      strncpy(txt, (const char*)buf, read);
      cout << txt;
    }

    if(hMyCertStore)
    {
        CertCloseStore(hMyCertStore, 0);
    }

  }
  
  XSECPlatformUtils::Terminate();
        XMLPlatformUtils ::Terminate();

}

Reply via email to