Search code examples
javascriptruby-on-railscryptographyaescryptojs

CryptoJS is not working with IV and key, but it works in Ruby


So, I have the encryption and decryption methods on ruby and they work fine ruby. I followed the answer from this question (How to decrypt message with CryptoJS AES. I have a working Ruby example) but it's returning an empty string.

Ruby Code

def load_vars
        @key = "2e35f242a46d67eeb74aabc37d5e5d05"
        @algorithm = "aes-128-cbc"
    end

    def encryption(data)
        begin
            key = @key
            aes = OpenSSL::Cipher.new(@algorithm)
            aes.encrypt()
            aes.key = key
            iv_value = aes.random_iv
            aes.iv = iv_value
            crypt = aes.update(data) + aes.final()
            crypt_string = (Base64.encode64(iv_value + crypt))
            return crypt_string
        end
    end

    def decryption(data)
        begin
            key = @key
            aes = OpenSSL::Cipher.new(@algorithm)
            iv_value = Base64.decode64(data)[0...16]
            data_value = Base64.decode64(data)[16..-1]
            aes.decrypt
            aes.key = @key
            aes.iv = iv_value
            results = aes.update(data_value) + aes.final
            return results
        end
    end

HTML JSFIDDLE

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>

<script>        
    data = "wlkAKa4ps+Xizx4VIdUSp43yfQvOmt9FNlVTQ1ANsCU=\n"; // The data received from the ruby encryption method
    key = "2e35f242a46d67eeb74aabc37d5e5d05";

    // Decode the base64 data so we can separate iv and crypt text.
    var rawData = atob(data);
    var iv = rawData.substring(0,16);
    var crypttext = rawData.substring(16);

    // Decrypt...
    var plaintextArray = CryptoJS.AES.decrypt(
      { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
      CryptoJS.enc.Hex.parse(key),
      { iv: CryptoJS.enc.Latin1.parse(iv) }
    );

    console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));
</script>

Solution

  • The problem is that Ruby expects the key to be in binary format, not hex. So you need to do this:

    #!/usr/bin/env ruby
    
    require 'openssl'
    require 'base64'
    
    data = "When in Rome do as the Romans do"
    key = "2e35f242a46d67eeb74aabc37d5e5d05"
    aes = OpenSSL::Cipher.new("aes-128-cbc")
    aes.encrypt()
    aes.key = key.scan(/../).collect{|x| x.hex}.pack("c*")
    iv_value = aes.random_iv
    aes.iv = iv_value
    crypt = aes.update(data) + aes.final()
    crypt_string = (Base64.encode64(iv_value + crypt))
    puts crypt_string
    

    For me that prints

    mdnLCY6MdwEONY1AxR/vjVKMssB+yrPsz4QMjfl6fDXxv68E0EUxtAqa4VUo
    fTkjq2Hqyd48UV3dyWmEbwXw5Q==
    

    If I put that into your HTML file (without changing any code), I get back the original message:

    <!DOCTYPE html>
    <html>
    <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
    
    <script>        
    data = "mdnLCY6MdwEONY1AxR/vjVKMssB+yrPsz4QMjfl6fDXxv68E0EUxtAqa4VUo\nfTkjq2Hqyd48UV3dyWmEbwXw5Q==\n";
    key = "2e35f242a46d67eeb74aabc37d5e5d05";
    
    var rawData = atob(data);
    var iv = rawData.substring(0,16);
    var crypttext = rawData.substring(16);
    
    // Decrypt...
    var plaintextArray = CryptoJS.AES.decrypt(
      { ciphertext: CryptoJS.enc.Latin1.parse(crypttext) },
      CryptoJS.enc.Hex.parse(key),
      { iv: CryptoJS.enc.Latin1.parse(iv) }
    );
    
    console.log(plaintextArray);
    console.log(CryptoJS.enc.Latin1.stringify(plaintextArray));
    </script>
    </head>
    <body>
    </body>
    </html>
    

    So the problem is that your Ruby code produced a gibberish cyphertext. Fix the key and re-encrypt, and the JS should start working.