views:

1930

answers:

5

I need to be able to send encrypted data between a Ruby client and a Python server (and vice versa) and have been having trouble with the ruby-aes gem/library. The library is very easy to use but we've been having trouble passing data between it and the pyCrypto AES library for Python. These libraries seem to be fine when they're the only one being used, but they don't seem to play well across language boundaries. Any ideas?

Edit: We're doing the communication over SOAP and have also tried converting the binary data to base64 to no avail. Also, it's more that the encryption/decryption is almost but not exactly the same between the two (e.g., the lengths differ by one or there is extra garbage characters on the end of the decrypted string)

+1  A: 

Kind of depends on how you are transferring the encrypted data. It is possible that you are writing a file in one language and then trying to read it in from the other. Python (especially on Windows) requires that you specify binary mode for binary files. So in Python, assuming you want to decrypt there, you should open the file like this:

f = open('/path/to/file', 'rb')

The "b" indicates binary. And if you are writing the encrypted data to file from Python:

f = open('/path/to/file', 'wb')
f.write(encrypted_data)
mhawke
Thanks, but in this case we're doing it via SOAP servers. I'm thinking there's a similar analog to this but for SOAP instead of binary files.
Chris Bunch
How is the encrypted (binary) data encapsulated in the SOAP packet? Sounds more like a SOAP issue to me. Perhaps you should add more details to the question.
mhawke
You could eliminate language and/or encryption library issues between Ruby and Python by trying to write to a binary file from one language and then decrypt with the other. If that works then look at the transport layer.
mhawke
Good idea! Let me check it out...
Chris Bunch
+1  A: 

It's hard to even guess at what's happening without more information ...

If I were you, I'd check that in your Python and Ruby programs:

  1. The keys are the same (obviously). Dump them as hex and compare each byte.
  2. The initialization vectors are the same. This is the parameter IV in AES.new() in pyCrypto. Dump them as hex too.
  3. The modes are the same. The parameter mode in AES.new() in pyCrypto.

There are defaults for IV and mode in pyCrypto, but don't trust that they are the same as in the Ruby implementation. Use one of the simpler modes, like CBC. I've found that different libraries have different interpretations of how the mode complex modes, such as PTR, work.

Wikipedia has a great article about how block cipher modes.

HughE
Yes, the keys, IVs, and modes are all the same. See my answer to see what ended up being the problem.
Chris Bunch
+3  A: 

Turns out what happened was that ruby-aes automatically pads data to fill up 16 chars and sticks a null character on the end of the final string as a delimiter. PyCrypto requires you to do multiples of 16 chars so that was how we figured out what ruby-aes was doing.

Chris Bunch
A: 

Basically what Hugh said above: check the IV's, key sizes and the chaining modes to make sure everything is identical.

Test both sides independantly, encode some information and check that Ruby and Python endoded it identically. You're assuming that the problem has to do with encryption, but it may just be something as simple as sending the encrypted data with puts which throws random newlines into the data. Once you're sure they encrypt the data correctly, check that you receive exactly what you think you sent. Keep going step by step until you find the stage that corrupts the data.

Also, I'd suggest using the openssl library that's included in ruby's standard library instead of using an external gem.

+3  A: 

(e.g., the lengths differ by one or there is extra garbage characters on the end of the decrypted string)

I missed that bit. There's nothing wrong with your encryption/decryption. It sounds like a padding problem. AES always encodes data in blocks of 128 bits. If the length of your data isn't a multiple of 128 bits the data should be padded before encryption and the padding needs to be removed/ignored after encryption.

Yea, it turned out that's essentially what it was.
Chris Bunch