Ok, So for several days now I've been trying to figure out what it is I'm doing wrong trying to decrypt information being encrypted in Yii2 and then sent to my windows form program.
I'm using Yii2 encrypt by key method which returns a string in the following format.
[keySalt][MAC][IV][ciphertext]
KeySalt is the key size in bytes. MAC is the length same as the output of MAC_HASH. IV is the length of blocksize.
I have it set in Yii2 to use AES-192-CBC. So the according to the yiiframwork yii-base-security, the block size is 16 and the key size is 24.
My web request looks like the following.
try
{
var data = new MemoryStream();
var WR = (HttpWebRequest)WebRequest.Create(url);
ServicePointManager.Expect100Continue = true;
ServicePointManager.DefaultConnectionLimit = 9999;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
WR.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
WR.UserAgent = "MultiPoolMiner V" + Application.ProductVersion;
var Response = WR.GetResponse();
var SS = Response.GetResponseStream();
SS.ReadTimeout = 20 * 100;
SS.CopyTo(data);
Response.Close();
byte[] dataByteArray = data.ToArray();
string plainTextData = Utils.AesCipher.DecryptString(dataByteArray, password);
//check if ticks from the db is bigger than 0;
}
catch (Exception e)
{
}
The macHash algo is set sha256 so I'm assuming the length of mac hash is 32 bytes.
public static string DecryptString(string data, string password)
{
byte[] allBytes = ToByteArray(data);
byte[] one = ToByteArray("1");
string plaintext = null;
// this is all of the bytes
byte[] passwordByteArray = ToByteArray(password);
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.BlockSize = BlockSize;
aes.Mode = CipherMode.CBC;
// get the key salt
byte[] keySalt = new byte[KeySize / 8];
Array.Copy(allBytes, keySalt, keySalt.Length);
// Yii2 says
//$key = $this->hkdf($this->kdfHash, $secret, $keySalt, $info, $keySize);
//
//Yii2 hkdf says
//$prKey = hash_hmac($algo, $inputKey, $salt, true);
//$hmac = '';
//$outputKey = '';
//for ($i = 1; $i <= $blocks; $i++) {
// $hmac = hash_hmac($algo, $hmac . $info . chr($i), $prKey, true);
// $outputKey .= $hmac;
//}
// chr($i) is the char byte of 1;
// the blocksize is 1
// info here is nothing
// hash first key with keysalt and password
HMACSHA256 hmac = new HMACSHA256(keySalt);
byte[] computedHash = hmac.ComputeHash(passwordByteArray);
// hash primary key with one byte and computed hash
HMACSHA256 hmac2 = new HMACSHA256(computedHash);
byte[] prKey = hmac2.ComputeHash(one);
byte[] key = new byte[KeySize/8];
Array.Copy(prKey, 0, key, key.Length);
// if we want to verify the mac hash this is where we would do it.
// Yii2 encryption data.
// $encrypted = openssl_encrypt($data, $this->cipher, $key, OPENSSL_RAW_DATA, $iv);
//
//$authKey = $this->hkdf($this->kdfHash, $key, null, $this->authKeyInfo, $keySize);
//hashed = $this->hashData($iv. $encrypted, $authKey);
//hashed = [macHash][data]
// get the MAC code
byte[] MAC = new byte[MacHashSize / 8];
Array.Copy(allBytes, keySalt.Length, MAC, 0, MAC.Length);
// get our IV
byte[] iv = new byte[BlockSize / 8];
Array.Copy(allBytes, (keySalt.Length + MAC.Length), iv, 0, iv.Length);
// get the data we need to decrypt
byte[] cipherBytes = new byte[allBytes.Length - iv.Length - MAC.Length - keySalt.Length];
Array.Copy(allBytes, (keySalt.Length + MAC.Length + iv.Length), cipherBytes, 0, cipherBytes.Length);
// Create a decrytor to perform the stream transform.
var decryptor = aes.CreateDecryptor(key, iv);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherBytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
//Read the decrypted bytes from the decrypting stream
//and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
public static byte[] ToByteArray(string value)
{
byte[] allBytes = new byte[value.Length];
int i = 0;
foreach (byte bite in value)
{
allBytes[i] = Convert.ToByte(bite);
i++;
}
return allBytes;
}
I'm having trouble getting the password to hash correctly. Which means the plain text decryption is definitely wrong.
In fact now that I'm thinking about it again. It's actually throwing a incomplete block exception.
The string bring passed to the function is collected from the server via a web request that returns the ciphertext string from yii2 encrypt by key method. The password being sent to the function is a hard coded string. I'm looking into more info about yii2 and the base type of the steing.
So yii2 says it's just returning a string but I looked up hash_hmac function in php and its returning raw binary output when rawData is set to true which yii2 does.
Update, I went ahead and copied over my web request to the above text above because I'm almost positive there is a issue between the server sending the data and the program receiving the data. I have also followed The advice below and changed Yii2's format to raw and pretty much copied his $response from below. Now I am receiving a error of "Padding is invalid and can't be removed". I am going to continue to trouble shoot and see if I can get it to work. I have tried to set the padding in aes and have returned the same result.
Answer, Thanks to vstm for all of his help in this problem. I would not have been able to solve it without his help. I have changed my code above to reflect the correct code needed to decrypt a string from a server running yii2 as a framwork. I noticed while I was trouble shooting that I had oneByte and computedHash flip flopped. So I have changed the code above to reflect the correct way. Again it was vstm's help by instructing me to set the output from yii2 to raw output and in the reading of the bytes that made this so difficult.