Stupid question I know. But I have looked for HOURS on examples of how to do this. Every single example I can find uses streamwriter or streamreader which reads and writes text, not byte arrays.

Is it possible to get a byte array from a CryptoStream?

The closest I can find is using a streamwriter and then using GetBytes(SomeString).

I don't understand, if CryptoString is encrypting binary data, why can't I get the byte array that the cryptostring creates directly from it? Seems like a huge chance for corruption by going from string data back to bytes.

Any help? I'm about to pull my hair out. I've tried everything I can think of.

CryptoStream has methods to get the sequence of bytes. Just tie the CryptoStream to a MemoryStream which is set to an array of bytes.

You can also use the CopyTo method if you want to get the bytes for some reason, but the CryptoStream is already tied to something like a file.

Momerath,

Funny...I think I'm going to go jump off the nearest bridge now...I swear I tried the CopyTo method and it didn't work.

I must be losing it. All work and no play...I think I need a vacation.

Thanks Momerath. I'll have to check on that one. I can't believe I missed it.

I was right.

Using CryptoStream.CopyTo() throws the "Stream does not support reading." exception.

Evidently CryptoStreams cannot be read from and are not seekable either.

Any other suggestions? I'm lost here.

I think that Encryption streams are uni-directional meaning that you can not read back what you put in.
A bit like the water in a stream being purified as it passes over the rocks; if you do not capture the water, it flows in to the sea and you have lost it.
Try chaining the stream in to a MemoryStream. You should then be able to read the encrypted bytes.

Edited 6 Years Ago by nick.crane: n/a

I don't necessarily want to read what I put into it, I want to read what comes out of it.

Like this:
OriginalData => CryptoStream => EncryptedData

I want to get EncryptedData...What's the point of encrypting data if you can't get the encrypted data and save it to a file, or manipulate it in some way?

OK, I had a go at this and it was not as straight forward as I first thought.
This is the encrypt/decrypt sample I came up with.

private void button4_Click(object sender, EventArgs e)
{
    // Data to encrypt
    string str = "This is a test message to encrypt";
    byte[] Data = ASCIIEncoding.ASCII.GetBytes(str);
    MessageBox.Show(string.Format("Data '{1}': Length = {0}", Data.Length, str));

    // storage for encrypted data
    MemoryStream OutData = new MemoryStream();

    // encrypt using base 64 transform
    CryptoStream cs = new CryptoStream(OutData, new ToBase64Transform(), CryptoStreamMode.Write);
    cs.Write(Data, 0, Data.Length);
    MessageBox.Show(string.Format("OutData Length = {0}", OutData.Length));

    // reset out buffer pointer to start
    OutData.Position = 0;

    // storage for decrypted data
    MemoryStream InData = new MemoryStream();

    // decrypt using base 64 transform
    cs = new CryptoStream(InData, new FromBase64Transform(), CryptoStreamMode.Write);
    // use the encrypted MemoryStream buffer as the source
    cs.Write(OutData.GetBuffer(), 0, (int)OutData.Length);

    // reset in buffer pointer to start
    InData.Position = 0;

    // check the message is returned ok
    str = ASCIIEncoding.ASCII.GetString(InData.GetBuffer(), 0, (int)InData.Length);
    MessageBox.Show(string.Format("InData '{1}': Length = {0}", InData.Length, str));
}

Edited 6 Years Ago by nick.crane: n/a

Nick,

With your example, the question then becomes: What if you are not encrypting a string? Using

byte[] Data = ASCIIEncoding.ASCII.GetBytes(str);

is great if you are encryping data of the ASCII variety, but if I am encrypting an object or a class instance, or an image, or whatever...isn't there the potential for data loss or corruption by using ASCIIEncoding on non-ASCII data? I would imagine that when you go back and forth between one encoding to another (bytes to ASCII etc...) it would be okay if the data is of that type, but if it's not that seems like a bad idea.

If I am encoding a MemoryStream (which is simply a byte array) I shouldn't need to use any type of encoding correct? bytes are bytes bit for bit so to speak. But changing from a byte array to ASCII and then back again...I dunno...seems fishy to me. Am I wrong?

Edited 6 Years Ago by zachattack05: n/a

That was just a way to get a set of data quickly that I could put up on the screen for confirmation.
The result was a byte array that I could (sort of) understand when I looked at it in Debug.
Also, the Base64 encoding I used is more suited to text strings.

The need to use the ASCIIEncoding to convert a string is because strings in C# are actually stored as Unicode and I only wanted a simple ascii based byte array.

However, you are right. Bytes are bytes.

Try changing the line for a block of bytes from another source.
Except for str in MessageBox.Show, it should give the same result.

Edited 6 Years Ago by nick.crane: n/a

Nick,

I'll play with the code I have and see what I come up with. I am working on a different project right now, but I'll start back up on the one that's using the encryption here in a few hours.

I'll let you know what I find...maybe the two of us can figure this out. Two heads are better than one :) ... that is if you don't mind me picking at your brain a bit more.

I found this link http://www.codeproject.com/KB/security/DotNetCrypto.aspx which might be helpful.

And this is my revised code snippet

[Serializable]
class testObj
{
    public int testval1 = 10;
    public int testval2 = 20;
    public int testval3 = 30;
    public int testval4 = 40;

    public override string ToString()
    {
        return string.Format("Values are {0}, {1}, {2}, {3}", 
            testval1, testval2, testval3, testval4);
    }
}

private void button5_Click(object sender, EventArgs e)
{
    testObj objData = new testObj();
    MessageBox.Show(string.Format("Starting Object:'{0}'", objData.ToString()));
    
    // Create a new Rijndael object to generate a key
    // and initialization vector (IV).
    Rijndael RijndaelAlg = Rijndael.Create();
    byte[] Key = RijndaelAlg.Key;
    byte[] IV = RijndaelAlg.IV;

    // formatter used to convert object instance to stream (and back)
    BinaryFormatter bf = new BinaryFormatter();

    // storage for encrypted data
    MemoryStream ms = new MemoryStream();
    // encrypt using Rijndael transform
    CryptoStream cs = new CryptoStream(ms, RijndaelAlg.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
    // serialize object to MemoryStream via CryptoStream
    bf.Serialize(cs, objData);

    // close the stream to finish the encryption (this is important!)
    cs.Close();

    // get byte array of encrypted stream
    byte[] encryptedData = ms.ToArray();

    // check output stream has data
    MessageBox.Show(string.Format("encryptedData Length = {0}", encryptedData.Length));

    // build new input memory stream from encrypted bytes
    ms = new MemoryStream(encryptedData);
    // decrypt using Rijndael transform
    cs = new CryptoStream(ms, RijndaelAlg.CreateDecryptor(Key, IV), CryptoStreamMode.Read);

    // read the object from the MemoryStream via CryptoStream
    testObj newObjData = bf.Deserialize(cs) as testObj;
    cs.Close();

    // test if data is OK
    if (newObjData == null)
        MessageBox.Show("Deserialize failed!");
    else
        MessageBox.Show(string.Format("Deserialized  Object:'{0}'", newObjData.ToString()));
}

On the assumption that no one here will probably ever use my software, and that those of you here that would could probably defeat the encryption methods anyway with enough practice, and simply because I am about to lose my mind on this one...I'm going to post my method I am using to encrypt and save user settings...overkill? probably, that's why I'm really not to worried about it.

Disclaimer: some of the information has changed from what I am really using. I did my best to make sure it would repeat the same problem if you are bold enough to copy/paste my madness into your VS. Nick, someone? Can you tell me what in the world I am doing so wrong here?


The situation: When my application first starts, it checks for a user settings file, if one does not exist, it creates one. Here is the relevant portions of the "Load()" event with irrelevant portions commented out:

//check to see if this is the first run
            //TODO: Change the file name here from LUser.usf to User.usf
            if (!File.Exists(Path.GetDirectoryName(Application.ExecutablePath) + "LUser.usf"))
            {
                //there is no user settings file, create a blank one
                LocalSettings NewUserSettings = new LocalSettings();
                
                //set some default values
                NewUserSettings.BalloonHelp = true;
                NewUserSettings.SysRunNumber = -1;
                NewUserSettings.SysFirstRun = true;
                NewUserSettings.dbmsIntegratedSecurity = false;
                NewUserSettings.dbmsPersistSecurity = false;
                
                //save the new settings file
                NewUserSettings.Save(NewUserSettings);

                //load settings (FOR DEBUG OF ENCRYPTION ONLY)
                UserSettings = UserSettings.Load();

                //open the setup wizard
                //SplashScreen.labelSplashActivity.Text = "Starting System Setup...";
                //SystemSetup.Start SystemSetupWizard = new SystemSetup.Start();
                //SystemSetupWizard.ShowDialog();

                //initialize the first run wizard
                //Wizards.FirstRun.Start FirstRunWizard = new Wizards.FirstRun.Start(this);
                //FirstRunWizard.ShowDialog();
            }

The Save() method runs fine...well, it doesn't throw any exceptions that is. However when we get to the Load() method, it dive bombs at this line:

//create an instance of the settings to return
                LocalSettings LoadedSettings = BinaryDataFormatter.Deserialize(OriginalMemStream) as LocalSettings;

with the all too lovely exception: "End of Stream encountered before parsing was completed."

Here's the lowdown on my Debugging so far to save time.
When the Save() method has reached the end of the line and is preparing to close all of the streams, the lengths of the streams are as follows:
OriginalMemString.Length = 295
EncryptedMemStream.Length = 480
ParentMemStream.Length = 1886

Now, when the Load() method has reached the exception point, the lengths of the streams are as follows:
OriginalMemString.Length = 16
EncryptedMemStream.Length = 480
ParentMemStream.Length = 1886

Something is not working. I have verified that my Key and IV are being stored and recalled correctly by using a streamwriter to write the values...well the UTF8Encoding representations of the values...to a text file. They are identical on save and load.

I'm not sure if the problem is with the decryptor, or if I am reading the memory incorrectly? I dunno. Anyway, here is the class with the methods and the class that is serialized:

Oh, PS - the load() method has a try block in it, I commented it out so I can get the exact location of the exceptions...just a FYI :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Cryptography;
using System.Windows.Forms;

namespace DATA
{
    /// <summary>
    /// User Settings file. This holds all of the settings for the local installation of the application.
    /// This includes:
    /// 
    /// UI variables - Variables that set or get various UI features
    /// System variables - Used for statistical reasons
    /// DBMS variables - Variables that the database management system dll uses
    /// </summary>
    [Serializable]
    class LocalSettings
    {
        //UI variables
        public bool BalloonHelp;
        
        //System variables
        public int SysRunNumber;
        public bool SysFirstRun;

        //DBMS variables
        public string dbmsConnectionType;
        public string dbmsDataSource;
        public string dbmsUserName;
        public string dbmsPassword;
        public string dbmsCatalog;
        public bool dbmsIntegratedSecurity;
        public bool dbmsPersistSecurity;

        /// <summary>
        /// Saves an instance of user settings to the user settings file
        /// </summary>
        /// <param name="settingsToSave">The instance to save</param>
        public void Save(LocalSettings settingsToSave)
        {
            try
            {
                //create a File Stream to save our settings file to disk
                FileStream FStream = new FileStream(
                    Path.GetDirectoryName(Application.ExecutablePath) + "\\User.usf",
                    FileMode.Create);

                //Create a binary formatter to save our data with the file stream
                BinaryFormatter BinaryDataFormatter = new BinaryFormatter();

                //the file version number to imbed in the file
                Int32 FileVersion = 1;

                //the length of the encrypted memory stream
                long EncryptedMemStreamLength;

                //The serialized version of the original, unencrypted settings
                MemoryStream OriginalMemStream = new MemoryStream();

                //the encrypted version of OriginalMemStream
                MemoryStream EncryptedMemStream = new MemoryStream();

                //the parent memory stream that holds a copy of the EncryptedMemStream and other, unencrypted
                //data that will be serialized to a .usf file
                MemoryStream ParentMemStream = new MemoryStream();

                //Serialize our unencrypted settings to the OriginalMemStream
                BinaryDataFormatter.Serialize(OriginalMemStream, settingsToSave);

                //create an encryptor to encrypt our OriginalMemStream
                Aes Encryptor = Aes.Create();
                Encryptor.KeySize = 256;
                Encryptor.BlockSize = 128;
                Encryptor.GenerateIV();
                Encryptor.GenerateKey();

                //imbed the version number to the parent memory stream at position 0
                ParentMemStream.Seek(0, 0);
                ParentMemStream.Write(BitConverter.GetBytes(FileVersion), 0, 4);

                //write the AES key and IV to the parent memory stream
                //write the AES Key at position 1013
                ParentMemStream.Seek(1013, 0);
                ParentMemStream.Write(Encryptor.Key, 0, 32);

                //write the AES IV at position 1247
                ParentMemStream.Seek(1247, 0);
                ParentMemStream.Write(Encryptor.IV, 0, 16);

                //initialize a crypto stream that will encrypt the OriginalMemStream and write it to
                //the EncryptedMemStream
                CryptoStream EncryptorStream = new CryptoStream(
                    EncryptedMemStream, 
                    Encryptor.CreateEncryptor(Encryptor.Key, Encryptor.IV),
                    CryptoStreamMode.Write);

                //serialize the encrypted OriginalMemStream to the EncryptedMemStream
                BinaryDataFormatter.Serialize(EncryptorStream, EncryptedMemStream);
                EncryptorStream.FlushFinalBlock();

                //write the length of EncryptedMemStream to the parent memory stream at position 4
                //(so we can find it later when the file is re-opened)
                EncryptedMemStreamLength = EncryptedMemStream.Length;
                ParentMemStream.Seek(4, 0);
                ParentMemStream.Write(BitConverter.GetBytes(EncryptedMemStreamLength), 0, 8);

                //add EncryptedMemStream to the parent stream at position 1406
                ParentMemStream.Seek(1406, 0);
                ParentMemStream.Write(EncryptedMemStream.ToArray(), 0, EncryptedMemStream.ToArray().Length);

                //save the parent stream to file
                BinaryDataFormatter.Serialize(FStream, ParentMemStream);

                //close all the streams
                FStream.Dispose();
                OriginalMemStream.Dispose();
                EncryptedMemStream.Dispose();
                ParentMemStream.Dispose();
                EncryptorStream.Dispose();
            }
            catch (Exception Exc)
            {
                MessageBox.Show("Could not save your settings.\n\n" +
                    Exc.Message,
                    Exc.Source,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Loads the settings last saved from the settings file
        /// </summary>
        /// <returns>Returns an instance of the LocalSettings class</returns>
        public LocalSettings Load()
        {
            //try
            //{
                //create a File Stream to open our settings file
                FileStream FStream = new FileStream(
                    Path.GetDirectoryName(Application.ExecutablePath) + "\\User.usf",
                    FileMode.Open);

                //Create a formatter to open our data with the file stream
                BinaryFormatter BinaryDataFormatter = new BinaryFormatter();

                //the active file version number
                Int32 CurrentFileVersion = 1;

                //the file version number retrieved from the settings file
                Int32 FileVersion;

                //the length of the EncryptedMemStream
                long EncryptedMemStreamLength;

                //the serialized version of the original, unencrypted settings
                MemoryStream OriginalMemStream = new MemoryStream();

                //the encrypted version of OriginalMemStream
                MemoryStream EncryptedMemStream = new MemoryStream();

                //the parent memory stream that holds a copy of the EncryptedMemStream and other, unencrypted
                //data that was retrieved from the .usf file
                MemoryStream ParentMemStream = new MemoryStream();

                //load the settings file into the ParentMemStream
                ParentMemStream = BinaryDataFormatter.Deserialize(FStream) as MemoryStream;

                //create a decryptor to decrypt the OrigialMemStream from EncryptedMemStream
                Aes Decryptor = Aes.Create();
                Decryptor.KeySize = 256;
                Decryptor.BlockSize = 128;
                Decryptor.GenerateIV();
                Decryptor.GenerateKey();

                //read the imbeded file version from the ParentMemStream at position 0
                FileVersion = BitConverter.ToInt32(ParentMemStream.ToArray(), 0);

                //make sure the file version is up to date, if not, update the file to the new version
                if (FileVersion != CurrentFileVersion)
                {
                    //right now this isn't important
                    MessageBox.Show("Corrupt settings file");
                    File.Delete(Path.GetDirectoryName(Application.ExecutablePath) + "User.usf");
                }

                //read the length of EncryptedMemStream that is stored in the ParentMemStream at position 4
                //NOTE: This is not the length of the EncryptedMemStream that is initialized in this method, it is 
                //      the length of the serialized one from the .usf file
                EncryptedMemStreamLength = BitConverter.ToInt64(ParentMemStream.GetBuffer(), 4);

                //read the AES key from the ParentMemStream at position 1013
                ParentMemStream.Seek(1013, 0);
                byte[] AesKey = new byte[32];
                ParentMemStream.Read(AesKey, 0, 32);
                Decryptor.Key = AesKey;

                //read the AES IV from the ParentMemStream at position 1247
                ParentMemStream.Seek(1247, 0);
                byte[] AesIV = new byte[16];
                ParentMemStream.Read(AesIV, 0, 16);
                Decryptor.IV = AesIV;

                //write the EncryptedMemStream stored in the ParentMemStream to the current instance of
                //EncryptedMemStream
                ParentMemStream.Seek(1406, 0);
                byte[] RawEncryptedMemStream = new byte[EncryptedMemStreamLength];
                ParentMemStream.Read(RawEncryptedMemStream, 0, RawEncryptedMemStream.Length);
                EncryptedMemStream.Write(RawEncryptedMemStream, 0, RawEncryptedMemStream.Length);
                EncryptedMemStream.Seek(0, 0);

                //initialize a crypto stream that will decrypt the RawEncryptedMemStream and write it to
                //the OriginalMemStream
                CryptoStream DecryptorStream = new CryptoStream(
                    EncryptedMemStream,
                    Decryptor.CreateDecryptor(Decryptor.Key, Decryptor.IV),
                    CryptoStreamMode.Read);

                //deserialize the now unencrypted OriginalMemStream
                OriginalMemStream = BinaryDataFormatter.Deserialize(DecryptorStream) as MemoryStream;
                OriginalMemStream.Seek(0, 0);

                //create an instance of the settings to return
                LocalSettings LoadedSettings = BinaryDataFormatter.Deserialize(OriginalMemStream) as LocalSettings;

                //close all the streams
                FStream.Dispose();
                OriginalMemStream.Dispose();
                EncryptedMemStream.Dispose();
                ParentMemStream.Dispose();
                DecryptorStream.Dispose();

                //return the loaded settings
                return LoadedSettings;
            //}
            //catch (Exception Exc)
            //{
            //    MessageBox.Show("Could not load your settings.\n\n" +
            //        Exc.Message,
            //        Exc.Source,
            //        MessageBoxButtons.OK,
            //        MessageBoxIcon.Error);
            //    return null;
            //}
        }
    }
}

I thing you are not saving the correct length of your encrypted stream.
This bit EncryptedMemStreamLength = EncryptedMemStream.Length; Should be EncryptedMemStreamLength = EncryptedMemStream.ToArray().Length;

Also, just notices you are trying to encrypt the encrypted memory stream to itself!
This bit BinaryDataFormatter.Serialize(EncryptorStream, EncryptedMemStream); Should be BinaryDataFormatter.Serialize(EncryptorStream, OriginalMemStream);

Comments
Awesome! Thank you so much!

Thank you so much! You have no idea how many HOURS I spent on this...thank you thank you thank you!

I also changed this line from:

//read the length of EncryptedMemStream that is stored in the ParentMemStream at position 4
                //NOTE: This is not the length of the EncryptedMemStream that is initialized in this method, it is 
                //      the length of the serialized one from the .usf file
                EncryptedMemStreamLength = BitConverter.ToInt64(ParentMemStream.GetBuffer(), 4);

to:

//read the length of EncryptedMemStream that is stored in the ParentMemStream at position 4
                //NOTE: This is not the length of the EncryptedMemStream that is initialized in this method, it is 
                //      the length of the serialized one from the .usf file
                EncryptedMemStreamLength = BitConverter.ToInt64(ParentMemStream.ToArray(), 4);

Don't know if GetBuffer() and ToArray work the same, but why take any chances?

This question has already been answered. Start a new discussion instead.