问题
I'm trying to synchronize my encryption and decryption methods between C# and PHP but something seems to be going wrong.
In the Windows Phone 7 SDK you can use AESManaged to encrypt your data
I use the following method:
public static string EncryptA(string dataToEncrypt, string password, string salt)
{
AesManaged aes = null;
MemoryStream memoryStream = null;
CryptoStream cryptoStream = null;
try
{
//Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));
//Create AES algorithm with 256 bit key and 128-bit block size
aes = new AesManaged();
aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // rfc2898.GetBytes(aes.BlockSize / 8);
// to check my results against those of PHP
var blaat1 = Convert.ToBase64String(aes.Key);
var blaat2 = Convert.ToBase64String(aes.IV);
//Create Memory and Crypto Streams
memoryStream = new MemoryStream();
cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
//Encrypt Data
byte[] data = Encoding.Unicode.GetBytes(dataToEncrypt);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
//Return Base 64 String
string result = Convert.ToBase64String(memoryStream.ToArray());
return result;
}
finally
{
if (cryptoStream != null)
cryptoStream.Close();
if (memoryStream != null)
memoryStream.Close();
if (aes != null)
aes.Clear();
}
}
I solved the problem of generating the Key. The Key and IV are similar as those on the PHP end. But then the final step in the encryption is going wrong.
here is my PHP code
<?php
function pbkdf2($p, $s, $c, $dk_len, $algo = 'sha1') {
// experimentally determine h_len for the algorithm in question
static $lengths;
if (!isset($lengths[$algo])) { $lengths[$algo] = strlen(hash($algo, null, true)); }
$h_len = $lengths[$algo];
if ($dk_len > (pow(2, 32) - 1) * $h_len) {
return false; // derived key is too long
} else {
$l = ceil($dk_len / $h_len); // number of derived key blocks to compute
$t = null;
for ($i = 1; $i <= $l; $i++) {
$f = $u = hash_hmac($algo, $s . pack('N', $i), $p, true); // first iterate
for ($j = 1; $j < $c; $j++) {
$f ^= ($u = hash_hmac($algo, $u, $p, true)); // xor each iterate
}
$t .= $f; // concatenate blocks of the derived key
}
return substr($t, 0, $dk_len); // return the derived key of correct length
}
}
$password = 'test';
$salt = 'saltsalt';
$text = "texttoencrypt";
#$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
#echo $iv_size . '<br/>';
#$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
#print_r (mcrypt_list_algorithms());
$iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
$key = pbkdf2($password, $salt, 1000, 32);
echo 'key: ' . base64_encode($key) . '<br/>';
echo 'iv: ' . base64_encode($iv) . '<br/>';
echo '<br/><br/>';
function addpadding($string, $blocksize = 32){
$len = strlen($string);
$pad = $blocksize - ($len % $blocksize);
$string .= str_repeat(chr($pad), $pad);
return $string;
}
echo 'text: ' . $text . '<br/>';
echo 'text: ' . addpadding($text) . '<br/>';
// -- works till here
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
echo '1.' . $crypttext . '<br/>';
$crypttext = base64_encode($crypttext);
echo '2.' . $crypttext . '<br/>';
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, addpadding($text), MCRYPT_MODE_CBC, $iv);
echo '1.' . $crypttext . '<br/>';
$crypttext = base64_encode($crypttext);
echo '2.' . $crypttext . '<br/>';
?>
So to point out, the Key and IV look similar on both .NET and PHP, but something seems to be going wrong in the final call when executing mcrypt_encrypt(). The end result, the encrypted string, differs from .NET.
Can anybody tell me what i'm doing wrong. As far as i can see everything should be correct.
Thank you!
EDIT:
Additional information on the AESManaged object in .NET
Keysize = 256 Mode = CBC Padding = PKCS7
回答1:
MCRYPT_RIJNDAEL_256
is the version of the Rijndael algorithm with block size 256 bits, while AES is only the versions with block size 128 bits. These are not compatible.
Use MCRYPT_RIJNDAEL_128
, to get an algorithm which is equivalent to AES. It still supports all three key sizes standardized as AES, i.e. 128 bits (16 bytes), 196 bits (24 bytes) and 256 bits (32 bytes). Simply pass a long enough string as key.
回答2:
I solved the problems. You need to make sure you use UTF8 Encoding on the strings and you are using the correct padding (PKCS7)
Here is the code:
public static string EncryptA(string dataToEncrypt, string password, string salt)
{
AesManaged aes = null;
MemoryStream memoryStream = null;
CryptoStream cryptoStream = null;
try
{
//Generate a Key based on a Password, Salt and HMACSHA1 pseudo-random number generator
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt));
//Create AES algorithm with 256 bit key and 128-bit block size
aes = new AesManaged();
aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
aes.IV = Encoding.UTF8.GetBytes("AAAAAAAAAAAAAAAA"); // new byte[] { 0x41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // rfc2898.GetBytes(aes.BlockSize / 8);
//Create Memory and Crypto Streams
memoryStream = new MemoryStream();
cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(aes.Key, aes.IV), CryptoStreamMode.Write);
//Encrypt Data
byte[] data = Encoding.UTF8.GetBytes(dataToEncrypt);
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
//Return Base 64 String
var temp = memoryStream.ToArray();
string result = Convert.ToBase64String(temp);
return result;
}
finally
{
if (cryptoStream != null)
cryptoStream.Close();
if (memoryStream != null)
memoryStream.Close();
if (aes != null)
aes.Clear();
}
}
and in php:
<?php
function pbkdf2($p, $s, $c, $dk_len, $algo = 'sha1') {
// experimentally determine h_len for the algorithm in question
static $lengths;
if (!isset($lengths[$algo])) { $lengths[$algo] = strlen(hash($algo, null, true)); }
$h_len = $lengths[$algo];
if ($dk_len > (pow(2, 32) - 1) * $h_len) {
return false; // derived key is too long
} else {
$l = ceil($dk_len / $h_len); // number of derived key blocks to compute
$t = null;
for ($i = 1; $i <= $l; $i++) {
$f = $u = hash_hmac($algo, $s . pack('N', $i), $p, true); // first iterate
for ($j = 1; $j < $c; $j++) {
$f ^= ($u = hash_hmac($algo, $u, $p, true)); // xor each iterate
}
$t .= $f; // concatenate blocks of the derived key
}
return substr($t, 0, $dk_len); // return the derived key of correct length
}
}
$text = "blaat";
$password = 'this is my secret passwordthis is my secret password';
$salt = 'thisismysalt';
#$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
#$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$iv = 'AAAAAAAAAAAAAAAA';
$key = pbkdf2($password, $salt, 1000, 32);
echo 'key size: ' . strlen($key) . '<br/>';
echo 'key: ' . base64_encode($key) . '<br/>';
echo 'iv size: ' . strlen($iv) . '<br/>';
#echo 'iv: ' . $iv . '<br/>';
echo 'iv: ' . base64_encode($iv) . '<br/>';
echo '<br/><br/>';
# $data = $this->paddingAlgorithm->padData($data, $blockSize);
# return $iv . mcrypt_encrypt($this->MCRYPT_DES, $keyBytes, $data, MCRYPT_MODE_CBC, $iv);
function addpadding($string)
{
$blocksize = 16;
$len = strlen($string);
$pad = $blocksize - ($len % $blocksize);
$string .= str_repeat(chr($pad), $pad);
return $string;
}
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, addpadding($text), MCRYPT_MODE_CBC, $iv);
$crypttext = base64_encode($crypttext);
echo '3. [' . $crypttext . ']<br/>';
echo '<br/>';
?>
Hope this will help somebody as well.
Paulo thanks for your help!
来源:https://stackoverflow.com/questions/8116133/wp-aesmanaged-encryption-vs-mcrypt-encrypt