在clickonce应用程序中安全地存储服务帐户凭据 [英] Storing service account credentials securely in clickonce application

查看:166
本文介绍了在clickonce应用程序中安全地存储服务帐户凭据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  //用于生成解密的acct凭据
private void EncryptText(string plaintext)
{
string outsrt = null;
RijndaelManaged aesAlg = null;

尝试
{
//从密钥和盐生成密钥
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedsecret,_salt);

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

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

使用(MemoryStream mEncrypt = new MemoryStream())
{
//前置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))
{
//将所有数据写入流
swEncrypt.Write(plaintext);
}
}

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

Console.WriteLine(outsrt);
}

这是解密方法:

 私有字符串GetServiceAcctPW()
{

//声明RijndaelManaged对象
//用于解密数据。
RijndaelManaged aesAlg = null;

//声明用于保存
//解密文本的字符串。
string plaintext = null;

尝试
{
//从共享密钥和盐生成密钥
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedsecret,_salt);

//创建用于解密的流。
byte [] bytes = Convert.FromBase64String(EncryptedValueHere);
使用(MemoryStream msDecrypt = new MemoryStream(bytes))
{
//使用指定的键创建一个RijndaelManaged对象
//和IV。
aesAlg = new RijndaelManaged();
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
//从加密流获取初始化向量
aesAlg.IV = ReadByteArray(msDecrypt);
//创建一个解密器来执行流转换。
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key,aesAlg.IV);
using(CryptoStream csDecrypt = new CryptoStream(msDecrypt,decryptor,CryptoStreamMode.Read))
{
using(StreamReader srDecrypt = new StreamReader(csDecrypt))

/ /从解密流
//读取解密的字节,并将它们放在字符串中。
plaintext = srDecrypt.ReadToEnd();
}
}
}
catch(异常e)
{
Console.WriteLine(Error decrypting password);
Console.WriteLine(e.StackTrace);
logger.WriteToLog(Logger.LogCodes.ERROR,解密服务帐户密码错误);
MessageBox.Show(尝试启动安装过程时发生错误请联系服务台获取进一步的帮助);
}
finally
{
//清除RijndaelManaged对象。
if(aesAlg!= null)
aesAlg.Clear();
}

返回明文;
}

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

解决方案

加密密钥在专用服务器上。



密码与要加密的ID一起发送到服务器,并为DB存储返回加密的密码。



当需要密码时,请求具有ID的专用服务器,并返回解密的密码。



密码永远不会保存到磁盘,密钥永远不可用于专用服务器。



专用服务器是一种类似于穷人的HSM。



这是加密,不是哈希。加密密钥是一个随机的IV,它与专用服务器上的id一起保存。密钥不可用,与密码无关,所以没有比强力对抗加密密钥更好的攻击,该加密密钥本质上是大到被强力攻击的。



服务器需要非常安全,只需要两个因素登录,而不能上网。


I'm writing a ClickOnce application that runs a batch file process with service account credentials. I need to store the service account credentials so that the program can add the username/password to the process.startinfo property before running the process. The users do not know this password, so there's no prompt for them to enter in a password. I believe this means I cannot store the hash and verify the password that way, the hash value 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 Frankenstein-type solution that works, but it's not very secure. Currently, I used this method to encrypt the password, stored the encrypted value, then use the decrypt method to obtain the password during runtime (the encrypt method is never ran during runtime, I ran it in Visual Studio during debug, copied the value, then used that value in the decrypt method below this):

// 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 decrypt 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 just fine, however, I know it's not secure. My code review guy said he was able to crack it with dotPeek in an hour because it's only adding a layer of obfuscation. What would be the best/proper way to store these credentials within the application?

解决方案

The encryption key is on a dedicated server.

The password is sent to the server along with an id to be encrypted and the encrypted password returned for DB storage.

When the the password is needed a request is made to the dedicated server with the id and a decrypted password is returned.

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

The dedicated server is kind-of-like a poor-mans HSM.

This is encryption, not hashing. The encryption key is secret along with a random IV that that is saved with the id on the dedicated server. The key is not available and not related to the password so there is no better attack than brute force against the encryption key which is essentially to large to be attacked by brute force.

The server needs to be very secure, only a couple of two factor logins and not available to the Internet.

这篇关于在clickonce应用程序中安全地存储服务帐户凭据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆