将C#加密算法转换为Ruby

3

你好,我有一个用C#编写的加密算法,需要将其移植到Ruby。

private string Encrypt(string clearText)
{
    string EncryptionKey = "ENC_KEY";
    byte[] clearBytes = Encoding.Unicode.GetBytes(clearText); 
    using (Aes encryptor = Aes.Create())
    {
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11 });
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        using (MemoryStream ms = new MemoryStream()) {
        using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(clearBytes, 0, clearBytes.Length); cs.Close();
        }
        clearText = Convert.ToBase64String(ms.ToArray()); }
    }
    return clearText;
}

据我理解,该算法生成AES密钥和iv并进行加密,返回base 64字符串。

我没有找到Rfc2898DeriveBytes的确切替代方法,我使用了PBKDF2 Gem。这是我的Ruby方法:

def self.encrypt clear_text
  iterations = 1000
  encryption_key = 'EncryptionKey'
  clearBytes = clear_text.encode( 'UTF-16LE' ).bytes.to_a
  enc_bytes = [0x1, 0x2, 0x3, 0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11]
  salt = enc_bytes.pack('C*')
  derived_a = PBKDF2.new do |p|
    p.password = encryption_key
    p.salt = salt
    p.iterations = iterations
    p.key_length = 32
  end

  derived_b = PBKDF2.new do |p|
    p.password = encryption_key
    p.salt = salt
    p.iterations = iterations
    p.key_length = 16
  end

  key = derived_a.bin_string
  # iV = derived_b.bin_string
  iV_a = iV_a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] # Static iV
  iV = iV_a.pack('C*')


  cipher = OpenSSL::Cipher::AES256.new(:CBC)
  cipher.encrypt
  cipher.key = key
  cipher.iv = iV
  encrypted = cipher.update(clear_text) + cipher.final
  Base64.encode64(encrypted)
end

我的代码中有两个问题。我无法在iv和使用它作为静态值时获取相同的值,而且返回值不匹配。

我没有太多的C#经验。我漏掉了什么?


1
MSDN指出getBytes函数在每次调用时会更改密钥大小。也就是说,如果您两次调用getBytes(10),那么这与直接调用getBytes(20)相同。对于这个问题有一些解决方法,您可以在第一次调用时生成一个更长的密钥,然后将其切片成派生密钥和IV。 - Mehmet Ince
如果你没有太多的C#经验,我建议你不要“翻译”代码,而是从头开始用Ruby编写。如果你更熟悉Ruby,这可能比尝试解密一个未知语言更容易。 - onebree
2个回答

2

看起来你的PBKDF2生成算法有固定的输入,因此得出的密钥初始化向量应该总是相同。

我刚在我的计算机上运行了修改后输出密钥初始化向量值的C#代码。它给了我:

takKsX7IBXq3R0Q5GWgJo/XhhEHDNfRFxSVru12vtU4=
y/lm9eKzBJTMdU+uA6GlXA==

作为Base64编码的KeyIV的值。您可以在Ruby代码中直接使用这些值,这将省去使用PBKDF2 gem生成这些值的需要。
因此,这段Ruby代码
clear_text = 'HELLO WORLD'
cipher = OpenSSL::Cipher::AES256.new(:CBC)
cipher.encrypt
cipher.key = Base64.decode64('takKsX7IBXq3R0Q5GWgJo/XhhEHDNfRFxSVru12vtU4=')
cipher.iv = Base64.decode64('y/lm9eKzBJTMdU+uA6GlXA==')
clearBytes = clearText.encode('UTF-16LE')
encrypted = cipher.update(clearBytes)
encrypted << cipher.final
puts Base64.encode64(encrypted)

将输出与Encrypt("HELLO WORLD")相同的内容。


非常感谢。当我将密钥设置为Base64字符串时,问题得到了解决。 - htkaya

1
首先,您有不同的加密密钥。
string EncryptionKey = "ENC_KEY";
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x5, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10, 0x11 });

这与以下内容不同:

encryption_key = 'EncryptionKey'
...
derived_a = PBKDF2.new do |p|
    p.password = encryption_key
    p.salt = salt
    p.iterations = iterations
    p.key_length = 32
  end

如果你确实使用相同的“everything”,在KDF之前需要确保你的密码短语相同 :)
此外,不要忘记将你的IV与密文一起打包,因为你不希望它(或者你的盐)是静态的,进行加密和MAC处理,所有这些好东西 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接