php中的c#MCRYPT_RIJNDAEL_256加密解密类

时间:2018-09-05 08:57:52

标签: c# php

我试图将c#应用程序转换为php,但我停留在C#提供基于RIJNDAEL算法进行加密和解密的Security类的地方。我正在尝试将其转换为php。

注意::我使用的是php 7.2,因此该版本不建议使用mcrypt。

C#代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace pharmarackencryption
{
    class Program
    {
        private const string initVector = "aw90rela942f65u2";

        // This constant is used to determine the keysize of the encryption algorithm.
        private const int keysize = 256;

        public static string Encrypt(string plainText, string passPhrase = "testing")
        {
            byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
            byte[] keyBytes = password.GetBytes(keysize / 8);
            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            cryptoStream.FlushFinalBlock();
            byte[] cipherTextBytes = memoryStream.ToArray();
            memoryStream.Close();
            cryptoStream.Close();
            return Convert.ToBase64String(cipherTextBytes);
        }

        public static string Decrypt(string cipherText, string passPhrase = "testing")
        {
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
            PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
            byte[] keyBytes = password.GetBytes(keysize / 8);
            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
            byte[] plainTextBytes = new byte[cipherTextBytes.Length];
            int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
            memoryStream.Close();
            cryptoStream.Close();
            return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            string enc_password = Encrypt("437217");
            string dec_password = Decrypt("C9xJGa03dRQx9ePm0nLnHg==");
            Console.WriteLine(enc_password);
            Console.WriteLine(dec_password);
        }
    }
}

加密:C9xJGa03dRQx9ePm0nLnHg ==

我在php中找到了一些类似的代码

PHP代码:

<?php 
    // key/iv in ASCII binary data, $str base64
    function decrypt_stuff($key, $str, $iv) {
        // $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($str), MCRYPT_MODE_CBC, $iv);
        $plaintext_dec = openssl_decrypt(base64_decode($str), "aes-256-cbc", $key,  OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);
        return $plaintext_dec;
    }

    // key/iv in ascii binary data, $str ascii
    function encrypt_stuff($key, $str, $iv) {
        // $ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $str, MCRYPT_MODE_CBC, $iv));
        if (($l = (strlen($str) & 15)) > 0) { $str .= str_repeat(chr(0), 16 - $l); }
        $ciphertext = base64_encode(openssl_encrypt($str, "aes-256-cbc", $key,  OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv));
        return $ciphertext;
    }

    echo encrypt_stuff("testing","437217","aw90rela942f65u2");
    //Result : LTbhEHjFgfa5PDJQXJEdKQ==

两者都是一样的,但结果仍然不同

4 个答案:

答案 0 :(得分:4)

您的PHP代码产生不同的结果,因为您没有使用相同的密钥。在C#代码中,您正在使用PasswordDeriveBytes创建密钥,这是Microsoft PBKDF1的实现。

PHP不支持PBKDF1,但是我们可以编写与C#兼容的函数。以下代码的灵感来自Maarten Bodewes出色的answer,已翻译为PHP。

function passwordDeriveBytes($password, $salt, $iterations = 100, $len = 32) {
    $key = $password . $salt;
    for($i = 0; $i < $iterations; $i++) {
        $key = sha1($key, true);
    }
    if (strlen($key) < $len) {
        $hx = passwordDeriveBytes($password, $salt, $iterations - 1, 20);
        $counter = 0;
        while (strlen($key) < $len) {
            $counter += 1;
            $key .= sha1($counter . $hx, true);
        }
    }
    return substr($key, 0, $len);
}

此外,您还需要bese64手动编码和填充数据。您正在执行零字节填充,但是在C#代码中,您正在使用PKCS7(默认和首选)填充。最好让openssl填充并编码数据。

function encrypt_stuff($key, $str, $iv) {
    return openssl_encrypt($str, "aes-256-cbc", $key, 0, $iv);
}

function decrypt_stuff($key, $str, $iv) {
    return openssl_decrypt($str, "aes-256-cbc", $key, 0, $iv);
}

使用从passwordDeriveBytes派生的密钥,此PHP代码产生与C#代码相同的结果。

$key = passwordDeriveBytes("testing", null);
$enc = encrypt_stuff($key,"437217","aw90rela942f65u2");
echo $enc;
//C9xJGa03dRQx9ePm0nLnHg==

但是,出于以下原因,我不建议使用此代码。

  • 最好将PBKDF2用作密钥。您可以在C#中使用Rfc2898DeriveBytes

    Rfc2898DeriveBytes kdf = new Rfc2898DeriveBytes(password, salt, iterations);
    byte[] key = kdf.GetBytes(32);
    

    hash_pbkdf2在PHP中:

    $key = hash_pbkdf2("sha1", $password, $salt, $iterations, 32, true);
    

    salt的长度至少应为8个字节,并且迭代次数至少应为10,000。

  • 您没有使用盐。您应该为每个密码使用随机盐,这会使您的密钥更坚固。

  • 您正在使用静态IV。 IV应该是唯一且不可预测的。您可以使用RNGCryptoServiceProvider创建一个随机IV:

    byte[] iv = new byte[16];
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); 
    rng.GetBytes(iv);
    

    openssl_random_pseudo_bytes

    $iv = openssl_random_pseudo_bytes(16);
    

    您也可以使用此代码编写盐。

  • 您没有使用经过身份验证的加密。如果您使用的是PHP 7,则可以使用GCM,但不幸的是.NET不提供任何AEAD算法。但是,如果您选择使用经过身份验证的加密,则可以使用bouncycastle


但是您的每次使用加密代码都会产生不同的结果,因为您应该使用随机IV。如果您具有IV,则可以正确解密密文。静脉注射不一定要保密。您可以将其存储在密文旁边。

如果您将mcryptMCRYPT_RIJNDAEL_256一起使用,您仍然可以使您的PHP代码与C#兼容,因为RijndaelManaged支持256位块大小。

symmetricKey.BlockSize = 256;

但是您不应该使用mcrypt,因为它没有得到维护,并且在PHP 7中已弃用。

答案 1 :(得分:1)

我对这个问题的C#结尾不熟悉。但是,我会指出一些可能与您的问题有关的信息。

  • RIJNDAEL函数中的描述涉及的是块大小,而不是密钥大小。例如,MCRYPT_RIJNDAEL_256指出您使用的块大小为256。它没有指定密钥大小。
  • 对于openssl AES函数,您要指定密钥大小。例如,aes-256-cbc指定您使用的是256位密钥。
  • 所有AES功能的块大小为128位。因此,您可以使用与AES块大小匹配的MCRYPT_RIJNDAEL_128。您可以对MCRYPT_RIJNDAEL_128aes-256-cbc都使用256位密钥,因为它们都使用128位块大小。
  • 因此,由于这些原因,aes-256-cbc无法与MCRYPT_RIJNDAEL_256一起使用。它们根本不是一回事。

  • 这是多余的。如果您使用的是aes-256-cbc,则需要确保使用的是256位密钥。显然,请确保您使用相同的密钥进行加密和解密。确保您的IV是正确的IV。我会将它们都设为静态以进行测试。一旦找到它,就可以尝试在加密时将IV添加到密码字符串中,并在解密时将IV与密文分开

  • 使用openssl_random_pseudo_bytes()生成256位(32字节)密钥。您还可以使用openssl_random_pseudo_bytes()生成IV。

在旁注。我强烈建议使用LibSodium库。现在它是PHP最新版本中的本机,并且还具有C#库。您可以在Github上轻松找到它。

一旦您了解了这一点,我将着眼于学习如何验证/验证您的加密。这是一个很好的开始链接。 Authentication Read

希望有帮助。

答案 2 :(得分:0)

不建议使用可逆算法将密码存储在数据库中,密码应使用昂贵的哈希值存储。您应该考虑将密码存储区切换为Argon2之类的哈希。

检查以下内容:http://php.net/manual/en/function.password-hash.php

答案 3 :(得分:-1)

mcrypt已移动。没有删除。您可以尝试安装此

sudo apt-get -y install gcc make autoconf libc-dev pkg-config
sudo apt-get -y install php7.2-dev
sudo apt-get -y install libmcrypt-dev
sudo pecl install mcrypt-1.0.1

PS:未经测试