2

我正在做这个应用程序,它取决于存储在 xml 文件中的设置。此文件应加密,其中的值由负责创建应用程序设置的人员提供,并用于根据用户安装的版本确定可用的功能选项。

我需要一种方法将硬编码的密码存储在我的软件中,以便能够在运行时解密该文件并读取其中的值以查看用户可以访问应用程序的哪些功能。

请记住,此文件不应被编辑,并作为软件的一部分提供。

我没有提供任何代码,因为它更多的是设计问题而不是编码问题。

我知道硬编码密码是愚蠢的,但我没有选择。

4

4 回答 4

8

如果您将应用程序提供给不值得信任的用户(即这是一个桌面应用程序,而不是在用户无法直接访问的 [ASP] 服务器上运行的代码),那么您无能为力。

如果您将代码提供给将解密配置文件的用户,那么他们将能够自己访问该文件。如果你投入时间/精力/金钱,你可以让它变得更难,甚至可能更难,但你不能让它变得不可能。以下是他们可以做的一些事情:

  1. 反编译您的程序并查找password = "12345"代码行。
  2. 监控程序的内存;查看它何时加载 XML 文件,并尝试在内存中找到它的解密版本。
  3. 找到您读取解密的 XML 文件的代码部分,并相应地执行一些操作并更改代码,以便无论文件中的内容如何,​​它总是可以做任何他们想做的事情(基本上只是注释掉if检查)。

您可以做一些事情来使上述步骤更难(但并非不可能)包括:

  1. 混淆你的代码。
  2. 签署您的代码。
  3. 做一些毫无意义的随机事情来试图混淆代码嗅探器(例如通过拥有 3 个文件来玩 shell 游戏,全部读取它们,全部解密它们,然后让其中的 2 个文件实际上没有被使用。
  4. 将配置文件发送到您的 Web 服务进行解密,而不是在本地解密。(这可以通过嗅探网络以获取解密结果来解决)。
  5. 拥有一个您可以查询的 Web 服务,以查看用户是否有权执行您想要的操作(同样,这可以通过嗅探/欺骗网络连接来解决)。

现在,有可能实际上阻止用户做“某事”,这取决于“某事”是什么,首先不给他们做这件事的代码。这些将(可能;如果编码正确)是牢不可破的:

  1. 在服务器上完成工作。
    1. 拥有一个可以完成一些敏感工作的 Web 服务。桌面应用程序仅管理 UI 或其他非敏感任务。如果你这样做,用户只能破坏你给他们的代码。
    2. 使整个应用程序成为网站或其他基于服务器的应用程序(例如 MMORPGS),在没有服务器的情况下它根本无法运行;它几乎完成了所有敏感(和非敏感)的工作。

请注意,唯一真正的解决方案要求所有用户在使用该应用程序时都可以使用互联网连接;他们不能离线。

于 2012-10-12T17:36:16.683 回答
4
internal const string XmlPassword = "This is more like security through "+
+"obfuscation than real security.  If it fits your purposes, cool, but you "+
+"might want to consider using real encryption, like public key encryption";
于 2012-10-12T17:26:54.110 回答
3

一种方法是始终根据文件的某些哈希值加密设置文件。然后,您的软件可以使用与密钥相同的哈希值来解密文件。

所以你基本上会做以下事情:

  1. 获取文件名(和/或文件的一些其他属性 - 大小,也许)
  2. 计算文件名的哈希
  3. 使用该哈希解密文件

并不是说这是一种好方法或我个人会在任何类型的生产软件中使用的方法,但至少您不依赖于代码中的纯文本或过度使用的字符串,它使您可以灵活地更改设置文件名并依赖于加密的约定。

同样,这可能只是“将密钥存储在源”解决方案之上的一步。

于 2012-10-12T17:42:16.870 回答
3

如果我没看错 - OP 需要用某种类型的第 3 方软件包解密提供的文件(已经加密),并且他已经从软件的原始发布者那里获得了解密密码(密钥)。

我会做类似于itsmatt建议的事情,只是不加密文件,加密提供的密码,将加密的密码存储在您的配置文件中,然后在运行时读取和解密密码,并使用它来解密文件。

这样,您就不会在源代码中硬编码明文密码,以便轻松嗅出。通过将其保存在您的配置文件中,您可以在将来需要时轻松更改它。

回复评论:

我使用 AES256。

我使用这个类来 AES256 加密/解密:

#region Preprocessor Directives

# if __FRAMEWORK_V35 || __FRAMEWORK_V4
#define __DotNet35Plus
#endif

#if !__DotNet35Plus
#warning AES implementation used by this compile of the library is not NIST certified as FIPS 140-2 compliant
#endif

#endregion

#region Namespaces
using System;
using System.Security.Cryptography;
using System.IO;
using System.Text;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
#endregion

namespace Simple
{
    public static class AES256Encryption
    {
        private static readonly object _lock = new object();
        private const Int32 KeySize = 256;
#if __DotNet35Plus
        private static AesCryptoServiceProvider thisCSP = new AesCryptoServiceProvider();
#else
        private static RijndaelManaged thisCSP = new RijndaelManaged();
#endif
        private static MemoryStream msEncrypt = new MemoryStream();
        private static CryptoStream csEncrypt;
        private static MemoryStream msDecrypt = new MemoryStream();
        private static CryptoStream csDecrypt;

        public enum stringIOType
        {
            base64EncodedString = 0,
            HexEncodedString = 1
        }

        public static bool NISTCertified()
        {
#if __DotNet35Plus
            return true;
#else
            return false;
#endif
        }

        #region Encryption Methods
        public static byte[] encryptBytes(byte[] Value, string PassPhrase, Encoding PassPhraseEncoding)
        {
            try
            {
                Monitor.Enter(_lock);
                return encryptBytes(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding));
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] encryptBytes(byte[] Value, byte[] Key, byte[] IV)
        {
            try
            {
                Monitor.Enter(_lock);
#if __DotNet35Plus
                thisCSP = new AesCryptoServiceProvider();
#else
                thisCSP = new RijndaelManaged();
#endif
                thisCSP.KeySize = KeySize;
                Int32 bitLength = Key.Length * 8;
                if (bitLength != thisCSP.KeySize)
                {
                    throw new ArgumentException("The supplied key's length [" + bitLength.ToString() + " bits] is not a valid key size for the AES-256 algorithm.", "Key");
                }
                bitLength = IV.Length * 8;
                if (bitLength != thisCSP.BlockSize)
                {
                    throw new ArgumentException("The supplied IV's length [" + bitLength.ToString() + " bits] is not a valid IV size for the AES-256 algorithm.", "IV");
                }
                ICryptoTransform Encryptor = thisCSP.CreateEncryptor(Key, IV);
                msEncrypt = new MemoryStream();
                csEncrypt = new CryptoStream(msEncrypt, Encryptor, CryptoStreamMode.Write);
                csEncrypt.Write(Value, 0, Value.Length);
                csEncrypt.FlushFinalBlock();
                Encryptor.Dispose();
                Encryptor = null;
                msEncrypt.Close();
                return msEncrypt.ToArray();
            }
            finally
            {
                thisCSP = null;
                Monitor.Exit(_lock);
            }
        }

        public static string encryptString(string Value, string PassPhrase, Encoding PassPhraseEncoding, Encoding inputEncoding, stringIOType outputType)
        {
            try
            {
                Monitor.Enter(_lock);
                return encryptString(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding), inputEncoding, outputType);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static string encryptString(string Value, byte[] Key, byte[] IV, Encoding inputEncoding, stringIOType outputType)
        {
            try
            {
                Monitor.Enter(_lock);
                byte[] baseValue = (byte[])Array.CreateInstance(typeof(byte), inputEncoding.GetByteCount(Value));
                baseValue = inputEncoding.GetBytes(Value);
                switch(outputType)
                {
                    case stringIOType.base64EncodedString:
                        return Convert.ToBase64String(encryptBytes(baseValue, Key, IV));
                    case stringIOType.HexEncodedString:
                        return ByteArrayToHexString(encryptBytes(baseValue, Key, IV));
                    default:
                        return Convert.ToBase64String(encryptBytes(baseValue, Key, IV));
                }
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }
        #endregion

        #region Decryption Methods
        public static byte[] decryptBytes(byte[] Value, string PassPhrase, Encoding PassPhraseEncoding)
        {
            try
            {
                Monitor.Enter(_lock);
                return decryptBytes(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding));
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] decryptBytes(byte[] Value, byte[] Key, byte[] IV)
        {
            try
            {
                Monitor.Enter(_lock);
#if __DotNet35Plus
                thisCSP = new AesCryptoServiceProvider();
#else
                thisCSP = new RijndaelManaged();
#endif
                thisCSP.KeySize = KeySize;
                Int32 bitLength = Key.Length * 8;
                if (bitLength != thisCSP.KeySize)
                {
                    throw new ArgumentException("The supplied key's length [" + bitLength.ToString() + " bits] is not a valid key size for the AES-256 algorithm.", "Key");
                }
                bitLength = IV.Length * 8;
                if (bitLength != thisCSP.BlockSize)
                {
                    throw new ArgumentException("The supplied IV's length [" + bitLength.ToString() + " bits] is not a valid IV size for the AES-256 algorithm.", "IV");
                }
                try
                {
                    byte[] Decrypted;
                    ICryptoTransform Decryptor = thisCSP.CreateDecryptor(Key, IV);
                    msDecrypt = new MemoryStream(Value);
                    csDecrypt = new CryptoStream(msDecrypt, Decryptor, CryptoStreamMode.Read);
                    Decrypted = (byte[])Array.CreateInstance(typeof(byte), msDecrypt.Length);
                    csDecrypt.Read(Decrypted, 0, Decrypted.Length);
                    Decryptor.Dispose();
                    Decryptor = null;
                    msDecrypt.Close();
                    Int32 trimCount = 0;
                    // Remove any block padding left over from encryption algorithm before returning
                    for (Int32 i = Decrypted.Length - 1; i >= 0; i--)
                    {
                        if (Decrypted[i] == 0) { trimCount++; } else { break; }
                    }
                    if (trimCount > 0)
                    {
                        byte[] buffer = (byte[])Array.CreateInstance(typeof(byte), Decrypted.Length - trimCount);
                        Array.ConstrainedCopy(Decrypted, 0, buffer, 0, buffer.Length);
                        Array.Clear(Decrypted, 0, Decrypted.Length);
                        Array.Resize<byte>(ref Decrypted, buffer.Length);
                        Array.Copy(buffer, Decrypted, buffer.Length);
                        buffer = null;
                    }
                    return Decrypted;
                }
                finally
                {
                    thisCSP = null;
                }
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static string decryptString(string Value, string PassPhrase, Encoding PassPhraseEncoding, stringIOType inputType, Encoding outputEncoding)
        {
            try
            {
                Monitor.Enter(_lock);
                return decryptString(Value, getKeyFromPassPhrase(PassPhrase, PassPhraseEncoding), getIVFromPassPhrase(PassPhrase, PassPhraseEncoding), inputType, outputEncoding);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static string decryptString(string Value, byte[] Key, byte[] IV, stringIOType inputType, Encoding outputEncoding)
        {
            try
            {
                Monitor.Enter(_lock);
                byte[] baseValue;
                switch (inputType)
                {
                    case stringIOType.base64EncodedString:
                        baseValue = Convert.FromBase64String(Value);
                        break;
                    case stringIOType.HexEncodedString:
                        baseValue = HexStringToByteArray(Value);
                        break;
                    default:
                        baseValue = Convert.FromBase64String(Value);
                        break;
                }
                return outputEncoding.GetString(decryptBytes(baseValue, Key, IV));
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }
        #endregion

        #region Key/Digest Generation Methods
        public static byte[] getKeyFromPassPhrase(string PassPhrase, Encoding encoder)
        {
            Monitor.Enter(_lock);
            try
            {
                return getDigest(PassPhrase, encoder, 32);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] getIVFromPassPhrase(string PassPhrase, Encoding encoder)
        {
            Monitor.Enter(_lock);
            try
            {
                byte[] buffer = (byte[])Array.CreateInstance(typeof(byte), encoder.GetByteCount(PassPhrase));
                byte[] reverseBuffer = (byte[])Array.CreateInstance(typeof(byte), encoder.GetByteCount(PassPhrase));
                buffer = encoder.GetBytes(PassPhrase);
                for (Int32 i = 0; i <= buffer.Length - 1; i++)
                {
                    reverseBuffer[i] = buffer[buffer.Length - i - 1];
                }
                return getDigest(reverseBuffer, 16);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] getDigest(string value, Encoding encoder, Int32 digestLength)
        {
            Monitor.Enter(_lock);
            try
            {
                return getDigest(encoder.GetBytes(value), digestLength);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] getDigest(object value, Int32 digestLength)
        {
            Monitor.Enter(_lock);
            try
            {
                BinaryFormatter bf = new BinaryFormatter();
                MemoryStream ms = new MemoryStream();
                bf.Serialize(ms, value);
                return getDigest(ms.ToArray(), digestLength);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] getDigest(byte[] value, Int32 digestLength)
        {
            Monitor.Enter(_lock);
            try
            {
                Int32 iterations = 0;
                // Find first non-zero byte value to use to calculate iterations
                for (Int32 i = 0; i < value.Length; i++)
                {
                    if (value[i] != 0) { iterations = (Int32)(value[i] * 10); break; }
                }
                // There were no non-zero byte values use the max for iterations
                if (iterations == 0) { iterations = (Int32)(byte.MaxValue * 10); }
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(value, new SHA256Managed().ComputeHash(value), iterations);
                return deriveBytes.GetBytes(digestLength);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }
        #endregion

        #region HexArray/String String/HexArray Converters
        public static string ByteArrayToHexString(byte[] ba)
        {
            try
            {
                Monitor.Enter(_lock);
                StringBuilder hex = new StringBuilder(ba.Length * 2);
                foreach (byte b in ba)
                    hex.AppendFormat("{0:x2}", b);
                return hex.ToString();
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public static byte[] HexStringToByteArray(String hex)
        {
            try
            {
                Monitor.Enter(_lock);
                int NumberChars = hex.Length;
                byte[] bytes = new byte[NumberChars / 2];
                for (int i = 0; i < NumberChars; i += 2)
                    bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
                return bytes;
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }
        #endregion
    }
}

如果您使用的是 .Net Framework 3.5 或 4.0,请将 __FRAMEWORK_V35 或 __FRAMEWORK_V40 定义为预处理器的值。使用的类不适用于低于 3.5 的框架版本。如果您使用的是较早的框架版本,只需不要定义预处理器值,将使用较早的类。

首先获取文件名。

然后使用以下加密 OEM 密码(我建议编写一个小工具来执行此操作):

string fileName = "Whatever The Filename is";
string password = "Whatever the OEM supplied password is";
string encryptedValue = Simple.AES256Encryption.encryptString(password, fileName, new UTF8Encoding(), new UTF8Encoding(), stringIOType.base64EncodedString);

将生成的 base64 编码字符串从 encryptString 保存到您的配置文件中。

要恢复 OEM 密码:

string encryptedPassword = "This is the base64 encoded string you read from your config file";
string decrytptedPassword = Simple.AES256Encryption.decryptString(encryptedPassword, fileName, new UTF8Encoding(), stringIOType.base64EncodedString, new UTF8Encoding());
于 2012-10-12T18:05:06.193 回答