Der*_*ong 12 php c# encryption aes rijndael
我有一个用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;
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;
}
Run Code Online (Sandbox Code Playgroud)
我有一些类似的PHP代码可能有所帮助.它没有完全按照需要做,但我认为这可能是一个好的开始.
<?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);
Run Code Online (Sandbox Code Playgroud)
?>
Rem*_*anu 17
您需要获得来自口令方式与C#代码相同确实在关键PasswordDeriveBytes.据记录,这可以根据RFC2898进行PBKDF1密钥派生:
此类使用PKCS#5 v2.0标准中定义的PBKDF1算法的扩展来派生适合用作密码的密钥材料的字节.该标准记录在IETF RRC 2898中.
有一些PHP库可以实现PBKDF1,但是基于RFC可以很容易地从头开始编写:
PBKDF1(P,S,c,dkLen)
选项:哈希
底层哈希函数输入:P
密码,八位字节串S盐,八位八位字符串c迭代计数,正整数dkLen预期长度,以导出密钥的八位字节为单位,正整数,最多16为MD2或MD5,20为SHA-1输出:DK派生密钥,dkLen-octet字符串
脚步:
Run Code Online (Sandbox Code Playgroud)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.
更新
当您在这种情况下发现自己时,通常会搜索一个示例实现,以显示每一步的值.例如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
Run Code Online (Sandbox Code Playgroud)
所以现在让我们编写一个执行此操作的PHP函数:
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;
}
Run Code Online (Sandbox Code Playgroud)
现在你可以看到你的方式的错误:你没有指定所有重要的raw=true参数sha1.让我们看看我们的功能输出是什么:
$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/>';
Run Code Online (Sandbox Code Playgroud)
这个输出恰好是预期的结果:
Key:dc19847e05c64d2f
IV:af10ebfb4a3d2a20
Expected: DC19847E05C64D2FAF10EBFB4A3D2A20
Run Code Online (Sandbox Code Playgroud)
接下来,我们可以验证C#函数是否执行相同的操作:
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();
Run Code Online (Sandbox Code Playgroud)
这会产生相同的输出:
Key: dc 19 84 7e 5 c6 4d 2f
IV: af 10 eb fb 4a 3d 2a 20
Run Code Online (Sandbox Code Playgroud)
QED
请不要做加密,如果你不知道究竟你在做什么.即使你的PHP实现正确,你发布的C#代码也有一些严重的问题.你正在使用代表十六进制转储的stirng混合字节数组,你使用硬编码的IV而不是从密码和盐中导出它,这只是整体上的错误.请使用现成的加密方案,如SSL或S-MIME,不要重新创建自己的加密方案.你会弄错的.