Click here to Skip to main content
Click here to Skip to main content

A C++ SHA1 and MD5 Implementation with CryptoAPI

, 20 Sep 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
This article proposes a C++ implementation for computing hashes (SHA1, MD5, MD4 and MD2) on Windows with the Microsoft CryptoAPI library.

Introduction

Cryptographic hash functions are widely used for securing communication, storing hash values of passwords in databases, verifying that a message or a file has been transmitted correctly between two entities and others. One of the most widely used algorithms is MD5 (Message Digest Five), which produces a 128-bit hash value, usually expressed as a 32 hex digits number. It is the successor of MD4 and MD2, all of them being developed by Ronald Rivest. Another hash algorithm, also based on MD4, is SHA-1, a cryptographically secure one-way hash algorithm that produces a 160-bit message digest (usually represented as a 40 hex digit number). This article proposes a C++ implementation for computing hashes (SHA1, MD5, MD4 and MD2) with the Microsoft CryptoAPI library. CryptoAPI requires minimum Windowx XP or Windows Server 2003.

Note: Both MD5 and SHA1 have been proved to have weaknesses. MD5 it is not collision resistant and has additional weaknesses, which makes it unsuitable for at least some security applications including SSL certificates. US-CERT has concluded that MD5 "should be considered cryptographically broken and unsuitable for further use". The US National Institute of Standards and Technology, the publisher of SHA1 is also currently looking for a replacement of SHA1.

A Few CryptoAPI Considerations

In order to generate a hash value with CyrptoAPI one must follow these steps:

  • Initialize a context for cryptography operations
    • CryptAcquireContext: acquires a handle to a key container within a particular cryptographic service provider; this handle is used with the subsequent calls to cryptography APIs.
  • Create a hash object
    • CryptCreateHash: creates a hash object for hashing a stream of data; returns a handle that is used in subsequent calls to hash data or session keys.
  • Hash data
    • CryptHashData: adds data to a previously created hash object. It can be called multiple times to add more data to the hash object.
  • Retrieve the hash value
    • CryptGetHashValue: retrieves data from the hash object. To retrieve the has value, one must first call this method with HP_HASHSIZE to determine the size of the hash value, allocate the memory for the hash value and then call it with HP_HASHVAL to get the hash value.
  • Clean up by destroying the hash object and releasing the cryptographic context
    • CryptDestroyHash: destroys a hash object previously created with CryptCreateHash.
    • CryptReleaseContext: releases the handle of a cryptographic service provider and a key container, previously acquired with a call to CryptAcquireContext.

Message Digest Implementation

My implementation consists of a couple of template C++ classes that provide: cryptohash_t is the class that provides hashing for streams of data, and cryptohash_helper_t is a helper class that provides simplified API for hashing tests and files. Both these classes are parameterized with the hash algorithm.

   template <ALG_ID algorithm>

   class cryptohash_t;
   
   template <ALG_ID algorithm>
   class cryptohash_helper_t;

Several typedefs are provided for a simplified use of these template classes.

   typedef cryptohash_t<CALG_MD2> md2_t;
   typedef cryptohash_t<CALG_MD4> md4_t;
   typedef cryptohash_t<CALG_MD5> md5_t;
   typedef cryptohash_t<CALG_SHA1> sha1_t;

   typedef cryptohash_helper_t<CALG_MD2> md2_helper_t;
   typedef cryptohash_helper_t<CALG_MD4> md4_helper_t;
   typedef cryptohash_helper_t<CALG_MD5> md5_helper_t;
   typedef cryptohash_helper_t<CALG_SHA1> sha1_helper_t;

cryptohash_t has the following public interface:

  • bool begin()
    Initializes the hashing routine, creating a hash object used for computing the hashing value of a stream of data. If the function fails check the last error.
  • bool update(unsigned char* const buffer, size_t size)
    Adds more data to the current hash. If the function fails check the last error.
  • bool finalize()
    Computes the hash value from the current hash object and destroys the hash object. If the function fails check the last error.
  • hash_t digest() const
    Retrieves the computed hash value in a binary form.
  • std::string hexdigest(bool uppercase = false) const
    Retrieves the computed hash value as a string of hex digits.
  • errorinfo_t lasterror() const
    Returns the last error.

cryptohash_helper_t has the following public interface:

  • hash_t digesttext(std::string const& text)
    Computes and returns the hash value (in binary format) of a text.
  • std::string hexdigesttext(std::string const& text, bool uppercase = false)
    Computes and returns the hash value (as a string of hex digits) of a text.
  • hash_t digestfile(std::string const& filename)
    Computes and returns the hash value (in binary format) of a file.
  • std::string hexdigestfile(std::string const& filename, bool uppercase = false)
    Computes and returns the hash value (as a string of hex digits) of a file.
  • errorinfo_t lasterror() const
    Returns the last error for any of the above functions.

Examples

Note: In all the following examples you can simply use any of the provided hash types (md5_t, md4_t, md2_t, sha1_t) or the helper types (md5_helper_t, md4_helper_t, md2_helper_t, sha1_helper_t), without any other change to the sample and get the appropriate results.

Compute SHA-1 for a String

std::string text = "mariusbancila";
std::string digest;

sha1_t hasher;
if(hasher.begin())
{
  if(hasher.update((unsigned char*)(text.c_str()), text.length()))
  {
    if(hasher.finalize())
    {
      digest = hasher.hexdigest();
    }
  }
}

if(digest.empty())
  std::cout << "error code = " << hasher.lasterror().errorCode 
            << ", error message = " << hasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

Compute SHA-1 for several strings

std::string text1 = "marius";
std::string text2 = "bancila";
std::string digest;

sha1_t hasher;
if(hasher.begin())
{
  if(hasher.update((unsigned char*)(text1.c_str()), text1.length()))
  {
    if(hasher.update((unsigned char*)(text2.c_str()), text2.length()))
    {
      if(hasher.finalize())
      {
        digest = hasher.hexdigest();
      }
    }
  }
}

if(digest.empty())
  std::cout << "error code = " << hasher.lasterror().errorCode 
            << ", error message = " << hasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

Compute MD5 for a string with a helper class

md5_helper_t hhasher;
std::string digest = hhasher.hexdigesttext("mariusbancila");

if(digest.empty())
  std::cout << "error code = " << hhasher.lasterror().errorCode 
            << ", error message = " << hhasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

Compute MD5 for a file with a helper class

md5_helper_t hhasher;
std::string digest = hhasher.hexdigestfile("c:\\temp\\sample.png");

if(digest.empty())
  std::cout << "error code = " << hhasher.lasterror().errorCode 
            << ", error message = " << hhasher.lasterror().errorMessage
            << std::endl;
else
  std::cout << digest << std::endl;

Demo Application

Attached to the project you can find the sources and binaries of a demo application (written in Visual Studio 2008 SP1 with MFC) that uses these hashing template classes to generate hash values (SHA1, MD5, MD4 and MD2) for a text or a selected file. Using the application is straightforward and you can see a screen shot below.

Additional Readings

License

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

Share

About the Author

Marius Bancila
Software Developer (Senior) Visma Software
Romania Romania
Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for Visma, a Norwegian-based company. He is mainly focused on building desktop applications with VC++ and VC#. He keeps a blog at http://www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++ programmers.
Follow on   Twitter

Comments and Discussions

 
QuestionDefinition of update method -- error PinmemberFelix Dombek18-Mar-14 10:26 
Questionhi can u please help me to convert string in Unicode bytes? PinmemberLe@rner21-Jan-14 21:20 
AnswerRe: hi can u please help me to convert string in Unicode bytes? PinmemberMarius Bancila21-Jan-14 21:42 
GeneralRe: hi can u please help me to convert string in Unicode bytes? PinmemberLe@rner21-Jan-14 23:54 
QuestionWIll it work on Linux? PinmemberMizan Rahman8-Jan-14 1:35 
AnswerRe: WIll it work on Linux? PinmemberMarius Bancila8-Jan-14 1:47 
Questionhow to generate binary hash value for given string? PinmemberLe@rner2-Jan-14 1:52 
AnswerRe: how to generate binary hash value for given string? PinmemberMarius Bancila2-Jan-14 12:32 
GeneralRe: how to generate binary hash value for given string? [modified] PinmemberLe@rner2-Jan-14 19:08 
GeneralRe: how to generate binary hash value for given string? PinmemberMarius Bancila4-Jan-14 12:08 
GeneralRe: how to generate binary hash value for given string? PinmemberLe@rner19-Jan-14 23:36 
QuestionNice article PinmemberWuRunZhe11-Dec-13 20:36 
GeneralMy vote of 5 PinmemberVolynsky Alex30-Aug-13 6:33 
QuestionVery nice PinmemberFernando A. Gomez F.20-Sep-12 11:08 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141216.1 | Last Updated 20 Sep 2012
Article Copyright 2012 by Marius Bancila
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid