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

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

By , 5 Jul 2005
 

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)

About the Author

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

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalconvert c#memberbrodhol31 Aug '10 - 21:51 
QuestionNice job on Rinhdael , please help!memberSuperEric24 Jun '09 - 2:51 
QuestionAdministrator rightsmemberpr0phetus17 Oct '08 - 4:58 
GeneralMinor typo in the article titlemvpRajesh R Subramanian24 Jun '08 - 23:53 
QuestionPassword ValidationmemberInAndOut15 Oct '06 - 8:25 
AnswerRe: Password Validation [modified]memberDouglas R. Keesler15 Oct '06 - 13:06 
GeneralRe: Password ValidationmemberInAndOut16 Oct '06 - 5:08 
AnswerRe: Password ValidationmemberDouglas R. Keesler16 Oct '06 - 10:10 
GeneralRe: Password ValidationmemberInAndOut16 Oct '06 - 17:16 
Generalpadding in encryption functionmemberdude_445316919 Jul '06 - 2:01 
GeneralRe: padding in encryption functionmemberDouglas R. Keesler19 Jul '06 - 12:59 
Questionundefined referencememberjozin11 Apr '06 - 7:31 
AnswerRe: undefined referencememberDouglas R. Keesler11 Apr '06 - 13:54 
GeneralRe: undefined referencememberjozin13 Apr '06 - 7:19 
GeneralRe: undefined referencememberDouglas R. Keesler13 Apr '06 - 17:03 
GeneralRe: undefined referencememberjozin13 Apr '06 - 18:50 
AnswerRe: undefined referencememberDouglas R. Keesler14 Apr '06 - 3:19 
Questionundefined reference to `DCipher::EncryptFileA(char const*,char const*,int*)memberjozin11 Apr '06 - 7:31 
GeneralReinventing the Wheel ha? :=)memberspinoza14 Mar '06 - 3:49 
QuestionUsing the codememberjamesw200326 Jan '06 - 9:39 
AnswerRe: Using the codememberDouglas R. Keesler26 Jan '06 - 10:26 
GeneralSome improvement suggestionsmemberDominik Reichl14 Jun '05 - 7:40 
GeneralRe: Some improvement suggestionsmemberScope16 Jun '05 - 10:13 
GeneralRe: Some improvement suggestionsmemberDouglas R. Keesler16 Jun '05 - 15:27 
GeneralRe: Some improvement suggestionsmemberTheJ719 Jun '05 - 15:55 
GeneralRe: Some improvement suggestionsmemberDouglas R. Keesler21 Jun '05 - 12:18 
GeneralRe: Some improvement suggestionsmemberScope21 Jun '05 - 5:02 
GeneralRe: Some improvement suggestionsmemberDouglas R. Keesler21 Jun '05 - 12:14 
GeneralRe: Some improvement suggestionsmemberDouglas R. Keesler16 Jun '05 - 14:54 
GeneralRe: Some improvement suggestionsmemberDominik Reichl17 Jun '05 - 8:05 
GeneralRe: Some improvement suggestionsmemberDouglas R. Keesler17 Jun '05 - 15:50 
GeneralRe: Some improvement suggestionsmemberDouglas R. Keesler19 Jun '05 - 14:12 
QuestionFileSize IS the password?memberBlake Miller13 Jun '05 - 10:40 
AnswerRe: FileSize IS the password?memberDouglas R. Keesler13 Jun '05 - 10:59 
GeneralA bug (critical, data loss!)memberOne Stone13 Jun '05 - 3:39 
GeneralRe: A bug (critical, data loss!)memberDouglas R. Keesler13 Jun '05 - 12:54 
QuestionSet password??memberOne Stone13 Jun '05 - 0:13 
AnswerRe: Set password??memberDouglas R. Keesler13 Jun '05 - 0:39 
GeneralRe: Set password??memberOne Stone13 Jun '05 - 4:28 
GeneralRe: Set password??memberDouglas R. Keesler19 Jun '05 - 13:57 
GeneralCould not link.memberWREY12 Jun '05 - 23:41 
GeneralRe: Could not link.memberDouglas R. Keesler13 Jun '05 - 12:43 

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 5 Jul 2005
Article Copyright 2005 by Douglas R. Keesler
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid