Using the crypt unit for something useful
Right, I posted the full crypto unit yesterday, but I didn’t have time to post an example of how to use it. So this time we are going to have some real-life practical examples.
How does this key stuff work?
We are all used to passwords and security these days, but there are a few things that have changed since the golden days of home-made services and DIY websites. Most notably is the fact that you should never store passwords at all. If you think back 5-8 years ago, it would have been perfectly valid for a webservice or a database driven website to store usernames and passwords in a database table. But should someone gain access to your database, they could in fact rob your database of passwords and ruin everything.
The solution? Dont store passwords. You store a hash of a password. A hash is just a number that is generated from analysing a piece of data, it’s sometimes also called a checksum. The important part about a hash is that it will generate the same number exclusively when the data matches. As you can imagine, it’s almost impossible to guess a password from a hash-number.
Another thing to note about key-pairs is that they are not designed to encrypt large sections of data. They are designed to encrypt small things (passwords) and have a limit ranging from 256 to 512 bytes – meaning somthing like 128 unicode characters. Encryption of files and streams is done by generating a hash from an encrypted password – and using that hash as the key for a common cipher like RC4 or Blowfish. These extra steps makes it more or less impossible to decode your data without the absolute and concrete original information.
Here is one way of generating a a portable, encrypted file:
- Generate a hash from a password
- Generate a cipher key-set
- Encrypt the hash using the public key
- Save the encrypted hash to the target-stream
- Save the public key to the target-stream
- Encrypt the password-hash using the private key
- Use encrypted hash as a key-stream for encrypting the “real” data
- Save encrypted data to the target-stream
This creates a dual-key lock. You need both the original private-key and password to decipher the data. Even if you steal the original key-set or gain the original password, the data remains useless without both. By de-coupling these two factors and only providing the public key, another person can only verify that the file belongs to you (by first generating a hash of your password, then decrypting the provided hash using the public key and comparing it), but you still cant read the content of such a file without the private key. This is more or less how secure document transportation works, where you can checkout a document anywhere in the world with a smart-card and personal password. The private key is stored on the card and without both items the data remains unreadable.
How you chose to play around with these schemes is up to you. In some cases rolling your own security is better than buying a solution, because the less that is known about a scheme – the harder it will be to crack.
To generate a key-pair you would write:
procedure TForm1.Button3Click(Sender: TObject); var mPrivate: TStream; mPublic: TStream; mTemp: String; begin if TCryptoAPI.rsaMakeKeys(mprivate,mPublic) then begin try if TCryptoAPI.rsaKeyToBase64(mPrivate,mTemp) then showmessage(mTemp); if TCryptoAPI.rsaKeyToBase64(mPublic,mTemp) then showmessage(mTemp); finally mPrivate.Free; mPublic.Free; end; end; end;
I included two helper functions for turning the streams into Base64 encoded strings, simply called rsaKeyToBase64() and rsaBase64ToKey(), its is sometimes easier to transport the keys as text (and also to display the results while working).
Encrypting a stream
procedure TForm1.Button2Click(Sender: TObject); var mKey: TStringStream; mText: String; mSrc: TStringStream; mdst: TStringStream; begin //Replace mSrc with your filestream mSrc:=TStringStream.Create; try mDst:=TStringStream.Create; try if TCryptoAPI.rsaBase64ToKey("Base64 key text here",TStream(mKey)) then begin try //Remove this obviously when encrypting a file or a memory stream mSrc.WriteString("Text/data to encrypt here"); mSrc.Position:=0; if not TCryptoAPI.rsaCryptStream(mKey,mSrc,mDst) then RaiseLastOSError; finally mKey.Free; end; end; finally mDst.Free; end; finally mSrc.Free; end; end;
To decrypt a stream, do the same as above but call TCryptoAPI.rsaDecryptStream() instead.
Voila! Next we will implement the code for encrypting passwords with the crypto-key-set!