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: [email protected]
> 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();
}