【问题标题】:Using plain text as key and IV in PHP's mcrypt在 PHP 的 mcrypt 中使用纯文本作为密钥和 IV
【发布时间】:2016-01-29 11:13:41
【问题描述】:

我有一个应用程序,它使用 CBC 方法在商定的密钥和 IV 上以 TripleDES 加密方式在供应商的应用程序之间交换数据。我的应用程序是 VB.NET 应用程序,而供应商的应用程序是 PHP 网页。奇怪的是,我似乎无法匹配供应商应用程序的输出。

密钥和初始向量以明文形式提供给我们双方。有趣的是,在供应商的应用程序中,他们不会散列或将密钥和 IV 转换为 256 位密钥和 128 位 IV。他们只是使用 PHP 中的 mcrypt 插件在代码中使用纯文本格式。

$key = 'plaintextkey';
$iv = 'plaintextIV'
$enc = mcrypt_encrypt(MCRYPT_3DES, $key, $string, MCRYPT_MODE_CBC, $iv)

它产生了一个有趣的文本,例如:

û!‰+«°z0"üÐÑn

我的问题是:

  1. 如果要明文做参数,PHP怎么做 实现/将其转换为 256 位字符?
  2. 如果使用CBC方法,它不应该依赖于之前的 加密文本?
  3. 在我给定的格式中是否有 TripleDES 结果?因为作为我 通过互联网搜索,我从未见过结果 这种形式的加密。

【问题讨论】:

  • 那么我想他们采取了一种相当简单的方法。尝试将它们指向PHP Manual page,它会建议正确的做事方式,所有这些都是在示例中而不是对 mcrypt 函数的描述
  • @RiggsFolly 那么,他们对密钥和 IV 的使用是错误的吗?我看到在以前版本的 PHP 中,他们添加了 \0 填充 以前的键和 IV 用 '\0' 字节填充到下一个有效大小。,所以如果他们使用最新的 PHP,它会'已经返回假?

标签: php encryption mcrypt tripledes


【解决方案1】:

感谢 Artjom 的回答提到您需要添加几个 0,直到密钥和 IV 分别达到 24 和 8 的长度。以下是 VB.NET 代码,以防有人需要。

Dim byteKey As Byte() = System.Text.Encoding.UTF8.GetBytes("myplaintextkey")
Dim byteVector As Byte() = System.Text.Encoding.UTF8.GetBytes("myplaintextiv")

Dim base64Key As String = Convert.ToBase64String(byteKey)
Dim base64Vector As String = Convert.ToBase64String(byteVector)

Dim key As Byte() = Convert.FromBase64String(base64Key)
Dim iv As Byte() = Convert.FromBase64String(base64Vector)

Dim encryptor As SymmetricEncryptor = New SymmetricEncryptor() 'Note, this an internal library

Try
    encryptor.Provider = New System.Security.Cryptography.TripleDESCryptoServiceProvider

    encryptor.Encoder = System.Text.Encoding.UTF8
    encryptor.Provider.Mode = CipherMode.CBC
    encryptor.Provider.Padding = PaddingMode.Zeros

    'Add missing zeros to key and IV
    If key.Length < 24 Then
        Dim i As Integer = key.Length
        ReDim Preserve key(23)
        While i < 24
            key(i) = 0
            i += 1
        End While
    ElseIf key.Length > 24 Then
        ReDim Preserve key(23)
    End If

    If iv.Length < 8 Then
        Dim i As Integer = iv.Length
        ReDim Preserve iv(8)
        While i < 8
            iv(i) = 0
            i += 1
        End While
    ElseIf iv.Length > 8 Then
        ReDim Preserve iv(8)
    End If
    encryptor.IV = iv
    encryptor.Key = key

    Dim output As String = encryptor.Encrypt(output)

    Console.Write(output)
Catch ex As Exception
    Console.Write(ex.Message)
End Try

我希望它对某人有所帮助,再次感谢 Artjom!

【讨论】:

    【解决方案2】:

    是否应该使用明文作为参数,PHP是如何实现/转换成256位字符的?

    三重 DES 是使用三个(不同的)DES 密钥对 DES 应用三次。一个 DES 密钥是 56 位长和 64 位带奇偶校验的。因此,一个 3DES 密钥应该由三个 不同 DES 密钥组成,这应该产生一个 192 位密钥。

    密钥应该具有高熵,因此有必要对相当多的密钥空间进行暴力破解。如果密钥是密码,那么这要容易得多,因为密码具有固有的格式和有限的字符集。这就是为什么需要从密码中派生密钥的原因。这通常使用 PBKDF2、bcrypt 或 scrypt(使用随机生成的盐和数千或数百万/高成本因素的大量迭代)来完成。

    现在,mcrypt 做出了一些值得怀疑的决定。例如,它通过将 0x00 字节填充到下一个有效大小或将它们裁剪到下一个有效大小来愉快地接受(在早期的 PHP 版本中)无效的键和 IV。这就是为什么使用“plaintextkey”不是一个好主意。它将导致第二个密钥的某些字节为 0x00 字节,第三个密钥为完整的 0x00 字节。

    mcrypt 是废弃软件,不应再使用。您可以使用 PHP 中的 OpenSSL 扩展或许多好的加密库之一,如 libsodium 或 defuse/php-encryption。

    如果使用CBC方法,不应该依赖之前的加密文本吗?

    您可以将最后一个密文块用作下一次加密的 IV,但这只会使加密代码复杂化。您确实应该每次都使用随机 IV,并将其添加到密文中。它不必是秘密的,而是不可预测的。您可以将它与之前的随机盐一起存储。另见:Why use an Initialization Vector (IV)?

    在我给定的格式中是否有 TripleDES 结果?因为当我在互联网上搜索时,我从未见过这种形式的加密结果。

    现代密码(保留格式的加密是一个明显的例外)产生的密文应该是indistinguishable from random "noise"。因此,您可能会收到不可打印的字符,例如 \x01 以及更多作为密文的一部分。如果您想打印密文,则需要使用 Hex 或 Base 64 编码成一些可打印的格式。

    【讨论】:

    • 哇,这真的很有帮助。所以简而言之,为了匹配供应商的输出,我必须在我的键和向量中添加一些零填充,对吧?我想告诉他们 mcrypt 已经过时了,但我不应该这样说。 :(
    • 是的,用 0x00 字节填充最多 24 个字节的密钥并在前 8 个字节处裁剪 IV 应该没问题。请记住,mcrypt 也对明文使用零填充。它会自动将明文填充到块大小的下一个倍数(3DES 为 8 字节)。但是,如果明文已经是块大小的倍数,它不会添加完整的填充块。这可能是与 System.Security.Cryptography.PaddingMode.Zeros 不同的行为,后者总是添加至少一个填充字节。你应该尝试一下,因为它在 PHP 中的工作方式,它可能仍然兼容。
    • 老兄,我终于做到了。非常感谢您的回答,我也会用我的解决方案更新我的问题。 长距离拳头撞击
    • 不,不要为您的问题添加解决方案。您可以自己发布答案。
    猜你喜欢
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-02
    • 1970-01-01
    • 2013-11-15
    • 2017-08-02
    相关资源
    最近更新 更多