重写的Rijndael 256 C#代码加密在PHP [英] Rewrite Rijndael 256 C# Encryption Code in PHP

查看:285
本文介绍了重写的Rijndael 256 C#代码加密在PHP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个加密/解密算法C#编写的 - 我需要能够产生在PHP中相同的加密这样我就可以发送加密的文本通过HTTP在C#方进行解密。
下面是加密的C#代码



  this.m_plainText =的String.Empty。 
this.m_passPhrase =passpharse;
this.m_saltValue =saltvalue;
this.m_hashAlgorithm =SHA1;
this.m_passwordIterations = 2;
this.m_initVector =1a2b3c4d5e6f7g8h;
this.m_keySize = 256;

公共字符串加密()
{
字符串明文= this.m_plainText;
串口令= this.m_passPhrase;
串saltValue = this.m_saltValue;
的HashAlgorithm串= this.m_hashAlgorithm;
INT passwordIterations = this.m_passwordIterations;
串initVector = this.m_initVector;
INT密钥长度= this.m_keySize;

//字符串转换成字节数组。
//我们假设串只包含ASCII码。
//如果字符串包括Unicode字符,使用Unicode,UTF7,UTF8或
//编码。
字节[] = initVectorBytes Encoding.ASCII.GetBytes(initVector);
字节[] = saltValueBytes Encoding.ASCII.GetBytes(saltValue);

//我们转换明文成字节数组。
//我们假设明文包含UTF8编码字符。
字节[] = plainTextBytes Encoding.UTF8.GetBytes(明文);

//首先,我们必须创建一个密码,从该密钥将被导出。
//此密码将在指定的密码和
//盐值生成。密码将使用指定的哈希值
//算法创建。密码创建可以在多次迭代来完成。
PasswordDeriveBytes密码=新PasswordDeriveBytes(
密码,
saltValueBytes,
的HashAlgorithm,
passwordIterations);

//使用密码来生成用于加密
//密钥的伪随机字节。指定密钥的字节大小(而不是位)。
字节[] = keyBytes password.GetBytes(密钥/ 8);

//创建初始化Rijndael加密对象。
RijndaelManaged的symmetricKey =新RijndaelManaged的();

//这是合理的设置的加密模式加密块链接
//(CBC)。使用其他对称密钥参数的默认选项。
symmetricKey.Mode = CipherMode.CBC;

//生成从现有的密钥字节和初始化
//矢量加密。关键尺寸将根据密钥
//字节数来限定。
ICryptoTransform的加密= symmetricKey.CreateEncryptor(
keyBytes,
initVectorBytes);

//定义将被用于保存加密数据存储器流。
的MemoryStream MemoryStream的=新的MemoryStream();

//定义的加密数据流(通常用于加密写入模式)。
的CryptoStream的CryptoStream =新的CryptoStream(的MemoryStream,
加密,
CryptoStreamMode.Write);
//开始加密。
cryptoStream.Write(plainTextBytes,0,plainTextBytes.Length);

//完成加密。
cryptoStream.FlushFinalBlock();

//从内存流转换我们的加密数据变成一个字节数组。
字节[] = cipherTextBytes memoryStream.ToArray();

//关闭两个流。
memoryStream.Close();
cryptoStream.Close();

//将加密后的数据为base64编码字符串。
串的密文= Convert.ToBase64String(cipherTextBytes);

//返回加密的字符串。
返回密文;
}



我有一些类似的PHP代码,可以帮助。 。根据需要,它没有做完全是,但我认为这可能是一个很好的起点。



 < PHP 

/ *
*定义常量
* /
$ HashPassPhrase =passpharse;
$ HashSalt =saltvalue;
$ =的HashAlgorithmSHA1;
$ HashIterations =2;
$ InitVector =1a2b3c4d5e6f7g8h; //必须是16个字节
$密钥大小=256;

级密码{
$私人securekey,$ IV;
功能__construct($ textkey){
$这个 - GT&; securekey =哈希($的HashAlgorithm,$ textkey,TRUE);
$这个 - GT&; 4 = $ InitVector;
}
功能加密($输入){
返回BASE64_ENCODE(mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$这个 - > securekey,$输入,MCRYPT_MODE_CBC,$这个 - > 4));
}
功能解密($输入){
返回修剪(mcrypt_decrypt(MCRYPT_RIJNDAEL_256,$这个 - > securekey,BASE64_DECODE($输入),MCRYPT_MODE_CBC,$这个 - > 4)) ;
}
}

$密码=新密码($ HashPassPhrase);

$ encryptedtext = $ cipher->加密(文本加密);
回声 - >加密= $ encryptedtext< BR />中;

$ decryptedtext = $ cipher->解密($ encryptedtext);
回声 - >解密= $ decryptedtext< BR />中;

的var_dump($密码);



?>


解决方案

您需要派生自口令密钥的方式相同的C#代码做了的 PasswordDeriveBytes 。这是记录做PBKDF1密钥导出,按 RFC2898




本类使用PKCS#5
V2.0标准定义的
PBKDF1算法的延伸推导出字节适合
用作关键材料从
密码。该标准文件
在IETF RRC 2898。




有实现PBKDF1那里PHP库,但其实很简单要从头写一个基于ONT他RFC:




PBKDF1(p,S,C,为dkLen)



选项:Hash结果
底层的散列函数



输入:p结果
口令,字节串
票盐,一八字节串
C迭代次数,正整数
为dkLen打算长度导出密钥,
A正整数的八位最多16 MD2
或$ b 20 $ b MD5和SHA-1



输出:DK衍生
键,为dkLen字节串



步骤:

  1.如果为dkLen> 16 MD2和MD5,或为dkLen> 20 SHA-1,输出
派生密钥太长,并停止。

2.应用基础散列函数哈希对于C迭代口令P的
级联和盐S,然后解压
第一dkLen字节以产生导出密钥DK:

T_1 =哈希(P || S),
T_2 =哈希(T_1),
...
T_C =哈希(T_ {C- 1}),
DK =锝&下; 0..dkLen-1。>

3.输出导出密钥DK。




更新



当你在这种情况下找到yo​​uself,你通常搜索一个例子implementaiton显示在每一步的值。例如一个在 http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf

 密码=密码
=(0X)70617373776F7264
盐=( 0X)78578E5A5D63CB06
计数= 1000
kLen = 16
键= PBKDF1(口令,盐,计数,kLen)
=(0X)DC19847E05C64D2FAF10EBFB4A3D2A20

P || S = 70617373776F726478578E5A5D63CB06
T_1 = D1F94C4D447039B034494400F2E7DF9DCB67C308
T_2 = 2BB479C1D369EA74BB976BBA2629744E8259C6F5
...
T_999 = 6663F4611D61571068B5DA168974C6FF2C9775AC
T_1000 = DC19847E05C64D2FAF10EBFB4A3D2A20B4E35EFE
键= DC19847E05C64D2FAF10EBFB4A3D2A20

所以,现在让写一个PHP函数,它这样的:

 函数PBKDF1($传球,$盐,$数,$为dkLen){
$ T = $ $通盐。
// echo的||病人:'.bin2hex($ T),'< BR />';
$ T = SHA1($ T,真正的);
//回声'T1:。 BIN2HEX($ T)。 '< BR />';
为($ i = 2; $ I< = $计数; $ I ++){$​​ B $ B $ T = SHA1($ T,真正的);
//回声'T'$我。':'。 BIN2HEX($ T)。 '< BR />';
}
$ T = SUBSTR($ T,0,$为dkLen);
$回报吨;
}

现在你可以看到你的方式ERRS:你没有指定的所有重要原料= TRUE 参数 SHA1 。让我们看看什么是我们的函数的输出:

  $ HashPassPhrase =包(H *,70617373776F7264); 
$ HashSalt =包(H *,78578E5A5D63CB06);
$ HashIterations = 1000;
$ devkeylength = 16;
$ devkey = PBKDF1($ HashPassPhrase,$ HashSalt,$ HashIterations,$ devkeylength);
回声'键:。 BIN2HEX(SUBSTR($ devkey,0,8))。 '< BR />';
回声'四:。 BIN2HEX(SUBSTR($ devkey,8,8))'< BR />'。
回声预计:DC19847E05C64D2FAF10EBFB4A3D2A20< BR />';



这个输出正是预期的结果:

 键:dc19847e05c64d2f 
四:af10ebfb4a3d2a20
预计:DC19847E05C64D2FAF10EBFB4A3D2A20

接下来,我们可以验证的C#函数的作用是相同的:

 字节[]密码=编码。 ASCII.GetBytes(密码); 
字节[] =盐新的字节[] {0x78,0×57,为0x8E,5AH即可,0x5d,0x63,0xcb,0×06};

PasswordDeriveBytes PDB =新PasswordDeriveBytes(
密码,盐,SHA1,1000);

的byte []键= pdb.GetBytes(8);
的byte [] IV = pdb.GetBytes(8);

Console.Out.Write(键:);
的foreach(以字节键B)
{
Console.Out.Write({0:X},B);
}
Console.Out.WriteLine();

Console.Out.Write(IV);
的foreach(BYTE B在四)
{
Console.Out.Write({0:X},B);
}
Console.Out.WriteLine();



这将产生同样的输出:

 键:DC 19 84 7E 5 C6 4D 2F 
IV:AF 10 EB FB 4A 3D 2A 20

QED



奖金的解释



请不要做加密,如果你不知道的究竟的你在做什么。你得到的PHP implementaiton正确的即使,你发布的C#代码有一些严重的问题。你是混合字节数组与stirng代表十六进制的转储,则使用硬编码的IV,而不是从密码和盐推导它,仅仅是整体错误的。请使用现成的加密方案,如SSL或S-MIME和不重新发明自己。你会得到它的错误


I have an encryption/decryption algorithm written in C# - I need to be able to produce the same encryption in PHP so I can send the encrypted text over HTTP to be decrypted on the C# side. Here is the C# code for the encryption.

this.m_plainText = string.Empty;
this.m_passPhrase = "passpharse";
this.m_saltValue = "saltvalue";
this.m_hashAlgorithm = "SHA1";
this.m_passwordIterations = 2;
this.m_initVector = "1a2b3c4d5e6f7g8h";
this.m_keySize = 256;

public string Encrypt()
{
    string plainText = this.m_plainText;
    string passPhrase = this.m_passPhrase;
    string saltValue = this.m_saltValue;
    string hashAlgorithm = this.m_hashAlgorithm;
    int passwordIterations = this.m_passwordIterations;
    string initVector = this.m_initVector;
    int keySize = this.m_keySize;

    // Convert strings into byte arrays.
    // Let us assume that strings only contain ASCII codes.
    // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
    // encoding.
    byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
    byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

    // Convert our plaintext into a byte array.
    // Let us assume that plaintext contains UTF8-encoded characters.
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    // First, we must create a password, from which the key will be derived.
    // This password will be generated from the specified passphrase and 
    // salt value. The password will be created using the specified hash 
    // algorithm. Password creation can be done in several iterations.
    PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                    passPhrase,
                                                    saltValueBytes,
                                                    hashAlgorithm,
                                                    passwordIterations);

    // Use the password to generate pseudo-random bytes for the encryption
    // key. Specify the size of the key in bytes (instead of bits).
    byte[] keyBytes = password.GetBytes(keySize / 8);

    // Create uninitialized Rijndael encryption object.
    RijndaelManaged symmetricKey = new RijndaelManaged();

    // It is reasonable to set encryption mode to Cipher Block Chaining
    // (CBC). Use default options for other symmetric key parameters.
    symmetricKey.Mode = CipherMode.CBC;

    // Generate encryptor from the existing key bytes and initialization 
    // vector. Key size will be defined based on the number of the key 
    // bytes.
    ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                     keyBytes,
                                                     initVectorBytes);

    // Define memory stream which will be used to hold encrypted data.
    MemoryStream memoryStream = new MemoryStream();

    // Define cryptographic stream (always use Write mode for encryption).
    CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                 encryptor,
                                                 CryptoStreamMode.Write);
    // Start encrypting.
    cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

    // Finish encrypting.
    cryptoStream.FlushFinalBlock();

    // Convert our encrypted data from a memory stream into a byte array.
    byte[] cipherTextBytes = memoryStream.ToArray();

    // Close both streams.
    memoryStream.Close();
    cryptoStream.Close();

    // Convert encrypted data into a base64-encoded string.
    string cipherText = Convert.ToBase64String(cipherTextBytes);

    // Return encrypted string.
    return cipherText;
}

I have some similar PHP code that may help. It doesn't do exactly as needed, but I think it may be a good place to start.

<?php

/*
 * DEFINE CONSTANTS
 */
$HashPassPhrase = "passpharse";
$HashSalt = "saltvalue";
$HashAlgorithm = "SHA1";
$HashIterations = "2";
$InitVector = "1a2b3c4d5e6f7g8h";        // Must be 16 bytes
$keySize = "256";

class Cipher {
    private $securekey, $iv;
    function __construct($textkey) {
        $this->securekey = hash($HashAlgorithm,$textkey,TRUE);
        $this->iv = $InitVector;
    }
    function encrypt($input) {
        return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_CBC, $this->iv));
    }
    function decrypt($input) {
        return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $this->securekey, base64_decode($input), MCRYPT_MODE_CBC, $this->iv));
    }
}

$cipher = new Cipher($HashPassPhrase);

$encryptedtext = $cipher->encrypt("Text To Encrypt");
echo "->encrypt = $encryptedtext<br />";

$decryptedtext = $cipher->decrypt($encryptedtext);
echo "->decrypt = $decryptedtext<br />";

var_dump($cipher);

?>

解决方案

You need to derive the key from the pass phrase the same way as the C# code does in the PasswordDeriveBytes. This is documented to do PBKDF1 key derivation, as per RFC2898:

This class uses an extension of the PBKDF1 algorithm defined in the PKCS#5 v2.0 standard to derive bytes suitable for use as key material from a password. The standard is documented in IETF RRC 2898.

there are PHP libraries that implement PBKDF1 out there, but is really simple to write one from scratch based ont he RFC:

PBKDF1 (P, S, c, dkLen)

Options: Hash
underlying hash function

Input: P
password, an octet string S salt, an eight-octet string c iteration count, a positive integer dkLen intended length in octets of derived key, a positive integer, at most 16 for MD2 or MD5 and 20 for SHA-1

Output: DK derived key, a dkLen-octet string

Steps:

  1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output
     "derived key too long" and stop.

  2. Apply the underlying hash function Hash for c iterations to the
     concatenation of the password P and the salt S, then extract
     the first dkLen octets to produce a derived key DK:

               T_1 = Hash (P || S) ,
               T_2 = Hash (T_1) ,
               ...
               T_c = Hash (T_{c-1}) ,
               DK = Tc<0..dkLen-1>

  3. Output the derived key DK.

Updated

When you find youself in this situation, you usually search for an example implementaiton that shows the values at every step. for instance the one at http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf:

Password = "password" 
         = (0x)70617373776F7264
Salt     = (0x)78578E5A5D63CB06
Count    = 1000
kLen     = 16
Key      = PBKDF1(Password, Salt, Count, kLen)
         = (0x)DC19847E05C64D2FAF10EBFB4A3D2A20

P || S = 70617373776F726478578E5A5D63CB06
T_1=     D1F94C4D447039B034494400F2E7DF9DCB67C308
T_2=     2BB479C1D369EA74BB976BBA2629744E8259C6F5
...
T_999=   6663F4611D61571068B5DA168974C6FF2C9775AC
T_1000=  DC19847E05C64D2FAF10EBFB4A3D2A20B4E35EFE
Key=     DC19847E05C64D2FAF10EBFB4A3D2A20

So now lets write a PHP function that does this:

function PBKDF1($pass,$salt,$count,$dklen) { 
    $t = $pass.$salt;
    //echo 'S||P: '.bin2hex($t).'<br/>';
    $t = sha1($t, true); 
    //echo 'T1:' . bin2hex($t) . '<br/>';
    for($i=2; $i <= $count; $i++) { 
        $t = sha1($t, true); 
        //echo 'T'.$i.':' . bin2hex($t) . '<br/>';
    } 
    $t = substr($t,0,$dklen); 
    return $t;      
}

Now you can see the errs of your ways: you did not specify the all important raw=true parameter to sha1. Lets see what is our function output:

$HashPassPhrase = pack("H*","70617373776F7264");
$HashSalt = pack("H*","78578E5A5D63CB06"); 
$HashIterations = 1000; 
$devkeylength = 16; 
$devkey = PBKDF1($HashPassPhrase,$HashSalt,$HashIterations,$devkeylength);
echo 'Key:' . bin2hex(substr($devkey, 0, 8)) . '<br/>';
echo 'IV:' . bin2hex(substr($devkey, 8, 8)) .'<br/>';
echo 'Expected: DC19847E05C64D2FAF10EBFB4A3D2A20<br/>';

this output exactly the expected result:

Key:dc19847e05c64d2f
IV:af10ebfb4a3d2a20
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20

Next, we can validate that the C# function does the same:

            byte[] password = Encoding.ASCII.GetBytes("password");
            byte[] salt = new byte[] { 0x78, 0x57, 0x8e, 0x5a, 0x5d, 0x63, 0xcb, 0x06};

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(
                password, salt, "SHA1", 1000);

            byte[] key = pdb.GetBytes(8);
            byte[] iv = pdb.GetBytes(8);

            Console.Out.Write("Key: ");
            foreach (byte b in key)
            {
                Console.Out.Write("{0:x} ", b);
            }
            Console.Out.WriteLine();

            Console.Out.Write("IV: ");
            foreach (byte b in iv)
            {
                Console.Out.Write("{0:x} ", b);
            }
            Console.Out.WriteLine();

this produces the very same output:

Key: dc 19 84 7e 5 c6 4d 2f
IV: af 10 eb fb 4a 3d 2a 20

QED

bonus explanation

Please don't do crypto if you don't know exactly what you're doing. Even after you get the PHP implementaiton correct, your posted C# code has some serious problems. You are mixing byte arrays with stirng representing hex dumps, you use a hard coded IV instead of deriving it from the passphrase and salt, is just overall plain wrong. Please use an off-the shelf encryption scheme, like SSL or S-MIME and do not re-invent your own. You will get it wrong.

这篇关于重写的Rijndael 256 C#代码加密在PHP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆