C#: cara menerapkan kunci lisensi sederhana/panggilan checklicence()

pemrograman


Apakah ada cara untuk memanggil metode CheckLicence() secara otomatis di setiap metode kelas statis?

Saya memiliki beberapa kelas statis dengan ratusan metode.
Jadi pemeriksaan lisensi global akan lebih nyaman daripada memanggil metode CheckLicence() di setiap metode kelas.

Penjelasan contoh:
LicenseKey diatur oleh aplikasi saat startup.
Jadi lisensinya oke seumur hidup.

Saya ingin menghilangkan panggilan CheckLicence() di AddMethod().

Latar belakang:
Saya memiliki perpustakaan alat besar yang digunakan dalam beberapa proyek. Perpustakaan diterapkan di proyek saya (situs web, aplikasi desktop). Saya ingin memiliki perlindungan dasar agar DLL tidak bisa langsung digunakan oleh pelanggan di proyek internal mereka sendiri.

Apa yang saya coba:

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

Solusi 1

TIDAK.

C# tidak akan memanggil apa pun secara otomatis untuk Anda – Anda harus secara eksplisit memberi tahu C# apa yang ingin Anda panggil, dan kapan.

Anda selalu dapat menambahkan tugas thread sekunder yang memeriksanya secara berkala, dan merespons thread utama dengan indikator “gagal” yang dapat diproses?

Solusi 2

Anda bisa melihatnya AOP (Pemrograman Berorientasi Aspek): GitHub – Virtuoze/NConcern: NConcern .NET AOP Framework[^]

Namun Anda mungkin menganggapnya terlalu rumit untuk memenuhi keinginan Anda…

Pilihan lainnya adalah menggunakan yang terkenal Posting Tajam perpanjangan: PostSharp – Pasar Visual Studio[^]

Solusi 3

Diselesaikan dengan pemeriksaan lisensi di konstruktor kelas statis.
Ini adalah solusi yang sangat mendasar untuk menyelamatkan dll saya agar tidak digunakan kembali oleh orang lain di perusahaan.
Orang mungkin merasa sangat menjengkelkan jika beberapa metode berhasil dan metode lainnya tidak jika tidak “berlisensi”, jadi itu sudah cukup.
Jika metode ini sangat penting untuk mengubah dunia, kode asli dapat dengan mudah dipulihkan dari dll, jadi di .net tidak ada perlindungan kode tanpa alat pihak ketiga…

Penggunaan:

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

Perpustakaan Kelas DLL dengan pemeriksaan lisensi sederhana:

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()";
        }
    }
}

Solusi 4

Kunci aktivasi

Berikut adalah struktur sederhana dari kunci aktivasi:

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

Mendapatkan hash dan enkripsi data

Langkah ini berisi sublangkah berikut:

  • membuat mesin enkripsi menggunakan kata sandi dan menyimpan vektor inisialisasi di Ekor Properti.
  • langkah selanjutnya, tanggal kedaluwarsa dan opsi dienkripsi dan data terenkripsi disimpan ke dalam Data Properti.
  • terakhir, mesin hashing menghitung hash berdasarkan tanggal kedaluwarsa, kata sandi, opsi, dan lingkungan, lalu memasukkannya ke dalam hash Properti.

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

Mengonversi menjadi string

Gunakan metode ToString untuk mendapatkan string yang berisi teks kunci, siap untuk ditransfer ke pengguna akhir.

Pengkodean berbasis N (di mana N adalah basis sistem bilangan) sering digunakan untuk mengubah data biner menjadi teks yang dapat dibaca manusia. Kunci aktivasi yang paling umum digunakan adalah base32. Keuntungan dari pengkodean ini adalah alfabet besar yang terdiri dari angka dan huruf yang tidak peka huruf besar-kecil. Kelemahannya adalah pengkodean ini tidak diterapkan di perpustakaan standar .NET dan Anda harus menerapkannya sendiri. Ada banyak contoh implementasi base32 di situs ini. Anda juga dapat menggunakan pengkodean hex dan base64 yang ada di mscorlib. Dalam contoh saya basis32 digunakan.

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

Memeriksa

Verifikasi kunci dilakukan menggunakan metode GetOptions dan Verify.

  • Dapatkan Opsi memeriksa kunci dan mengembalikan data yang disematkan sebagai array byte atau nol jika kunci tidak valid.
  • Memeriksa cukup periksa kuncinya.

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

Contoh

Di Sini adalah contoh lengkap pembuatan kunci aktivasi menggunakan kombinasi Anda sendiri dari sejumlah data – teks, string, angka, byte, dll.

Contoh penggunaan:

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: Mewakili kunci aktivasi yang digunakan untuk melindungi aplikasi C# Anda.[^]

コメント

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