November 21, 2014
Hot Topics:

Mastering Windows Mobile Crypto API: Using Signatures and Certificates

  • November 29, 2005
  • By Alex Gusev
  • Send Email »
  • More Articles »

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.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel