Implementing a secure socket


A recent post about SSL and Windows sockets in Windows Embedded Compact Platform Development forum reminded me that when I needed to use SSL in a Windows CE application it was very difficult to find information and sample code. On MSDN you can find basically only a page which summarize what you have to do. In this post you can see working code for estabilish a secure connection validating a certificate provided by the server. The sample assumes the certificate is installed on the device by the user using control panel rather than by the application itself (see the enrollment sample in %_WINCEROOT%\PUBLIC\COMMON\SDK\SAMPLES\ENROLL). The sample assumes several things and can undoubtly be improved: for example it does not use SO_SSL_GET_PROTOCOLS control code to determine the default protocols. 

Basically you have two functions: one ‘initializes’ SSL,  the other is the certificate validation callback which determines if the connection will be completed. Once you init the SSL related stuff and you call the connect function the callback will be invoked. If the function returns SSL_ERR_OKAY you can use send/recv functions which will automatically encode and decode data. The sample callback will return SSL_ERR_OKAY according to the options you specify in the SSL_Init:

  • do not check the server certificate at all and always return SSL_ERR_OKAY
  • succeds if in the certificate store a certificate from the same issuer as the server one can be found
  • succeds if in the certificate store the exact certificate from the server can be found
 
#include <windows.h>
#include <winsock2.h>
#include <sslsock.h>
#include <schnlsp.h>

#define SSL_OPT_NO_CHECK  0
#define SSL_OPT_ISSUER_CHECK 1
#define SSL_OPT_STRICT_CHECK 2

#define SSL_OPT_DEFAULT   SSL_OPT_STRICT_CHECK

#define DBG_ON 0

static int g_SslOptions = SSL_OPT_DEFAULT;

int
CALLBACK
SSLValidateCertHook(
   DWORD  dwType,          // in
   LPVOID pvArg,           // in
   DWORD  dwChainLen,      // in
   LPBLOB pCertChain,      // in
   DWORD dwFlags)         // in
{
 HCERTSTORE hCertStore = NULL;
 PCCERT_CONTEXT pCertInStore = NULL, pCertPassed = NULL, pPrevCertContext = NULL;
 const LPCTSTR SystemStore[] = {_T("CA"), _T("ROOT"), _T("MY")};
 unsigned int i;

 RETAILMSG(DBG_ON,(_T("+SSLValidateCertHook")));

 //The only thing we can handle
 if(dwType != SSL_CERT_X509)
  return SSL_ERR_CERT_UNKNOWN;

 //Do not check the certificate at all
 if(g_SslOptions == SSL_OPT_NO_CHECK)
  return SSL_ERR_OKAY;

 //Get a context struct from the certificate blob
 pCertPassed = CertCreateCertificateContext(X509_ASN_ENCODING,pCertChain->pBlobData,pCertChain->cbSize);

 if(!pCertPassed)
  return SSL_ERR_FAILED;

 //Check for a matching certificate in the stores
 for (i=0; i<(sizeof(SystemStore)/sizeof(SystemStore[0])); i++)
 {
  //open the store
  hCertStore = CertOpenSystemStore((HCRYPTPROV)NULL,SystemStore[i]);

  if(!hCertStore)
   continue;

  RETAILMSG(DBG_ON,(_T("Opened %s"), SystemStore[i]));

  pPrevCertContext = NULL;

  do
  {
   //Find a certificate from the same issuer
   pCertInStore = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING,
    0, CERT_FIND_ISSUER_NAME,&pCertPassed->pCertInfo->Issuer,pPrevCertContext);

   if(pCertInStore)
   {
    RETAILMSG(DBG_ON,(_T("Find certificate with the correct issuer")));

    //It's enough if we have a certificate from te same issuer
    if(g_SslOptions == SSL_OPT_ISSUER_CHECK)
    {
     CertFreeCertificateContext(pCertInStore);
     CertCloseStore(hCertStore,0);
     CertFreeCertificateContext(pCertPassed);
     return SSL_ERR_OKAY;
    }
    else if(g_SslOptions == SSL_OPT_STRICT_CHECK)
    {
     //Compare (exact match) the certificate with the one we have found
     if(CertCompareCertificate(X509_ASN_ENCODING,pCertInStore->pCertInfo,pCertPassed->pCertInfo))
     {
      RETAILMSG(DBG_ON,(_T("Find a certificate *exact match*")));
      CertFreeCertificateContext(pCertInStore);
      CertCloseStore(hCertStore,0);
      CertFreeCertificateContext(pCertPassed);
      return SSL_ERR_OKAY;
     }
    }
   }

   pPrevCertContext = pCertInStore;
  }
  while(pCertInStore);

  CertCloseStore(hCertStore,0);
 }

 if(pCertInStore)
  CertFreeCertificateContext(pCertInStore);

 if(hCertStore)
  CertCloseStore(hCertStore,0);

 if(pCertPassed)
  CertFreeCertificateContext(pCertPassed);

 RETAILMSG(DBG_ON,(_T("-SSLValidateCertHook")));

 return SSL_ERR_NO_CERT;
}

int
SSLInit(
 SOCKET s,
 int SslOptions
)
{
 DWORD optval = SO_SEC_SSL;
    SSLVALIDATECERTHOOK hook;
    SSLPROTOCOLS protocolsToUse;
    int ret;

 RETAILMSG(DBG_ON,(_T("+SSLInit (opt:%i)"),SslOptions));

 g_SslOptions = SslOptions;//WSAIoctl cannot handle the pointer copy, use a global variable

 if( setsockopt(s, SOL_SOCKET, SO_SECURE, (LPSTR)&optval,sizeof(optval)) ==
  SOCKET_ERROR)
 {
  RETAILMSG(DBG_ON,(_T("setsockopt SO_SECURE failed")));
  goto SSLInit_Error;
 }

 hook.HookFunc = SSLValidateCertHook;
    hook.pvArg = NULL; //WSAIoctl cannot handle the pointer copy, use a global variable

 if(WSAIoctl(s, SO_SSL_SET_VALIDATE_CERT_HOOK,
        &hook, sizeof(SSLVALIDATECERTHOOK),
        NULL, 0, NULL, NULL, NULL) == SOCKET_ERROR)
 {
  RETAILMSG(DBG_ON,(_T("SO_SSL_SET_VALIDATE_CERT_HOOK failed")));
  goto SSLInit_Error;
 }

 //specify TLS1 protocol

 protocolsToUse.dwCount = 1;
 protocolsToUse.ProtocolList[0].dwFlags = 0;
 protocolsToUse.ProtocolList[0].dwProtocol = SSL_PROTOCOL_TLS1;
 protocolsToUse.ProtocolList[0].dwVersion = 0;

 if( WSAIoctl( s, SO_SSL_SET_PROTOCOLS,
  (LPVOID)&protocolsToUse, sizeof(protocolsToUse),
  NULL, 0, NULL, NULL, NULL ) == SOCKET_ERROR)
 {
  RETAILMSG(DBG_ON,(_T("SO_SSL_SET_PROTOCOLS failed")));
  goto SSLInit_Error; 
 }

 return 0;

SSLInit_Error:

 ret = WSAGetLastError();

 RETAILMSG(DBG_ON,(_T("-SSLInit error %i"),ret));

    return ret;
}
Advertisements
This entry was posted in Windows Embedded CE, Windows Embedded Compact and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s