使用AES进行Rails加密,过于复杂

时间:2018-12-20 23:40:30

标签: ruby encryption openssl aes ecb

我在加密使用的第三方供应商的值时遇到麻烦。

他们的说明如下:

1) Convert the encryption password to a byte array.
2) Convert the value to be encrypted to a byte array.
3) The entire length of the array is inserted as the first four bytes onto the front 
   of the first block of the resultant byte array before encryption.
4) Encrypt the value using AES with:
        1. 256-bit key size,
        2. 256-bit block size, 
        3. Encryption Mode ECB, and
        4. an EMPTY initialization vector.
5) After encryption, you should now have a byte array that holds the encrypted value. 
6) Convert each byte to a HEX format and string all the HEX values together.
7) The final result is a string of HEX values. This is the final encrypted value to be passed. 
   The length of the final value will always be an even number.

EXAMPLE:
Given the following input values:
plainText: 2017/02/07 22:46
secretKey: ABCD1234FGHI5678
The following string will be produced:
D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04

到目前为止我尝试过的...

plain_text = "2017/02/07 22:46"
secret_key = "ABCD1234FGHI5678"

plain_text_byte_array = plain_text.bytes
plain_text_byte_array.unshift(0).unshift(0).unshift(0).unshift(16) # I found a Java example in their documentation and this is what they do. They prepend their byte array with 16, 0, 0, 0
secret_byte_array = secret_key.bytes
secret_byte_array = secret_byte_array.concat([0, 0, 0,...]) # also from their java example, they append the secret_byte array with 16 0's in order to get its length to 32

cipher = OpenSSL::Cipher::AES256.new(:ECB)
cipher.key = secret_byte_array.pack("C*")
encrypted = cipher.update(plain_text_byte_array.pack("C*")) + cipher.final

p encrypted.unpack("H*").first.to_s.upcase

# Result is: 
#    "84A0E5DCA7D704C41332F86E707DDAC244A1A87C38A906145DE4060D2BC5C8F4"

您可以看到我的结果与实际结果不符,应该是 “ D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04”

有人知道我是否正在丢失某些东西或做一些奇怪的事情。他们的指令对我来说很难理解,所以也许我遗漏了一些东西。感谢您提供任何帮助! (我已经对您在上面看到的内容尝试了很多不同的变化)。我只需要一些指导,或者至少需要有人告诉我,我不因不理解他们的指示而发疯。

2 个答案:

答案 0 :(得分:5)

我设法重现了他们的结果-他们所使用的过程非常复杂,而且可能不尽人意。我已经附加了更具描述性的解释,说明了达到其结果所需的步骤以及我曾经使用过的C#源代码。

  1. 将密码转换为字节数组。字节数组必须的长度为32个字节,如果密码不够长,则应为用0字节右填充。因此,其十六进制编码的密码成为4142434431323334464748493536373800000000000000000000000000000000

  2. 将要加密的值转换为字节数组。这很简单,只需使用UTF-8进行编码。

  3. 在加密之前,将数组的整个长度作为前四个字节插入到结果字节数组的第一个块的开头。这很愚蠢,没有用,但是将步骤2中的字节数组的长度作为 32位无符号整数,并转换为 little endian 字节数组。将其前缀到第2步中的数组。

  4. 使用AES加密值。嗯不,不要那样做。使用 Rijndael 对值进行加密,使用256位块大小,256位密钥大小,ECB模式和零填充。

  5. 其余的操作很简单,只需将加密结果转换为十六进制即可。

下面用于实现此结果的代码在C#中。我不太了解Ruby。

    // 1. Convert the encryption password to a byte array.
    byte[] passwordBytesOriginal = Encoding.UTF8.GetBytes("ABCD1234FGHI5678");
    byte[] passwordBytes = new byte[32];
    Array.Copy(passwordBytesOriginal, 0, passwordBytes, 0, passwordBytesOriginal.Length);


    // 2. Convert the value to be encrypted to a byte array.
    byte[] valueBytes = Encoding.UTF8.GetBytes("2017/02/07 22:46");

    // 3. The entire length of the array is inserted as the first four bytes onto the front 
    // of the first block of the resultant byte array before encryption.
    byte[] valueLengthAsBytes = BitConverter.GetBytes((uint)valueBytes.Length);
    byte[] finalPlaintext = new byte[valueBytes.Length + valueLengthAsBytes.Length];
    Array.Copy(valueLengthAsBytes, 0, finalPlaintext, 0, valueLengthAsBytes.Length);
    Array.Copy(valueBytes, 0, finalPlaintext, valueLengthAsBytes.Length, valueBytes.Length);

    // 4. Encrypt the value using AES...
    byte[] ciphertext;
    using (RijndaelManaged rijn = new RijndaelManaged())
    {
        rijn.BlockSize = 256;
        rijn.KeySize = 256;
        rijn.Key = passwordBytes;
        rijn.Mode = CipherMode.ECB;
        rijn.Padding = PaddingMode.Zeros;

        var encryptor = rijn.CreateEncryptor();
        ciphertext = encryptor.TransformFinalBlock(finalPlaintext, 0, finalPlaintext.Length);
    }

    // 5., 6., 7...
    string result = BitConverter.ToString(ciphertext).Replace("-", "").ToUpper();
    Console.WriteLine(result); // D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04

答案 1 :(得分:4)

基于Luke's excellent answer的是Ruby版本。我必须使用ruby-mcrypt gem并使用brew install libmcrypt在本地安装mcrypt库。

正如卢克的答案所指出的那样,密钥应正确填充0。这是我的代码:

   plain_text = "2017/02/07 22:46"
   secret_text = "ABCD1234FGHI5678"
   answer = "D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04"

   def format_byte_arrays(plain, secret)
     zero_byte_array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
     length_array = [16, 0, 0, 0]

     plain_bytes = length_array.concat(plain.bytes)
     secret_bytes = secret.bytes.concat(zero_byte_array)

     [plain_bytes, secret_bytes]
   end

   plain_bytes, secret_bytes = format_byte_arrays(plain_text, secret_text)
   final_plain, final_secret = [plain_bytes.pack("C*"), secret_bytes.pack("C*")]

   cipher = Mcrypt.new("rijndael-256", :ecb, final_secret, nil, :zeros)
   encrypted = cipher.encrypt(final_plain)
   result = encrypted.unpack("H*").first.to_s.upcase

结果将是正确的答案。