Safe storage of account credentials in clickonce app

I am writing a ClickOnce application that starts a batch file process with the credentials of a service account. I need to store the credentials of the service account so that the program can add username / password to the process.startinfo property before starting the process. Users do not know this password, so there is no need to enter a password. I believe this means that I cannot store the hash and validate the password this way, the hash value that I generate must be reversible so that it can add the correct password to the startinfo property. I searched around this site and came up with a solution like Frankenstein that works but is not very secure. I have currently used this method to encrypt a password, store an encrypted value,and then used the decryption method to get the password at runtime (the encryption method was never triggered at runtime, I ran it in Visual Studio while debugging, copied the value, then used that value in the decryption method below that):

// used to generate decrypted acct creds
    private void EncryptText(string plaintext)
    {
        string outsrt = null;
        RijndaelManaged aesAlg = null;

        try
        {
            // generate key from secret and salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedsecret, _salt);

            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            using (MemoryStream mEncrypt = new MemoryStream())
            {
                // prepend the IV
                mEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                mEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
                using (CryptoStream csEncrypt = new CryptoStream(mEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        // write all data to the stream
                        swEncrypt.Write(plaintext);
                    }
                }

                outsrt = Convert.ToBase64String(mEncrypt.ToArray());
            }
        }
        finally
        {
            if (aesAlg != null)
                aesAlg.Clear();
        }

        Console.WriteLine(outsrt);
    }

      

Here's the decryption method:

private string GetServiceAcctPW()
    {

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedsecret, _salt);

            // Create the streams used for decryption.                
            byte[] bytes = Convert.FromBase64String("EncryptedValueHere");
            using (MemoryStream msDecrypt = new MemoryStream(bytes))
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                // Get the initialization vector from the encrypted stream
                aesAlg.IV = ReadByteArray(msDecrypt);
                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
        catch(Exception e)
        {
            Console.WriteLine("Error decrypting password");
            Console.WriteLine(e.StackTrace);
            logger.WriteToLog(Logger.LogCodes.ERROR, "Error decrypting service account password");
            MessageBox.Show("An error occurred while trying to start the installation process\nPlease contact the Service Desk for further assistance");
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return plaintext;
    }

      

This code works fine, however I know it is not secure. My code review guy said that he was able to hack it with dotPeek after an hour because it only adds an obfuscation layer. What would be the best / correct way to store these credentials in the application?

+2


source to share


1 answer


The encryption key is located on a dedicated server.

The password is sent to the server along with the identifier to be encrypted and the encrypted password returned to the database store.

When a password is required, a request is sent to the dedicated server and the decrypted password is returned.

The password is never saved to disk and the key is never available on the dedicated server.



A dedicated server is a kind of low-service HSM.

This is encryption, not hashing. The encryption key is secret along with a random IV, which is stored with an identifier on a dedicated server. The key is not available and is not associated with a password, so there is no better attack than brute force against an encryption key, which is essentially big to brute force attack.

The server must be very secure, only password-protected from two factor accounts and not accessible to the internet.

+1


source







All Articles