Click here to Skip to main content
15,893,190 members
Articles / Programming Languages / C#
Article

SSL/TLS client/server for .NET and SSL tunnelling

Rate me:
Please Sign up or sign in to vote.
4.83/5 (35 votes)
31 Jul 2003CPOL9 min read 919K   32.3K   202   158
SSL/TLS Client/Server classes for .NET that uses Windows SSPI for SSL/TLS protocol

Samples that use MS SSPI SSL:

Samples that use OpenSSL:

Tested on Windows 2000.

Contents:

SSPI Overview and Steps

SSPI stands for Security Support Provider Interface. It is an abstraction layer over the security services provided by windows. SSL/TLS itself is implemented in Secure Channel security provider and SSPI abstracts it for us. SSPI works by taking and returning data blobs to be sent to remote party. This way it allows us most flexibility in choosing what protocol (for ex., tcp/ip) to use and what to do with encrypted/decrypted data. One thing to be aware of is making sure the data is a stream and nothing is out of sequence. SSPI provides a number of built-in protocols Kerberos, NTLM, SSL/TLS. There is almost exactly the same sequence of function calls for either of them. In SSL/TLS case there is a little more to be done.

Client Side:

  1. Open Certificate store. Choose what certificate to use. Call AcquireCredentialsHandle.
  2. When you need to connect. Call InitializeSecurityContext. Send out data blob that is returned.
  3. Go into handshake loop with remote party by calling InitializeSecurityContext passing in received data blobs and sending out returned data blobs until success is returned.
  4. When need to send data out, call EncryptMessage and send returned encrypted data blob to remote party.
  5. When need to decrypt received data, call DecryptMessage and process decrypted data blob
  6. When done, call ApplyControlToken with SCHANNEL_SHUTDOWN. Call InitializeSecurityContext
    and send out returned data blob.

Server Side:

  1. Open Certificate store. Choose what certificate to use. Call AcquireCredentialsHandle.
  2. When you receive new connection from client, call AcceptSecurityContext. Send out data blob that is returned.
  3. Go into handshake loop with remote party by calling AcceptSecurityContext passing in received data blobs and sending out returned data blobs until success is returned.
  4. When need to send data out, call EncryptMessage and send returned encrypted data blob to remote party.
  5. When need to decrypt received data, call DecryptMessage and process decrypted data blob
  6. When done, call ApplyControlToken with SCHANNEL_SHUTDOWN. Call AcceptSecurityContext and send out returned data blob.

These basically are the steps that have to be taken to use SSL/TLS with SSPI. There are some details that need to be taken care of in the code that is not listed above. Such as respond appropriately to any error codes returned by SSPI functions. For example, renegotiation, disconnection, having read incomplete message/too much data (that is one complete message and extra data from next message).

OpenSSL

OpenSSL is an open source project for commercial-grade, full featured implementation of SSL/TLS. You can read more about it at: ttp://www.openssl.org/ (Documentation at: http://www.openssl.org/docs, Source at: http://www.openssl.org/source)

First let's compile the OpenSSL library. Download the latest distribution for example openssl-0.9.7b.tar.gz

  • Extract the files. Read INSTALL.W32 file in the OpenSSL root directory.
  • Open "Visual Studio .NET Command Prompt" window.
  • Go into openssl-0.9.7b directory.
  • Type "perl Configure VC-WIN32" in the OpenSSL root directory. (Install ActivePerl for windows if you don't have it)
  • Type "ms\do_ms" in the OpenSSL root directory.
  • Now type "nmake -f ms\nt.mak" (This is for static version of OpenSSL library)

Everything should build nicely and .libs should be outputed into openssl-0.9.7b\out32 directory.

  • Set this directory in your "VC++ Directories" option for the "Library Files".
  • Set openssl-0.9.7b\inc32\ directory in your "VC++ Directories" option for the "Include Files".
  • Rename the generated libraries to libeay32r.lib and ssleay32r.lib for Release mode. Build separate one
  • for Debug mode with /MDd. They are NOT interchangable.

At this point the Managed C++ project should compile without problems.

  • Read the OpenSSL FAQ on how to create certificates with openssl.exe or you can search google for more information and examples. For example if you're using MS CA you can download CA certificate from http://131.107.152.153/ save it to CA.cer file and use it in the sample code for CA file parameter.

Now let's issue a certificate request to this CA.

  • First generate the key as in the above FAQ: "openssl genrsa -des3 -out privkey.pem 2048"
  • Now generate the request "openssl req -new -key privkey.pem -out cert.csr"
  • Using the "advanced certificate request" form on the MS CA web site, choose "Submit a certificate request by using a base-64-encoded CMC or PKCS #10 file, or submit a renewal request by using a base-64-encoded PKCS #7 file." link. Tell it where to find the .csr file and select submit. Now download the generated certificate.
  • Convert it to .pem "openssl x509 -inform DER -in certnew.cer -out clientcert.pem"
  • Combine certificate and private key into one file. "type clientcert.pem privkey.pem >client.pem"

Now you're ready to use this client.pem for the client side. Do same steps for the server side. This is just an example.
Explore the options that suits you better.

So you need at least 2 things to get the sample project working with OpenSSL.

  1. Generate client/server certificate and key into .pem format
  2. Download CA certificate

There are other ways to get working certificates for testing purposes. You can explore them on your own.

How to get Certificates:

When you're ready to deploy your app you can order a real/verifiable certificate from http://www.verisign.com/ or http://www.thawte.com/

However if you just want to play/test with SSL you have at least 3 other options.

  1. Best option is: Microsoft's free test Certificate Authority Server located at: http://131.107.152.153/

    Just click on Request a certificate. Web Browser Certificate. Use advanced Certificate form. Fill out the fields. Generate both Client and Server Authentication Certificates. Leave the defaults except for Key size, change it to bigger one if you want. You can also generate SSL test certificate for your IIS server there if you want.
  2. You can explorer the options offered by OpenSSL. Check this FAQ for more info:
    http://www.openssl.org/docs/HOWTO/certificates.txt

  3. Another option is using the built-in CA of windows 2000 Server edition. You can find step by step example in ATL7 SecureSOAP Sample's readme that comes with VS.NET MSDN.

  4. Yet another option is to use makecert.exe that comes with .NET SDK/PSDK. New version that comes with current PSDK supports exportable private keys (-pe option).

To find out the Certificate's hash/thumbprint open the .cer file in explorer. Go to Details | Field | Thumbprint. Select it. Then just copy paste the byte values into your code.

Image 1

Viewing Installed Certificates:

To View your current certificates on NT: Run mmc.exe. Go to Console | Add/Remove Snap-in | Add | Certificates. Your personal or "MY" store will be under Personal | Certificates tree items.

Image 2

Client class:

MC++
class SSLConnection
{
public: 
    //initiate connection, given server's ip and client's certificate
    //hash, data to be sent will be returned in WriteSSL callback 
    void InitiateHandShake(String* ipAddress, Byte thumbPrint[], 
                           Common::Misc::SecurityProviderProtocol prot, 
                           Object* state); 
    //encrypt data, encrypted data is retuned in WriteSSL callback 
    void EncryptSend(Byte data[], int ActualLen, Object* state); 
    //decrypt data, decrypted data is returned in PlainData callback 
    void DecryptData(Byte data[], Int32 ActualSize, Object* state); 
    //disconnect from server 
    bool Disconnect(Object* state); 
    //clean up 
    void Dispose(); 
    //load new client's credentials from NewCertificate callback 
    void LoadNewClientCredentials(Byte sha1hash[]); 

public: 
    //maximum data chunk to use for send/recv at a time 
    __property int get_MaxDataChunkSize();

    //recommended initial chunk size when negotiation just starts, 
    //this is max auth token size 
    __property int get_MaxInitialChunkSize();

public: 
    //callback for encrypted data 
    WriteSSL* DoWrite; 
    //callback for decrypted data 
    PlainData* DoPlainData; 
    //optional 
    NewCertificate* DoRenegotiate; 
    //optional 
    VerifyServCert* DoServerCertVerify; 
    //optional 
    HandShakeSuccess* DoHandShakeSuccess;
    ...
};

Quick Example:

C#
Socket sock = ...//init Socket;
SSLConnection conn = new SSLConnection();
conn.DoWrite       = new SSL.Client.WriteSSL(Send);
conn.DoPlainData   = new SSL.Client.PlainData(OnPlainData);
conn.DoRenegotiate = new SSL.Client.NewCertificate(Renegotiate);
conn.DoServerCertVerify = new SSL.Client.VerifyServCert(ServerCertVerify);
conn.DoHandShakeSuccess = new SSL.Client.HandShakeSuccess(HandShakeSuccess);
sock.Connect(....);

//try not supplying client certificate, null for thumbprint
conn.InitiateHandShake(url, null, 
           SSL.Common.Misc.SecurityProviderProtocol.PROT_TLS1, 
           Guid.Empty);
while(!connected)
{
        // recv data
        // call conn.DecryptData
}

string Request = "GET / HTTP/1.1\r\n" + "Host: localhost\r\n\r\n";
byte[] data = ASCIIEncoding.ASCII.GetBytes(Request);
conn.EncryptSend(data, data.Length, null);

//recv
//conn.DecryptData
conn.Disconnect(null);
conn.Dispose();

////////////////////////////////////////////////////////////////
bool Send(byte[] data, object state)
{
    sock.Send(data, 0, data.Length, SocketFlags.None);
}

void OnPlainData(Byte[] data, object state)
{
    //do something with decrypted data
}

void Renegotiate(SSL.Client.SSLConnection conn)
{
    //server asked for client certificate
    //TODO: change values to match your client's certificates hash
     byte[] ThumbPrint = new Byte[] {0xBE,0xD7,0x44,0xF4,0x57,
                                     0x54,0xF2,0x08,0x7F,0x06,
                                     0x03,0x0B,0x01,0x33,0xE5,
                                     0x60,0x78,0x3D,0xAF,0x35};
    conn.LoadNewClientCredentials(ThumbPrint);
}

void HandShakeSuccess(){connected = true;}

void ServerCertVerify(SSL.Common.Misc.CeriticateInfo ServCertInfo)
{
    X509Certificate cert = new X509Certificate(ServCertInfo.CertData);
    //display server's certificate information
}

Server class:

MC++
class SSLServer
{ 
public: 
    //disconnect from given client, based on id 
    void DisconnectFromClient(Guid ClientID, Object* state); 
    //clean up 
    void Dispose(); 
    //encrypt data, encrypted data is retuned in WriteSSL callback 
    void EncryptSend(Byte data[], int ActualLen, Guid ClientID, 
                     Object* state); 
    //decrypt data, decrypted data is returned in PlainData callback 
    void DecryptData(Byte data[], Int32 ActualLen, Guid ClientID, 
                     Object* state); 
    //remove client based on id from internal list of clients 
    void RemoveClient(Guid ClientID); 
    //maximum data chunk to use for send/recv at a time 
    int MaxDataChunkSize(Guid ClientID);
    //setup credentials, certThumbPrint is certificate's hash 
    void SetupCredentials(Byte certThumbPrint[], 
            Common::Misc::SecurityProviderProtocol prot); 
    //ask client to renegotiate and possibly get different credentials 
    void AskForRenegotiate(Guid ClientID, Object* state); 

public: 
    //recommended initial chunk size when negotiation just starts, 
    // this is max auth token size 
    __property int get_MaxInitialChunkSize(); 
    //ask client to provide certificate or not 
    __property void set_AskClientForAuth(bool value); 

public: 
    //callback for encrypted data 
    WriteSSL* DoWrite; 
    //callback for decrypted data 
    PlainData* DoPlainData; 
    //optional 
    VerifyClientCert* DoClientCertVerify; 
    //optional 
    HandShakeSuccess* DoHandShakeSuccess; 

private:
    ...
};

Quick Example:

C#
SSLServer SSL   = new SSLServer();
SSL.DoPlainData = new SSL.Server.PlainData(OnPlainData);
SSL.DoWrite     = new SSL.Server.WriteSSL(OnSend);
SSL.DoClientCertVerify
         = new SSL.Server.VerifyClientCert(OnClientCertInfo);
SSL.DoHandShakeSuccess
         = new SSL.Server.HandShakeSuccess(OnClientHandShakeSuccess);
SSL.AskClientForAuth = false;

//TODO: change values to match your server's certificates hash
byte[] ServerCertThumbPrint = new byte[]{0xA4, 0x01, 0xC2, 0xB1, 0x73,
                                         0xD9, 0xD9, 0xF4, 0x77, 0x68, 
                                         0x60, 0xE5, 0xAD, 0x25, 0x34, 
                                         0xEE, 0x59, 0xEB, 0x0E, 0x8D};
SSL.SetupCredentials(ServerCertThumbPrint,
                  SSL.Common.Misc.SecurityProviderProtocol.PROT_TLS1);
//listen for incoming connections on Socket
//Accept new connection if any
//begin receiving data from client
Guid clientID = Guid.NewGuid();
SSL.DecryptData(buff, bytesRead, clientID, null);
SSL.DisconnectFromClient(clientID);
//When client handshake complete, 
//do SSL.EncryptSend(data, data.Length, ClientID, null); when needed
SSL.Dispose();

////////////////////////////////////////////////////////////////
void OnPlainData(byte[] data, Guid ClientID, object o)
{
    //process data
}

bool OnSend(byte[] data, Guid ClientID, object o)
{
    //send data out on socket
}

void OnClientCertInfo(SSL.Common.Misc.CeriticateInfo clientCert)
{
    X509Certificate cert = new X509Certificate(clientCert.CertData);
    //display client's cert info
}

void OnClientHandShakeSuccess(Guid ClientID)
{
    // handshake with client is successful
}

SSL Tunnel Sample:

NOTE: Never Never try this with your real/production SQL server. Try AT YOUR OWN RISK with your test installation of db or something else that goes through known TCP/IP connection. Also, go through code see what it does, make sure it suits your specific purposes.

To use it simply build the client and server .exes. Make sure the .config file is where the .exe is. You can test how it work by securing MS SQL connection with SSL/TLS. Here are the steps to do it: Make sure you installed your own certificate and updated the hash/thumbprints in code.

Open the MS SQL Client Network utility. Go to Alias | Add, then set the options as below:

  • Server Alias: SSL
  • Network Libraries: TCP/IP
  • Server Name: Your Client.exe's IP (for ex., "localhost")
  • Port Number: Your Client's listening Port (that's the "PortToListenForRedirect" value in Client.exe.config)

In Client.exe.config "SSLDestIP" and "SSLDestPort" will be your Server.exe's IP, and port that it listens on for redirect.

In Server.exe.config "PortToListenForSSL" is port Server.exe listens on for Client.exe, and "DestIP" "DestPort" are MS SQL Servers real IP and Port where everything from client will be sent. So the Cnnection in between client and exe will be secure. Client.exe will run on local machine for example and Server.exe will run on machine closer to SQL Server.

Run the SSL Client/Server applications.

Open the MS SQL Enterprise Manager console. Choose "New SQL Server registration". For server name put SSL, fill in the login info. Click OK. Now, you should be connected through SSL to your SQL Server.

References:

MS SChannel related:

Disclaimer: THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSOS for SSL Connection [modified] Pin
nicuzzo_web9-Oct-06 6:09
nicuzzo_web9-Oct-06 6:09 
GeneralLeak or unreleased memory Pin
valsorbod3-Aug-06 19:54
valsorbod3-Aug-06 19:54 
GeneralThe referenced component 'SSL' could not be found. Pin
ashokvarda18-Apr-06 21:23
ashokvarda18-Apr-06 21:23 
GeneralRe: The referenced component 'SSL' could not be found. Pin
busyboy24-Apr-06 20:53
busyboy24-Apr-06 20:53 
GeneralRe: The referenced component 'SSL' could not be found. Pin
Zach Gorman2-Nov-06 11:54
Zach Gorman2-Nov-06 11:54 
QuestionUsing un-signed certificates Pin
Sanju084613-Apr-06 22:07
Sanju084613-Apr-06 22:07 
QuestionReusing SSL credentials Pin
Azraelgod25-Mar-06 6:02
Azraelgod25-Mar-06 6:02 
QuestionHow about C++ Pin
Iven Xu28-Feb-06 4:05
Iven Xu28-Feb-06 4:05 
Hi Leon,

Nice code!

I translate this code to C++, but something does not work very well. In my program, some code is from Using SSPI with a Windows Sockets Clienthttp://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/using_sspi_with_a_windows_sockets_client.asp[^]. I want use the SSPI implement a smtp client to connect to Gmail server. The handshake and DectryptMessage is OK. But EnctryptMessage seem to occur error. Do you have any idea about this? Here is my code:

// SSPI.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "SSPI.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif


//--------------------------------------------------------------------
// Client-side program to establish an SSPI socket connection
// with a server and exchange messages.

//--------------------------------------------------------------------
// Define macros and constants.

#define SECURITY_WIN32
#define BIG_BUFF 2048
#define SEC_SUCCESS(Status) ((Status) >= 0)
#define g_usPort 465
// The following #define statement must be changed. ServerName must be
// defined as the name of the computer running the server sample.
// TargetName must be defined as the logon name of the user running
// the server program.
#define ServerName "64.233.167.111" //gmail smtp
#define TargetName "64.233.167.111"
#define cbMaxMessage 12000
#define MessageAttribute (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |\
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |\
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM)

//#include <windows.h>
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <sspi.h>
#include <schannel.h>
#include "security.h"

#ifdef __cplusplus
extern "C" {
#endif

void MyHandleError(char *s);
BOOL ConnectAuthSocket (
SOCKET *s,
CredHandle *hCred,
struct _SecHandle *hcText);
BOOL ReceiveBytes (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf,
DWORD *pcbRead);
BOOL EncryptThis (
PBYTE pMessage,
ULONG cbMessage,
struct _SecHandle *hCtxt,
BYTE * pOutput,
ULONG * pcbOutput);
BOOL DecryptThis(
PBYTE pBuffer,
LPDWORD pcbMessage,
struct _SecHandle *hCtxt,
ULONG cbSecurityTrailer);
BOOL DoAuthentication (
SOCKET s,
CredHandle *hCred,
struct _SecHandle *hcText);

BOOL GenClientContext (
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone,
CHAR *pszTarget,
CredHandle *hCred,
struct _SecHandle *hcText);
BOOL SendMsg (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf);
BOOL ReceiveMsg (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf,
DWORD *pcbRead);
void PrintHexDump(
DWORD length,
PBYTE buffer);
BOOL SendBytes (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf);

void main()
{

SOCKET Client_Socket;
BYTE Data[BIG_BUFF];
PCHAR pMessage;
WSADATA wsaData;
CredHandle hCred;
struct _SecHandle hCtxt;
SECURITY_STATUS ss;
DWORD cbRead;
ULONG cbMaxSignature;
ULONG cbSecurityTrailer;
SecPkgContext_Sizes SecPkgContextSizes;
SecPkgContext_NegotiationInfo SecPkgNegInfo;

//-------------------------------------------------------------------
// Initialize the socket and the SSP security package.
//

if(WSAStartup (MAKEWORD( 2, 0 ), &wsaData))
{
MyHandleError("Could not initialize winsock ");
}

//--------------------------------------------------------------------
// Connect to a server
//

if (!ConnectAuthSocket (
&Client_Socket,
&hCred,
&hCtxt))
{
MyHandleError("Authenticated server connection ");
}

cbRead = recv(Client_Socket, (char*)Data, BIG_BUFF, 0);


DecryptThis(
Data,
&cbRead,
&hCtxt,
0);

printf ("The message from the server is \n -> %.*s \n", cbRead, Data);

BYTE pBuff[BIG_BUFF];// = (PBYTE) malloc(BIG_BUFF * sizeof(BYTE));
ULONG nLen = 0;
char* aCmd = "EHLO IVEN\r\n";
PBYTE pCmd = (PBYTE) malloc((strlen(aCmd) + 1) * sizeof(BYTE));
strcpy((char*)pCmd, aCmd);

EncryptThis(pCmd, 11, &hCtxt, pBuff, &nLen);

SendBytes(Client_Socket, pBuff, nLen);



BYTE csData[2048];
int len = recv(Client_Socket, (char*)csData, 2048, 0);

DecryptThis(
csData,
(LPDWORD)&len,
&hCtxt,
0);

printf ("The message from the server is \n -> %.*s \n", cbRead, Data);

//--------------------------------------------------------------------
// Terminate socket and security package.
//

DeleteSecurityContext (&hCtxt);
FreeCredentialHandle (&hCred);
shutdown (Client_Socket, 2);
closesocket (Client_Socket);
if (SOCKET_ERROR == WSACleanup ())
{
MyHandleError("Problem with socket cleanup ");
}

int x; scanf("%d", &x);
exit (EXIT_SUCCESS);
} // end main.

//--------------------------------------------------------------------
// ConnectAuthSocket establishes an authenticated socket connection
// with a server and initializes needed security package resources.
//

BOOL ConnectAuthSocket (
SOCKET *s,
CredHandle *hCred,
struct _SecHandle *hcText)
{
unsigned long ulAddress;
struct hostent *pHost;
SOCKADDR_IN sin;

//--------------------------------------------------------------------
// Lookup the server's address.
//

ulAddress = inet_addr (ServerName);

if (INADDR_NONE == ulAddress)
{
pHost = gethostbyname (ServerName);
if (NULL == pHost)
{
MyHandleError("Unable to resolve host name ");
}
memcpy((char FAR *)&ulAddress, pHost->h_addr, pHost->h_length);
}

//--------------------------------------------------------------------
// Create the socket
//

*s = socket (
PF_INET,
SOCK_STREAM,
0);

if (INVALID_SOCKET == *s)
{
MyHandleError("Unable to create socket");
}
else
{
printf("Socket created.\n");
}

sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ulAddress;
sin.sin_port = htons (g_usPort);

//--------------------------------------------------------------------
// Connect to the server.
//

if (connect (*s, (LPSOCKADDR) &sin, sizeof (sin)))
{
closesocket (*s);
MyHandleError( "Connect failed ");
}

//--------------------------------------------------------------------
// Authenticate the connection.
//

if (!DoAuthentication (
*s,
hCred,
hcText))
{
closesocket (*s);
MyHandleError("Authentication ");
}

return(TRUE);
} // end ConnectAuthSocket

BOOL DoAuthentication (
SOCKET s,
CredHandle *hCred,
struct _SecHandle *hcText)

{
BOOL fDone = FALSE;
DWORD cbOut = 0;
DWORD cbIn = 0;
PBYTE pInBuf;
PBYTE pOutBuf;


if(!(pInBuf = (PBYTE) malloc(cbMaxMessage)))
{
MyHandleError("Memory allocation ");
}

if(!(pOutBuf = (PBYTE) malloc(cbMaxMessage)))
{
MyHandleError("Memory allocation ");
}

cbOut = cbMaxMessage;
if (!GenClientContext (
NULL,
0,
pOutBuf,
&cbOut,
&fDone,
TargetName,
hCred,
hcText
))
{
return(FALSE);
}

if (!SendMsg (s, pOutBuf, cbOut ))
{
MyHandleError("Send message failed ");
}

while (!fDone)
{
if (!ReceiveMsg (
s,
pInBuf,
cbMaxMessage,
&cbIn))
{
MyHandleError("Receive message failed ");
}

cbOut = cbMaxMessage;

if (!GenClientContext (
pInBuf,
cbIn,
pOutBuf,
&cbOut,
&fDone,
TargetName,
hCred,
hcText))
{
MyHandleError("GenClientContext failed");
}
if (!SendMsg (
s,
pOutBuf,
cbOut))
{
MyHandleError("Send message 2 failed ");
}
}

//MyHandleError(_T("End"));
free(pInBuf);
free(pOutBuf);
return(TRUE);
}

BOOL GenClientContext (
BYTE *pIn,
DWORD cbIn,
BYTE *pOut,
DWORD *pcbOut,
BOOL *pfDone,
CHAR *pszTarget,
CredHandle *hCred,
struct _SecHandle *hcText)
{
SECURITY_STATUS ss;
TimeStamp Lifetime;
SecBufferDesc OutBuffDesc;
SecBuffer OutSecBuff;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuffs[2];
ULONG ContextAttributes;
static TCHAR lpPackageName[1024];
PSCHANNEL_CRED pSChannelCred = new SCHANNEL_CRED;
memset(pSChannelCred, 0, sizeof(SCHANNEL_CRED));

pSChannelCred->dwVersion = SCHANNEL_CRED_VERSION;

pSChannelCred->grbitEnabledProtocols = SP_PROT_NONE;
pSChannelCred->dwFlags |= SCH_CRED_NO_DEFAULT_CREDS|SCH_CRED_MANUAL_CRED_VALIDATION;

if( NULL == pIn )
{
lstrcpy(lpPackageName, UNISP_NAME_A);
ss = AcquireCredentialsHandle (
NULL,
UNISP_NAME_A,
SECPKG_CRED_OUTBOUND,
NULL,
pSChannelCred,
NULL,
NULL,
hCred,
&Lifetime);

if (!(SEC_SUCCESS (ss)))
{
MyHandleError("AcquireCreds failed ");
}
}
delete pSChannelCred;

//--------------------------------------------------------------------
// Prepare the buffers
//

OutBuffDesc.ulVersion = 0;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;

OutSecBuff.cbBuffer = *pcbOut;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = NULL;

//-------------------------------------------------------------------
// The input buffer is created only if a message has been received from the server.
//

if (pIn)
{
InBuffDesc.ulVersion = 0;
InBuffDesc.cBuffers = 2;
InBuffDesc.pBuffers = InSecBuffs;

InSecBuffs[0].cbBuffer = cbIn;
InSecBuffs[0].BufferType = SECBUFFER_TOKEN;
InSecBuffs[0].pvBuffer = pIn;
InSecBuffs[1].cbBuffer = 0;
InSecBuffs[1].BufferType = SECBUFFER_EMPTY;
InSecBuffs[1].pvBuffer = NULL;

ss = InitializeSecurityContext (
hCred,
hcText,
NULL,
MessageAttribute,
0,
SECURITY_NATIVE_DREP,
&InBuffDesc,
0,
hcText,
&OutBuffDesc,
&ContextAttributes,
&Lifetime);
}
else
{
ss = InitializeSecurityContext (
hCred,
NULL,
"64.233.167.111",/*pszTarget,*/
MessageAttribute,
0,
SECURITY_NATIVE_DREP,
NULL,
0,
hcText,
&OutBuffDesc,
&ContextAttributes,
&Lifetime);
//pOut = static_cast<byte*>(OutBuffDesc.pBuffers->pvBuffer);
}

memcpy(pOut, OutBuffDesc.pBuffers->pvBuffer, OutBuffDesc.pBuffers->cbBuffer);
if (!SEC_SUCCESS (ss))
{
MyHandleError ("InitializeSecurityContext failed " );
}

//-------------------------------------------------------------------
// If necessary, complete the token.
//

if ((SEC_I_COMPLETE_NEEDED == ss)
|| (SEC_I_COMPLETE_AND_CONTINUE == ss))
{
ss = CompleteAuthToken (hcText, &OutBuffDesc);
if (!SEC_SUCCESS(ss))
{
fprintf (stderr, "complete failed: 0x%08x\n", ss);
return FALSE;
}
}

*pcbOut = OutSecBuff.cbBuffer;

*pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));

printf ("Token buffer generated (%lu bytes):\n", OutSecBuff.cbBuffer);
PrintHexDump (OutSecBuff.cbBuffer, (PBYTE)OutSecBuff.pvBuffer);
return TRUE;

}

BOOL EncryptThis (
PBYTE pMessage,
ULONG cbMessage,
struct _SecHandle *hCtxt,
BYTE * pOutput,
ULONG * pcbOutput)
{
SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[4];
ULONG SigBufferSize;
char* pInBuf;
SecPkgContext_StreamSizes szStream;

ss = QueryContextAttributes(hCtxt, SECPKG_ATTR_STREAM_SIZES, &szStream);
if (!SEC_SUCCESS(ss))
{
fprintf (stderr, "QueryContextAttributes faild: 0x%08x\n", ss);
return(FALSE);
}

SigBufferSize = szStream.cbMaximumMessage + szStream.cbHeader + szStream.cbTrailer;

pInBuf = (char*) malloc(SigBufferSize);
if (!pInBuf)
{
printf(_T("Memery over erro!\r\n"));
exit (EXIT_FAILURE);
}


memcpy(pInBuf + szStream.cbHeader, pMessage, cbMessage);

printf ("Data before encryption: %s\n", pMessage);
printf ("Length of data before encryption: %d \n",cbMessage);


//------------------------------------------------------------------
// Prepare buffers
//

BuffDesc.ulVersion = 0;
BuffDesc.cBuffers = 4;
BuffDesc.pBuffers = SecBuff;

SecBuff[0].cbBuffer = szStream.cbHeader;
SecBuff[0].BufferType = SECBUFFER_STREAM_HEADER;
SecBuff[0].pvBuffer = pInBuf;

SecBuff[1].cbBuffer = cbMessage;
SecBuff[1].BufferType = SECBUFFER_DATA;
SecBuff[1].pvBuffer = pInBuf + szStream.cbHeader;

SecBuff[2].cbBuffer = szStream.cbTrailer;
SecBuff[2].BufferType = SECBUFFER_STREAM_TRAILER;
SecBuff[2].pvBuffer = pInBuf + szStream.cbHeader + szStream.cbTrailer;

SecBuff[3].cbBuffer = 0;
SecBuff[3].BufferType = SECBUFFER_EMPTY;
SecBuff[3].pvBuffer = NULL;

ss = EncryptMessage(
hCtxt,
0,
&BuffDesc,
0);

if (!SEC_SUCCESS(ss))
{
fprintf (stderr, "EncryptMessage failed: 0x%08x\n", ss);
return(FALSE);
}
else
{
printf("The message has been encrypted. \n");
}


*pcbOutput = SecBuff[0].cbBuffer + SecBuff[1].cbBuffer + SecBuff[2].cbBuffer;

memcpy (pOutput, pInBuf, *pcbOutput);

free(pInBuf);

printf ("data after encryption including trailer (%lu bytes):\n", *pcbOutput);
PrintHexDump (*pcbOutput, pOutput);

return TRUE;

} // end EncryptThis


BOOL DecryptThis(
PBYTE pBuffer,
LPDWORD pcbMessage,
struct _SecHandle *hCtxt,
ULONG cbSecurityTrailer)
{
BOOL bRet = FALSE;
SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[4];
ULONG ulQop = 0;
PBYTE pSigBuffer;
void* pDataBuffer;
DWORD SigBufferSize;

//-------------------------------------------------------------------
// By agreement, the server encrypted the message and set the size
// of the trailer block to be just what it needed. DecryptMessage
// needs the size of the trailer block.
// The size of the trailer is in the first dword of the
// message received.
//

SigBufferSize = *pcbMessage;
printf ("data before decryption including trailer (%lu bytes):\n",
*pcbMessage);
PrintHexDump (*pcbMessage, (PBYTE) pBuffer);

//--------------------------------------------------------------------
// By agreement, the server placed the trailer at the beginning
// of the message sent right after the trailer size dword.

pSigBuffer = pBuffer;

//--------------------------------------------------------------------
// The data comes after the trailer.
//

//pDataBuffer = pSigBuffer;

//--------------------------------------------------------------------
// *pcbMessage is reset to the size of just the encrypted bytes.
//

//*pcbMessage = *pcbMessage - SigBufferSize - sizeof(DWORD);

//--------------------------------------------------------------------
// Prepare the buffers to be passed to the DecyrptMessage function.
//

BuffDesc.ulVersion = 0;
BuffDesc.cBuffers = 4;
BuffDesc.pBuffers = SecBuff;

SecBuff[0].cbBuffer = SigBufferSize;
SecBuff[0].BufferType = SECBUFFER_DATA;
SecBuff[0].pvBuffer = pSigBuffer;

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

SecBuff[2].cbBuffer = 0;
SecBuff[2].BufferType = SECBUFFER_EMPTY;
SecBuff[2].pvBuffer = NULL;

SecBuff[3].cbBuffer = 0;
SecBuff[3].BufferType = SECBUFFER_EMPTY;
SecBuff[3].pvBuffer = NULL;

ss = DecryptMessage(
hCtxt,
&BuffDesc,
0,
NULL/*&ulQop*/);

if (!SEC_SUCCESS(ss))
{
fprintf(stderr, "DecryptMessage failed");
}

//-------------------------------------------------------------------
// Return a pointer to the decrypted data. The trailer data
// is discarded.

for(int i = 1; i < 4; i++)
{
if (SecBuff[i].BufferType == SECBUFFER_DATA)
{
pDataBuffer = SecBuff[i].pvBuffer;
*pcbMessage = bRet = SecBuff[i].cbBuffer;
break;
}
}

memcpy(pBuffer, pDataBuffer, bRet);

return bRet;

}

PBYTE VerifyThis(
PBYTE pBuffer,
LPDWORD pcbMessage,
struct _SecHandle *hCtxt,
ULONG cbMaxSignature)
{

SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[2];
ULONG ulQop = 0;
PBYTE pSigBuffer;
PBYTE pDataBuffer;

//-------------------------------------------------------------------
// The global cbMaxSignature is the size of the signature
// in the message received.

printf ("data before verifying (including signature):\n");
PrintHexDump (*pcbMessage, pBuffer);

//--------------------------------------------------------------------
// By agreement with the server,
// the signature is at the beginning of the message received
// and the data that was signed comes after the signature.
//

pSigBuffer = pBuffer;
pDataBuffer = pBuffer + cbMaxSignature;

//-------------------------------------------------------------------
// The size of the message is reset to the size of the data only.
//

*pcbMessage = *pcbMessage - (cbMaxSignature);

//--------------------------------------------------------------------
// Prepare the buffers to be passed to the signature verification
// function.
//

BuffDesc.ulVersion = 0;
BuffDesc.cBuffers = 2;
BuffDesc.pBuffers = SecBuff;

SecBuff[0].cbBuffer = cbMaxSignature;
SecBuff[0].BufferType = SECBUFFER_TOKEN;
SecBuff[0].pvBuffer = pSigBuffer;

SecBuff[1].cbBuffer = *pcbMessage;
SecBuff[1].BufferType = SECBUFFER_DATA;
SecBuff[1].pvBuffer = pDataBuffer;

ss = VerifySignature(
hCtxt,
&BuffDesc,
0,
&ulQop
);

if (!SEC_SUCCESS(ss))
{
fprintf(stderr, "VerifyMessage failed");
}
else
{
printf("Message was properly signed.\n");
}

return pDataBuffer;

}// end VerifyThis


void PrintHexDump(
DWORD length,
PBYTE buffer)
{
DWORD i,count,index;
CHAR rgbDigits[]="0123456789abcdef";
CHAR rgbLine[100];
char cbLine;

for(index = 0; length; length -= count, buffer += count, index += count)
{
count = (length > 16) ? 16:length;

sprintf(rgbLine, "%4.4x ",index);
cbLine = 6;

for(i=0;i<count;i++)
="" {
="" rgbline[cbline++]="rgbDigits[buffer[i]">> 4];
rgbLine[cbLine++] = rgbDigits[buffer[i] & 0x0f];
if(i == 7)
{
rgbLine[cbLine++] = ':';
}
else
{
rgbLine[cbLine++] = ' ';
}
}
for(; i < 16; i++)
{
rgbLine[cbLine++] = ' ';
rgbLine[cbLine++] = ' ';
rgbLine[cbLine++] = ' ';
}

rgbLine[cbLine++] = ' ';

for(i = 0; i < count; i++)
{
if(buffer[i] < 32 || buffer[i] > 126)
{
rgbLine[cbLine++] = '.';
}
else
{
rgbLine[cbLine++] = buffer[i];
}
}

rgbLine[cbLine++] = 0;
printf("%s\n", rgbLine);
}
}

BOOL SendMsg (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf)
{
if (0 == cbBuf)
return(TRUE);

//----------------------------------------------------------
// Send the size of the message.
//
//if (!SendBytes (s, (PBYTE)&cbBuf, sizeof (cbBuf)))
// return(FALSE);

//----------------------------------------------------------
// Send the body of the message.
//
if (!SendBytes (
s,
pBuf,
cbBuf))
{
return(FALSE);
}

return(TRUE);
}

BOOL ReceiveMsg (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf,
DWORD *pcbRead)

{
DWORD cbRead;
DWORD cbData;


if (!ReceiveBytes (
s,
pBuf,
cbBuf,
&cbRead))
{
return(FALSE);
}

//if (cbRead != cbData)
// return(FALSE);

*pcbRead = cbRead;
return(TRUE);
} // end ReceiveMessage

BOOL SendBytes (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf)
{
PBYTE pTemp = pBuf;
int cbSent;
int cbRemaining = cbBuf;

if (0 == cbBuf)
return(TRUE);

while (cbRemaining)
{
cbSent = send (
s,
(const char *)pTemp,
cbRemaining,
0);
if (SOCKET_ERROR == cbSent)
{
fprintf (stderr, "send failed: %u\n", GetLastError ());
return FALSE;
}

pTemp += cbSent;
cbRemaining -= cbSent;
}

return TRUE;
}

BOOL ReceiveBytes (
SOCKET s,
PBYTE pBuf,
DWORD cbBuf,
DWORD *pcbRead)
{
PBYTE pTemp = pBuf;
int cbRead, cbRemaining = cbBuf;

//while (cbRemaining)
{
cbRead = recv (
s,
(char *)pTemp,
cbRemaining,
0);
if (SOCKET_ERROR == cbRead)
{
fprintf (stderr, "recv failed: %u\n", GetLastError ());
return FALSE;
}

cbRemaining -= cbRead;
pTemp += cbRead;
}

*pcbRead = cbBuf - cbRemaining;

return TRUE;
} // end ReceiveBytes


void MyHandleError(char *s)
{

fprintf(stderr,"%s error. Exiting.\n",s);
int x; scanf("%d", &x);
exit (EXIT_FAILURE);
}
#ifdef __cplusplus
}
#endif
GeneralDecryption Failed.Context Expired Pin
Pakeeru13-Feb-06 12:38
Pakeeru13-Feb-06 12:38 
GeneralSEC_E_ALGORITHM_MISMATCH 80090331 Pin
Larry Herbinaux18-Aug-05 14:10
Larry Herbinaux18-Aug-05 14:10 
GeneralI fixed this problem, but ran into another Pin
Larry Herbinaux23-Aug-05 11:57
Larry Herbinaux23-Aug-05 11:57 
GeneralWorking with expired Certificates. Pin
Senthil Sivasubramanian23-Jun-05 21:26
Senthil Sivasubramanian23-Jun-05 21:26 
GeneralRe: Working with expired Certificates. Pin
Leon Finker24-Jun-05 1:50
Leon Finker24-Jun-05 1:50 
GeneralAuthinticate server for SSL certificates to browser Pin
10-Jun-05 3:35
suss10-Jun-05 3:35 
Questionhow to process SSL server requests on browser Pin
Anonymous7-Jun-05 21:03
Anonymous7-Jun-05 21:03 
GeneralTLS \ Cert problem Pin
DanM319-Apr-05 5:44
DanM319-Apr-05 5:44 
GeneralRe: TLS \ Cert problem Pin
Leon Finker19-Apr-05 14:52
Leon Finker19-Apr-05 14:52 
GeneralRe: TLS \ Cert problem Pin
DanM320-Apr-05 2:19
DanM320-Apr-05 2:19 
GeneralMigrating SSL to .NET and use PInvoke Pin
Gery Dorazio8-Nov-04 17:36
Gery Dorazio8-Nov-04 17:36 
GeneralRe: Migrating SSL to .NET and use PInvoke Pin
Leon Finker9-Nov-04 14:27
Leon Finker9-Nov-04 14:27 
GeneralThe Client is not on the same machine with Server Pin
VANNAM4-Nov-04 21:26
VANNAM4-Nov-04 21:26 
GeneralRe: The Client is not on the same machine with Server Pin
Leon Finker5-Nov-04 1:39
Leon Finker5-Nov-04 1:39 
GeneralProblem with the Client app using SSPI Pin
SenthilSS17-Sep-04 1:25
SenthilSS17-Sep-04 1:25 
GeneralRe: Problem with the Client app using SSPI Pin
Leon Finker19-Sep-04 6:07
Leon Finker19-Sep-04 6:07 
GeneralRe: Problem with the Client app using SSPI Pin
SenthilSS20-Sep-04 0:11
SenthilSS20-Sep-04 0:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.