4

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;
    }
}
Agnel Kurian
  • 57,975
  • 43
  • 146
  • 217
Razor
  • 17,271
  • 25
  • 91
  • 138

3 Answers3

8

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.

Serafina Brocious
  • 30,433
  • 12
  • 89
  • 114
  • 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. – Harley Klein Sep 23 '08 at 01:27
  • 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 Sep 23 '08 at 01:30
  • It'll be padded with zeros before encryption, IIRC. And even so, you'd only see differing data in the last bytes. Of course, the asker didn't specify /how/ different the data was, so who knows. – Serafina Brocious Sep 23 '08 at 01:31
  • 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 Sep 23 '08 at 02:22
  • 2
    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 Jan 19 '09 at 13:28
  • Adding to Nick's comment -- if the same passwords encrypt to the same ciphertext, you're leaking information about those passwords. It's better if they encrypt to _different_ ciphertext ...as long as you can still decrypt them. Fortunately you *can* decrypt them still! The standard approach is to save the (random) IV as part of the ciphertext. – Rob Whelan Jan 21 '17 at 08:22
3

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

Community
  • 1
  • 1
Purfideas
  • 3,288
  • 1
  • 24
  • 17
2

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
  • 5,155
  • 22
  • 27