With IE 3.02, Microsoft added to its standard libraries the encrypting mechanism called CryptoAPI.
Owing to complex interface one may, in an easy way, perform things like:
passwords encrypting, encrypting data sent over the net, digital data signature etc.
I had the opportunity to use some CryptoAPI functions when a necessity of keeping passwords in the registry occured.
The encrypting (decrypting) algorithm looks like this:
- Openning a connection with cryptographic provider
- Creation of a hashing object
- Hashing our secret text by means of the object above
- Cryptographic key creation by means of our hashed
object
- Our password or other message encrypting, decrypting
Openning a connection with cryptographic provider
CryptoAPI 2.0 version contains several standard providers
(CSP - Cryptographic Service Providers).
The most standard among them is
The Microsoft Base Cryptographic Provider
and this is the one we will use.
Current providers list can be found in the registry with the key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider.
Every provider belongs to one type. There is also a possiblity to check in the registry
to what type our provider belongs. All the information can of course be obtained by means
of certain CryptoAPI functions that won't be mentioned at all in here :).
Anyway, our provider belongs to
PROV_RSA_FULL
type.
After that short 'theoretical' introduction it is time for practice.
We need now to get our provider's handle.
To do so we use
CryptAcquireContext function:
var
hProv : HCRYPTPROV;
begin
if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then Exit;
|
It may happen that
CryptAcquireContext function will fail and
NTE_KEYSET_NOT_DEF error will be returned. Then one can call this
function again with
CRYPT_NEWKEYSET flag.
var
hProv : HCRYPTPROV;
begin
if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, 0) then
begin
if GetLastError <> NTE_KEYSET_NOT_DEF then Exit;
if not CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_NEWKEYSET)
then Exit;
end;
|
Creation of a hashing object
Our provider consists of the following hashing algorithms: MD2, MD5, SHA and MAC.
I only used MD5, but there is probably no obstacle to use others.
To create a hashing object one needs to use
CryptCreateHash function. The source code could look like this:
var
hHash : HCRYPTHASH;
begin
{...}
if not CryptCreateHash(hProv, CALG_MD5, 0, 0, @hHash) then Exit;
|
The handle -
hHash - points at our hashing object, which uses MD5
algorithm.
Hashing our secret text by means of the object above
Our key will be secret and there will be only one such key, always the same, and it won't be
stored anywhere.
Because of this the application must have the possibility to evaluate the key any time there
is a need to encrypt or decrypt some information.
That's why we created the hashing object, so that later on the key can be genereted from it.
However we need to
hash first. In general it looks like this, we have a secret
password (secret text), we hash it and create the key from it. The algorithm when being
given the same input generates always the same key (output). Note that changing a single
input bit changes the output key completely. Well, let's do some hashing.
We will use
CryptHashData
function. And here is some source code:
var
dwLength : DWORD;
Key : String;
begin
{...}
dwLength := Length(Key);
if not CryptHashData(hHash, PByte(Key), dwLength, 0) then Exit;
|
This way we have a hashed bit sequence, which will be used to create the key.
Cryptographic key creation by means of our hashed object
Now there is nothing more than to create the key. The algorithm must be specified now.
We have the following at disposal: RC2 and RC4. It's not much, but these are all the block
ciphers our provider provides us with. Based on the hashed password key creation function
is called
CryptDeriveKey
. The code should look like this:
var
hKey : HCRYPTKEY;
begin
{...}
if not CryptDeriveKey(hProv, CALG_RC2, hHash, 0, @hKey) then Exit;
|
In
hKey there is the handle to RC2 key.
Our password or other message encrypting, decrypting
It is high time we took the last step, the most complicated one, which doesn't mean
difficult :).
Using
CryptEncrypt
/
CryptDecrypt
function we need to encrypt/decrypt a password or some other message.
Encrypting source code fragment could look this:
var
dwLength : DWORD;
pbKeyBlob: PByte;
dwBlobLen: Integer;
s : String;
Password : String;
begin
{...}
dwBlobLen := Length(Password);
CryptEncrypt(hKey, 0, True, 0, nil, @dwBlobLen, 0);
dwLength := Length(Password);
GetMem(pbKeyBlob, dwBlobLen);
try
Move(Password[1], pbKeyBlob^, dwLength);
if not CryptEncrypt(hKey, 0, True, 0, pbKeyBlob, @dwLength, dwBlobLen) then Exit;
SetLength(s, dwBlobLen);
Move(pbKeyBlob^, s[1], dwBlobLen);
finally
FreeMem(pbKeyBlob);
end;
|
I don't present decrypting source code because it is even simpler.
At the end one must of course free all the handles. The following are the functions to do so:
CryptDestroyHash,
CryptDestroyKey
and
CryptReleaseContext.
If anyone would like to see how it looks in my sources then I share here
two modules.
One is the wincrypt.h translation to Pascal, indespensible to do anything described above,
the other contains to functions to encrypt and decrypt passwords.
That's all. Have a nice encrypting.
Michał B±kowski