Your code has one big problem :
It takes only a string as input.
No more compiler errors !
(And your code is written in MFC and is not usable without modifications in a project (e.g. bad error handling))
But if you need the MD5 of a file and the file is 3 Gigabyte of size you will not be able to load the file into a string !
For that you need an algorithm that is capable to calculate from multiple smaller blocks of the file.
I attach a reusable C++ class (without MFC) which performs this task with highest speed !
After you are ready destroy the MD5 class or call FreeBuffer();
The MD5FinalToString() method returns an upper case hash, which is great for hashing files. You don't want to mess around with case matching to find out if the file was modified. However, if you're getting a hash from string input, most likely you are either creating an encryption key or storing a password validation string. In the latter event string case doesn't matter, but if you're creating an encryption key, mixed case makes a much stronger key. So I added a new function called MD5ToEncryptionKey(). It is basically a copy/paste of the aforementioned method, with a slight modification. Here is the code:
char* DCipher::MD5ToEncryptionKey() //commented lines are my modifications
srand(16); // Seed the Random Number Generator
int val; // Declare an integer
ms8_MD5 = 0;
for (int i=0; i<16; i++)
val = rand() % 2; // val = remainder of rand()/2
if(val < 1) // if even num, use upper - else use lower
sprintf(s8_Temp, "%02X", signature[i]);
sprintf(s8_Temp, "%02x", signature[i]);
I simply seeded the random number generator to ensure the same random numbers would be generated on each call. Then by taking the modulus (remainder) of the random number divided by two, I determined whether it was odd or even. If an even random number use upper case hex, else use lower case hex (of course it has no effect on decimal digits).
Then I changed CalcMD5FromString() to return a call to this function.
One suggestion: if you use this for file encryption, change to function to accept an integer as a parameter and pass the file-size to the function, then you could seed the random number generator with that number. That way you would generate a different sequence of random numbers for each file, but ensure the sequence would repeat reliably for each file.
BTW, I incorporated this class into an overall file encryption class I was working on. The class is designed for internal file encryption. I use it in conjunction with my serialization overrides, to be able to read and save data with standard serialization methods, but always have the data encrypted on disk. The point of this class was to be able to generate encryption keys that were not generated of user input and were not stored anywhere. [It generates a 64-byte (512 bit) encryption key] I also wanted to be able to add a signature to the file that is not part of the encryption scheme, but would not allow files to be decrypted unless the signature matched - this is not for security, but for file control - so we don't try to decrypt files that don't belong to the app.
All of this has been incorporated into a single class which is achieved by creating an instance of the class, and then calling the method passing only the filepath and your apps signature. It's availabe in both generic class and DLL. I plan on posting an article on CP when I get time, offering this back to the public domain. It may be a couple weeks or so before I get time to write the article. If you want this, or need it sooner, post a message and I'll try to make a "article-less" download available.
It has some MFC in it, but very little. It could easily be modified for cross-platform projects.
Cheers to all!
In business, if two people always agree, one of them is unnecessary.
gives me the following error:
error C2664: 'CreateFileW' : cannot convert parameter 1 from 'const char *' to 'LPCWSTR'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
What is the correct syntax to avoid this error? I don't feel confident to experiment myself.
Likewise, with a non-unicode build I get the following warnings:
warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
warning C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
I would prefer not to disable the warnings, so if someone could tell me how to code this correctly so that it works with all builds in both VC6 and VS2008 I would very much appreciate it.
Do NOT replace the other chars* in the code !!!
They hold the MD5 data.
You switch off the usless warning with
#pragma warning(disable: 4996)
You can REALLY ignore the warnings.
There is NO problem with using sprintf() if you do it the right way.
Mircrosoft only tells you, that these functions have the risk of a buffer overflow if the code is badly written. But a clean code will NEVER produce a buffer overflow.
Newer code will use strings instead, but I suppose that you will not change all the code, which works perfectly, because of a stupid warning?
I feel this constructed is not a good way to do the operation,
The Digest method is allocating memeory using GetBuffer and aquiring handle for the context, how is error handled if either of them fail?
This one i am not sure but looks obvious correct me if i am wroung
The return value of Digest is CString, while the return value of the constructor is the object itself, so how am i going to get the return value from this constructor?
the value is simplely lost making this constructor of no effect.
Tech.Support : Mam, is your pc running under windows?
Customer : No actually its close to the main door.
Well, I wouldn't comment anything on the article or style of coding. I'm new to Crypto API, so this gave me a headstart. However, there wa a major issue I had to sort out to weed out the "undeclared identifier" compile time error associated with wincrypt.h. I noticed that wincrypt.h has an #if(_WIN32_WINNT >= 0x0400) in the benginning and a corresponding #endif statement at the end. I was compiling on Windows 2000. So the undeclared identifier issue was sorted out by commenting these 2 lines! Whew!
I was wondering whether your solution to comment out lines from wincrypt.h was warranted or not because I do not know why did Microsoft put the following:
#if(_WIN32_WINNT >= 0x0400)
in the first place; and I think its not an elegant idea to hack around with header files outside the source control of the company which owns them, even if it means to just copy a new and modified wincrypt.h to your own project.
Maybe the following could be a more "non-invasive" method. Place the following lines in md5Capi.h:
#define _WIN32_WINNT 0x0400
#include // Cryptographic API Prototypes and Definitions
However, I once again caution everyone who wishes to use this workaround that there must be a reason why the condition #if(_WIN32_WINNT >= 0x0400) is there in the first place, maybe someone could point it out to us.
The file you need is "wincrypt.h" which is located to PlatformSDK folder; this folder is part of Microsoft Visual C++ .NET package; if you are using Visual C++ 5.0/6.0 please install the Windows SDK package, available free of charge from Microsoft website. Good luck!
There are several improvements that both article and code need:
1. More careful code packaging. It's not enough to ZIP all files in the directory. You end up with some temporary files included in the package. Remember that the more compact is solution, the more elegant it looks. In your package there are about 200 KB of files included. However, 3 largest files (with extension NCB, OPT and APS) are intermediate files that occupy about 150 KB (75%!) of space and are not part of the project. You should examine source files before including them into a package.
2. Better C++ coding. Let's take as an example the following method:
CString &Cmd5Capi::Digest(CString & csBuffer)
Why is buffer declared as "CString&" and "const CString&" (or even "LPCTSTR")? It should not be modified by the method. The same goes for return value. Why is it "CString&" and not "const CString&"?
3. The method digest might as well be declared as static, so MD5 digest could be computed without allocating an instance of Cmd5Capi class.
However, it's a good attempt for a first article.
Vagif Abilov MCP (Visual C++)
If you're in a war, instead of throwing a hand grenade at the enemy, throw one of those small pumpkins. Maybe it'll make everyone think how stupid war is, and while they are thinking, you can throw a real grenade at them.
Jack Handey, Deep Thoughts
Last Visit: 31-Dec-99 19:00 Last Update: 29-May-15 2:37