.NET file Decryption - Bad Data
I am in the process of rewriting an old application. The old app stored data in a scoreboard file that was encrypted with the following code:
private const String SSecretKey = @"?B?n?Mj?";
public DataTable GetScoreboardFromFile()
{
FileInfo f = new FileInfo(scoreBoardLocation);
if (!f.Exists)
{
return setupNewScoreBoard();
}
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
//A 64 bit key and IV is required for this provider.
//Set secret key For DES algorithm.
DES.Key = ASCIIEncoding.ASCII.GetBytes(SSecretKey);
//Set initialization vector.
DES.IV = ASCIIEncoding.ASCII.GetBytes(SSecretKey);
//Create a file stream to read the encrypted file back.
FileStream fsread = new FileStream(scoreBoardLocation, FileMode.Open, FileAccess.Read);
//Create a DES decryptor from the DES instance.
ICryptoTransform desdecrypt = DES.CreateDecryptor();
//Create crypto stream set to read and do a
//DES decryption transform on incoming bytes.
CryptoStream cryptostreamDecr = new CryptoStream(fsread, desdecrypt, CryptoStreamMode.Read);
DataTable dTable = new DataTable("scoreboard");
dTable.ReadXml(new StreamReader(cryptostreamDecr));
cryptostreamDecr.Close();
fsread.Close();
return dTable;
}
This works fine. I have copied the code into my new app so that I can create a legacy loader and convert the data into the new format. The problem is I get a "Bad Data" error:
System.Security.Cryptography.CryptographicException was unhandled Message="Bad Data.\r\n" Source="mscorlib"
The error fires at this line:
dTable.ReadXml(new StreamReader(cryptostreamDecr));
The encrypted file was created today on the same machine with the old code. I guess that maybe the encryption / decryption process uses the application name / file or something and therefore means I can not open it.
Does anyone have an idea as to:
A) Be able explain why this isn't working? B) Offer a solution that would allow me to be able to open files that were created with the legacy application and be able to convert them please?
Here is the whole class that deals with loading and saving the scoreboard:
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.IO;
using System.Data;
using System.Xml;
using System.Threading;
namespace JawBreaker
{
[Serializable]
class ScoreBoardLoader
{
private Jawbreaker jawbreaker;
private String sSecretKey = @"?B?n?Mj?";
private String scoreBoardFileLocation = "";
private bool keepScoreBoardUpdated = true;
private int intTimer = 180000;
public ScoreBoardLoader(Jawbreaker jawbreaker, String scoreBoardFileLocation)
{
this.jawbreaker = jawbreaker;
this.scoreBoardFileLocation = scoreBoardFileLocation;
}
// Call this function to remove the key from memory after use for security
[System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
public static extern bool ZeroMemory(IntPtr Destination, int Length);
// Function to Generate a 64 bits Key.
private string GenerateKey()
{
// Create an instance of Symetric Algorithm. Key and IV is generated automatically.
DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create();
// Use the Automatically generated key for Encryption.
return ASCIIEncoding.ASCII.GetString(desCrypto.Key);
}
public void writeScoreboardToFile()
{
DataTable tempScoreBoard = getScoreboardFromFile();
//add in the new scores to the end of the file.
for (int i = 0; i < jawbreaker.Scoreboard.Rows.Count; i++)
{
DataRow row = tempScoreBoard.NewRow();
row.ItemArray = jawbreaker.Scoreboard.Rows[i].ItemArray;
tempScoreBoard.Rows.Add(row);
}
//before it is written back to the file make sure we update the sync info
if (jawbreaker.SyncScoreboard)
{
//connect to webservice, login and update all the scores that have not been synced.
for (int i = 0; i < tempScoreBoard.Rows.Count; i++)
{
try
{
//check to see if that row has been synced to the server
if (!Boolean.Parse(te开发者_如何学PythonmpScoreBoard.Rows[i].ItemArray[7].ToString()))
{
//sync info to server
//update the row to say that it has been updated
object[] tempArray = tempScoreBoard.Rows[i].ItemArray;
tempArray[7] = true;
tempScoreBoard.Rows[i].ItemArray = tempArray;
tempScoreBoard.AcceptChanges();
}
}
catch (Exception ex)
{
jawbreaker.writeErrorToLog("ERROR OCCURED DURING SYNC TO SERVER UPDATE: " + ex.Message);
}
}
}
FileStream fsEncrypted = new FileStream(scoreBoardFileLocation, FileMode.Create, FileAccess.Write);
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
DES.Key = ASCIIEncoding.ASCII.GetBytes(sSecretKey);
DES.IV = ASCIIEncoding.ASCII.GetBytes(sSecretKey);
ICryptoTransform desencrypt = DES.CreateEncryptor();
CryptoStream cryptostream = new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write);
MemoryStream ms = new MemoryStream();
tempScoreBoard.WriteXml(ms, XmlWriteMode.WriteSchema);
ms.Position = 0;
byte[] bitarray = new byte[ms.Length];
ms.Read(bitarray, 0, bitarray.Length);
cryptostream.Write(bitarray, 0, bitarray.Length);
cryptostream.Close();
ms.Close();
//now the scores have been added to the file remove them from the datatable
jawbreaker.Scoreboard.Rows.Clear();
}
public void startPeriodicScoreboardWriteToFile()
{
while (keepScoreBoardUpdated)
{
//three minute sleep.
Thread.Sleep(intTimer);
writeScoreboardToFile();
}
}
public void stopPeriodicScoreboardWriteToFile()
{
keepScoreBoardUpdated = false;
}
public int IntTimer
{
get
{
return intTimer;
}
set
{
intTimer = value;
}
}
public DataTable getScoreboardFromFile()
{
FileInfo f = new FileInfo(scoreBoardFileLocation);
if (!f.Exists)
{
jawbreaker.writeInfoToLog("Scoreboard not there so creating new one");
return setupNewScoreBoard();
}
else
{
DESCryptoServiceProvider DES = new DESCryptoServiceProvider();
//A 64 bit key and IV is required for this provider.
//Set secret key For DES algorithm.
DES.Key = ASCIIEncoding.ASCII.GetBytes(sSecretKey);
//Set initialization vector.
DES.IV = ASCIIEncoding.ASCII.GetBytes(sSecretKey);
//Create a file stream to read the encrypted file back.
FileStream fsread = new FileStream(scoreBoardFileLocation, FileMode.Open, FileAccess.Read);
//Create a DES decryptor from the DES instance.
ICryptoTransform desdecrypt = DES.CreateDecryptor();
//Create crypto stream set to read and do a
//DES decryption transform on incoming bytes.
CryptoStream cryptostreamDecr = new CryptoStream(fsread, desdecrypt, CryptoStreamMode.Read);
DataTable dTable = new DataTable("scoreboard");
dTable.ReadXml(new StreamReader(cryptostreamDecr));
cryptostreamDecr.Close();
fsread.Close();
return dTable;
}
}
public DataTable setupNewScoreBoard()
{
//scoreboard info into dataset
DataTable scoreboard = new DataTable("scoreboard");
scoreboard.Columns.Add(new DataColumn("playername", System.Type.GetType("System.String")));
scoreboard.Columns.Add(new DataColumn("score", System.Type.GetType("System.Int32")));
scoreboard.Columns.Add(new DataColumn("ballnumber", System.Type.GetType("System.Int32")));
scoreboard.Columns.Add(new DataColumn("xsize", System.Type.GetType("System.Int32")));
scoreboard.Columns.Add(new DataColumn("ysize", System.Type.GetType("System.Int32")));
scoreboard.Columns.Add(new DataColumn("gametype", System.Type.GetType("System.String")));
scoreboard.Columns.Add(new DataColumn("date", System.Type.GetType("System.DateTime")));
scoreboard.Columns.Add(new DataColumn("synced", System.Type.GetType("System.Boolean")));
scoreboard.AcceptChanges();
return scoreboard;
}
private void Run()
{
// For additional security Pin the key.
GCHandle gch = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned);
// Remove the Key from memory.
ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2);
gch.Free();
}
}
}
That looks like code to decrypt a file and deserialise your scoreboard object from it. We really need to see the original encryption side to compare, if you can post that.
I have two observations from the decryption code:
- Setting the IV the same as the key doesn't look right (though it might be correct if that's what your encryption code is doing)
- Are those really question marks in SSecretKey? Could they be control or high-byte-set characters fallen victim to mis-encoding and/or mispasting?
Added: Thanks for the update. A bit of googling suggests that Bad Data is often caused by the decrypted data not being exactly what was output from the encryption, that's a place to focus (but do keep looking into whether the key string is correct). I must admit I'm a little out of my depth in C#, so the following are only suggestions:
- In the
writeScoreboardToFile
you write the XML to a MemoryStream, then write the contents of that stream to your CryptoStream. Is there any reason why you don't write directly to the CryptoStream? - Is it legal to set
ms.Position
directly? Doesms.Seek(0,SeekOrigin.Begin)
make any difference? - You may need to call
cryptostream.FlushFinalBlock()
before closing it, it's not clear from MSDN whether it's automatically called. - The encrypted data is binary, you may need to use a
BinaryReader
andBinaryWriter
to avoid it getting smashed by the encodings, as Chris pointed out. - When exactly is
Run()
being called? If I've understood correctly, that is zeroing out the secret key string - obviously you don't want this to happen before you're finished creating your crypto transforms.
On top of this I'd comment that setting the IV equal to the key is not very sound cryptographically; the IV should really be different every time you encrypt using one key.
You might want to check this link out: http://blogs.msdn.com/shawnfa/archive/2005/11/10/491431.aspx
It talks about problems with encryption/decryption and ascii encoding.
精彩评论