问题
I encounter an error when using the AesCryptoServiceProvider to Encrypt some files config. The summary code is below
private static byte[] secretKey = {
(byte)0x63, (byte)0x23, (byte)0xdf, (byte)0x2a,
(byte)0x59, (byte)0x1a, (byte)0xac, (byte)0xcc,
(byte)0x50, (byte)0xfa, (byte)0x0d, (byte)0xcc,
(byte)0xff, (byte)0xfd, (byte)0xda, (byte)0xf0
};
private static byte[] iv = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
public static byte[] EncryptStringToBytes(String plainText, byte[] secretKey, byte[] IV)
{
try
{
// Check arguments.
if (plainText == null || plainText.Length <= 0)
throw new ArgumentNullException("plainText");
if (secretKey == null || secretKey.Length <= 0)
throw new ArgumentNullException("secretKey");
if (IV == null || IV.Length <= 0)
throw new ArgumentNullException("secretKey");
byte[] encrypted;
// Create an AesCryptoServiceProvider object
// with the specified key and IV.
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = secretKey;
aesAlg.IV = IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
return encrypted;
}
catch (System.Exception ex)
{
LogWriter.Instance.LogError(ClassName, "EncryptStringToBytes()", ex.Message + ";\tplainText=" + plainText + ";\t" + ex.StackTrace);
return null;
}
}
int main()
{
byte[] encryptText = EncryptStringToBytes("some plain text", secretKey, iv);
if (encryptText != null)
{
try
{
File.WriteAllBytes(filePath, encryptText);
}
catch (Exception ex)
{
LogWriter.Instance.LogError(ClassName, "SaveBuffToFile()", ex.Message + ";\tFilePath=" + path + ";\t" + ex.StackTrace);
}
}
}
In the main function, I Encrypt the plain text and save the result to the file by calling File.WriteAllBytes(filePath, encryptText);
. But sometime the content file contains all Null character ('\0'). The bellow image is the content of file when I open with HexaEditor
This error happen about once a month for the app running 8 hours per day.
I considered that the file may be corrupted. But I think that this case is not caused by corruption, because there are 10 config files in a folder, but only have 7 files that using Encryption is encountered this error, whereas 3 files that saved by plain text (not using Encryption) has never encountered this error.
So I think the problem caused by AesCryptoServiceProvider. Anyone please help me.
Thank you!
回答1:
NOTE: Thanks to Damien_The_Unbeliever and his testing, this answer has been found to be WRONG and may be deleted soon. Until then, it should help others avoid going down this path.
You may have a race condition with the ToArray()
call on your MemoryStream
.
The code you posted calls that method outside of the using
statement for StreamWriter
, so that object will have flushed any buffers it may be using to the underlying CryptoStream
by that point, but the call is inside the using statement for CryptoStream
, which means there is no guarantee that that object will have flushed any buffers to the underlying MemoryStream
by that point.
That would explain the intermittent nature of the bug:
In the cases where you get the desired result, it might be that the CryptoStream
's buffer is being automatically flushed to the MemoryStream
before the call to ToArray()
, just by luck.
In the cases where you're getting the erroneous result, the ToArray()
call is returning the data from the MemoryStream
's internal buffer (which is most likely being initialized to zeros) before the MemoryStream
has received all (or any) of the data from the CryptoStream
.
If that is all correct, you can make the buffer flushing deterministic and guarantee a correct result every time by calling FlushFinalBlock() on the CryptoStream
object before accessing the data from its underlying MemoryStream
.
After that change, the code would be:
[...]
// Create the streams used for encryption.
using ( MemoryStream msEncrypt = new MemoryStream() ) {
using ( CryptoStream csEncrypt = new CryptoStream( msEncrypt, encryptor, CryptoStreamMode.Write ) ) {
using ( StreamWriter swEncrypt = new StreamWriter( csEncrypt ) ) {
swEncrypt.Write( plainText );
}
csEncrypt.FlushFinalBlock();
encrypted = msEncrypt.ToArray();
}
}
[...]
See:
CryptoStream.HasFlushedFinalBlock
This StackOverflow answer is tangentially related to your issue: https://stackoverflow.com/a/19742806
来源:https://stackoverflow.com/questions/49269579/encrypt-aescryptoserviceprovider-return-zero-byte-array