Make .txt file unreadable / uneditable - c#

I have a program which saves a little .txt file with a highscore in it:
// Create a file to write to.
string createHighscore = _higscore + Environment.NewLine;
File.WriteAllText(path, createText);
// Open the file to read from.
string createHighscore = File.ReadAllText(path);
The problem is that the user can edit the file as simple as possible – with a texteditor. So I want to make the file unreadable / uneditable or encrypt it.
My thinking was that I could save the data in a resource file, but can I write in a resource file?
Or save it as .dll, encrypt/decrypt it or look for a MD5-sum/hash.

You can't prevent the user from modifying the file. It's their computer, so they can do whatever they want (that's why the whole DRM issue is… difficult).
Since you said you're using the file to save an high-score, you have a couple of alternatives. Do note that as previously said no method will stop a really determined attacker from tampering with the value: since your application is running on the user computer he can simply decompile it, look at how you're protecting the value (gaining access to any secret used in the process) and act accordingly. But if you're willing to decompile an application, find out the protection scheme used and come up with a script/patch to get around it only to change a number only you can see, well, go for it?
Obfuscate the content
This will prevent the user from editing the file directly, but it won't stop them as soon as the obfuscation algorithm is known.
var plaintext = Encoding.UTF8.GetBytes("Hello, world.");
var encodedtext = Convert.ToBase64String(plaintext);
Save the ciphertext to the file, and reverse the process when reading the file.
Sign the content
This will not prevent the user from editing the file or seeing its content (but you don't care, an high-score is not secret) but you'll be able to detect if the user tampered with it.
var key = Encoding.UTF8.GetBytes("My secret key");
using (var algorithm = new HMACSHA512(key))
{
var payload = Encoding.UTF8.GetBytes("Hello, world.");
var binaryHash = algorithm.ComputeHash(payload);
var stringHash = Convert.ToBase64String(binaryHash);
}
Save both the payload and the hash in the file, then when reading the file check if the saved hash matches a newly computed one. Your key must be kept secret.
Encrypt the content
Leverage .NET's cryptographic libraries to encrypt the content before saving it and decrypt it when reading the file.
Please take the following example with a grain of salt and spend due time to understand what everything does before implementing it (yes, you'll be using it for a trivial reason, but future you β€” or someone else β€” may not). Pay special attention on how you generate the IV and the key.
// The initialization vector MUST be changed every time a plaintext is encrypted.
// The initialization vector MUST NOT be reused a second time.
// The initialization vector CAN be saved along the ciphertext.
// See https://en.wikipedia.org/wiki/Initialization_vector for more information.
var iv = Convert.FromBase64String("9iAwvNddQvAAfLSJb+JG1A==");
// The encryption key CAN be the same for every encryption.
// The encryption key MUST NOT be saved along the ciphertext.
var key = Convert.FromBase64String("UN8/gxM+6fGD7CdAGLhgnrF0S35qQ88p+Sr9k1tzKpM=");
using (var algorithm = new AesManaged())
{
algorithm.IV = iv;
algorithm.Key = key;
byte[] ciphertext;
using (var memoryStream = new MemoryStream())
{
using (var encryptor = algorithm.CreateEncryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
using (var streamWriter = new StreamWriter(cryptoStream))
{
streamWriter.Write("MySuperSecretHighScore");
}
}
}
ciphertext = memoryStream.ToArray();
}
// Now you can serialize the ciphertext however you like.
// Do remember to tag along the initialization vector,
// otherwise you'll never be able to decrypt it.
// In a real world implementation you should set algorithm.IV,
// algorithm.Key and ciphertext, since this is an example we're
// re-using the existing variables.
using (var memoryStream = new MemoryStream(ciphertext))
{
using (var decryptor = algorithm.CreateDecryptor())
{
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
using (var streamReader = new StreamReader(cryptoStream))
{
// You have your "MySuperSecretHighScore" back.
var plaintext = streamReader.ReadToEnd();
}
}
}
}
}

As you seem to look for relatively low security, I'd actually recommend going for a checksum. Some pseudo-code:
string toWrite = score + "|" + md5(score+"myKey") + Environment.NewLine
If the score would be 100, this would become
100|a6b6b0a8e56e42d8dac51a4812def434
To make sure the user didn't temper with the file, you can then use:
string[] split = readString().split("|");
if (split[1] != md5(split[0]+"myKey")){
alert("No messing with the scores!");
}else{
alert("Your score is "+split[0]);
}
Now of course as soon as someone gets to know your key they can mess with this whatever they want, but I'd consider that beyond the scope of this question. The same risk applies to any encryption/decryption mechanism.
One of the problems, as mentioned in the comments down below, is that once someone figures out your key (through brute-forcing), they could share it and everybody will be able to very easily change their files. A way to resolve this would be to add something computer-specific to the key. For instance, the name of the user who logged in, ran through md5.
string toWrite = score + "|" + md5(score+"myKey"+md5(System.username /**or so**/)) + Environment.NewLine
This will prevent the key from being "simply shared".

Probably your best bet is securing the whole file using standard NT security and programmatically change the access control list to protect the whole file from being edited by unwanted users (excepting the one impersonating your own application, of course).
Cryptography can't help here because the file could be still editable using a regular text editor (for example, notepad) and the end user can corrupt the file just adding an extra character (or dropping one too).
There's an alternate approach which doesn't involve programming effort...
Tell your users that once they've manually edited the whole text file they've lost your support. At the end of the day, if you're storing this data is because it's required by your application. Corrupting it or doing the risky task of manually editing it can make your application produce errors.
Another alternate approach which involves programming effort...
Whenever you change the file from your application, you can compute a MD5 or SHA hash and store in a separate file, and once you want to read or write it again, you're going to check that the whole file produces the same hash before writing on it again.
This way, the user can still edit your file manually, but you'll know when this unexpected behavior was done by the user (unless the user also manually computes the hash whenever the file is changed...).

Something I have not yet seen mentioned is storing the high score on an online leader board. Obviously this solution requires a lot more development, but since you are talking about a game, you could probably make use of a third party provider like Steam, Origin, Uplay, ... This has the added advantage of leader boards not just being for your machine.

You cannot save data in a dll, and both Resource file and txt file are editable. It sounds like encryption is the only way for you. You can encrypt the string before saving it to a txt file. Take a look at this thread:
Encrypt and decrypt a string

You can serialize it and deserialize with encryption with CryptoStream :
Serialize file :
Create and open FileStream in write mode
Create Cryptostream and pass your filestream
Write contents to Cryptostream (encrypt)
Deserialize file :
Create and open FileStream in read mode
Create Cryptostream and pass your filestream
Read from Cryptostream (decrypt)
You can find examples and more information here :
msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream.aspx
http://www.codeproject.com/Articles/6465/Using-CryptoStream-in-C
Example :
byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8 }; // Where to store these keys is the tricky part,
byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
string path = #"C:\path\to.file";
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
// Encryption and serialization
using (var fStream = new FileStream(path, FileMode.Create, FileAccess.Write))
using (var cryptoStream = new CryptoStream(fStream , des.CreateEncryptor(key, iv), CryptoStreamMode.Write))
{
BinaryFormatter serializer = new BinaryFormatter();
// This is where you serialize your data
serializer.Serialize(cryptoStream, yourData);
}
// Decryption
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var cryptoStream = new CryptoStream(fs, des.CreateDecryptor(key, iv), CryptoStreamMode.Read))
{
BinaryFormatter serializer = new BinaryFormatter();
// Deserialize your data from file
yourDataType yourData = (yourDataType)serializer.Deserialize(cryptoStream);
}

Simple solution:
To mitigate the hackish user ability to change the score, you can write it as a binary I guess.
Another solution:
Write the data in a SQLite DB?

You can name your file as something that doesn't suggest it has a score table in it (e.g. YourApp.dat) and encrypt the contents.
The accepted answer here contains the code for encryption and decryption of text.
Update
I also suggest using some Guid as a password for the encryption.

You can't write in Resources, more information exists in this answer
The reason that you can't change a resource string at runtime, is
because the resource is compiled into your executable. If you reverse
engineer the compiled *.exe or *.dll file, you can actually see your
string in the code. Editing an already compiled executable file is
never a good idea (unless you're trying to hack it), but when you try
to do it from the executables code, it just plain impossible, as the
file is locked during execution.
You can add Read Only or Hidden attributes to your files using
File.SetAttributes, But still user can remove the attributes
from windows and edit the file.
An example:
File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
Another way I could suggest is to save the data in a file with some
weird extensions so that the user can't think of it as an editable or
important file. somthing like ghf.ytr (Can't think of somthing
more weird right now!)
I'd also suggest making a text file with .dll extension and saving it in one of windows folders like system32. This way user will have a really hard time trying to find out where does the score information go!

Here is a code to make an text file not editable. in the same way you use this technique to make it not readable etc.
string pathfile = #"C:\Users\Public\Documents\Filepath.txt";
if (File.Exists(pathfile))
{
File.Delete(pathfile);
}
if (!File.Exists(pathfile))
{
using (FileStream fs = File.Create(pathfile))
{
Byte[] info = new UTF8Encoding(true).GetBytes("your text to be written to the file place here");
FileSecurity fsec = File.GetAccessControl(pathfile);
fsec.AddAccessRule(new FileSystemAccessRule("Everyone",
FileSystemRights.WriteData, AccessControlType.Deny));
File.SetAccessControl(pathfile, fsec);
}
}

Related

File.ReadAllBytes takes a lot of RAM, is there a better way?

I am busy working with an encryption lib, and I want to encrypt large files (with AES GCM). Currently I have this for encrypting files, after writing a temp file, Chipher, from the CryptoStream:
byte[] Chiper = File.ReadAllBytes(BUFFER_PATH);
// Retrieve tag and create array to hold encrypted data.
byte[] AuthTag = encryptor.GetTag();
byte[] encrypted = new byte[Chiper.Length + aes.IV.Length + AuthTag.Length];
// Set needed data in byte array.
aes.IV.CopyTo(encrypted, 0);
AuthTag.CopyTo(encrypted, IV_LENGTH);
Chiper.CopyTo(encrypted, IV_LENGTH + TAG_LENGTH);
File.WriteAllBytes(END_PATH, encrypted);
This function works fine, however it takes a lot of RAM depending on the filesize. Is there a better way though this? I tried using a FileStream though it starts conflicting with my code. Is there a way to use less, or no memory to save Chiper(byte[])?
It appears that you're trying to write a composite file that has three pieces of information - the tag, IV and cipher text. Given that you can't get the tag value until after the encryption completes, you are trying to composite the data after encryption completes.
The problem comes in when you attempt to load a large encrypted file into memory. Fortunately, streams provide a nice simple solution for this:
byte[] authTag = encryptor.GetTag();
using (var tempfile = File.OpenRead(BUFER_PATH))
using (var outstream = File.Create(END_PATH))
{
// write tag
outstream.Write(authTag, 0, authTag.Length);
// write IV
outstream.Write(aes.IV, 0, aes.IV.Length);
// copy data from source file to output file
tempfile.CopyTo(outstream);
}
On the other hand you could also write the data straight to the output file if you know ahead of time what size the tag and IV are going to be. Just allocate space for the tag value at the start of the file and come back and write it in after the fact. That saves you having to use a temporary file.

How do I encrypt/decrypt the same xml file in C#?

So I need the ability to encrypt/decrypt the same settings XML file in my application. I can only find examples that use a seperate output file. I don't need an output file. Nothing super secure. I just basically need to make the settings file not readable to protect a couple connections strings. I am able to encrypt the file just fine using this code:
public static void EncryptAndSerialize(Object obj)
{
UnicodeEncoding aUE = new UnicodeEncoding();
byte[] key = aUE.GetBytes("password");
RijndaelManaged RMCrypto = new RijndaelManaged();
using (FileStream fs = File.Open(#"D:\Sample.xml", FileMode.Create))
{
using (CryptoStream cs = new CryptoStream(fs, RMCrypto.CreateEncryptor(key, key), CryptoStreamMode.Write))
{
XmlSerializer xmlser = new XmlSerializer(obj.GetType());
xmlser.Serialize(cs, obj);
}
fs.Close();
}
I just need to know how to use this same sort of process to decrypt that same file. If anyone could point me in the right direction it would be much appreciated. Keep in mind ultra security is not an issue. Forgive my ignorance this task is new to me.
What he said plus...
MemoryStream...
https://msdn.microsoft.com/en-us/library/system.io.memorystream(v=vs.110).aspx
using (var sink = new MemoryStream())
{
// Write your encyphered data to the sink
// (from your FileStream, via the encryption provider)
// and then later, read (or copy) from sink back into
// the FileStream. Don't forget to re-position your
// FileStream before doing so.
}
If it were me I'd use two independent FileStreams. One for read only and, later, another one for writing back to the .xml file (that you just read from). I'd only bother constructing and writing the later if and only if I knew I had a fully formed/filled sink.
You might also investigate the use of SecureString (but I'm digressing):
https://msdn.microsoft.com/en-us/library/system.security.securestring(v=vs.110).aspx
All that said, you NEVER want to directly overwrite a file (i.e., in place edit). If you do, and the write fails (part way through) your original file will be, well, corrupt. Leaving you with zip/nadda/crap. Always write to a new, temp file and only after that completes 100% do you promote the temp file to the source path (typically via FileInfo.MoveTo).
https://msdn.microsoft.com/en-us/library/system.io.fileinfo.moveto(v=vs.110).aspx
If bad stuff happens along the way (and it will!!!), all you do is delete the temp file (off of a catch block, etc.) and you're back where you started from.
The humans won't know that you actually worked two files (and if you have some monitor automation that is getting tripped up by tandem files then rewrite it cause it's well, crap).
Something like this. Notice that this calls CreateDecryptor with the CryptoStreamMode.Read flag telling it to read from the stream.
var decryptor = new RijndaelManaged ();
var transformer = decryptor.CreateDecryptor(_decryptKey, _decryptSeed);
var cryptoStream = new CryptoStream(encryptedStream, transformer, CryptoStreamMode.Read);
cryptoStream.CopyTo(resultStream);
resultStream.Close();
Some editorial comments:
1) AES would be better and here's why:
https://blogs.msdn.microsoft.com/shawnfa/2006/10/09/the-differences-between-rijndael-and-aes/
2) Don't underestimate .net encryption - Microsoft doesn't fool around
3) Bad encryption can be worse than no encryption because it provides a false sense of security

Accessing a decrypted Rijndael encrypted file with a StreamReader without writing to disk

I'm currently working on encryption/decryption of plain text files that are embedded in a .dll file. The plain text files are scripts, and are being parsed as and when they're needed.
At the minute, this is how the (plain text) script files are being loaded :
string RunLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Assembly _assembly = Assembly.LoadFrom(RunLocation + "\\<name of dll>.dll");
s = new StreamReader(_assembly.GetManifestResourceStream(File));
RunScript(s);
The RunScript method reads a single line of the script into a string, then parses it. It looks through each line in turn, and performs each of the actions we define in the script as they are found.
Since out dll files aren't encrypted in anyway, we've decided that we should encrypt each of the script files before they are embedded into the dll. We decided on the Rijndael algorithm because of the ease of which it can be implemented in C#/.NET.
Currently encryption runs like this:
FileStream fileStreamForEncryption = new FileStream (inputFileToBeEncrypted, FileMode.Create);
RijndaelManaged rijndaelCryptography = new RijndaelManaged();
CryptoStream encryptionStream = new CryptoStream(fileStreamForEncryption, rijndaelCryptography .CreateEncryptor(key, iv), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fileStreamForEncryption .Close()
This means that decryption is a matter of running a few commands and getting the decrypted file... except that, because of the design of the rest of the code (encryption seems to have been a "once we've got the system running to a given standard"), the file needs to be returned as a StreamReader object. At the minute, this is how I'm decrypting a requested file:
string decodedFile = null;
FileStream fileToDecrypt= new FileStream(_assembly.GetManifestResourceStream(File).ToString(), FileMode.Open);
/* I know that the above line is overly complicated, it's because our files are read into StreamReader objects (as I've explained above) */
RijndaelManaged rijndaelCryptography = new RijndaelManaged();
try
{
CryptoStream cs = new CryptoStream(fileToDecrypt, rijndaelCryptography .CreateDecryptor(key, iv), CryptoStreamMode.Read);
using (StreamReader decryptReader = new StreamReader(cs))
{
decodedFile = decryptReader.ReadToEnd();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
Does anyone know how I can decrypt a file, using Rijndael, but have the output accessible as a / read into an instance of a StreamReader object?
I've had a long look around The Internet for ways to do this and the closest I came was copying the contents of decryptReader into another StreamReader object instead of decrypting out to a string, but it seems that you can't copy the contents of one StreamReader to another StreamReader - correct me if I'm wrong.
I'd love to have to change as little of the existing (that which I haven't shared here) code base as possible as that could cause bugs to arise. I'd also love to do this without writing the file back to disk (there's a reason we're encrypting them, obviously).
Thank you in advance, and sorry if I haven't been clear enough or a little vague. I can add more information if you need it to help me.
Jamie
You could simply convert your decoded file contents to a byte array and expose this as a stream:
var stream = new System.IO.MemoryStream(System.Text.Encoding.Unicode.GetBytes(decodedFile));
var streamReader = new StreamReader(stream);
Also, you should probably be disposing of your RijndaelManaged instance by placing it in a using block:
using (RijndaelManaged rijndaelCryptography = new RijndaelManaged())
{
...
}
Update
You can get the encrypted file like so, and the CryptoStream constructor will still be happy:
Stream fileToDecrypt= _assembly.GetManifestResourceStream(File);
...
CryptoStream cs = new CryptoStream(fileToDecrypt, rijndaelCryptography.CreateDecryptor(key, iv), CryptoStreamMode.Read);
I've had a long look around The Internet for ways to do this and the closest I came was copying the contents of decryptReader into another StreamReader object instead of decrypting out to a string, but it seems that you can't copy the contents of one StreamReader to another StreamReader - correct me if I'm wrong.
You are only slightly correct. While you can't copy from one StreamReader to another, you can read from one StreamReader and write to a StreamWriter backed by, say, a MemoryStream. You can then wrap the StreamReader around the data in the MemoryStream that was written to by a StreamWriter.
So what you want to do is quite possible. In fact, the biggest problem I see for your setup is dealing with the decryption key.

Error in C# encrypt code when decrypting!

A bit more background info as suggested:
I'm finsihing of an Intranet CMS web app where I have to use the products API (ASP.NET based). Because of time constraints and issues with Windows authen' I need another way to ensure staff do not need to re login everytime they visit the site to view personalised content. The way it works is that once a user logs in (username/password), a Session ID storing a new different Security context value is generated that is used to display the personalised content. The API login method called uses the username and password as parameters. The only way I can think of automatically logging in the next time the staff visits the site is by storing the password in a enrypted cookie and checking of its existing when the site is visited and then calling the API login method using the username and decrypted password cookie values.
Any other ideas as an alternative welcomed.
Mo
Hi,
I'm using some code found on the web to encrypt and decrypt a password string. It encrypts fine but when it calls the code below to decrypt the string it throws the error "Length of the data to decrypt is invalid" How can I resolve this?
Thanks in advance.
Mo
System.Text.Encoding enc = System.Text.Encoding.ASCII;
byte[] myByteArray = enc.GetBytes(_pword);
SymmetricAlgorithm sa = DES.Create();
MemoryStream msDecrypt = new MemoryStream(myByteArray);
CryptoStream csDecrypt = new CryptoStream(msDecrypt, sa.CreateDecryptor(), CryptoStreamMode.Read);
byte[] decryptedTextBytes = new Byte[myByteArray.Length];
csDecrypt.Read(decryptedTextBytes, 0, myByteArray.Length);
csDecrypt.Close();
msDecrypt.Close();
string decryptedTextString = (new UnicodeEncoding()).GetString(decryptedTextBytes);
A couple of things here...
You shouldn't encrypt passwords usually. You should hash them.
If you decide to continue down the road of encryption..
You are using the DES algorithm. This is considered insecure and flawed. I'd recommend looking at the AES algorithm.
Depending on how much data you are working with, the CryptoStream might be overkill.
Using the ASCII encoding can cause loss of data that isn't ASCII, like Cyrillic letters. The recommended fix is to use something else, like UTF8.
Here is an example:
string text = "Hello";
using (var aes = new AesManaged())
{
var bytes = System.Text.Encoding.UTF8.GetBytes(text);
byte[] encryptedBytes;
using (var encrypt = aes.CreateEncryptor())
{
encryptedBytes = encrypt.TransformFinalBlock(bytes, 0, bytes.Length);
}
byte[] decryptedBytes;
using (var decrypt = aes.CreateDecryptor())
{
decryptedBytes = decrypt.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
}
var decryptedText = System.Text.Encoding.UTF8.GetString(decryptedBytes);
Console.Out.WriteLine("decryptedText = {0}", decryptedText);
}
This will use a random key every time. It is likely that you will need to encrypt some data, then decrypt it at a later time. When you create the AesManaged object, you can store the Key and IV property. You can re-use the same Key if you'd like, but different data should always be encrypted with a different IV (Initialization Vector). Where you store that key, is up to you. That's why hashing might be a better alternative: there is no key, and no need to worry about storing the key safely.
If you want to go down the hashing route, here is a small example:
var textToHash = "hello";
using (SHA1 sha = new SHA1Managed())
{
var bytesToHash = System.Text.Encoding.UTF8.GetBytes(textToHash);
var hash = sha.ComputeHash(bytesToHash);
string base64hash = Convert.ToBase64String(hash);
}
This uses the SHA1 algorithm, which should work fine for passwords, however you may want to consider SHA256.
The concept is simple: a hash will produce a (mostly) unique output for an input, however the output cannot be converted back to the input - it's destructive. Whenever you want to check if a user should be authenticated, check hash the password they gave you, and check it against the hash of the correct password. That way you aren't storing anything sensitive.
I've actually had this error before and it took me 3 days to figure out the solution. The issue will be the fact that the machine key you need for descryption needs to be registered on your machine itself.
Read fully up on DES encryption, it works by an application key, and a machine-level key. The error you're getting is likely because of the machine key missing.
Compare the bytes used to create the _pword string (in the encryption method) to the bytes retrieved with GetBytes. Probably you will notice a change in the data there.
To store the encrypted bytes, I think you should use Convert.ToBase64String and Convert.FromBase64String turn the encrypted password to/from a string.
I also do not see the code where you set the Key and IV. So I guess you are using a different key to encrypt and decrypt the password.
If the current Key property is null,
the GenerateKey method is called to
create a new random Key. If the
current IV property is null, the
GenerateIV method is called to create
a new random IV.
DES is a block based cipher - only certain lengths of buffers are valid. If I remember correctly, the block size for DES is 64 bits, so you need to ensure that your byte array is a multiple of 8 bytes long.
(That should fix your immediate problem, but I'd reference other peoples advice here - you really ought not to be using DES for any new code, and for passwords it's usually more appropriate to hash than to encrypt).

File Encryption and Decryption issue

I've been playing around with encrypting and decrypting files in VC# Express 2010.
All the tutorials and documentation I've seen require two FileStreams in order to encrypt the file - one for reading the unencrypted version, and the other for encrypting. When I actually wrote the code it kept throwing an error telling me it could not open the file because it was opened by another process at the output filestream.
I'm assuming that's because the file is opened by the input filestream. So that means I have to specify a different filename? So even after the operation is successful I'll now have the original unencrypted file in the directory and a separate encrypted version? Doesn't that defeat the point? Or am I doing something wrong here? My code is similar to this...
public string filename = "test.xml";
using (FileStream input = new FileStream(filename, FileMode.Open, FileAccess.Read))
using (FileStream output = new FileStram(filename, FileMode.Open, FileAccess.Write))
using (....all the crypto stream and transform stuff...)
{
...do the encryption....
}
You're right but it's not defeating the point. The (streaming) crypto APIs are intended to encrypt from Src to Dst. Think encrypting output while sending/receiving over a network etc. This keeps them simple, as they should be.
You complicate the issue by using the same file for Src and Dst. That is not totally impossible but like Copying a File over itself it needs some extra care.
Consider that in general, encrypting will increase the File size. So it is not safe to Encrypt a file in place. Decrypting might be, but I wouldn't risk it.
What you need is a Temp file and a rename action after completion.
In your example, you can't create a separate filestream for both input and output on the same file, but you can create a handle that will read and write. The FileAccess enum has the flags attribute, so you'd just say var handle = new FileStream(filename, FileAccess.Read | FileAccess.Write); The obvious downside to this is you are going to have data lost if your encryption doesn't complete successfully.
I recommend having a separate file for the output though, atleast that way you won't lose data if your program breaks unexpectedly. If the encryption completes successfully, then delete the original and rename the encrypted file with the original file name.
Use File.ReadAllBytes. Then those bytes post to your encryptor, must work.
There is another parameter where you can specify whether or not to allow another process to read or write to the file.
openFile is a string that represents the file name.
using (FileStream fileIn = new FileStream(openFile, FileMode.Open, FileAccess.Read, FileShare.Write))
using (FileStream fileOut = new FileStream(openFile, FileMode.Open, FileAccess.Write, FileShare.Open))
This way, you can read and write to the same file.
while (myfileStream.Position < fileLength)
{
fileIn.Read(buffer, 0, 51200);
buffer = encrypt(buffer);
fileOut.Write(buffer, 0, 51200);
}
While this is easy and you don't have to write to a temporary file or have to move/rename etc, this can be really dangerous because if the encryption breaks suddenly for some reason, you will lose data!
Also, the encrypt function is something I implemented. AesCryptoServiceProvider along with CryptoStream can be used :)

Categories