2

私はこのアプリケーションを実行していますが、xmlファイルに保存されている設定によって異なります。このファイルは暗号化する必要があり、その中の値はアプリケーションセットアップの作成を担当する人によって提供され、ユーザーがインストールしたバージョンに応じて使用可能な機能オプションを決定するために使用されます。

ソフトウェアにハードコードされたパスワードを保存して、実行時にそのファイルを復号化し、そのファイルの値を読み取って、ユーザーがアクセスできるアプリの機能を確認できるようにする方法が必要です。

このファイルは編集してはならず、ソフトウェアの一部として提供されていることに注意してください。

コーディングの問題というよりも設計の問題であるため、コードは提供していません。

パスワードをハードコーディングするのはばかげていることは知っていますが、選択肢がありません。

4

4 に答える 4

8

信頼できないユーザーにアプリケーションを提供する場合 (つまり、ユーザーが直接アクセスできない [ASP] サーバー上で実行されるコードではなく、デスクトップ アプリである場合)、できることは何もありません。

構成ファイルを復号化するコードをユーザーに提供している場合、ある時点で、ユーザーはそのファイルに自分でアクセスできるようになります。時間/労力/お金を投入すれば、もっと難しくすることもできますが、不可能にすることはできません。彼らができることは次のとおりです。

  1. プログラムを逆コンパイルし、コード行を探しpassword = "12345"ます。
  2. プログラムのメモリを監視します。XML ファイルをロードするタイミングを確認し、メモリ内で復号化されたバージョンを見つけようとします。
  3. if復号化された XML ファイルを読み取り、それに応じて何らかのアクションを実行するコードのセクションを見つけて、ファイルの内容に関係なく常に必要なことを実行するようにコードを変更します (基本的には、チェックをコメントアウトするだけです)。

上記の手順をより困難にする (ただし不可能ではない) ためにできることには、次のようなものがあります。

  1. コードの難読化。
  2. コードに署名しています。
  3. 混乱させようとしてランダムに意味のないことをするのは、コード スニファーです (たとえば、3 つのファイルを用意し、それらをすべて読み取り、すべてを解読し、そのうちの 2 つを実際には使用しないようにして、シェル ゲームをプレイします。
  4. 構成ファイルをローカルで復号化するのではなく、復号化する Web サービスに送信します。(これは、復号化された結果をネットワークでスニッフィングすることで無効にすることができます)。
  5. ユーザーが必要なことを行うためのアクセス許可を持っているかどうかを確認するためにクエリを実行する Web サービスを用意します (これは、ネットワーク接続をスニッフィング/スプーフィングすることで無効にすることができます)。

さて、「何か」が何であるかに応じて、ユーザーが「何か」を実行するのを実際に防ぐことができる可能性があります。これは、最初にそれを実行するコードをユーザーに提供しないことによって可能です。これらは (潜在的に; 正しくコーディングされていれば) 壊れません:

  1. サーバー上で作業を行います。
    1. 機密性の高い作業を行う Web サービスを用意します。デスクトップ アプリは、UI またはその他の機密性の低いタスクのみを管理します。これを行うと、ユーザーは与えられたコードを壊すことしかできなくなります。
    2. アプリ全体を Web サイトにするか、サーバーなしではまったく機能しない他のサーバー ベースのアプリケーション (つまり、MMORPG を考えてください) にします。機密性の高い (および機密性の低い) 作業のほとんどすべてを行います。

唯一の真の解決策は、アプリケーションを使用するときにすべてのユーザーが利用できるインターネット接続を必要とすることに注意してください。オフラインにすることはできません。

于 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

私が質問を正しく読んでいる場合-OPは、何らかのタイプのサードパーティソフトウェアパッケージで提供された(すでに暗号化されている)ファイルを復号化する必要があり、ソフトウェアの元の発行者から復号化パスワード(キー)を取得しています。

ファイルを暗号化する代わりに、提供されたパスワードを暗号化し、その暗号化されたパスワードを構成ファイルに保存し、実行時にパスワードを読み取って復号化し、それを使用してファイルを復号化します。

このようにして、簡単に盗聴できるようにソースに平文のパスワードをハードコーディングする必要はありません。構成ファイルに保持することで、必要に応じて将来簡単に変更できます。

コメントへの回答:

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 に答える
3

アプローチは、ファイルのハッシュに基づいて設定ファイルを常に暗号化することです。次に、ソフトウェアは同じハッシュをキーとして使用してファイルを復号化できます。

したがって、基本的には次のようにします。

  1. ファイル名を取得します(および/またはファイルの他の属性-サイズなど)
  2. ファイル名のハッシュを計算する
  3. そのハッシュを使用してファイルを復号化します

これが良いアプローチである、または私が個人的にあらゆる種類のプロダクション ソフトウェアで使用するアプローチであると言っているわけではありませんが、少なくともコード内のプレーンテキストまたは使用されていない文字列に依存しておらず、設定ファイル名を柔軟に変更できます。暗号化の規則に依存します。

繰り返しますが、これはおそらく「ソースにキーを保存する」ソリューションよりも上のステップにすぎません。

于 2012-10-12T17:42:16.870 に答える