I recently got involved in a project where the client had very specific security requirements. Within the very comprehensive security document given to us, there was a section about encryption. All the web service requests that involved PII needed to be encrypted with PGP. On top of that, all the requests needed to happen over SSL (yeah, I know…). Ironically, this implementation happened a few weeks before the Heartbleed Bug was discovered, so maybe the client wasn’t that paranoid after all. The client software was running on an iPad, and the development team had a hard time finding an iOS encryption library that was compatible with the other pieces. After a few failed attempts, we decided to switch the encryption mechanism to a combination of AES and RSA, which made our lives easier. I believe tests should be simple and avoid duplicating logic existent in the system under test. The problem is that when you are testing something that requires an encrypted input, unless you can provide that, you are not going to get any valid outputs. Since all my tests were written in Ruby using Rspec, I had to come up with some Ruby code that could encrypt and decrypt JSON data. The encryption algorithm we used was based on RFC3394 and it’s basically an AES encrypted message, where the encryption key is then asymmetrically encrypted with an RSA key.
Let’s get coding
1 2 3 4 5 6 7 8 9 10 11 12 |
|
At this point the message is encrypted with a random 32 byte key (256 bits) and a 16 byte initialization vector. Since these two are random on each session, it makes it harder for the key to be cracked. Next we need to encrypt the random session key with a 2048 bit RSA key:
1
|
|
Now comes the tricky part. The web service is expecting some binary data in a specific format: 4 bytes + encrypted key + 4 bytes + iv + ciphertext. The first 4 bytes represent the size of the encrypted key. The 4 bytes after the key, represent the size of the initialization vector. Also the size representations need to be in 8 bit unsigned big-endian format. We can accomplish this with Ruby’s pack method for arrays.
1 2 3 4 5 |
|
Now we base64 encode the whole thing and return it:
1 2 3 4 5 6 |
|
Since the server is sending us an encrypted response, we also need to create another method to do the reverse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Let’s test it to make sure it works:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Conclusion
Most of the tests I wrote validated the responses and the JSON data being returned (e.g. When a customer gets created). This is probably more code than one wishes to write when creating tests for a web service, but it’s a good exercise to understand how AES key wrapping works and how to validate the encryption.