Microsoft & .NETMastering Windows Mobile Crypto API: Using Signatures and Certificates

Mastering Windows Mobile Crypto API: Using Signatures and Certificates

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Signing Your Data

To cover one incomplete topic from the previous article, let me briefly overview how you can sign your data. Actually, we have almost all pieces handy. All you need to do is to hash incoming data and then call the CryptSignHash function. Such s technique is illustrated below:

BOOL CEncryptImpl::CreateSignature(LPCTSTR lpszDataFile,
                                   LPCTSTR lpszSignedDataFile)
{
   BOOL bResult = TRUE;
   HCRYPTHASH hHash = NULL;
   CFile fileData;
   CFile fileSignedData;
   CByteArray baData;
   DWORD dwLen;
   BYTE pBuffer[BLOCK_SIZE];
   CByteArray baSignedData;
   DWORD dwSigLen = 0;
   CByteArray baSignature;

   if ( !fileData.Open(lpszDataFile,CFile::modeRead) )
   {
      return FALSE;
   }

   if(!CryptCreateHash(m_hProv,CALG_MD5,0,0,&hHash))
   {
      DWORD dwErr = GetLastError();
      TRACE(_T("Error during CryptCreateHash: 0x%Xn"),dwErr);
      return FALSE;
   }

   while((dwLen = fileData.Read(pBuffer,BLOCK_SIZE)))
   {
      if(!CryptHashData(hHash,pBuffer,dwLen,0))
      {
         TRACE(_T("Error during CryptHashData: 0x%Xn"),
               GetLastError ());
         bResult = FALSE;
         goto CleanUp;
      }
      fileSignedData.Write(pBuffer,dwLen);
   }

   if(!CryptSignHash(hHash,AT_SIGNATURE,NULL,0,NULL,&dwSigLen))
   {
      TRACE(_T("Error during CryptSignHash: 0x%Xn"),
            GetLastError ());
      bResult = FALSE;
      goto CleanUp;
   }

   baSignature.SetSize(dwSigLen);

   if(!CryptSignHash(hHash,AT_SIGNATURE,NULL,0,
                     baSignature.GetData(),&dwSigLen))
   {
      TRACE(_T("Error during CryptSignHash: 0x%Xn"),
            GetLastError ());
      bResult = FALSE;
      goto CleanUp;
   }

   if ( !fileSignedData.Open(lpszSignedDataFile,
                             CFile::modeCreate|CFile::modeWrite) )
   {
      bResult = FALSE;
      goto CleanUp;
   }

   fileSignedData.Write(baSignature.GetData(),dwSigLen);
   fileData.SeekToBegin();
   baData.SetSize(fileData.GetLength());
   fileData.Read(baData.GetData(),baData.GetSize());
   fileSignedData.Write(baData.GetData(),baData.GetSize());

CleanUp:
   if ( hHash )
      CryptDestroyHash (hHash);

   return bResult;
}

You see, it is literarily as follows: Create a hash object, fill it in by data, and, finally, sign. Signature algorithms are slow enough, so that’s why Crypto API doesn’t provide a direct way for data signing, only after hashing. Yhrn, you can save such digital signature either together with signed data, or separately. For instance, if you take an example from the previous article, you might want to sign an encrypted message as well as to verify it at the receiver’s side.

A signature’s verification is a similar process. To make it more complicated, use external public to check the received signature. As in the previous sample, you first have to hash the data and finally call CryptVerifySignature. The next code snippet explains the process:

BOOL CEncryptImpl::VerifySignature(CFile& fileIn,
                                   CFile& fileSignedData)
{
   BOOL bResult = TRUE;
   HCRYPTKEY      hPublicKey = NULL;
   HCRYPTHASH     hHash = NULL;
   DWORD          dwKeyBlobLen = 0;
   CByteArray     baKeyBlob;
   DWORD          dwCount = 0;
   BOOL           bEOF = 0;
   DWORD dwSigLength = 0;
   CByteArray baSignature;
   DWORD dwLen;
   BYTE pBufData[BLOCK_SIZE];

   //////////////////////////////////////////////////////////////
   // 1. Obtain external public key
   //////////////////////////////////////////////////////////////
   if( fileIn.Read(&dwKeyBlobLen,sizeof(DWORD)) != sizeof(DWORD) )
   {
      TRACE(_T("Corrupted key file: 0x%Xn"),GetLastError ());
      bResult = FALSE;
      goto CleanUp;
   }

   baKeyBlob.SetSize(dwKeyBlobLen);
   if ( fileIn.Read(baKeyBlob.GetData(),
        dwKeyBlobLen) != dwKeyBlobLen )
   {
      TRACE(_T("Corrupted key file: 0x%Xn"),GetLastError ());
      bResult = FALSE;
      goto CleanUp;
   }

   if( !CryptImportKey(m_hProv,baKeyBlob.GetData(),dwKeyBlobLen,
       0,0,&hPublicKey) )
   {
      TRACE(_T("Error during CryptImportKey: 0x%Xn"),
            GetLastError ());
      bResult = FALSE;
      goto CleanUp;
   }

   //////////////////////////////////////////////////////////////
   // 2. Hash a message
   //////////////////////////////////////////////////////////////
   if(!CryptCreateHash(m_hProv,CALG_MD5,0,0,&hHash))
   {
      DWORD dwErr = GetLastError();
      TRACE(_T("Error during CryptCreateHash: 0x%Xn"),dwErr);
      bResult = FALSE;
      goto CleanUp;
   }

   if(fileSignedData.Read(&dwSigLength,sizeof(DWORD)) !=
      sizeof(DWORD))
   {
      DWORD dwErr = GetLastError ();
      TRACE(_T("Error during Read Data file: 0x%Xn"),dwErr);
      bResult = FALSE;
      goto CleanUp;
   }
   if(dwSigLength <= 0 )
   {
      TRACE(_T("Error during Read Data filen"));
      bResult = FALSE;
      goto CleanUp;
   }

   baSignature.SetSize(dwSigLength);
   fileSignedData.Read(baSignature.GetData(),dwSigLength);

   memset(pBufData,0,BLOCK_SIZE);
   while((dwLen = fileSignedData.Read(pBufData,BLOCK_SIZE)))
   {
      if(!CryptHashData(hHash,pBufData,dwLen,0))
      {
         DWORD dwErr = GetLastError();
         TRACE(_T("Error during CryptHashData: 0x%Xn"),dwErr);
         bResult = FALSE;
         goto CleanUp;
      }
      memset(pBufData,0,BLOCK_SIZE);
   }

   //////////////////////////////////////////////////////////////
   // 3. Verify signature
   //////////////////////////////////////////////////////////////
   if(!CryptVerifySignature(hHash,baSignature.GetData(),
                            dwSigLength,hPublicKey,NULL,0))
   {
      DWORD dwErr = GetLastError();
      TRACE(_T("Error during CryptVerifySignature: 0x%Xn"),dwErr);
      bResult = FALSE;
      goto CleanUp;
   }

CleanUp:
   if ( hHash )
      CryptDestroyHash (hHash);

   return bResult;
}

The above sample uses external public key, just like you did in the previous article, to verify received data. The only thing to keep in mind is that this public key should be taken for export as AT_SIGNATURE rather than AT_EXCHANGE.

Certificates

A certificate is another face of cryptology. It is a set of data that is intended to identify a person or an organization (known as Certificate Subject). Certificate associates a public key with a person. An organization that creates a certificate is called a certification authority (CA).

Certificates can be issued either by some trusted organization or by yourself. In this last case, it can cause some trouble because not all targets will trust to your certificate by default. But, it can be fixed by installing a certificate at every required location.

My goal is neither to explain what the certificates are nor to dive deeply into this subject. RFC, MSDN, and SDK documentation have enough articles on this topic. I only want to provide this basic tutorial so you will be introduced to this technique.

Note: The Windows Mobile implementation of Crypto API doesn’t support the same function set as at the desktop, so mobile applications can benefit from it only partially. Thus, you start from certificate creation.

Creating your own certificate

MS Visual Studio 2003 and later has a special utility called MakeCert that allows you to create a certificate. Below, you will find the content of a simple batch file that finally issues a certificate file DevCom.cer:

C:
cd "C:Program FilesMicrosoft Visual Studio 8SDKv2.0Bin"
makecert.exe -n "CN=Alex Gusev" -ss My -sr LocalMachine
             -sp "Microsoft Enhanced Cryptographic Provider v1.0"
             -sky exchange -r c:SamplesDevCom.cer

As a result, you will get a DevCom.cer file; copy it to your PDA and install it using regular File Explorer. Then, you will be able to see it among other installed certificates:

You can also find many certificate generation tools on the Web, for instance, like this: Crypto4 PKI. In any case, the certificate file will contain the issuer’s public key and some other data that helps identify the issuer and set an expiration period for the certificate.

Verifying a certicicate

After such a certificate is installed, you can use Crypto API to perform various operations on the certificates store, setting and getting different data properties, and so forth. The accompanying zip file has several modified samples available from the Microsoft site; they perform different manipulations upon certificate stores and certificates themselves. Below is just one example:

void CCertDlg::OnButtonVerify()
{
   HCERTSTORE      hSystemStore;
   PCCERT_CONTEXT  pTargetCert=NULL;
   PCERT_INFO      pTargetCertInfo;
   TCHAR           szSubjectName[] = L"Alex Gusev";

   if ( hSystemStore = CertOpenSystemStore(NULL,L"ROOT") )
   {
      TRACE(L"CertOpenStore succeeded. The ROOT store is open. n");
   }
   else
   {
      HandleError(L"Error opening the Root store.");
   }

   if(pTargetCert = CertFindCertificateInStore(
      hSystemStore,
      X509_ASN_ENCODING,
      0,
      CERT_FIND_SUBJECT_STR,
      szSubjectName,
      pTargetCert))
   {
      TRACE(L"Found the certificate. n");
   }
   else
   {
      DWORD dwErr = GetLastError();
      HandleError(L"Could not find the required certificate");
   }

   pTargetCertInfo = pTargetCert->pCertInfo;
   switch(CertVerifyTimeValidity(
      NULL,
      pTargetCertInfo))
   {
   case -1 :
      {
         TRACE(L"Certificate is not valid yet. n");
         break;
      }
   case 1:
      {
         TRACE(L"Certificate is expired. n");
         break;
      }
   case 0:
      {
         TRACE(L"Certificate's time is valid. n");
         break;
      }
   };

   CFile cert;
   if ( !cert.Open(L"DevCom.cer",CFile::modeRead) )
      return;

   CByteArray baCert;
   baCert.SetSize(cert.GetLength());
   cert.Read(baCert.GetData(),baCert.GetSize());
   cert.Close();

   PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(
      X509_ASN_ENCODING,
      baCert.GetData(),
      baCert.GetSize());

   BOOL bRes1 = CertCompareCertificate(
      X509_ASN_ENCODING,
      pCertContext->pCertInfo,
      pTargetCertInfo);


   if (pCertContext)
      CertFreeCertificateContext(pCertContext);

   if (pTargetCert)
      CertFreeCertificateContext(pTargetCert);
   if(hSystemStore)
   {
      if (!CertCloseStore(
         hSystemStore,
         CERT_CLOSE_STORE_CHECK_FLAG))
         HandleError(L"Could not close the certificate store");
   }
}

The sample above illustrates the main route of manipulating the certificate stores and data. All this all is built similar to a native CE database. You have to open the store, search for desired certificate, obtain its context, query or set properties, and so forth. You also can read a certificate from some carrier—for example, from a file or from transmitted message—create a new certificate context, and finally compare it against that one from local certificate store. Crypto API has a lot of functions that allow you get your hands on all this business.

Windows Mobile 5.0 Security

Security has become much stronger under Windows Mobile 5.0; since this version, there have been several security configurations for mobile devices. These security models allow the OS to differentiate applications by trusting and privilege terms. Besides, in Win Mobile 2003, any application might run freely, making it potentially risky. In WM 5.0, every application has to have a signature of its publisher. The OS asks you (at least once) ehryhrt you really want to launch this particular application that was not signed.

Such a prompt appears only once, but this can cause problems if your application launches other ones silently. In general, all this starts to behave like desktop versions; for example, when you’re going to install some control from the Internet and so forth. There is a link to a few fine articles in MSDN describing these WM 5.0 security features in more detail in the Useful Links section below.

Conclusion

These two articles have discussed Crypto API at its high level. Obviously, you have to play around with this API to get a basic feeling for what it is and what it is used for. The homework is on you, as always…

Download

Download the accompanying code’s zip file here.

Useful Links

About the Author

Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers’ lives in the mobile jungles a little bit simpler.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories