【解決方法】C#: 単純なライセンス キーを実装する方法 / checklicence() を呼び出す方法

[ad_1]

静的クラスのすべてのメソッドで CheckLicence() メソッドを自動的に呼び出す方法はありますか?

何百ものメソッドを持ついくつかの静的クラスがあります。
したがって、グローバル ライセンス チェックは、すべてのクラス メソッドで CheckLicence() メソッドを呼び出すよりも簡単です。

例の説明:
licenceKey はアプリケーションの起動時に設定されます。
したがって、ライセンスは一生大丈夫です。

AddMethod() の CheckLicence() 呼び出しを削除したいと考えています。

背景:
いくつかのプロジェクトで使用される大きなツール ライブラリがあります。 ライブラリは私のプロジェクト (Web サイト、デスクトップ アプリケーション) にデプロイされます。 顧客が独自の内部プロジェクトで DLL を直接使用できないように、基本的な保護が必要です。

私が試したこと:

public static class LicensedClass
{
    public static string licenceKey = "";

    public static bool IsLicensed
    {
        get { return (licenceKey == "ABC123"); }
    }

    public static void CheckLicence()
    {
        if (!IsLicensed) 
                throw new System.ArgumentException("Wrong licence key.", licenceKey);
    }

    public static double AddMethod(double number1, double number2)
    {
        CheckLicence();

        return number1 + number2;
    }
}

解決策 1

いいえ。

C# は自動的に何も呼び出しません。何をいつ呼び出すかを明示的に指示する必要があります。

定期的にチェックし、代わりに処理できる「失敗」インジケーターをメインスレッドに応答するセカンダリスレッドタスクをいつでも追加できますか?

解決策 2

見てみるといいでしょう AOP (アスペクト指向プログラミング): GitHub – Virtuoze/NConcern: NConcern .NET AOP フレームワーク[^]

しかし、必要なものに対しては複雑すぎると思われるかもしれません…

もう 1 つのオプションは、よく知られているメソッドを使用することです。 ポストシャープ 拡大: PostSharp – Visual Studio マーケットプレイス[^]

解決策 3

静的クラス コンストラクターのライセンス チェックで解決しました。
これは、社内の他の人が自分の DLL を再利用しないようにするための非常に基本的な解決策です。
一部のメソッドが機能し、「ライセンス」がないと他のメソッドが機能しない場合、人々は非常に煩わしいと感じるかもしれないので、それで十分です。
その方法が世界を変えるほど重要な場合、元のコードは DLL から簡単に復元できるため、.net では、サードパーティのツールを使用しない限り、コードは保護されません…

使用法:

2c#」
DllClassLibrary.License.SetLicense("ABC123");
var result = DllClassLibrary.Foo.DoSomething();

簡単なライセンスチェックを備えた DLL-Class-Library:

C#
namespace DllClassLibrary
{
    public static class License
    {
        private static bool isLicensed = false;
        public static bool SetLicense(string licenseKey)
        {
            isLicensed = (licenseKey == "ABC123");
            return isLicensed;
        }
        public static void CheckLicense()
        {
            if (!isLicensed)
                throw new Exception("License not set.");
        }
    }
    public static class Foo
    {
        static Foo()
        {
            License.CheckLicense();
        }
        public static string DoSomething()
        {
            return $"[{DateTime.Now.ToString()}] DoFoo()";
        }
    }
}

解決策 4

アクティベーションキー

アクティベーション キーの簡単な構造は次のとおりです。

<code>class ActivationKey
{
    public byte[] Data { get; set; } // Encrypted part.
    public byte[] Hash { get; set; } // Hashed part.
    public byte[] Tail { get; set; } // Initialization vector.
}
</code>
<p>This tool will use cryptographic transformations to generate the key.</p>
<h2>Generating</h2>
<p>The algorithm for obtaining a unique activation key for a data set consists of several steps:</p>
<ul>
<li>data collection,</li>
<li>getting the hash and data encryption,</li>
<li>converting activation key to string.</li>
</ul>
<h3>Data collection</h3>
<p>At this step, you need to get an array of data such as serial number, device ID, expiration date, etc. This purpose can be achieved using the following method:</p>
<pre class="lang-cs prettyprint-override"><code>unsafe byte[] Serialize(params object[] objects)
{
  using (MemoryStream memory = new MemoryStream())
  using (BinaryWriter writer = new BinaryWriter(memory))
  {
    foreach (object obj in objects)
    {
      if (obj == null) continue;
      switch (obj)
      {
        case string str:
          if (str.Length > 0)
            writer.Write(str.ToCharArray());
          continue;
        case DateTime date:
          writer.Write(date.Ticks);
          continue;
        case bool @bool:
          writer.Write(@bool);
          continue;
        case short @short:
          writer.Write(@short);
          continue;
        case ushort @ushort:
          writer.Write(@ushort);
          continue;
        case int @int:
          writer.Write(@int);
          continue;
        case uint @uint:
          writer.Write(@uint);
          continue;
        case long @long:
          writer.Write(@long);
          continue;
        case ulong @ulong:
          writer.Write(@ulong);
          continue;
        case float @float:
          writer.Write(@float);
          continue;
        case double @double:
          writer.Write(@double);
          continue;
        case decimal @decimal:
          writer.Write(@decimal);
          continue;
        case byte[] buffer:
          if (buffer.Length > 0)
            writer.Write(buffer);
          continue;
        case Array array:
          if (array.Length > 0)
            foreach (var a in array) writer.Write(Serialize(a));
          continue;
        case IConvertible conv:
          writer.Write(conv.ToString(CultureInfo.InvariantCulture));
          continue;
        case IFormattable frm:
          writer.Write(frm.ToString(null, CultureInfo.InvariantCulture));
          continue;
        case Stream stream:
          stream.CopyTo(stream);
          continue;
        default:
          try
          {
            int rawsize = Marshal.SizeOf(obj);
            byte[] rawdata = new byte[rawsize];
            GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
            Marshal.StructureToPtr(obj, handle.AddrOfPinnedObject(), false);
            writer.Write(rawdata);
            handle.Free();
          }
          catch(Exception e)
          {
            // Place debugging tools here.
          }
          continue;
      }
    }
    writer.Flush();
    byte[] bytes = memory.ToArray();
    return bytes;
  }
}
</code>

ハッシュとデータ暗号化の取得

このステップには次のサブステップが含まれています。

  • パスワードを使用して暗号化エンジンを作成し、初期化ベクトルを しっぽ 財産。
  • 次のステップでは、有効期限とオプションが暗号化され、暗号化されたデータが データ 財産。
  • 最後に、ハッシュ エンジンは有効期限、パスワード、オプション、環境に基づいてハッシュを計算し、それを ハッシュ 財産。

ActivationKey Create<TAlg, THash>(DateTime expirationDate, 
                                  object password, 
                                  object options = null, 
                                  params object[] environment)
    where TAlg : SymmetricAlgorithm
    where THash : HashAlgorithm
{
    ActivationKey activationKey = new ActivationKey();
    using (SymmetricAlgorithm cryptoAlg = Activator.CreateInstance<TAlg>())
    {
        if (password == null)
        {
            password = new byte[0];
        }
        activationKey.Tail = cryptoAlg.IV;
        using (DeriveBytes deriveBytes = 
        new PasswordDeriveBytes(Serialize(password), activationKey.Tail))
        {
            cryptoAlg.Key = deriveBytes.GetBytes(cryptoAlg.KeySize / 8);
        }
        expirationDate = expirationDate.Date;
        long expirationDateStamp = expirationDate.ToBinary();
        using (ICryptoTransform transform = cryptoAlg.CreateEncryptor())
        {
            byte[] data2 = Serialize(expirationDateStamp, options);
            activationKey.Data = transform.TransformFinalBlock(data2, 0, data2.Length);
        }
        using (HashAlgorithm hashAlg = Activator.CreateInstance<THash>())
        {
            byte[] data = Serialize(expirationDateStamp, 
                                    cryptoAlg.Key, 
                                    options, 
                                    environment, 
                                    activationKey.Tail);
            activationKey.Hash = hashAlg.ComputeHash(data);
        }
    }
    return activationKey;
}

文字列に変換する

ToString メソッドを使用して、エンド ユーザーに転送できるキー テキストを含む文字列を取得します。

N ベースのエンコード (N は数体系の基数) は、バイナリ データを人間が読めるテキストに変換するためによく使用されていました。 アクティベーション キーで最も一般的に使用されるのは、base32 です。 このエンコーディングの利点は、大文字と小文字が区別されない数字と文字で構成される大きなアルファベットであることです。 欠点は、このエンコーディングが .NET 標準ライブラリに実装されていないため、自分で実装する必要があることです。 このサイトには、base32 の実装例が多数あります。 mscorlib に組み込まれている 16 進エンコーディングと Base64 を使用することもできます。 私の例では 基数32 使用されている。

string ToString(ActivationKey activationKey)
{
    if (activationKey.Data == null 
       || activationKey.Hash == null 
       || activationKey.Tail == null)
    {
        return string.Empty;
    }
    using (Base32 base32 = new Base32())
    {
        return base32.Encode(Data) + "-" + base32.Encode(Hash) + "-" +
        base32.Encode(Tail);
    }
}

チェック中

キーの検証は、GetOptions メソッドと Verify メソッドを使用して実行されます。

  • GetOptions キーをチェックし、埋め込まれたデータをバイト配列として復元するか、キーが有効でない場合は null を復元します。
  • 確認する キーをチェックするだけです。

byte[] GetOptions<TAlg, THash>(object password = null, params object[] environment)
    where TAlg : SymmetricAlgorithm
    where THash : HashAlgorithm
{
    if (Data == null || Hash == null || Tail == null)
    {
        return null;
    }
    try
    {
        using (SymmetricAlgorithm cryptoAlg = Activator.CreateInstance<TAlg>())
        {
            cryptoAlg.IV = Tail;
            using (DeriveBytes deriveBytes = 
            new PasswordDeriveBytes(Serialize(password), Tail))
            {
                cryptoAlg.Key = deriveBytes.GetBytes(cryptoAlg.KeySize / 8);
            }
            using (ICryptoTransform transform = cryptoAlg.CreateDecryptor())
            {
                byte[] data = transform.TransformFinalBlock(Data, 0, Data.Length);
                int optionsLength = data.Length - 8;
                if (optionsLength < 0)
                {
                    return null;
                }
                byte[] options;
                if (optionsLength > 0)
                {
                    options = new byte[optionsLength];
                    Buffer.BlockCopy(data, 8, options, 0, optionsLength);
                }
                else
                {
                    options = new byte[0];
                }
                long expirationDateStamp = BitConverter.ToInt64(data, 0);
                DateTime expirationDate = DateTime.FromBinary(expirationDateStamp);
                if (expirationDate < DateTime.Today)
                {
                    return null;
                }
                using (HashAlgorithm hashAlg = 
                Activator.CreateInstance<THash>())
                {
                    byte[] hash = 
                    hashAlg.ComputeHash(
                         Serialize(expirationDateStamp, 
                                   cryptoAlg.Key, 
                                   options, 
                                   environment, 
                                   Tail));
                    return ByteArrayEquals(Hash, hash) ? options : null;
                }
            }
        }
    }
    catch
    {
        return null;
    }
}

bool Verify<TAlg, THash>(object password = null, params object[] environment)
    where TAlg : SymmetricAlgorithm
    where THash : HashAlgorithm
{
    try
    {
        byte[] key = Serialize(password);
        return Verify<TAlg, THash>(key, environment);
    }
    catch
    {
        return false;
    }
}

ここ これは、テキスト、文字列、数値、バイトなどの任意の量のデータを独自に組み合わせて使用​​してアクティベーション キーを生成する完全な例です。

使用例:

string serialNumber = "0123456789"; // The serial number.
const string appName = "myAppName"; // The application name.

// Generating the key. All the parameters passed to the costructor can be omitted.
ActivationKey activationKey = new ActivationKey(
//expirationDate:
DateTime.Now.AddMonths(1),  // Expiration date 1 month later.
                            // Pass DateTime.Max for unlimited use.
//password:
null,                       // Password protection;
                            // this parameter can be null.
//options:
null                       // Pass here numbers, flags, text or other
                           // that you want to restore 
                           // or null if no necessary.
//environment:
appName, serialNumber      // Application name and serial number.
);
// Thus, a simple check of the key for validity is carried out.
bool checkKey = activationKey.Verify((byte[])null, appName, serialNumber);
if (!checkKey)
{
  MessageBox.Show("Your copy is not activated! Please get a valid activation key.");
  Application.Exit();
}

GitHub – ng256/Activation-Key: C# アプリケーションを保護するために使用されるアクティベーション キーを表します。[^]

[ad_2]

コメント

タイトルとURLをコピーしました