Encryption in windows - making output change more

Soldato
Joined
26 Nov 2003
Posts
6,674
Location
East Sussex
Hi there, hoping someone can help me with this.
I've been asked to modify an encryption routine for a clients software that uses windows cryptography provider to encode a software licence before distribution.

The problem with the existing code is that the output doesn't "change enough".

For example, the string "Ocuk Rocks" encrypts as "123456789".
The string "OcUK Rokks" encrypts as "123456689", only a very small portion of the resultant text actually changes.

The client would like it if even a tony change in the string resulted in near-total change to the encoded text.

I do not know how to achieve this, and would greatly appreciate any advice :)

Code uses function chain: CryptAcquireContext, CryptCreateHash, CryptHashData (hashes the hardcoded passphrase), CryptDeriveKey, CryptEncrypt.

Alternatively, any recommended forums for asking this question?
Thankee.
 
Last edited:
Use a well established encryption routine.. RC4 will probably do fine for you. Unless that is the default Windows encryption routine/is the one you are already using :p
 
Ad DJ Jestar says, use a better crypto system. Windows mostly uses RC4 and 3DES, but the problem is with how you are configuring them.

Any decent symmetric encryption algorithm should have something called the Avalanche Property, which means that if a single bit changes in the input, then every bit in the output should change (clearly this is what you are after). This level of security/randomness is usually achieved by using a strong cipher with mode-of-operation such as cipher block chaining with the cipher.

I have 0 experience with the MS crypto functions you mentioned, but it sounds like your MS crypto functions are just applying a raw block cipher, with no sign of randomness in the system (this is called Electronic Codebook Mode, and this is really really insecure!)

From a quick look, the MS functions should support block-chaining mode, and you provide an initialization vector (IV) as a source of randomness as the first block for each encryption. (This will cause every encryption to give a different ciphertext, even if you encrypt the same plaintext twice)

So my suggestion is to either, read the MSDN docs thoroughly to see how you can configure the cipher to use chaining mode with an IV or use a better 3rd party cryptography library such as crypto++
 
Last edited:
I would use AES (RC4 as mentioned above) it should be as easy as changing your provider name string on the CryptAcquireContext and passing in the algoID for AES to CryptDerviceKey.

I would typical have a variable IV in the form of a timestamp that is stored with the encrypted data. This does NOT compromise security and will ensure 'uniqueness' of cypher text. Also I would use CBC.

If you're stuck MSDN can answer 90% of questions for everything else there is Stackoverflow.
 
I would typical have a variable IV in the form of a timestamp that is stored with the encrypted data. This does NOT compromise security and will ensure 'uniqueness' of cypher text. Also I would use CBC.

Sorry I'm having a little trouble with this. How could I add a timestamp but not have to send that timestamp to the user for them to decrypt the output?
 
Sorry I'm having a little trouble with this. How could I add a timestamp but not have to send that timestamp to the user for them to decrypt the output?

You have to do that. The IV does not need to be kept secret, it just needs to be unique. So, it's usually prefixed to the first block of the ciphertext and sent to the user in plaintext.
 
Hmm.

Well heres the flow:

CryptAcquireContext gets the CSP (MS_ENH_RSA_AES_PROV)
CryptCreateHash opens a hash object using RC4
CryptHashData hashes a hardcoded password.
CryptDeriveKey uses the hashed password to create an encryption key item (with RC4 specified).
CryptEncrypt uses the key to encrypt the data.

Every time this flow is executed the output is the same, when the data changes slightly the output changes slightly. The first segment of the output seems to never change, one presumes this is a header of some form.

Come time to decrypt, the flow is very similar, except CryptEncrypt is CryptDecrypt. It still creates a key in the same way.

Sorry to be an absolute tard about this, but I lack fundamental understanding of what is wrong in this flow! :(
Which parts are wrong, where would the initialization vector come into play?
 
OK I think I need to use CryptSetKeyParam after the "deriveKey" call to add the IV. Looks like IVs default to zero.
Also can use this to enforce CBC... testing this now.
 
OK I think I need to use CryptSetKeyParam after the "deriveKey" call to add the IV. Looks like IVs default to zero.
Also can use this to enforce CBC... testing this now.

I think you will/have hit problems because you are using RC4 which is a stream cipher, can you switch it over to AES (>128bits)? As you cant use block cipher modes like CBC with RC4, and RC4 uses initialization vectors very differently to a block cipher.
 
I used RC4 on recommendations above, can switch it to a few choices I think (need win2000 supported algs).
Is CALG_3DES (Triple DES) any better?
Basicaly, going with the AES provider I have these choices:

http://msdn.microsoft.com/en-us/library/aa375545(VS.85).aspx

Ok, go with AES with 256bit key - CALG_AES_256 and use the hash CALG_SHA_256 to create your key.

Make a random IV equal to the block size, which in this case is 32 bytes, eg:
Code:
	BYTE iv[32] = {0x04, 0xFF, 0xFF, 0xCC, 0xAA, 0xBB, 0xAB, 0xDF, 
				0x04, 0x01, 0xFF, 0xCC, 0xFF, 0xBB, 0xAB, 0xDD,
				0x04, 0x01, 0xFF, 0xCC, 0xFF, 0xBB, 0xAB, 0xDD,
				0x04, 0x01, 0xFF, 0xCC, 0xFF, 0xBB, 0xAB, 0xDD };

Then configure you're AES key to use CBC mode, using the IV you have defined:

Code:
res = CryptSetKeyParam(
        key,
        KP_MODE,
        CRYPT_MODE_CBC,
        0
    );

res = CryptSetKeyParam(
        key,     
        KP_IV,
        iv,
        0
    );

I had a quick try and encrypting the same string with a different IV each time gave me very unique ciphertexts, but for some reason my ciphertext was 16 bytes, rather than 32, maybe you can get it working correctly with yours.

Certainly using a block cipher like AES or even 3DES if you want, with CBC will fix your ciphertext similarity issue :) (it's just time consuming working out microsofts functions... :p)
 
I really appreciate your help with this, and will embark on that path. I have some more questions though, if that's ok.

So once I've done all that and run the Encrypt function, from the target pc when it receives the encrypted output presumably I then have to retreive the IV from it somehow... And then with the IV I can run through all the same steps and decrypt...

If that's correct, would you happen to know what command retreives the IV?
 
Ok say your CryptEncrypt() function chucks out the ciphertext string C, and you have your IV stored locally in a byte array (pretty much in my code above), this might be a timestamp or a counter, doesn't matter as long as its unique for each encryption.

When you want to distribute this to a remote target PC, just send both the ciphertext C and the IV byte array. (You have to do this manually and don't worry about keeping the IV secret)

This wiki page describes the CBC mode and how it makes use of an IV, http://en.wikipedia.org/wiki/Cipher_block_chaining#Cipher-block_chaining_.28CBC.29

You can see that for decryption the IV is essentially the first block of the ciphertext.

Hope that helps, so basically just send out the IV byte array along with your ciphertext, you cant "retrieve the IV from the ciphertext" if that's what you meant. So to decrypt you will just configure the key again with your IV and take the same steps.
 
Ok, go with AES with 256bit key - CALG_AES_256 and use the hash CALG_SHA_256 to create your key.

From MSDN:
CALG_SHA_256
SHA hashing algorithm. Key length: 256 bits.
*Windows XP and Windows 2000: This algorithm is not supported.

Most of our users are on XP, so I guess this one is out.
There is "CALG_SHA" available though, that any good?
 
From MSDN:
CALG_SHA_256
SHA hashing algorithm. Key length: 256 bits.
*Windows XP and Windows 2000: This algorithm is not supported.

Most of our users are on XP, so I guess this one is out.
There is "CALG_SHA" available though, that any good?

Yep that should be ok, it's safer than MD5 anyway :)
 
Well after a couple days of broken computer, I seem to have this working pretty much as you say, and it does indeed cause complete output change when using a randomly generated IV :) Excellent, thanks very very much! I really appreciate the help!
 
Back
Top Bottom