【发布时间】:2018-11-27 14:34:36
【问题描述】:
好的,所以几天来我一直在试图弄清楚我在尝试解密在 Yii2 中加密的信息然后发送到我的 Windows 窗体程序时做错了什么。
我正在使用 Yii2 加密方法,它返回以下格式的字符串。
[keySalt][MAC][IV][密文]
KeySalt 是以字节为单位的密钥大小。 MAC 与 MAC_HASH 的输出长度相同。 IV 是块大小的长度。
我在 Yii2 中设置为使用 AES-192-CBC。 所以根据 yiiframwork yii-base-security,块大小为 16,密钥大小为 24。
我的网络请求如下所示。
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)
{
}
macHash 算法设置为 sha256,所以我假设 mac 哈希的长度为 32 个字节。
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;
}
我无法让密码正确散列。这意味着纯文本解密肯定是错误的。
事实上,现在我又在考虑它了。它实际上抛出了一个不完整的块异常。
传递给函数的字符串是通过 web 请求从服务器收集的,该请求返回来自 yii2 加密方法的密文字符串。发送给函数的密码是硬编码字符串。我正在研究有关 yii2 和 steing 的基本类型的更多信息。
所以 yii2 说它只是返回一个字符串,但我在 php 中查找了 hash_hmac 函数,当 rawData 设置为 true 时,它返回的原始二进制输出是 yii2 所做的。
更新,我继续将我的网络请求复制到上面的文本,因为我几乎可以肯定发送数据的服务器和接收数据的程序之间存在问题。我也遵循了下面的建议并将 Yii2 的格式更改为原始格式,并且几乎从下面复制了他的 $response。现在我收到“填充无效且无法删除”的错误。我将继续排除故障,看看我是否可以让它工作。我试图在 aes 中设置填充并返回相同的结果。
回答,感谢 vstm 在这个问题上提供的所有帮助。如果没有他的帮助,我将无法解决它。我已经更改了上面的代码,以反映从运行 yii2 作为框架的服务器中解密字符串所需的正确代码。我在解决问题时注意到我的 oneByte 和 computedHash 失败了。所以我改变了上面的代码以反映正确的方法。同样是 vstm 的帮助,它指示我将 yii2 的输出设置为原始输出,并在读取字节时使这变得如此困难。
【问题讨论】:
-
那么到底是什么问题,是有异常还是生成的明文错误?你是在 YII 端使用基于密码还是基于密钥的加密?
-
我在yii2中使用的是密钥加密方式。而且我似乎什至无法获得正确散列的密钥。更不用说解密字符串了。
-
您将密文和密钥作为
string值传递,您如何传输这些?您在两者之间使用 base64 吗? -
密文是通过仅返回字符串的 Web 请求从服务器收集的。我不完全确定 Yii2 ir php 使用的是什么基础。我想我需要调查一下。但是随后字符串只是作为 c# 中的字符串传递给函数。密码只是一个硬编码字符串。
-
Yii2 说它是一个字符串值,但是 php 中的 hash_hmac 说如果 rawData 设置为 true,yii2 会返回原始二进制数据。
标签: c# php encryption yii2 aes