free web page counters

Windows Mobile Pocket PC Smartphone Programming

==>Click here for the SiteMap<==. Original contents with decent amount of source codes.

Saturday, March 25, 2006

Windows Mobile Secure Socket Implementation Series 2: Sample Source Code

====>SiteMap of this Blog<===

Windows Mobile Secure Socket Implementation Series 2: Sample Source Code

In previous post, I talked about general ideas to write secure socket applications in Windows Mobile device (or actually on Windows CE platforms). The current post has the source code and simple explanations on the code.



Function: Establish a secure socket connection to a remote host:port

Notice the remote host name is passed as certificate validation function's pvArg, which will be given back to us by Winsock when the validation function is called.


int SecureConnect(int in_socket,
   const struct sockaddr * in_pHostInetAddr,
   int in_port )
{
   struct sockaddr_in theInetAddr;
   int sockerror = 0;

   /* initialize the address structures */
   memset (&theInetAddr, 0, sizeof(theInetAddr));

   theInetAddr.sin_family = AF_INET;
   theInetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   theInetAddr.sin_port = htons(in_port);

   // make it secure
   {
      DWORD dwOptVal = SO_SEC_SSL;
      DWORD dwBytes = 0;
      SSLVALIDATECERTHOOK sslValidateFunc;

      sockerror = setsockopt(in_socket, SOL_SOCKET,
         SO_SECURE, (LPSTR)&dwOptVal, sizeof(dwOptVal));

      if (SOCKET_ERROR == sockerror){
         // error logging
         return 0;
      }

      // register certificate validation callback
      sslValidateFunc.HookFunc = certificateValidationCallback;
      sslValidateFunc.pvArg = "www.sample.com"; // ... passing server name from your context

      sockerror = WSAIoctl(in_socket, SO_SSL_SET_VALIDATE_CERT_HOOK,
         &sslValidateFunc, sizeof(sslValidateFunc), NULL, 0, &dwBytes, NULL, NULL);

      if (SOCKET_ERROR == sockerror){
         // error logging
         return 0;
      }
   }


   // connect
   sockerror = connect(in_socket,
      (struct sockaddr *)in_pHostInetAddr, sizeof (*in_pHostInetAddr));
   if (sockerror == SOCKET_ERROR) {
      // error logging
      return 0;
   }

   {
      // show SSLCONNECTIONINFO
      SSLCONNECTIONINFO SSLConnectionInfo;
      DWORD dwBytes = 0;
      sockerror = WSAIoctl(in_socket, SO_SSL_GET_CONNECTION_INFO,
         NULL, 0, &SSLConnectionInfo, sizeof(SSLConnectionInfo), &dwBytes, NULL,NULL);
      if (sockerror == SOCKET_ERROR) {
         // error logging
         return 0;
      }
      ShowConnectionInfo(&SSLConnectionInfo);
   }

   return 1;
}


Function: Load the functions from schannel.dll, to crack a BLOB into X.509 certificate

The two functions pointers are defined as global variables so that they do not need to be dynamically loaded or unloaded during each SSL handshake phase.



#include <wincrypt.h>
#include <schnlsp.h>

// load SslCrackCertificate and SslFreeCertificate
#define SSL_CRACK_CERTIFICATE_NAME TEXT("SslCrackCertificate")
#define SSL_FREE_CERTIFICATE_NAME TEXT("SslFreeCertificate")

HRESULT LoadSSL()
{
   // already loaded?
   if (gSslCrackCertificate && gSslFreeCertificate) return S_OK;

   hSchannelDLL = LoadLibrary(TEXT("schannel.dll"));
   if (!hSchannelDLL) {
      // error logging
      return E_FAIL;
   }

   gSslCrackCertificate = (SSL_CRACK_CERTIFICATE_FN)GetProcAddress(hSchannelDLL, SSL_CRACK_CERTIFICATE_NAME);
   gSslFreeCertificate = (SSL_FREE_CERTIFICATE_FN)GetProcAddress(hSchannelDLL, SSL_FREE_CERTIFICATE_NAME);

   if (!gSslCrackCertificate || !gSslFreeCertificate) {
      // error logging
      gSslCrackCertificate = NULL;
      gSslFreeCertificate = NULL;
      FreeLibrary(hSchannelDLL);
      hSchannelDLL = NULL;
      return E_FAIL;
   } else {
      return S_OK;
   }
}

HRESULT FreeSSL()
{
   if (hSchannelDLL) {
      FreeLibrary(hSchannelDLL);
      hSchannelDLL = NULL;
   }
   return S_OK;
}


Function: Show SSL connection information for debugging or learning purpose

Do not put it into your release build.



void ShowConnectionInfo(SSLCONNECTIONINFO *pConnectionInfo)
{
   TCHAR szTemp[1028];
   memset(szTemp, 0, 1028*sizeof(TCHAR));

   switch(pConnectionInfo->dwProtocol)
   {
   case SSL_PROTOCOL_SSL3:
   swprintf(szTemp+wcslen(szTemp), TEXT("Protocol: SSL3"));
   break;

   case SSL_PROTOCOL_PCT1:
   swprintf(szTemp+wcslen(szTemp), TEXT("Protocol: PCT"));
   break;

   case SSL_PROTOCOL_SSL2:
   swprintf(szTemp+wcslen(szTemp), TEXT("Protocol: SSL2"));
   break;

   default:
   swprintf(szTemp+wcslen(szTemp), TEXT("Protocol: 0x%08x"), pConnectionInfo->dwProtocol);
   }
   wcscat(szTemp, TEXT("\n"));

   switch(pConnectionInfo->aiCipher)
   {
   case CALG_RC4:
   swprintf(szTemp+wcslen(szTemp), TEXT("Cipher: RC4"));
   break;

   case CALG_3DES:
   swprintf(szTemp+wcslen(szTemp), TEXT("Cipher: Triple DES"));
   break;

   case CALG_RC2:
   swprintf(szTemp+wcslen(szTemp), TEXT("Cipher: RC2"));
   break;

   case CALG_DES:
   swprintf(szTemp+wcslen(szTemp), TEXT("Cipher: DES"));
   break;

   case CALG_SKIPJACK:
   swprintf(szTemp+wcslen(szTemp), TEXT("Cipher: Skipjack"));
   break;

   default:
   swprintf(szTemp+wcslen(szTemp), szTemp, TEXT("Cipher: 0x%08x"), pConnectionInfo->aiCipher);
   }
   wcscat(szTemp, TEXT("\n"));

   swprintf(szTemp+wcslen(szTemp), TEXT("Cipher strength: %d"), pConnectionInfo->dwCipherStrength);
   wcscat(szTemp, TEXT("\n"));

   switch(pConnectionInfo->aiHash)
   {
   case CALG_MD5:
   swprintf(szTemp+wcslen(szTemp), TEXT("Hash: MD5"));
   break;

   case CALG_SHA:
   swprintf(szTemp+wcslen(szTemp), TEXT("Hash: SHA"));
   break;

   default:
   swprintf(szTemp+wcslen(szTemp), szTemp, TEXT("Hash: 0x%08x"), pConnectionInfo->aiHash);
   }
   wcscat(szTemp, TEXT("\n"));

   swprintf(szTemp+wcslen(szTemp), TEXT("Hash strength: %d"), pConnectionInfo->dwHashStrength);
   wcscat(szTemp, TEXT("\n"));

   switch(pConnectionInfo->aiExch)
   {
   case CALG_RSA_KEYX:
   case CALG_RSA_SIGN:
   swprintf(szTemp+wcslen(szTemp), TEXT("Key exchange: RSA"));
   break;

   case CALG_KEA_KEYX:
   swprintf(szTemp+wcslen(szTemp), TEXT("Key exchange: KEA"));
   break;

   default:
   swprintf(szTemp+wcslen(szTemp), TEXT("Key exchange: 0x%08x"), pConnectionInfo->aiExch);
   }
   wcscat(szTemp, TEXT("\n"));

   swprintf(szTemp+wcslen(szTemp), TEXT("Key exchange strength: %d"), pConnectionInfo->dwExchStrength);
   MessageBox(NULL, szTemp, NULL, NULL);
}


Function: Certificate validation callback

Notice the order how it checks the certificate and other parameters. Sometimes Winsock passes false alarms in dwFlags. The way how Winsock internally handles the certificate in Windows Mobile 5.0 devices (Windows CE 5.0 based) is also changed from Windows Mobile 2003 devices (CE 4.2 based). This will be discussed in my later post.



// the certificate validattion for SSL
int certificateValidationCallback(
   DWORD dwType,
   LPVOID pvArg,
   DWORD dwChainLen,
   LPBLOB pCertChain,
   DWORD dwFlags)
{
   X509Certificate* pCert = NULL;
   int nRet = SSL_ERR_CERT_UNKNOWN;

   // dwType must be SSL_CERT_X.509
   if (dwType != SSL_CERT_X509) {
      // error logging
      return nRet;
   }

   if (dwFlags & SSL_CERT_FLAG_ISSUER_UNKNOWN) {
      // error logging
      return nRet;
   }

   if (pCertChain == NULL) return nRet;
   ASSERT(dwChainLen == 1);

   if (!gSslCrackCertificate || !gSslFreeCertificate) {
      // error logging
      return nRet; // unable to crack
   }

   // crack X.509 Certificate
   if (!gSslCrackCertificate(pCertChain->pBlobData, pCertChain->cbSize, TRUE, &pCert)) {
      // error logging
      return SSL_ERR_BAD_DATA;
   }

   // Site check
   {
      char* pchSubject = NULL;
      char* pchCN = NULL;
      BOOL bMatched = FALSE;
      char* pchRemoteHost = (char*)pvArg;

      pchSubject = pCert->pszSubject;

      // here you need to parse the subjec to retrieve the CN name
      pchCN = ParseCN(pchSubject);
      if (!pchCN) {
         goto FuncExit;
      }

      // CN comparison
      bMatched = !(_stricmp(pchRemoteHost, pchCN));
      if (!bMatched) {
         // error logging
         goto FuncExit;
      }
   }

   // validFrom, validUntil check
   {
      SYSTEMTIME stNow;
      FILETIME ftNow;
      FILETIME ftValidFrom = pCert->ValidFrom;
      FILETIME ftValidUntil = pCert->ValidUntil;

      GetSystemTime(&stNow);
      SystemTimeToFileTime(&stNow, &ftNow);

      if (!(IsEarlierThan(&ftValidFrom, &ftNow) && IsEarlierThan(&ftNow, &ftValidUntil))) {
         // give user an option to continue or not
         // a little more lenient than Subject check
      }
   }

   nRet = SSL_ERR_OKAY;

FuncExit:
   gSslFreeCertificate(pCert);

   return nRet;
}


Category: [SSL / Socket / Networking / Connection Manager]

====>SiteMap of this Blog<===




[ [permalink] ]

8 Comments:

At April 27, 2006 7:27 PM, Anonymous Anonymous said...

Have you tried secure client authentication? Just like web browsers do, present a digital cert during a SSL handshake?

thanks.

Inting

 
At April 28, 2006 12:21 AM, Blogger Lao K said...

I do not know the answer because I've never encountered such requirement. However, this sounds like a cool project, so I did a few researches for you:
- Looks like Pocket IE supports Client Authentication (http://www.jacco2.dds.nl/networking/p12imprt.html#web_client)
- That Pocket IE works does not necessarily mean Winsock in Windows CE supports Client Authentication. The SSL handshake phase is completely taken care by Winsock, so I am not sure whether Winsock implementation supports Client Authentication. As a test, write a simple program using my source code, and connect it to a Web Server that requires Client Authentication. (I might do it if I get time).
- Worst case: Write it yourself. Refer to the Platform SDK sample. It is not trivial task, as you have to implement "virtually" SSL handshake yourself.

 
At May 01, 2006 7:40 PM, Anonymous Anonymous said...

Hi!

I was just wondering last time if you did the client authentication. There's a part of the SSL handshake that the server sends the valid Root CAs which then be used to filter out personal certs prior to displaying it. But i find your code very helpful. Thanks =)

Actually, i did tried implementing SSL using the Platform SDK sample. But i can't get SCHANNEL to compile. Just adding schannel.h on your code will result to errors. =(

 
At May 18, 2006 5:46 AM, Blogger HP said...

Do you know where i can get Winsock for windows CE?

 
At August 08, 2007 9:41 AM, Anonymous Anonymous said...

Hi,

Thank you for this post. It has been very useful to help me get a prototype running on my smartphone. However, my application is almost all in C# and I need to port my unsecured connection to use SSL. I've ported your code sample to C#, but I am now recieving out of memory or access violation errors that bring down my entire app.

I suspect that the point of failure is when I attempt to specify the certification callback by passing in the Hook Func to the C# IOControl function. I suspect that the function pointer that the native code is recieving is sending it off into the weeds causing the access violation.

I realize that C# and P/Invoke may not be your area of focus, but any insight would be great and perhaps you can refer someone who can help pinpoint my issue.

Thanks,

Graeme

 
At January 02, 2008 10:27 PM, Anonymous Anonymous said...

Hi,
I need your help. I am writing a server SSL socket program on WM 5.0. Here are the steps i am following:

1. Create a TCP socket.

2. Set socket options as level = SOL_SOCKET, optname = SO_SECURE, optval = SO_SEC_SSL

3. Call WSAIoctl and set protocol as SSL_PROTOCOL_TLS1

4. Call WSAIoctl and register the certificate validation callback using SO_SSL_SET_VALIDATE_CERT_HOOK

5. Once this is done, I call listen on this socket

6. When the client tries to initate a TLS connection, accept is called. Accept returns a new socket FD. For this new socket, i am performing the actions in steps 2, 3, 4.

7. From the ethereal logs, i can see that the inital TCP connection has happened and TCP ack is sent.

8. After this, client sends Client Hello. For this, the lower layer at server is responding with a TCP ack successfully.

9. But after this step, everything stops. The lower layer at server end is not sending Server hello. Hence, after sometime, the connection is getting reset.

Can u pls tell me what i am doing wrong? Is there something else that needs to be done for supporting a server SSL socket? What are the reasons for lower layer to not send a Server hello?


The same code is working fine when i behave as Client and initate a connection with TLS server.

Pls help me!

 
At January 04, 2008 5:45 AM, Anonymous Anonymous said...

Hi,

I need to authenticate to a WIFI AP using WPA-TKIP with authentication (EAP-TLS). I use for this a certificate.
With a computer and with WM2005/2006 PPC I can connect without any problem, but with WM2005/2006 Smartphones I can't.
So I'm looking for a solution to connect to this AP.
I think the problem is because the Smartphone wants to validate the server certificate, on my computer I need to uncheck this option to make connection work.
So I'm looking for a registry tweak or a "EAP-TLS" client for Smartphones (I found client for PPC but not for Smartphones).
If someone can help me ..
Regards

 
At December 19, 2008 2:11 PM, Anonymous Anonymous said...

Can Anyone point me learn (c#) how to implement TLS Socket connection in windows mobile 5 as client. From my mobile i need to put request and get resp.

 

Post a Comment

<< Home