I find myself in a bit of a situation. I have a need for a rather simple encryption function that will allow me to me save settings in a database. The goal isn't to defeat a dedicated attacker having access to the whole database but rather to provide -some- level of protection for these particular strings (this will be things like username and password to the SMTP server that will be used to send emails, for instance).

I looked through the provided samples and, unfortunately, they seem to be written with simplicity in mind and not correctness. Case in point: the SymmetricCrypto sample (which seems to be doing pretty much exactly what I wanted) is using a null IV (and rightfully warns about it).

So, I wrote code to do that same thing is a way that I /think/ is correct but, of course, it's hard to know for sure that I'm using the system securely.

What I do:
- Uses AES256 in CBC mode, PKCS#5 padding. (TElAESSymmetricCrypto);
- Create a Random IV (using the windows CryptoAPI RNG) of the same size cypher block.
- Extract the key from a random block of data (generated when the database is seeded from the same CrpytoAPI RNG). For that, I simply take the first N bits from that random "key" with N being the Key size for the algo. (*)
- Use the "Encrypt" method of TElAESSymmetricCrypto.
- Concatenate the IV and the CypherText into the output (and base-64 encode the result, not that it matters much).

I have several questions regarding the process outlined above:
- I picked CBC and a random IV since it seems to be fitting my need: the same clear text will result in a different cypher text each time the system is applied, I don't need a stream cypher since I will not perform encryption on large data, need to randomly access the clear text and since using a padded block operation mode actually should hide some of the property of the clear text (the exact length). Is there any reasons I should be using another mode ?
- Assuming my key is large (it's actually 4096 bytes), is there a better way to derive the encryption key (assuming the data is truly random, of course) than simply grabbing the first N bytes ? Would hashing that data provide any actual security value ?
- Is the IV large enough ? Could it be simple replaces by adding a fixed amount of random data at the start of the cleartext and have the resulting system simply ignore that part of the result ?
- On a wider scale, is there a better way to implement this ? I really tried to find something in SBB that would encapsulate all these decisions into a nice and easy to use package but next best thing would have been to use PGP and that would introduce a lot of complexity (and produce much larger cypher text).

Any comment on the above would be welcom.

Thank you,
Stephane

(*) I am aware that storing the key in the database is a weakness but the only way around it that I could find - store it on a HSM - is impractical for my solution: the key needs to be accessible from several different server (many will actually be VMs) and, overall, the costs associated to solving that particular issue in a proper way aren't worth the price of the data that will be protected.

- Create a Random IV (using the windows CryptoAPI RNG) of the same size cypher block.

You should better use our SBRandom module instead.

Quote

- Assuming my key is large (it's actually 4096 bytes), is there a better way to derive the encryption key (assuming the data is truly random, of course) than simply grabbing the first N bytes ? Would hashing that data provide any actual security value ?

In the key is random then there is no sense to hash it. However you wrote that this key is stored in the database. Is it stored as-is without any protection? If yes then there is no sense in encryption as everything can be decrypted having that key.

Quote

Is there any reasons I should be using another mode ?

You can use CFB to produce output of the same length as input data, but CBC is also a good choise.

Quote

- Is the IV large enough ? Could it be simple replaces by adding a fixed amount of random data at the start of the cleartext and have the resulting system simply ignore that part of the result ?

IV length should be equal to cipher block length. Its 16 bytes for AES. Its more correct to use IV than random data at cleartext start.

In the key is random then there is no sense to hash it. However you wrote that this key is stored in the database. Is it stored as-is without any protection? If yes then there is no sense in encryption as everything can be decrypted having that key.

I know that the storage of that key is a weakness (see my last comment above). I can't really fix that in a way that makes economical sense. As I said, I'm ok with that: the data that will be protected that way isn't really high-value: if the password to the SMTP server is stolen, it's a trivial enough task to have it changed.

In practice, the key is XORed with a fixed keys from the program itself: it doesn't provide any real protection but, at least, you need two elements to perform the decryption. It's more a protection against casual inspection than FIPS-compliant security.

However, I do care about the algorythm I used: I intend to use it to extend the current technique used to protect local configuration files (which uses the Windows CrytProtectData API) and an older set of functions that I wrote about 8 years ago (which used the "let's add some random data in front of the actual dat" method and that I would like to replace with more "correct" system). Therefore, even if the key used in this instance isn't really properly protected, I'd like my implementation to be as secure as possible.

The specifics of CryptGenRandom's algorithm have not been officially published. As with any unpublished random number generation algorithm, it may be susceptible to theoretical weaknesses including the use of outdated algorithms...

I've implemented a (pardon my french) half-assed crypto mechanism to store passwords in the database. It uses a fake, self-generated certificate that is stored locally, so every time you use the program in a new computer you are warned that your password isn't available anymore, and you have to enter it again. This is to store your mail password in the database but without giving the key if the database is compromised.

It seems to work, but I'm not confident enough about its correctness.

Since crypto is a hard subject, and most programmers (like me) aren't really qualified to design secure systems, and this being a common problem, I think this is a good candidate for a small and concise but correct sample program ("store password in local file / database".)

The main issue about storing sensitive data locally is keeping the encryption key safe. If the key is stored 'as it is' somewhere in the application code or in a local unprotected storage (e.g. registry), it can be extracted by an adversary. E.g. if your encryption certificate's private key is stored on a hard drive, it can be easily accessed by a third party and used to decrypt the password database.

There are a number of good practices that can be used to increase the strength of the scheme:

1. Protected Storage (DPAPI; Windows only). This mechanism allows your application to rely on Windows in storing sensitive information, such as encryption keys. You will need to export/import the keys when moving your application's settings to a different computer (just as you do at the moment).

2. Using non-exportable private keys (Windows only). A good improvement to your scheme is storing your certificate in the Windows system store with non-exportable private key. Your code will be able to do the decryption (yet, any other application running under the same user account will be able to do it as well), but the private key could never be exported.

3. Using password-based encryption schemes. The encryption key is generated using one-way function from the master password provided by the user (e.g. on application start). As the encryption key is never saved locally in any form, it is impossible for an adversary to steal it.

We use cookies to help provide you with the best possible online experience. By using this site, you agree that we may store and access cookies on your device. You can find out more about and set your own preferences here.