c# - 在 clickonce 应用程序中安全地存储服务帐户凭据

标签 c# security encryption clickonce credentials

我正在编写一个 ClickOnce 应用程序,它使用服务帐户凭据运行批处理文件进程。我需要存储服务帐户凭据,以便程序可以在运行进程之前将用户名/密码添加到 process.startinfo 属性。用户不知道此密码,因此不会提示他们输入密码。我相信这意味着我不能以这种方式存储散列和验证密码,我生成的散列值必须是可逆的,以便它可以将正确的密码添加到 startinfo 属性。我搜索了这个网站并提出了一个科学怪人类型的解决方案,但它不是很安全。目前,我使用此方法加密密码,存储加密值,然后在运行时使用 decrypt 方法获取密码(加密方法在运行时从不运行,我在调试时在 Visual Studio 中运行它,复制值,然后在下面的解密方法中使用该值):

// 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);
    }

解密方法如下:

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;
    }

此代码工作正常,但是,我知道它不安全。我的代码审查人员说他能够在一个小时内使用 dotPeek 破解它,因为它只是添加了一层混淆。在应用程序中存储这些凭据的最佳/正确方法是什么?

最佳答案

加密 key 在专用服务器上。

密码连同要加密的 id 一起发送到服务器,并返回用于数据库存储的加密密码。

当需要密码时,会向带有 id 的专用服务器发出请求,并返回解密后的密码。

密码永远不会保存到磁盘,并且 key 永远不会在专用服务器上可用。

专用服务器有点像穷人的 HSM。

这是加密,不是散列。加密 key 与随机 IV 一起是 secret 的,它与专用服务器上的 id 一起保存。 key 不可用且与密码无关,因此没有比暴力破解加密 key 更好的攻击了,加密 key 本质上太大而无法被暴力破解。

服务器需要非常安全,只有几个两因素登录并且不能用于 Internet。

关于c# - 在 clickonce 应用程序中安全地存储服务帐户凭据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40598563/

相关文章:

php - 如何减少这个脚本的执行时间?

javascript - 什么颜色格式允许正确的颜色值 "adding"或 "mixing"?

sql - SQL 注入(inject)漏洞中的奇怪格式?

asp.net - 如何在 IIS 中使用 nonce 进行内容安全策略?

security - 这种使用 HttpOnly Cookie 的 XSS 保护能起作用吗?

c# - C/C++/C#中的DES实现

java - 可恢复密码的推荐加密方法

c# - 如何使用递归编程填充包含多个类别的列表框

c# - 将 Resharper 中的间距行为更改为多行 lambda 现场表达式

c# - 比私有(private)更私有(private)? (C#)