最近我朋友接了一个活,遇到一个很不靠谱的甲方,故事是这样的,谈的时候对方就满嘴跑火车,拉近关系。最后以低于行业加个接下了,用了一周完成测试上线。上线后我朋友要求结算一半,起初对方是各种不愿意,那我朋友也不装了直接摊牌,不结算锁死软件。对方无奈只能先结算一半。另一半答应一个月后再结算。为了防止变卦,我朋友便写了今天要分享的加密功能。
上代码>>>>>>>>>>>>>>>>>
using Microsoft.Win32;
using System;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
namespace ConsoleApp1
{
internal class Program
{
private const string HardcodedDate = "20230715"; //硬编码时间
private const string RegistryKeyPath = @"Software\DiJiaAoTeMan\HelloWorld";//注册表路径
static void Main()
{
//写入初始加密数据
//WriteRegistryValue("License", EncryptString("20230715", "THr5MOBP4Yxb9FsSSkS13g=="));
VerifyLicense(); //检查许可证
Console.WriteLine("进入系统");
Console.ReadKey();
}
/// <summary>
/// 写注册表,提供一对键值
/// </summary>
/// <param name="valueName"></param>
/// <param name="value"></param>
public static void WriteRegistryValue(string valueName, object value)
{
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(RegistryKeyPath))
{
key.SetValue(valueName, value);
}
}
/// <summary>
/// 读注册表,提供一个key值返回对应value
/// </summary>
/// <param name="valueName"></param>
/// <returns></returns>
public static string ReadRegistryValue(string valueName)
{
try
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath))
{
if (key != null)
{
return key.GetValue(valueName).ToString();
}
}
}
catch { }
return string.Empty;
}
public static void VerifyLicense()
{
string savedDate = ReadRegistryValue("License");
// 检查许可证文件是否存在
if (savedDate == string.Empty)
{
MessageBox.Show("许可证不存在!请联系厂家!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
// 解密许可证密文
try
{
savedDate = DecryptString(savedDate, "THr5MOBP4Yxb9FsSSkS13g==");
}
catch
{
MessageBox.Show("许可证解密失败:请联系厂家!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
DateTime cd = DateTime.ParseExact(savedDate, "yyyyMMdd", CultureInfo.InvariantCulture);
DateTime hd = DateTime.ParseExact(HardcodedDate, "yyyyMMdd", CultureInfo.InvariantCulture);
// 检查系统日期是否小于保存的日期,如果小于说明系统时间被篡改
if (DateTime.Now < cd)
{
MessageBox.Show("系统时间被篡改,请检查系统时间设置!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
if (cd < hd)
{
MessageBox.Show("许可证时间无效,请联系厂家!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
// 检查当前日期是否超过硬编码的日期30天,如果超过说明许可证过期
if (cd > hd.AddDays(30))
{
MessageBox.Show("许可证已过期:请联系厂家!", "许可证过期", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Environment.Exit(0);
}
else
{
// 加密当前日期
string currentDate = EncryptString(DateTime.Now.ToString("yyyyMMdd"), "THr5MOBP4Yxb9FsSSkS13g==");
// 更新许可证密文
WriteRegistryValue("License", currentDate);
}
}
/// <summary>
/// 加密字符串,提供一个字符串和密钥,返回一个加密后的字符串
/// </summary>
/// <param name="plainText"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string EncryptString(string plainText, string key)
{
byte[] encryptedBytes;
byte[] combinedBytes;
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.GenerateIV();
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encryptedBytes = msEncrypt.ToArray();
}
}
// Combine IV and encrypted data
combinedBytes = new byte[aes.IV.Length + encryptedBytes.Length];
Array.Copy(aes.IV, 0, combinedBytes, 0, aes.IV.Length);
Array.Copy(encryptedBytes, 0, combinedBytes, aes.IV.Length, encryptedBytes.Length);
}
return Convert.ToBase64String(combinedBytes);
}
/// <summary>
/// 解密字符串,提供一个加密字符串和密钥,返回一个明文字符串
/// </summary>
/// <param name="encryptedText"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string DecryptString(string encryptedText, string key)
{
byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
byte[] iv = new byte[16];
byte[] encryptedData = new byte[encryptedBytes.Length - iv.Length];
Array.Copy(encryptedBytes, 0, iv, 0, iv.Length);
Array.Copy(encryptedBytes, iv.Length, encryptedData, 0, encryptedData.Length);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream msDecrypt = new MemoryStream(encryptedData))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
}
}
}
实现起来也十分简单,主要是分析一下加密验证的思想。 使用硬编码时间,和系统时间,还有注册表加密时间,三个时间校验。
1.验证注册表密文是否存在,那可能要问了,第一次用哪来得密文,所以新电脑或者第一次使用需要手动调用方法将初始时间密文写入到注册表中
WriteRegistryValue("License", EncryptString("20230715", "THr5MOBP4Yxb9FsSSkS13g=="));
2.解密注册表时间
// 解密许可证密文
try
{
savedDate = DecryptString(savedDate, "THr5MOBP4Yxb9FsSSkS13g==");
}
catch
{
MessageBox.Show("许可证解密失败:请联系厂家!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
这里要try一下,防止君子手动修改密文,当然再没有密钥得情况下,修改了密文会导致解密失败,程序直接退出
3.验证时间是否被篡改,如果注册表保存的时间大于当前时间,那么就说明,正人君子把系统时间往回调了。直接退出好吧。
// 检查系统日期是否小于保存的日期,如果小于说明系统时间被篡改
if (DateTime.Now < cd)
{
MessageBox.Show("系统时间被篡改,请检查系统时间设置!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
4.验证许可证时间
if (cd < hd)
{
MessageBox.Show("许可证时间无效,请联系厂家!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(0);
}
// 检查当前日期是否超过硬编码的日期30天,如果超过说明许可证过期
if (cd > hd.AddDays(30))
{
MessageBox.Show("许可证已过期:请联系厂家!", "许可证过期", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Environment.Exit(0);
}
else
{
// 加密当前日期
string currentDate = EncryptString(DateTime.Now.ToString("yyyyMMdd"), "THr5MOBP4Yxb9FsSSkS13g==");
// 更新许可证密文
WriteRegistryValue("License", currentDate);
}
首先判断注册表时间不能小于硬编码时间,然后再判断是否超过硬编码30天,根据实际情况修改天数,最后再对代码进行加密处理,防止反编译出源码。
转载请注明出处
最后,Respect