You need to encrypt it... and I'm not talking about some sort of XOR/scrambling logic that a cypher tech could break in five minutes, but real RSA encryption. The Windows CryptoAPI provides the tools to do this, but the documentation is rather complicated and the steps are not particularly obvious.
A typical scenario might be that you need to make a database connection that requires a password:
1) Get input of the password used in a SQL Server connection string in a settings dialog.
2) Encrypt: Make the password data unusable except to your program.
3) Store the encrypted data into the System Registry.
... later ...
4) Decrypt: Recover the original cleartext data.
5) Build the database connection string using the cleartext password.
I wrote a smallish (140-line) C++ class to do steps 2 and 4, and I'll provide the entire source code in this article. Here is the rough outline of the steps:
Setup:
CryptAcquireContext
CryptCreateHash
CryptHashData
CryptDeriveKey (create the key needed below)
Encrypt:
CryptEncrypt
Convert binary to hexadecimal (for easy transport/storage)
Decrypt:
Convert hexadecimal to binary (for using the CryptoAPI)
CryptDecrypt
It boils down to: Make a CryptoAPI key and use it in calls to CryptEncrypt and CryptDecrypt. Creating that key is the only tricky (non-obvious) part. You start with a base raw key (any string of characters), hash it, and then convert that hash into a key that can be used by the CryptoAPI.
Base Raw Key and Salt
So what do you use as the original base raw key string of characters? I want my code to be extremely simple and not require that the caller know the base key. I wanted to be able to use this tool from various program modules transparently, so I provided a built-in "default" base key as a member variable. You may see the security hole there: Anyone who has a copy of my program can decrypt anything that has been (default-ly) encrypted by my program.
To give a warm feeling to my clients, I added a feature called a salt -- an optional, user-provided string that can be appended to the default base key string. Now, if my users add a salt, then anybody who does not know that salt value can never decrypt the passwords (or Social Security Numbers or other encrypted data). Note: I put a big warning in my documentation: "If you forget your salt value, you will lose all of the encrypted data and there is no backdoor. Don't call tech support because we can't help you."
Problems with Binary Data
One significant problem you will encounter with encrypted data is that it is no longer simple text. It ends up as a very random-looking array of binary data -- including unprintable characters, apostrophes and percentage signs (SQL users beware!), and worst of all, it may contain embedded binary NULL values (0x00). As a C/C++ programmer you know that it is possible to work around such situations in various ways, but you also know that you will eventually run into headaches.
I chose the simple expedient of always storing encrypted data as a C-style string of hexadecimal digits. The hex-encoded secret data is always exactly twice as long as the encrypted data, which makes it somewhat easier to work with than base-64 encoding or other techniques.
Enough with the jawing... let's get to the code.
Crypt.h -- Header for the Encryption Object
Crypt.cpp -- Code of the Encryption Object
Here is an example of usage:
Notes:
- The EncryptStrToHex function assumes that the input data is simple text -- it calculates the length using strlen(). Don't use it to encrypt binary data (any data that might contain an embedded NULL).
- The CryptEncrypt and CryptDecrypt CryptoAPIs do their work in place; that is, they overwrite the original with the modified data. I wanted to be able to pass LPCSTR data (constant string) to the functions, so I chose to make a copy of the incoming data. This would be inefficient if working with larger buffers.
- My actual (production) code provides a few variations of the main functions; for instance, it can encrypt binary data (not just text strings) and one variation does not encode to hex. I removed most of this extraneous code from this article because those functions are not needed nearly as often -- and I wanted to leave something for you to do on your own :-)
- You may want to "disguise" the length of the secret data. For instance, a codebreaker could save a lot of time knowing that the cleartext string is only 3 characters (six hexadecimal digits). The simplest technique is to pad the input string with spaces, and then strip them off when the cleartext is needed.
But you can also use the lower-level Crypt::EncryptDecrypt function -- it does not assume that the input is clean text, so it will accept embedded NULLs; thus, you could append a NULL and pad with random characters. For instance, if the input text is
"Secret Password\0asXXefg"
then after decrypting, the string will end at the right place.
- Just an afterthought: If all you need is a way to authenticate the person who uses your program, there is no need to encrypt or store or transport his password at all. Instead just create and store a hash of his password. Each time he logs on, hash the input password and compare it to the stored value. That way the cleartext password is irrelevant -- never needed and never stored.
References:
Cryptography Reference
http://msdn.microsoft.com/
=-=-=-=-=-=-=-=-=-=-=-=-=-
If you liked this article and want to see more from this author, please click the Yes button near the:
Was this article helpful?
label that is just below and to the right of this text. Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-
by: Sumit_Makhe on 2012-03-01 at 07:06:04ID: 44463
This code is not working for me...
1. I create new class "Crypt.h"
2.then "Crypt.cpp"
then during compilation VC++ asked me to add '#include "StdAfx.h"' in "Crypt.cpp" after that following list of errors appeared...
1>------ Build started: Project: EditPath, Configuration: Debug Win32 ------
1>Build started 3/1/2012 8:22:24 PM.
1>InitializeBuildStatus:
1> Touching "Debug\EditPath.unsuccessf
1>ClCompile:
1> All outputs are up-to-date.
1> Crypt.cpp
1>d:\ibs\editpath\editpath
1> Add directive to 'StdAfx.h' or rebuild precompiled header
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1> with
1> [
1> BaseType=wchar_t,
1> StringTraits=StrTraitMFC_D
1> ]
1> Constructor for class 'ATL::CStringT<BaseType,St
1> with
1> [
1> BaseType=wchar_t,
1> StringTraits=StrTraitMFC_D
1> ]
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1> with
1> [
1> BaseType=wchar_t,
1> StringTraits=StrTraitMFC_D
1> ]
1> Constructor for class 'ATL::CStringT<BaseType,St
1> with
1> [
1> BaseType=wchar_t,
1> StringTraits=StrTraitMFC_D
1> ]
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1>d:\ibs\editpath\editpath
1> with
1> [
1> BaseType=wchar_t,
1> StringTraits=StrTraitMFC_D
1> ]
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>d:\ibs\editpath\editpath
1>
1>Build FAILED.