Frage

I'm writing this question + answer because I struggled a lot (maybe because of a lack of experience), got lost in many different ways of encrypting/decrypting things with node or python.

I thought maybe my case could help people in the future.

What I needed to do:

  • Get data from a form, encrypt them using Crypto (node-js)
  • Pass the encrypted data in Python and decrypt it using PyCrypto.

I chose to use the AES encryption.

Here is how I started (I'm not gonna go through everything I tried):

  • I followed the example at the end of this page

    Which gave in my case:

    (this might be a very bad mix between javascript and coffeescript)

    crypto = require "crypto"
    [...]
    key = "mykeywhatever"
    cipher = crypto.createCipher('aes192', key)
    cipher.update('string i want to encode', 'binary', 'hex')
    encoded_string = cipher.final('hex')
    [...]
    

    This worked pretty fine to encode my string.

  • Then I wrote my python script to decrypt this string, using the readme on PyCrypto's github's page:

    from Crypto.Cipher import AES
    [...]
    my_string = data_coming_from_rabbitmq
    obj = AES.new('mykeywhatever', AES.MODE_CBC)
    obj.decrypt(ciphertext)
    [...]
    

    This obviously didn't work: in the readme there is an IV but since I didn't gave one in the node script, why would I give one in the python one?

After more googling, I learnt that node's Crypto uses OpenSSL, while PyCrypto apparently doesn't. So I looked into that and found those pages:

So things got complicated, no one is doing the same thing to decrypt data, I got lost, and asked for help.

The answer is what my coworker and I came up with (well, mostly my corworker).

War es hilfreich?

Lösung

So we started from the "How can i decrypt... OpenSSL" 's answer.

  • We needed to modify the encryption script which gave:

    crypto = require "crypto"
    [...]
    var iv = new Buffer('asdfasdfasdfasdf')
    var key = new Buffer('asdfasdfasdfasdfasdfasdfasdfasdf')
    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    cipher.update(new Buffer("mystring"));
    var enc = cipher.final('base64');
    [...]
    

    iv needs to be 16bytes long, key is 32bytes. And we changed createCipher to createCipheriv.

  • Back to the python decryption script:

    Process was simply reading PyCrypto's documentation, and compare with the code we started from.

    Then we decided to just stick to the API, and start from scratch. And it gave:

    from base64 import b64decode
    from Crypto.Cipher import AES
    [...]
    iv = 'asdfasdfasdfasdf'
    key = 'asdfasdfasdfasdfasdfasdfasdfasdf'
    encoded = b64decode('my_encrypted_string')
    
    dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
    value = dec.decrypt(encoded)
    

And it was as simple as that... Hope it'll help some of you!

Update:

As Perseids wrote in the comments of his answer, the IV has to be random and different for every message

Andere Tipps

The system you are building is probably insecure

Except for storage you basically never want to just encrypt your data, but also authenticate it. Authentication in this context means that a valid message can only be generated by someone who knows the key. A widely used authentication scheme is HMAC.

If you do not authenticate your messages anyone can feed data into your service. An attacker might not be able to fully control the outcome after decryption but he/she might still be very dangerous. For example, if you use CBC (which you do) and the most common paddings schemes (AES is a block cipher and can only encrypt 128bit Blocks of data) and an attacker can differentiate between a padding error and any other error then all your messages can be decrypted by an attacker. This is called a padding oracle attack and is far too common.

To protect from this class of attacks you can use an authenticated encryption scheme, for example the GCM blockcipher mode.

Also you have to protect against replay attacks. Consider a banking application and the data you are transmitting is a bank transfer order. Barring any TAN an attacker might record a previous transaction and replay this transaction to your service again and again thus transferring a multiple of the money the customer originally wanted to.

Is the form you are getting the data from transmitted over HTTPS? If not: Can the key be eavesdropped by an attacker? How does a user know he got the form from you and not anybody else (SSL/TLS is as much about authentication as it is about confidentiality).

Probably I have forgotten some other attack vectors simple CBC-encryption offers.

Alternatives

Probably the easiest way to protect against these attacks is to transmit the form data over HTTPS. SSL/TLS was designed to prevent all of the above attacks and the client and server side implementations had a long time to mature.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top