Symmetric Encryption for Windows and iPhone (part 1)
One of our customer asked to use encrypted files (RC4-128) in the Apple iPhone Application. Files are created and encrypted on MS Windows and my goal is to decrypt and read these files in the Apple iPhone Application.
I will describe all the problems were met and solutions were found during the implementation. This chapter contains the implementation details for Windows module which encrypts the data. The next chapter contains implementation details for iPhone Application.
Let’s start from the Windows code. There is awhere all theory can be obtained. In a few words we need to do only several steps:
1. Acquire Cryptographic Context
2. Create a key form the “secret word” and derive into context.
3. Encrypt the data with the already created key.
Looking through the process:
Acquiring Cryptographic Context is rather simple procedure, but there are some important things that I should mention to be able to decrypt the data on the iPhone platform. The function manages this procedure is.
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))
// if the key container doesn’t exist
if (GetLastError() != NTE_BAD_KEYSET)
// too bad – we can not acquire context
// you should do something
// trying to create it for the first time
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET|CRYPT_NEWKEYSET);
What things should be mentioned: The 1st parameter is the pointer of context we are going to create; the 2nd is container – usually we use the name of the container as the application name, for this case we set it to NULL; the 3d parameter – cryptographic provider should be NULL – do not use any MS_DEF_bla-bla-bla providers if you want your file to be decrypted in the iPhone Application; the 4th – cryptographic provider type – should be PROV_RSA_FULL; flags – we use CRYPT_MACHINE_KEYSET and CRYPT_NEWKEYSET (in case when creating the context for the first time).
Creating the Key using some predefined “Secret Key” is pretty simple thing:
const char * szSecretKey = “my password”;
HCRYPTHASH hHash = NULL;
DWORD dwFlags = CRYPT_NO_SALT;
// trying to create hash
if (CryptCreateHash(hProv, CALG_MD5, NULL, 0, &hHash))
// trying to crypt hash data
if (CryptHashData(hHash, (LPCBYTE) szSecretKey, (DWORD) strlen(szSecretKey), 0))
// key is
if (CryptDeriveKey(hProv, CALG_RC4, hHash, dwFlags, &hKey))
// destroying hash – we do not need it
// key is created and derived – ready to use
What things should be mentioned: the Secret Key is presented as char * szSecretKey (1 byte per character) in this implementation, if you are going to use some other structure (LPCTSTR for example) – please note that the you can have completely different key value; DWORD dwFlags is set to CRYPT_NO_SALT, there is also a description to set the key length in this parameter but for RC4 key it seems not necessary.
And the last thing – encrypting procedure. There is for this.
BYTE * pbData = NULL;
DWORD dwDataLength = 0;
CryptEncrypt(hKey, NULL, TRUE, 0, pbData, &dwDataLength, dwDataLength);
What things should be mentioned: we encrypting at once, without any hash data,without any specific flags; pbData – is a pointer to bytes we need to encrypt, after encrypting procedure the function will return the encrypted data in this pointer; the 6th parameter &dwDataLength – is the pointer to DWORD value where the function will put the result how many bytes where encrypted; the 7th parameter dwDataLength – how many bytes should be encrypted – as we are encrypting data at once two this parameters should be the same;
Well, please find herewith some referring to Crypto API sample codes that will help you to understand the encrypting mechanism: