将DES-EDE3-CBC从PHP移植到Ruby会得到不同的结果

发布时间:2020-07-06 11:13

我正在尝试将PHP库移植到ruby,并且crypto / decrypt函数没有给出相同的结果:

我正在尝试转换此方法:

function encrypt_3DES($message, $key){
  $l = ceil(strlen($message) / 8) * 8;
  $padded_message = $message . str_repeat("\0", $l - strlen($message));
  $iv = "\0\0\0\0\0\0\0\0";
  return substr(openssl_encrypt($padded_message, 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, $iv), 0, $l);
}

对此:

def encrypt_3DES(message, key)
  cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
  cipher.encrypt
  cipher.key = key
  cipher.iv = "\0" * 8
  output = cipher.update(message) + cipher.final
  output 
end

这给了我一些问题:

  1. 原始密钥长24个字节,而ruby引发异常

    • 原始密钥:Base64.decode64('sq7HjrUOBfKmC576ILgskD5srU870gJ7')
    • 用红宝石引发:ArgumentError (key must be 16 bytes)
    • 在php中,它可以与任何键长一起使用
  2. 使用ruby接受的相同消息和密钥,php输出会有所不同

    • 消息:message1
    • 键:1234567890123456
    • base64中的php输出:uJxPvodsbLs=
    • 在base64中的红宝石输出:K8v3K/w4pS7R2DHXr0UHbQ==

我在做什么错了?

我正在使用PHP 7.3.11和ruby 2.6.5p114

回答1

des-ede-cbc(在Ruby中使用)与Tripple-des的变体稍有不同,与PHP中使用的des-ede3-cbc略有不同。对于des-ede-cbc,您将在两个加密阶段使用相同的密钥(这说明了为什么只需要两个密钥,即2 * 8 = 16字节),而对于des-ede3-cbc,您将使用三个密钥,因此3 * 8 = 24字节。

如果使用正确的算法,则会从OpenSSL获得相同的输出(但请参见下文)。

function encrypt_3DES($message, $key){
  $l = ceil(strlen($message) / 8) * 8;
  $padded_message = $message . str_repeat("\0", $l - strlen($message));
  $iv = "\0\0\0\0\0\0\0\0";
  return substr(openssl_encrypt($padded_message, 'des-ede3-cbc', $key, OPENSSL_RAW_DATA, $iv), 0, $l);
}

echo base64_encode(encrypt_3DES('message1', base64_decode('sq7HjrUOBfKmC576ILgskD5srU870gJ7')));
# => "OO4olPtGedE="
def encrypt_3DES(message, key)
  cipher = OpenSSL::Cipher.new('des-ede3-cbc')
  cipher.encrypt
  cipher.key = key
  cipher.iv = "\0" * 8
  output = cipher.update(message)
  output 
end

Base64.encode64 encrypt_3DES('message1', Base64.decode64('sq7HjrUOBfKmC576ILgskD5srU870gJ7'))
# => "OO4olPtGedE6qq1lHRAtgQ==\n"

您可以看到两个实例中的前缀相同。唯一的区别是,在您的PHP实现中,您没有生成(重新删除)最终的块,但是最终的块实际上是解密数据所必需的。通过从PHP版本中删除substr逻辑,在两种情况下,您都应该获得正确的加密数据。

话虽如此,请考虑3DES是一种非常过时且(对于大多数当前应用而言)不安全的算法。如果您打算在任何玩具应用程序之外使用此代码(即:如果您打算将其与此附带的生产代码一起使用,则应使用更现代的算法。一个很好,安全且维护良好的选项是使用libsodium的密码箱。) 7.2,这是PHP本身提供的,较早的版本可以使用PECL软件包。some documentation上有关于如何使用它的信息。

在Ruby中,您可以使用rbnacl宝石,它在底层也使用libsodium并具有相同的功能。