I am been trying to similar pattern in PHP of following c# code . I had spent 7 days, trying to implement a simple encrypted communication in my EPin API application. The thing is, that response from the php script is giving me Blank
public static string AESDecryptText(string input, string key)
{
// Get the bytes of the string
byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
keyBytes = SHA256.Create().ComputeHash(keyBytes);
byte[] bytesDecrypted = AESDecrypt(bytesToBeDecrypted, keyBytes);
string result = Encoding.UTF8.GetString(bytesDecrypted);
return result;
}
public static byte[] AESDecrypt(byte[] bytesToBeDecrypted, byte[] keyBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(keyBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
MY PHP Code from other thread ..
function DecryptString($content, $password =
'4J2lh3Lz4q6ACo16VrL1oLDnh3k7G1KaXliUPVPV8o0='){
$password = mb_convert_encoding($password, "utf-16le");
$padding = 32 - (strlen($password) % 32);
$password .= str_repeat("\0", $padding);
$iv = substr($password, 0, 8);
$data = base64_decode($content);
$decrypted = openssl_decrypt($data, 'AES-256-CBC', $password,
OPENSSL_RAW_DATA, $iv);
$decrypted = mb_convert_encoding($decrypted, "utf-8", "utf-16le");
return $decrypted;
}
function decryption(){
$password = "4J2lh3Lz4q6ACo16VrL1oLDnh3k7G1KaXliUPVPV8o0=";
$content = "wKamNpehMEqJQ4NcUueNuXq1PbupsxwEvwcJ0CeI+8Q=";
echo $this->DecryptString($content, $password);
}
Every time I prints , showing empty blank. Please help on this regards
In your C# code you use SHA256 to hash the password and Rfc2898DeriveBytes to create the key and IV. The PHP analogous functions are hash (with sha256) and hash_pbkdf2.
A PHP equivalent of your C# AESDecryptText method:
function AESDecryptText($content, $password){
$password = hash('sha256', $password, true);
$salt = pack("C*",1,2,3,4,5,6,7,8);
$bytes = hash_pbkdf2("sha1", $password, $salt, 1000, 48, true);
$key = substr($bytes, 0, 32);
$iv = substr($bytes, 32, 16);
return openssl_decrypt($content, 'AES-256-CBC', $key, 0, $iv);
}
Some notes:
Salt should be unique for each password.
IV should be random and not related to the password or key.
You could use openssl_random_pseudo_bytes to create random bytes.
Salt and IV don't have to be secret, you can store them next to the ciphertext.
You should consider using HMAC to authenticate your ciphertext.
You could use hash_hmac for that purpose.
Related
I have a php code code like below
$cipher = "AES-128-CBC";
$key = openssl_digest("OfVb3CjsZhnX81ZzjfAHuIiMtKEUyrt6", 'SHA256', TRUE);
$ivlen = openssl_cipher_iv_length($cipher);
$iv = openssl_random_pseudo_bytes($ivlen);
$ciphertext_raw = openssl_encrypt("wsUser", $cipher, $key, OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext_raw, $key, true);
$result = base64_encode($iv . $hmac . $ciphertext_raw);
//$result value will decode by http_build_query
// $request_query["Key"] = $result;
// $last_query = http_build_query($request_query);
I converted PHPcode to C# like this ;
var cipher = "AES-128-CBC-----";
var key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes("OfVb3CjsZhnX81ZzjfAHuIiMtKEUyrt6"));
var ivlen = cipher.Length;
byte[] iv = new byte[16]; Random rnd = new Random();
rnd.NextBytes(iv);
var aes = Aes.Create("AesManaged");
//C# 4.5
byte[] m = new byte[16];
Array.Copy(key, 0, m, 0, 16);
aes.Key = m;
//C# 8.0
aes.Key = key[0..16];
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
var encryptor = aes.CreateEncryptor(aes.Key,iv);
var ciphertextRaw = encryptor.TransformFinalBlock(Encoding.UTF8.GetBytes(txtwsuser.Text), 0, txtwsuser.Text.Length); //this is "wsUser"
var hmac = new HMACSHA256(key).ComputeHash(ciphertextRaw);
string cw = Convert.ToBase64String(iv.Concat(hmac).Concat(ciphertextRaw).ToArray());
textBox1.Text = HttpUtility.UrlEncode(cw).Replace("%3d", "%3D"); //this line for debug purposes
but even though the two hashes (key value and hmac value) are the same, PHP decryptor block doesn'T work.
my PHP decryptor code block like this;
$encrypted_value = urldecode("data_from_client"); // can be used textBox1.Text value for this.
$cipher = "AES-128-CBC";
$key = openssl_digest(wsKey, 'SHA256', TRUE);
$cw = base64_decode($encrypted_value);
$ivlen = openssl_cipher_iv_length($cipher);
$iv = substr($cw, 0, $ivlen);
$hmac = substr($cw, $ivlen, $sha2len=32);
$cw3 = substr($cw, $ivlen+$sha2len);
$calcmac = hash_hmac('SHA256', $cw3, $key, true);
$result = openssl_decrypt($cw3, $cipher, $key, OPENSSL_RAW_DATA, $iv);
decrytor PHP code works with PHP code above, but C# doesn't work. I tried some AES methods but no change.How can AES-128 encryption be the same in php and C#. ?
Thanks in advance
C# code generated
sCn1%2bY2Kx9RzIiDIsWL1xWRxecMO%2bkPhIdkBfXbV6uD7%2by75f3EvwpyP4e3CHLDjBZ7U0CrVDsK4MdBUn25K0Q%3D%3D
value. PHP cannot decrypt the data. but hash values are correct.
PHP Code generated
%2FUMZJmd2LfIJzJntETKcgo%2F7VDaBV8U9iLY%2Bopq2Up5%2BrtOLRMxR%2B3OzbzgLEx0ZZErjZFP8oOtMmnZ%2FEX1dxA%3D%3D
this works fine.
After Add lines to C# Code at belows, problem solved
//C# 4.5
byte[] m = new byte[16];
Array.Copy(key, 0, m, 0, 16);
aes.Key = m;
//C# 8.0
aes.Key = key[0..16];
I seem to be having problems getting an AES256 string to decode between a PHP and .NET application. I get an error in the .Net application stating "Padding is invalid and cannot be removed." This error fires in the using statement for the CrytoStream. The workflow is pretty straight forward. The PHP application encrypts a value and passes it to the .NET application as a URL parameter. The .NET application needs to decrypt that value for later use. The .NET method works from .NET to .NET, but PHP to .NET is the problem.
The PHP code:
function encrypt($text) {
$key = "M2AZULUALPHA";
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, 'TripBuilder2017');
return base64_encode($crypttext);
}
The .NET Decrypt method:
private string Decrypt(string cipherText)
{
string EncryptionKey = "M2AZULUALPHA";
byte[] saltArray = Encoding.ASCII.GetBytes("TripBuilder2017");
cipherText = cipherText.Replace(" ", "+");
byte[] cipherBytes = Convert.FromBase64String(cipherText);
using (Aes encryptor = Aes.Create())
{
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, saltArray);
encryptor.KeySize = 256;
encryptor.Padding = PaddingMode.PKCS7;
encryptor.Key = pdb.GetBytes(32);
encryptor.IV = pdb.GetBytes(16);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherBytes, 0, cipherBytes.Length);
cs.Close();
}
cipherText = Encoding.Unicode.GetString(ms.ToArray());
}
}
return cipherText;
}
UPDATE
I changed the mode in PHP to Rijndael256 and I also changed AES to RijndaelManaged in .NET. Again, I am able to get this working between .NET applications, but not with the PHP application. I am wondering if there is an issue with the padding the PHP application is using.
I updated answer completely after messages :-)
php:
function encrypt($text)
{
//$key = "M2AZULUALPHA"; // type 2 -- mcrypt_encrypt(): Key of size 12 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported -- at line 7
$key = "M2AZULUALPHA1234";
//$vi ='TripBuilder2017'; // type 2 -- mcrypt_encrypt(): Received initialization vector of size 15, but size 16 is required for this encryption mode -- at line 9
$vi ='TripBuilder20170';
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);
$crypttext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $vi));
return $crypttext;
}
$result = encrypt("abcdcddsfdafdfe");
echo"$result";
output is "pPPH8amRhqbdX6D83jr74A=="
c# with RijndaelManaged
public String Decrypt(string cipherText)
{
var result = "";
var cypher = Convert.FromBase64String(cipherText);
var encoding = System.Text.Encoding.UTF8;
var Key = encoding.GetBytes("M2AZULUALPHA1234");
var IV = encoding.GetBytes("TripBuilder20170");
using (var rj = new RijndaelManaged())
{
rj.Padding = PaddingMode.PKCS7;
rj.Mode = CipherMode.CBC;
rj.KeySize = 256;
rj.Key = Key;
rj.IV = IV;
var ms = new MemoryStream(cypher);
using (var cs = new CryptoStream(ms, rj.CreateDecryptor(Key, IV), CryptoStreamMode.Read))
using (var sr = new StreamReader(cs))
result = sr.ReadToEnd();
}
return result;
}
test:
var result = Decrypt("pPPH8amRhqbdX6D83jr74A==");
Debug.WriteLine(result);
output is "abcdcddsfdafdfe"
I'm trying to decrypt a string in PHP.
I use this code for encrypting the string in C#:
public string EncryptMessage(string text, string key)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(text);
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Padding = PaddingMode.Zeros;
aes.Mode = CipherMode.CBC;
aes.Key = Encoding.Default.GetBytes(key);
aes.GenerateIV();
string IV = ("specialstring" + Encoding.Default.GetString(aes.IV));
ICryptoTransform AESEncrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] buffer = plainTextBytes;
return
Convert.ToBase64String(Encoding.Default.GetBytes(Encoding.Default.GetString(AESEncrypt.TransformFinalBlock(buffer, 0, buffer.Length)) + IV));
}
I also use this code for decrypting the string in PHP:
function decrypt($text, $pkey)
{
$key = $pkey;
$text = base64_decode($text);
$IV = substr($text, strrpos($text, "specialstring") );
$text = str_replace("specialstring".$IV, "", $text);
$res = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $IV), "\0");
return $res;
}
The PHP code always return empty string. Whats wrong in my code and how can I fix it?
I did some encryption using PHP in my Database and would normally decrypt using:
$encrypt_method = "AES-256-CBC";
$secret_key = "testing";
$secret_iv = "testingyes!!!";
$key = hash('sha256', $secret_key); // hash the key
$iv = substr(hash('sha256', $secret_iv), 0, 16); // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
echo(openssl_decrypt(base64_decode($data), $encrypt_method, $key, 0, $iv)); // the decrypted data
I'm trying to do the same task but with C# 2013 to decrypt the same data, any ideas?
I would encrypt in php using:
$encrypt_method = "AES-256-CBC";
$secret_key = "testing";
$secret_iv = "testingyes!!!";
$key = hash('sha256', $secret_key); // hash the key
$iv = substr(hash('sha256', $secret_iv), 0, 16); // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning
echo(base64_encode(openssl_encrypt($data, $encrypt_method, $key, 0, $iv))); // the encrypted data
encrypting: this is a test
gives: d0EzQ2MvMHkxRks2cXg5NkFkK2twZz09=
I tried this in C#:
public static String sha256_hash(String value)
{
StringBuilder Sb = new StringBuilder();
using (SHA256 hash = SHA256Managed.Create())
{
Encoding enc = Encoding.UTF8;
Byte[] result = hash.ComputeHash(enc.GetBytes(value));
foreach (Byte b in result)
Sb.Append(b.ToString("x2"));
}
return Sb.ToString();
}
private static String AES_decrypt(String Input)
{
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = Convert.FromBase64String(sha256_hash("testing"));
aes.IV = Convert.FromBase64String(sha256_hash("testingyes!!!").Substring(0, 16));
var decrypt = aes.CreateDecryptor();
byte[] xBuff = null;
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
{
byte[] xXml = Convert.FromBase64String(Input);
cs.Write(xXml, 0, xXml.Length);
}
xBuff = ms.ToArray();
}
String Output = Encoding.UTF8.GetString(xBuff);
return Output;
}
string cipherData = "d0EzQ2MvMHkxRks2cXg5NkFkK2twZz09=";
string f = AES_decrypt(cipherData);
Console.Write(f);
But I'm getting error: specified key is not a valid size for this algorithm
However the key I'm using is working when I use PHP
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 256;
Block size should be 128 to be compatible with AES-256-CBC.
Rijndael supports variable block sizes - AES does not.
I've been ask to do a task in a project that involves encryption on a windows 8.
The scenario is something like this:
I get a byte[] from a server, the first 16 bytes are the IV, the next 128 are the Salt and the remaining ones are the File itself.
The user then provides a password and with that and the salt i should create a PKCS5 Key with 40 iterations and the key should have 32bytes length.
Right now i've splitted the byte[] in th 3 i require, but i dont know how the rest is done in windows C#.
I've done some work with encryption/decryption, but let me give you the resource I used for AES 256 bit encryption. Hopefully this will give you an idea of how to switch it over to PKCS5, but everything else I'm pretty sure is the same. It's a little lengthy, but let me know if this applies to your situation. I'm curious how much different it would be for PKCS5 instead of AES256.
Edit: Because the code they posted wasn't clear on the iterations, the iterations is controlled by the line var key = Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); using 1000 iterations.
http://www.codeproject.com/Articles/769741/Csharp-AES-bits-Encryption-Library-with-Salt
Core Encryption Code
using System.Security.Cryptography;
using System.IO;
Encryption
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
byte[] encryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
cs.Close();
}
encryptedBytes = ms.ToArray();
}
}
return encryptedBytes;
}
Decryption
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
// Set your salt here, change it to meet your flavor:
// The salt bytes must be at least 8 bytes.
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
Getting Randomized Encryption Result with Salt
If we encrypt the same context (i.e. string of "Hello World") for 10 times, the encrypted results will be the same. What if we want the results different from each time it is encrypted?
What I do is appending a random salt bytes in front of the original bytes before encryption, and remove it after decryption.
Example of Appending Randomized Salt Before Encrypting a String
public string Encrypt(string text, string pwd)
{
byte[] originalBytes = Encoding.UTF8.GetBytes(text);
byte[] encryptedBytes = null;
byte[] passwordBytes = Encoding.UTF8.GetBytes(pwd);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
// Generating salt bytes
byte[] saltBytes = GetRandomBytes();
// Appending salt bytes to original bytes
byte[] bytesToBeEncrypted = new byte[saltBytes.Length + originalBytes.Length];
for (int i = 0; i < saltBytes.Length; i++)
{
bytesToBeEncrypted[i] = saltBytes[i];
}
for (int i = 0; i < originalBytes.Length; i++)
{
bytesToBeEncrypted[i + saltBytes.Length] = originalBytes[i];
}
encryptedBytes = AES_Encrypt(bytesToBeEncrypted, passwordBytes);
return Convert.ToBase64String(encryptedBytes);
}
Example of Removing the Salt after Decryption
public string Decrypt(string decryptedText, string pwd)
{
byte[] bytesToBeDecrypted = Convert.FromBase64String(decryptedText);
byte[] passwordBytes = Encoding.UTF8.GetBytes(pwd);
// Hash the password with SHA256
passwordBytes = SHA256.Create().ComputeHash(passwordBytes);
byte[] decryptedBytes = AES_Decrypt(bytesToBeDecrypted, passwordBytes);
// Getting the size of salt
int _saltSize = 4;
// Removing salt bytes, retrieving original bytes
byte[] originalBytes = new byte[decryptedBytes.Length - _saltSize];
for (int i = _saltSize; i < decryptedBytes.Length; i++)
{
originalBytes[i - _saltSize] = decryptedBytes[i];
}
return Encoding.UTF8.GetString(originalBytes);
}
Code for getting random bytes
public byte[] GetRandomBytes()
{
int _saltSize = 4;
byte[] ba = new byte[_saltSize];
RNGCryptoServiceProvider.Create().GetBytes(ba);
return ba;
}
Step 1: Split the incoming data into IV, salt and cyphertext. You say you have done this.
Step 2: Pass the supplied password and the salt from step 1 as inputs to the PKCS5 key generation method, using 40 iterations. There should be a PKCS5 class in your crypto library. The output from this step will be a key.
Step 3: Use the key from step 2 and the IV from step 1 to decrypt the cyphertext from step 1. Use the specified decryption algorithm, probably AES, in the specified mode. Since an IV is supplied then it is likely that CBC mode is intended, so you will probably need to use the AES-CBC mode from your cypher library. Check the problem specification to confirm both algorithm and cypher mode -- I am only guessing here.
If you have a problem with any of these steps, ask here again, showing your code and explaining the errors you are getting.