C# software license application example

When we use some software products that require copyright purchase, or the commercial software we make needs to be sold, in order to collect fees, we generally need a software license, and then enter this license into the software to use the software. The simple one is a string of serial codes or a license file, the more complex one is a customized plug-in package. So some friends began to wonder how this license is implemented, especially how it authorizes software offline while avoiding being cracked.

License application scenarios

This article mainly introduces authorization in the form of license.

1. How to control use only on designated devices

If you do not control the specified device, then a license is issued. As long as you copy multiple copies of the software and install it, it can be used everywhere, which is not conducive to copyright maintenance. Each device has a unique identifier: mac address, IP address, motherboard serial number, etc., in If a unique identifier is specified in the license, it can only be used by the specified device.

2. How to control the software usage period

In order to achieve sustainable revenue from copyright, if you want to set a time limit for software use, renew it upon expiration, etc., you need to configure the start and end dates in the license.

License implementation plan

1. Process design

  • Format: The license is issued in the form of a file and placed in the designated location on the client computer.
  • Content: The above control content is placed in the file in the form of dom node
  • Process: Encrypt the control items and write them to the license file node, deploy them to the client machine, and then read the license file content and match the actual parameters of the client machine for verification when the client machine is used.

2. File anti-cracking

  • Prevent tampering: file content is encrypted using AES, but the same key is used for AES encryption and decryption; an asymmetric public and private key (RSA used in this article) is used to encrypt and decrypt the content, but there is a limit on the content length; a comprehensive solution uses AES The key (internally defined) is encrypted with RSA. The public key is placed in the encryption tool and is held internally. The private key is placed in the decryption tool and introduced into the software product for decryption use.
  • Prevent modifying the system time from bypassing the license usage time: the license carries a release timestamp, and regularly changes the running time to a file. If the system time is less than this timestamp, it cannot be used even if it is greater than the starting time of the license limit.
  • Increase the difficulty of cracking: Those who know technology can decompile the code and modify the code file to directly bypass the verification, so code obfuscation is required. Those who have tested xjar have better obfuscation effects.

License verification flow chart

Regarding the license verification software legality flow chart, it is as follows:

Core source code

This example mainly explains the actual verification process of License, which is divided into three parts:

  1. Test client [LicenceTest] is mainly used to simulate the process of client verification of License.
  2. The generation tool [LicenceTool] is mainly used to generate the corresponding License based on the computer files generated by the customer.
  3. LicenseCommon, License public general class, mainly implements computer information acquisition, asymmetric encryption, file saving and other functions.

1. LicenceCommon

1.1 Obtaining computer information

The client computer hardware related configuration information is mainly obtained through ManagementClass, as shown below:

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
 
namespace DemoLicence.Common
{
    public class ComputerHelper
    {
        public static Dictionary<string,string> GetComputerInfo()
        {
            var info = new Dictionary<string,string>();
            string cpu = GetCPUInfo();
            string baseBoard = GetBaseBoardInfo();
            string bios = GetBIOSInfo();
            string mac = GetMACInfo();
            info.Add("cpu", cpu);
            info.Add("baseBoard", baseBoard);
            info.Add("bios", bios);
            info.Add("mac", mac);
            return info;
        }
        private static string GetCPUInfo()
        {
            string info = string.Empty;
            info = GetHardWareInfo("Win32_Processor", "ProcessorId");
            return info;
        }
        private static string GetBIOSInfo()
        {
            string info = string.Empty;
            info = GetHardWareInfo("Win32_BIOS", "SerialNumber");
            return info;
        }
        private static string GetBaseBoardInfo()
        {
            string info = string.Empty;
            info = GetHardWareInfo("Win32_BaseBoard", "SerialNumber");
            return info;
        }
        private static string GetMACInfo()
        {
            string info = string.Empty;
            info = GetMacAddress();//GetHardWareInfo("Win32_NetworkAdapterConfiguration", "MACAddress");
            return info;
        }
 
        private static string GetMacAddress()
        {
            var mac = "";
            var mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
            var moc = mc.GetInstances();
            foreach (var o in moc)
            {
                var mo = (ManagementObject)o;
                if (!(bool)mo["IPEnabled"]) continue;
                mac = mo["MacAddress"].ToString();
                break;
            }
            return mac;
        }
 
        private static string GetHardWareInfo(string typePath, string key)
        {
            try
            {
                ManagementClass managementClass = new ManagementClass(typePath);
                ManagementObjectCollection mn = managementClass.GetInstances();
                PropertyDataCollection properties = managementClass.Properties;
                foreach (PropertyData property in properties)
                {
                    if (property.Name == key)
                    {
                        foreach (ManagementObject m in mn)
                        {
                            return m.Properties[property.Name].Value.ToString();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //这里写异常的处理
            }
            return string.Empty;
        }
    }
}

 

1.2 RSA asymmetric encryption

It mainly performs asymmetric encryption on the computer information and validity period provided by the client, as shown below:

public class RSAHelper
{
 
	private static string keyContainerName = "star";
 
	private static string m_PriKey = string.Empty;
 
	private static string m_PubKey = string.Empty;
 
 
	public static string PriKey
	{
		get
		{
			return m_PriKey;
		}
 
		set
		{
			m_PriKey = value;
		}
	}
 
	public static string PubKey
	{
		get
		{
			return m_PubKey;
		}
 
		set
		{
			m_PubKey = value;
		}
	}
 
	public static string Encrypto(string source)
	{
		if (string.IsNullOrEmpty(m_PubKey) && string.IsNullOrEmpty(m_PriKey))
		{
			generateKey();
		}
		return getEncryptoInfoByRSA(source);
	}
 
	public static string Decrypto(string dest)
	{
		if (string.IsNullOrEmpty(m_PubKey) && string.IsNullOrEmpty(m_PriKey))
		{
			generateKey();
		}
		return getDecryptoInfoByRSA(dest);
	}
 
	public static void generateKey()
	{
		CspParameters m_CspParameters;
		m_CspParameters = new CspParameters();
		m_CspParameters.KeyContainerName = keyContainerName;
		RSACryptoServiceProvider asym = new RSACryptoServiceProvider(m_CspParameters);
		m_PriKey = asym.ToXmlString(true);
		m_PubKey = asym.ToXmlString(false);
		asym.PersistKeyInCsp = false;
		asym.Clear();
	}
 
	private static string getEncryptoInfoByRSA(string source)
	{
		byte[] plainByte = Encoding.ASCII.GetBytes(source);
		//初始化参数
		RSACryptoServiceProvider asym = new RSACryptoServiceProvider();
		asym.FromXmlString(m_PubKey);
		int keySize = asym.KeySize / 8;//非对称加密,每次的长度不能太长,否则会报异常
		int bufferSize = keySize - 11;
		if (plainByte.Length > bufferSize)
		{
			throw new Exception("非对称加密最多支持【" + bufferSize + "】字节,实际长度【" + plainByte.Length + "】字节。");
		}
		byte[] cryptoByte = asym.Encrypt(plainByte, false);
		return Convert.ToBase64String(cryptoByte);
	}
 
	private static string getDecryptoInfoByRSA(string dest)
	{
		byte[] btDest = Convert.FromBase64String(dest);
		//初始化参数
		RSACryptoServiceProvider asym = new RSACryptoServiceProvider();
		asym.FromXmlString(m_PriKey);
		int keySize = asym.KeySize / 8;//非对称加密,每次的长度不能太长,否则会报异常
									   //int bufferSize = keySize - 11;
		if (btDest.Length > keySize)
		{
			throw new Exception("非对称解密最多支持【" + keySize + "】字节,实际长度【" + btDest.Length + "】字节。");
		}
		byte[] cryptoByte = asym.Decrypt(btDest, false);
		return Encoding.ASCII.GetString(cryptoByte);
	}
}

 

1.3 Generate files

Mainly, the encrypted information, decryption key and other contents are saved to the file, as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace DemoLicence.Common
{
    public class RegistFileHelper
    {
        public static string ComputerInfofile = "ComputerInfo.key";
        public static string RegistInfofile = "Licence.key";
        public static void WriteRegistFile(string info,string keyFile)
        {
            string tmp = string.IsNullOrEmpty(keyFile)?RegistInfofile:keyFile;
            WriteFile(info, tmp);
        }
        public static void WriteComputerInfoFile(string info)
        {
            WriteFile(info, ComputerInfofile);
        }
        public static string ReadRegistFile(string keyFile)
        {
            string tmp = string.IsNullOrEmpty(keyFile) ? RegistInfofile : keyFile;
            return ReadFile(tmp);
        }
        public static string ReadComputerInfoFile(string file)
        {
            string tmp = string.IsNullOrEmpty(file) ? ComputerInfofile : file;
            return ReadFile(tmp);
        }
 
        private static void WriteFile(string info, string fileName)
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(fileName, false))
                {
                    sw.Write(info);
                    sw.Close();
                }
            }
            catch (Exception ex)
            {
            }
        }
        private static string ReadFile(string fileName)
        {
            string info = string.Empty;
            try
            {
                using (StreamReader sr = new StreamReader(fileName))
                {
                    info = sr.ReadToEnd();
                    sr.Close();
                }
            }
            catch (Exception ex)
            {
            }
            return info;
        }
    }
}

 

The functions of the above three parts are independent of each other and call each other through LicenseHelper, as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
 
namespace DemoLicence.Common
{
    public class LicenceHelper
    {
        /// <summary>
        /// 获取电脑信息,并生成文件
        /// </summary>
        public static string GetComputerInfoAndGenerateFile()
        {
            string computerKeyFile = string.Empty;
            try
            {
                var info = GetComputerInfo();
                if (info != null && info.Count > 0)
                {
                    //获取到电脑信息
                    var strInfo = new StringBuilder();
                    foreach (var computer in info)
                    {
                        strInfo.AppendLine($"{computer.Key}={computer.Value}");
                    }
                    RegistFileHelper.WriteComputerInfoFile(strInfo.ToString());
                    computerKeyFile = RegistFileHelper.ComputerInfofile;
                }
            }catch(Exception ex)
            {
                throw ex;
            }
            return computerKeyFile;
        }
 
        public static Dictionary<string,string> GetComputerInfo()
        {
            var info = ComputerHelper.GetComputerInfo();
            return info;
        }
 
        public static bool CheckLicenceKeyIsExists()
        {
            var keyFile = RegistFileHelper.RegistInfofile;
            if (File.Exists(keyFile))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        public static string GetComputerInfo(string computerInfoFile)
        {
            return RegistFileHelper.ReadComputerInfoFile(computerInfoFile);
        }
 
        public static void GenerateLicenceKey(string info,string keyfile)
        {
            string encrypto = RSAHelper.Encrypto(info);
            string priKey = RSAHelper.PriKey;//公钥加密,私钥解密
            byte[] priKeyBytes = Encoding.ASCII.GetBytes(priKey);
            string priKeyBase64=Convert.ToBase64String(priKeyBytes);
            StringBuilder keyInfo= new StringBuilder();
            keyInfo.AppendLine($"prikey={priKeyBase64}");
            keyInfo.AppendLine($"encrypto={encrypto}");
            RegistFileHelper.WriteRegistFile(keyInfo.ToString(), keyfile);
        }
 
        public static string ReadLicenceKey(string keyfile)
        {
            var keyInfo = RegistFileHelper.ReadRegistFile(keyfile);
            if (keyInfo == null)
            {
                return string.Empty;
            }
            string[] keyInfoArr = keyInfo.Split("\r\n");
            var priKeyBase64 = keyInfoArr[0].Substring(keyInfoArr[0].IndexOf("=")+1);
            var encrypto = keyInfoArr[1].Substring(keyInfoArr[1].IndexOf("=")+1);
            var priKeyByte= Convert.FromBase64String(priKeyBase64);
            var priKey = Encoding.ASCII.GetString(priKeyByte);
            RSAHelper.PriKey= priKey;
            var info = RSAHelper.Decrypto(encrypto);
            return info;
        }
 
        public static string GetDefaultRegisterFileName()
        {
            return RegistFileHelper.RegistInfofile;
        }
 
        public static string GetDefaultComputerFileName()
        {
            return RegistFileHelper.ComputerInfofile;
        }
        
        public static string GetPublicKey()
        {
            if (string.IsNullOrEmpty(RSAHelper.PubKey))
            {
                RSAHelper.generateKey();
            }
            return RSAHelper.PubKey;
        }
 
        public static string GetPrivateKey()
        {
            if (string.IsNullOrEmpty(RSAHelper.PriKey))
            {
                RSAHelper.generateKey();
            }
            return RSAHelper.PriKey;
        }
    }
}

 

2. Client LicenseTest

The client verifies the validity of the License. When the License is valid, the software can be used normally. When the License is invalid, the software cannot be used normally. As follows:

using DemoLicence.Common;
 
namespace LicenceTest
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
 
        private void MainForm_Load(object sender, EventArgs e)
        {
            try
            {
 
                string info = string.Empty;
                string msg = string.Empty;
                //初始化加载
                if (LicenceHelper.CheckLicenceKeyIsExists())
                {
                    string keyFile = LicenceHelper.GetDefaultRegisterFileName();
                    info = LicenceHelper.ReadLicenceKey(keyFile);
                }
                else
                {
                    var dialogResult = MessageBox.Show("没有找到默认首选文件,是否手动选择授权文件?", "询问", MessageBoxButtons.YesNo);
                    if (dialogResult == DialogResult.Yes)
                    {
                        OpenFileDialog openFileDialog = new OpenFileDialog();
                        openFileDialog.Title = "请选择授权文件";
                        openFileDialog.FileName = LicenceHelper.GetDefaultRegisterFileName();
                        if (openFileDialog.ShowDialog() == DialogResult.OK)
                        {
                            var keyFile = openFileDialog.FileName;
                            info = LicenceHelper.ReadLicenceKey(keyFile);
                            //验证成功后,将手动选择的文件复制到程序根目录,且修改为默认名称
                            File.Copy(keyFile, LicenceHelper.GetDefaultRegisterFileName());
 
                        }
                        else
                        {
                            string computerFile = LicenceHelper.GetComputerInfoAndGenerateFile();
                            if (!string.IsNullOrEmpty(computerFile))
                            {
                                msg = $"您还没有被授权,请将程序根目录下的{computerFile}文件,发送到管理员,获取Licence.";
                            }
                        }
                    }
                    else
                    {
                        string computerFile = LicenceHelper.GetComputerInfoAndGenerateFile();
                        if (!string.IsNullOrEmpty(computerFile))
                        {
                            msg = $"您还没有被授权,请将程序根目录下的{computerFile}文件,发送到管理员,获取Licence.";
                        }
                    }
                }
                if (!string.IsNullOrEmpty(info) && string.IsNullOrEmpty(msg))
                {
                    string[] infos = info.Split("\r\n");
                    if (infos.Length > 0)
                    {
                        var dicInfo = new Dictionary<string, string>();
                        foreach (var info2 in infos)
                        {
                            if (string.IsNullOrEmpty(info2))
                            {
                                continue;
                            }
                            var info2Arr = info2.Split("=");
                            dicInfo.Add(info2Arr[0], info2Arr[1]);
                        }
                        if (dicInfo.Count > 0)
                        {
                            string localMacAddress = string.Empty;
                            var computerInfo = LicenceHelper.GetComputerInfo();
                            if (computerInfo != null)
                            {
                                localMacAddress = computerInfo["mac"];
                            }
                            //比较本地信息和Licence中的信息是否一致
                            if (localMacAddress == dicInfo["mac"])
                            {
                                var endTime = DateTime.Parse(dicInfo["endTime"]);
                                if (DateTime.Now < endTime)
                                {
                                    //在有效期内,可以使用
                                }
                                else
                                {
                                    msg = $"软件授权使用时间范围:[{endTime}之前],已过期";
                                }
                            }
                            else
                            {
                                msg = "软件Licence不匹配";
                            }
                        }
                        else
                        {
                            msg = $"软件Licence非法.";
                        }
                    }
                    else
                    {
                        msg = $"软件Licence非法.";
                    }
                }
                if (!string.IsNullOrEmpty(msg))
                {
                    MessageBox.Show(msg);
                    foreach (var control in this.Controls)
                    {
                        (control as Control).Enabled = false;
                    }
                    return;
                }
            }
            catch (Exception ex)
            {
                string error = $"程序异常,请联系管理人员:{ex.Message}\r\n{ex.StackTrace}";
                MessageBox.Show(error);
                foreach (var control in this.Controls)
                {
                    (control as Control).Enabled = false;
                }
            }
        }
    }
}

 

3. License generation tool

LicenseTool mainly generates the corresponding License based on the computer information provided by the client, and then sends it to the client, so as to achieve the purpose of authorizing the client computer to use the software. As follows:

using DemoLicence.Common;
using System.Text;
 
namespace LicenceTool
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }
 
 
        private void MainForm_Load(object sender, EventArgs e)
        {
            this.txtPublicKey.Text=LicenceHelper.GetPublicKey();
            this.txtPrivateKey.Text=LicenceHelper.GetPrivateKey();
        }
 
 
 
        private void btnBrowser_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "电脑信息文件|*.key";
            ofd.Multiselect = false;
            ofd.Title = "请选择电脑信息文件";
            ofd.FileName=LicenceHelper.GetDefaultComputerFileName();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                this.txtSourceFile.Text = ofd.FileName;
            }
        }
 
        private void btnGenerate_Click(object sender, EventArgs e)
        {
 
            try
            {
                if (string.IsNullOrEmpty(this.txtSourceFile.Text))
                {
                    MessageBox.Show("请先选择电脑信息文件");
                    return;
                }
                if (File.Exists(this.txtSourceFile.Text))
                {
                    //读取电脑文件
                    var info = LicenceHelper.GetComputerInfo(this.txtSourceFile.Text);
                    int days = GetLicenceDays();
                    var keyInfos = new StringBuilder(info);
                    var beginTime = DateTime.Now;
                    var endTime = DateTime.Now.AddDays(days);
                    //keyInfos.AppendLine($"beginTime={beginTime.ToString("yyyy-MM-dd HH:mm:ss")}");
                    keyInfos.AppendLine($"endTime={endTime.ToString("yyyy-MM-dd HH:mm:ss")}");
                    //
                    info = keyInfos.ToString();
                    SaveFileDialog saveFileDialog = new SaveFileDialog();
                    saveFileDialog.Title = "保存生成的Licence文件";
                    saveFileDialog.FileName = LicenceHelper.GetDefaultRegisterFileName();
                    if (saveFileDialog.ShowDialog() == DialogResult.OK)
                    {
                        LicenceHelper.GenerateLicenceKey(info, saveFileDialog.FileName);
                        MessageBox.Show("生成成功");
                    }
                }
                else
                {
                    MessageBox.Show("电脑信息文件不存在!");
                    return;
                }
            }catch(Exception ex)
            {
                string error = $"生成出错:{ex.Message}\r\n{ex.StackTrace}";
                MessageBox.Show(error);
            }
        }
 
        /// <summary>
        /// 获取有效期天数
        /// </summary>
        /// <returns></returns>
        private int GetLicenceDays()
        {
            int days = 1;
            RadioButton[] rbtns = new RadioButton[] { this.rbtSeven, this.rbtnTen, this.rbtnFifteen, this.rbtnThirty, this.rbtnSixty, this.rbtnSixMonth, this.rbtnNinety, this.rbtnSixMonth, this.rbtnForver };
            foreach (RadioButton rb in rbtns)
            {
                if (rb.Checked)
                {
                    if (!int.TryParse(rb.Tag.ToString(), out days))
                    {
                        days = 0;
                    }
                    break;
                }
            }
            days = days == -1 ? 9999 : days;//永久要转换一下
            return days;
        }
    }
}

 

Test verification

Verification will be performed when starting the software. If there is no License, there will be a message prompt and the software cannot be used, as shown below:

Lincence generation tool

Based on the computer information files provided by the customer, the corresponding License is generated, as shown below:

Generate the License and place it in the default directory of the client, and you can use the software normally, as shown below:

Note: The secret key generated by asymmetric encryption is different each time, so the decryption key needs to be saved in the generated License file. Otherwise, the decryption cannot be performed if the secret keys are different.

An example of the generated computer information file ComputerInfo.key is as follows:

The contents of the generated License.key file are as follows:

Source code download

Source code can be downloaded in the following 3 ways:

1. Reply with public account keywords

Follow the personal official account and reply with the keyword [Licence] to obtain the source code, as shown below:

2. Download through gitee (code cloud)

The relevant source code in this example has been uploaded to gitee (code cloud), the link is as follows:

3. Download through CSDN

Paid downloads through resources on CSDN are not expensive, just the price of a breakfast.

https://download.csdn.net/download/fengershishe/88294433?spm=1001.2014.3001.5501

The above is the entire content of the software license application examples. I hope you can inspire others, learn together, and make progress together. To learn programming, start by paying attention to [Old Code Knows the Way]! ! !

Guess you like

Origin blog.csdn.net/fengershishe/article/details/132653311