views:

573

answers:

3

Hi there,

I'm trying to store a password in a file that I'd like to retrieve for later. Hashing is not an option as I need the password for connecting to a remote server for later.

The following code works well, but it creates a different output each time even though the key is the same. This is bad as when the application shuts down and restarts I won't be able to retrieve my password any more. How can I store passwords in a file and retrieve them later?

public class EncyptDecrypt {

    static System.Security.Cryptography.TripleDESCryptoServiceProvider keyProv = new System.Security.Cryptography.TripleDESCryptoServiceProvider();

    public static System.Security.Cryptography.TripleDESCryptoServiceProvider KeyProvider {
        get {
            keyProv.Key = new byte[] { /* redacted with prejudice */ };
            return keyProv;
        }
    }

    public static string Encrypt(string text, SymmetricAlgorithm key) {

        if (text.Equals(string.Empty)) return text;

        // Create a memory stream.
        MemoryStream ms = new MemoryStream();

        // Create a CryptoStream using the memory stream and the
        // CSP DES key.
        CryptoStream encStream = new CryptoStream(ms, key.CreateEncryptor(), CryptoStreamMode.Write);

        // Create a StreamWriter to write a string
        // to the stream.
        StreamWriter sw = new StreamWriter(encStream);

        // Write the plaintext to the stream.
        sw.WriteLine(text);

        // Close the StreamWriter and CryptoStream.
        sw.Close();
        encStream.Close();

        // Get an array of bytes that represents
        // the memory stream.
        byte[] buffer = ms.ToArray();

        // Close the memory stream.
        ms.Close();

        // Return the encrypted byte array.
        return System.Convert.ToBase64String(buffer);
    }

    // Decrypt the byte array.
    public static string Decrypt(string cypherText, SymmetricAlgorithm key) {

        if (cypherText.Equals(string.Empty)) return cypherText;

        string val;

        try {
            // Create a memory stream to the passed buffer.
            MemoryStream ms = new MemoryStream(System.Convert.FromBase64String(cypherText));

            // Create a CryptoStream using the memory stream and the
            // CSP DES key.
            CryptoStream encStream = new CryptoStream(ms, key.CreateDecryptor(), CryptoStreamMode.Read);

            // Create a StreamReader for reading the stream.
            StreamReader sr = new StreamReader(encStream);

            // Read the stream as a string.
            val = sr.ReadLine();

            // Close the streams.
            sr.Close();
            encStream.Close();
            ms.Close();
        }
        catch (System.Exception) {

            return string.Empty;
        }

        return val;
    }
}
+7  A: 

I believe that what's happening is that the crypto provider is randomly generating an IV. Specify this and it should no longer differ.

Edit: You can do this in your 'keyProvider' by setting the IV property.

Cody Brocious
As a note, an IV is an additional piece of data that goes along with your key. This information is public and doesn't have to be hidden and adds nothing to security if it is. It is used to make cryptanalysis harder.
Julian Krause
Could the implementation also be padding with random data? I don't know enough to say whether it is or not, just enough to say that in some block cipher modes that's appropriate.
Steve Jessop
It'll be padded with zeros before encryption, IIRC. And even so, you'd only see differing data in the last <blocksize> bytes. Of course, the asker didn't specify /how/ different the data was, so who knows.
Cody Brocious
I was just thinking along the lines of "if this idea doesn't fix it, what's next". Since you've been accepted, I'm guessing it was just the IV.
Steve Jessop
Manually specifying the IV is also (usually) a bad idea. Encrypting multiple plaintexts with the same IV is a _really_ bad idea. Why is it important that the output always be the same for the same plaintext?
Nick Johnson
+3  A: 

According to the docs of CreateEncryptor:

If the current IV property is a null reference (Nothing in Visual Basic), the GenerateIV method is called to create a new random IV.

This will make the ciphertext different every time.

Note: a way around this is discussed here where I suggest you can prepend the plaintext with a mac ... then the first block of ciphertext is effectively the IV, but it's all repeatable

Purfideas
+1  A: 

You need to specify an IV (initialization vector), even if you generate a random one. If you use random IV then you must store it along with the ciphertext so you can use it later on decryption, or you can derive an IV from some other data (for example if you're encrypting a password, you can derive the IV from the username).

Chochos