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

A Simple, Portable, Rinjdael (AES) Based Stream Cipher Class

, 5 Jul 2005
Rate this:
Please Sign up or sign in to vote.
A file encryption/decryption class with built in MD5 string and file hashing.

Introduction

This article represents a major code update from the initial release, based largely on readers suggestions.

I have removed the Win32 calls in CalcMD5FromFile() and replaced them with UNIX compatible fopen(), fread(), etc. I believe the code is now fully portable.

This update replaces the previous encryption scheme with a Rinjdael block cipher implementation - it wraps a class by Szymon Stefanek. The Rinjdael implementation I am using is a 128-bit CBC block cipher, with a 256-bit key. The DCipher class wraps this in a stream cipher class, which encrypts files in data chunks. You can set the chunk size by calling SetChunkSize(int numKB) before encrypting. If you don't set the size it will use a default 65 kb chunk size. There are a few items to note here:

  1. that the larger the chunk size the more secure the encryption -- ideally you would like to encrypt a file in a single data chunk to take full advantage of Rijndael's block chaining functionality,
  2. however, you must ensure adequate available memory, keeping in mind that input and output memory blocks must be allocated, so to use a 100K data chunk, you must have 200K + 16 bytes memory available. While the heap can use virtual memory, DCipher validates chunk size against available physical memory to prevent sensitive data from being written to disk.
  3. Finally, you must use the same chunk size for decryption that you used for encryption or data will be corrupted.

I've also changed the interface: the class methods have been modified to accept plain-text passwords. The plain-text password is then converted by the class to a 256-bit hex Rijndael session key.

Also, you no longer initialize the session in the constructor; everything is passed directly to the functions. To Encrypt/Decrypt you simply pass a full filepath, a password, and a 19-byte integer array. See the excerpt below:

////////////////////////////////////
// Implementation
////////////////////////////////////

int EncryptFile(const char *src, const char* pwd, 
                                      int head[19]);
int DecryptFile(const char *src, const char* pwd, 
                                      int head[19]);

char* GetSignature(const char *file);
void SetChunkSize(int numKB);

char* CalcMD5FromString(const char *s8_Input);
char* CalcMD5FromFile  (const char *s8_Path);

The integer array allows you to give multiple apps unique signatures so that if you are using app-generated keys, you don't have an app corrupting another apps file. It also allows you to give individual files unique signatures (e.g.. timestamp - see demo app code for example), if you want to use file-hashing to check for modification.

The GetSignature() method was added to assist in this regard. You could store a file signature and its corresponding plaintext file hash and its encrypted file hash in a database. You could then get its signature as a look-up key, or you could get its file hash to use as the look-up key. Either way you could verify if the file was modified after encryption (if so it won't decrypt correctly), you could then restore a backup or notify user of invalid data file. You could also compare plaintext file hashes after decryption, to ensure the file was decrypted properly.

Also, Rijndael requires block sizes to be in exact multiples of 16. Padding characters are added if needed to meet this requirement. The DCipher class records the padded value in the final two bytes of the signature (making a total tag of 25-bytes, as DCipher also uses an internal 4-byte signature to check file status). Then on decryption DCipher is able to read this value and strips these bytes in the decryption process, so the decrypted file exactly matches the original file.

Using the code

Using this code is simple. The demo download demonstrates using this class in an MFC app.

To use the code, copy DCipher.h, DCipher.cpp, rijndael.h, and rijndael.cpp to your project folder, then using Project/Add To Project/Files add these to your project. Then in the class file which will be calling these functions (usually a view class or dialog class), add #include "DCipher.h" at the top of the file with your other include statements.

I'm going to describe using this in a MFC app, since you NON-MFC people should already understand a const char*.

The EncryptFile() and DecryptFile() methods require that you pass the full filename (including path) along with a password and your signature array. Now normally you would declare your signature array globally or generate it inside a function, and retrieve your filepath from a CFileDialog or the CDocument class' GetPathname() or the AppClass' m_lpCmdLine variable if the file is being opened from a double-click or command line request, or from the HDROPINFO structure if it's a drag/drop operation. In any case, I will hard code them into this example for simplicity and readability. Your code, then, to encrypt a file would look like this:

int sig1[] =  {1,1,0,0,4,8,6,0,2,9,4,1,1,0,5,6,0,7,3};
CString szFilename = "c:\\somepath\\somefile.ext";    
CString szPass = "SomePassKey";

DCipher dc;
int nResult = dc.EncryptFile((LPCTSTR)szFilename, 
                             (LPCTSTR)szPass, sig1);
         
if(nResult < 1)
   // Encryption was successful
else
   // Encryption failed - see defines in DCipher.h 
   // for error codes

Decrypting the file would use the exact same code, except you would call DecryptFile() instead of EncryptFile().

CalcMD5FromString() returns a mixed-case hash from string input -- pretty self-explanatory.

CalcMD5FromFile() returns a 32-byte uppercase hash of a file. This doesn't just hash the string value of the filename -- it hashes the file contents and is used to check if a file has been modified. For that reason, you do not want to use this as an encryption key, because you would never be able to decrypt the file, unless the key was stored somewhere (which I think is a bad idea).

As an example, calling these methods in an MFC app would look like this:

//Get hash of file
DCipher dc;
CString szHashValue = 
    dc.CalcMD5FromFile((LPCTSTR)szFullFilePath);
                 
//Get hash from string
DCipher dc;
CString szHashValue = 
    dc.CalcMD5FromString((LPCTSTR)szTextString);

That's all there is to it. It's an extremely easy class to use. It will work on any kind of file, including images. I have tested this class on both Win2000 and XP (always compiled with VC6)... and I have tested with files as small as 1K to 300K. I have not tested with extremely large files (say 2GB)... but it is very fast on the files I have tested.

I don't know of any compiler issues. Should you find anything that needs to be corrected, improved, or should you make significant modifications to the code, please pass those on to me so that I can include them in future updates, I will gladly credit you for your contributions.

Disclaimers

I should state that I did not author the MD5 code. I found it in the comments section of a CodeProject article, posted by someone aliased Elmue. As he didn't claim authorship for the code, I'm assuming it may have originally come from another source. If anyone knows who the rightful author is, I would be happy to give credit here.

I only made minor modifications:

  1. originally the file hash and string hash methods used the same string conversion function which returned an uppercase hex string. I added a separate return function for the string hash method, which returns a mixed case hex string -- which makes for a stronger encryption key, and
  2. replaced the Win32 file reading functions with UNIX compatible functions for portability.

History

  • 2005.Jun.12

    Initial CodeProject release.

  • 2005.Jun.19

    Changed Interface, added user passwords, added Rijndael algorithm.

  • 2005.Jun.21

    Removed Win32 calls in MD5 - hopefully fully portable now.

  • 2005.July.5

    Added GetSignature(), SetChunkSize() and memory validation.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

Share

About the Author

Douglas R. Keesler

United States United States
I have no biography, but then, I don't have an obituary yet either -- Thank God!!

Comments and Discussions

 
Generalconvert c# Pinmemberbrodhol31-Aug-10 21:51 
QuestionNice job on Rinhdael , please help! PinmemberSuperEric24-Jun-09 2:51 
QuestionAdministrator rights Pinmemberpr0phetus17-Oct-08 4:58 
GeneralMinor typo in the article title PinmvpRajesh R Subramanian24-Jun-08 23:53 
QuestionPassword Validation PinmemberInAndOut15-Oct-06 8:25 
AnswerRe: Password Validation [modified] PinmemberDouglas R. Keesler15-Oct-06 13:06 
GeneralRe: Password Validation PinmemberInAndOut16-Oct-06 5:08 
AnswerRe: Password Validation PinmemberDouglas R. Keesler16-Oct-06 10:10 
GeneralRe: Password Validation PinmemberInAndOut16-Oct-06 17:16 
Generalpadding in encryption function Pinmemberdude_445316919-Jul-06 2:01 
GeneralRe: padding in encryption function PinmemberDouglas R. Keesler19-Jul-06 12:59 
Questionundefined reference Pinmemberjozin11-Apr-06 7:31 
AnswerRe: undefined reference PinmemberDouglas R. Keesler11-Apr-06 13:54 
GeneralRe: undefined reference Pinmemberjozin13-Apr-06 7:19 
GeneralRe: undefined reference PinmemberDouglas R. Keesler13-Apr-06 17:03 
GeneralRe: undefined reference Pinmemberjozin13-Apr-06 18:50 
AnswerRe: undefined reference PinmemberDouglas R. Keesler14-Apr-06 3:19 
Questionundefined reference to `DCipher::EncryptFileA(char const*,char const*,int*) Pinmemberjozin11-Apr-06 7:31 
GeneralReinventing the Wheel ha? :=) Pinmemberspinoza14-Mar-06 3:49 
QuestionUsing the code Pinmemberjamesw200326-Jan-06 9:39 
AnswerRe: Using the code PinmemberDouglas R. Keesler26-Jan-06 10:26 
GeneralSome improvement suggestions PinmemberDominik Reichl14-Jun-05 7:40 
GeneralRe: Some improvement suggestions PinmemberScope16-Jun-05 10:13 
GeneralRe: Some improvement suggestions PinmemberDouglas R. Keesler16-Jun-05 15:27 
GeneralRe: Some improvement suggestions PinmemberTheJ719-Jun-05 15:55 
GeneralRe: Some improvement suggestions PinmemberDouglas R. Keesler21-Jun-05 12:18 
GeneralRe: Some improvement suggestions PinmemberScope21-Jun-05 5:02 
GeneralRe: Some improvement suggestions PinmemberDouglas R. Keesler21-Jun-05 12:14 
GeneralRe: Some improvement suggestions PinmemberDouglas R. Keesler16-Jun-05 14:54 
GeneralRe: Some improvement suggestions PinmemberDominik Reichl17-Jun-05 8:05 
GeneralRe: Some improvement suggestions PinmemberDouglas R. Keesler17-Jun-05 15:50 
GeneralRe: Some improvement suggestions PinmemberDouglas R. Keesler19-Jun-05 14:12 
QuestionFileSize IS the password? PinmemberBlake Miller13-Jun-05 10:40 
AnswerRe: FileSize IS the password? PinmemberDouglas R. Keesler13-Jun-05 10:59 
GeneralA bug (critical, data loss!) PinmemberOne Stone13-Jun-05 3:39 
GeneralRe: A bug (critical, data loss!) PinmemberDouglas R. Keesler13-Jun-05 12:54 
QuestionSet password?? PinmemberOne Stone13-Jun-05 0:13 
AnswerRe: Set password?? PinmemberDouglas R. Keesler13-Jun-05 0:39 
GeneralRe: Set password?? PinmemberOne Stone13-Jun-05 4:28 
GeneralRe: Set password?? PinmemberDouglas R. Keesler19-Jun-05 13:57 
GeneralCould not link. PinmemberWREY12-Jun-05 23:41 
GeneralRe: Could not link. PinmemberDouglas R. Keesler13-Jun-05 12:43 

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 | Mobile
Web01 | 2.8.140814.1 | Last Updated 5 Jul 2005
Article Copyright 2005 by Douglas R. Keesler
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid