<think>我们将创建一个C#程序,该程序执行以下步骤:
1. 获取电脑的MAC地址(选择第一个非虚拟的MAC地址)。
2. 获取当前日期,并生成一个有效期限(例如30天)。
3. 将MAC地址和有效期限(截止日期)结合,使用某种加密算法(如AES)生成一个加密的密钥。
4. 将加密后的密钥和截止日期等信息存储在一个JSON文件中,放在应用程序所在目录。
5. 程序启动时,检查该JSON文件是否存在,如果存在则解密并验证当前日期是否在有效期内,同时验证当前电脑的MAC地址是否与加密信息中的一致。
6. 如果验证通过,则程序正常运行;否则,程序退出。
注意:由于MAC地址可能有多块网卡,我们选择物理网卡且非回环的地址。
我们将使用以下技术:
- 使用Newtonsoft.Json(Json.NET)来处理JSON文件(需要安装NuGet包)。
- 使用AES加密算法(对称加密)来加密数据。
步骤:
1. 创建控制台应用程序(也可以改为Windows窗体等,这里以控制台为例)。
2. 安装Newtonsoft.Json包:在NuGet包管理器中安装。
3. 编写代码。
由于代码较长,我们分部分实现:
首先,定义一个类来存储许可证信息(加密后的文件内容):
```csharp
public class License
{
public string EncryptedData { get; set; } // 加密后的数据(包含MAC地址和截止日期)
public DateTime ExpiryDate { get; set; } // 截止日期(未加密,用于快速检查)
}
```
但为了安全,我们也可以将MAC地址和截止日期一起加密,然后存储整个加密字符串。然后在验证时解密整个字符串,再解析出MAC地址和截止日期。
因此,我们定义另一个内部使用的数据类:
```csharp
[Serializable]
public class LicenseData
{
public string MacAddress { get; set; }
public DateTime ExpiryDate { get; set; }
}
```
然后,我们将`LicenseData`对象序列化为JSON字符串,再加密,然后将加密后的字符串和过期日期(为了快速检查,也可以不存,但为了双重验证,我们存储)存入`License`对象,最后将`License`对象序列化为JSON写入文件。
但是注意:存储过期日期在外部是未加密的,因此用户可能修改。所以我们必须在解密后验证内部存储的过期日期和外部的是否一致,并且以内部存储的为准。
或者,我们也可以只存储加密后的字符串,然后在解密后得到`LicenseData`,这样就不需要存储两个过期日期了。这样更安全,因为整个信息都被加密了。
因此,我们调整方案:
- 将`LicenseData`(包含MAC地址和截止日期)序列化成JSON字符串。
- 使用AES加密该字符串,得到Base64字符串。
- 将Base64字符串写入一个文件(或者作为整个JSON文件的一个属性,但我们这里为了简单,整个文件就是加密后的Base64字符串?)。但为了便于扩展,我们还是使用一个类来存储,可能包含版本等信息。
重新定义存储文件内容的类:
```csharp
public class LicenseFile
{
public string Data { get; set; } // 加密后的Base64字符串
}
```
步骤:
1. 生成许可证时:
- 获取MAC地址。
- 设置过期日期(当前日期+30天)。
- 创建`LicenseData`对象。
- 序列化为JSON字符串。
- 使用AES加密(使用一个固定的密钥和IV,但注意:固定密钥和IV不安全,最好使用用户提供的口令派生密钥,但这里为了简化,我们使用固定密钥,实际应用中应使用更安全的方式)。
- 将加密后的字节转换为Base64字符串。
- 创建`LicenseFile`对象,写入JSON文件。
2. 验证时:
- 读取JSON文件,得到`LicenseFile`对象。
- 将Base64字符串解密,得到原始JSON字符串。
- 反序列化得到`LicenseData`对象。
- 检查当前日期是否超过`LicenseData.ExpiryDate`,并且当前电脑的MAC地址是否与`LicenseData.MacAddress`一致。
注意:AES加密需要密钥(Key)和初始化向量(IV)。这里为了演示,我们将密钥和IV硬编码在程序中,但实际应用中应该将其存储在安全的地方(或者使用用户提供的口令生成)。
由于硬编码密钥不安全,我们可以考虑使用其他方式,比如将密钥的一部分基于机器特征生成,但这里为了简单,我们硬编码。
代码实现:
首先,实现一个AES加密辅助类:
```csharp
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public static class AesEncryption
{
// 这里使用固定的密钥和IV(128位,16字节),实际应用中应使用安全的方式生成和存储
private static readonly byte[] Key = Encoding.UTF8.GetBytes("Your16ByteKey123"); // 16字节
private static readonly byte[] Iv = Encoding.UTF8.GetBytes("Your16ByteIV1234"); // 16字节
public static string Encrypt(string plainText)
{
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = Iv;
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
return Convert.ToBase64String(msEncrypt.ToArray());
}
}
}
}
public static string Decrypt(string cipherText)
{
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = Iv;
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(buffer))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
return srDecrypt.ReadToEnd();
}
}
}
}
}
}
```
然后,实现获取MAC地址的方法:
```csharp
using System.Linq;
using System.Net.NetworkInformation;
public static string GetMacAddress()
{
return NetworkInterface.GetAllNetworkInterfaces()
.Where(nic => nic.OperationalStatus == OperationalStatus.Up &&
nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
.Select(nic => nic.GetPhysicalAddress().ToString())
.FirstOrDefault();
}
```
注意:MAC地址可能有多块网卡,我们选择第一个处于运行状态的且非回环的网卡的MAC地址。
然后,我们实现许可证的生成和验证:
主程序代码:
```csharp
using System;
using System.IO;
using Newtonsoft.Json;
class Program
{
static void Main(string[] args)
{
// 许可证文件路径
string licenseFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "license.lic");
// 如果许可证文件不存在,则创建(模拟第一次运行)
if (!File.Exists(licenseFilePath))
{
GenerateLicense(licenseFilePath);
}
// 验证许可证
if (ValidateLicense(licenseFilePath))
{
Console.WriteLine("许可证验证通过,程序正常运行!");
// 这里放置程序的主要逻辑
}
else
{
Console.WriteLine("许可证验证失败,程序将退出!");
Environment.Exit(0);
}
Console.ReadLine();
}
static void GenerateLicense(string filePath)
{
// 获取MAC地址
string macAddress = GetMacAddress();
// 设置过期日期(30天后)
DateTime expiryDate = DateTime.Now.AddDays(30);
// 创建许可证数据
LicenseData data = new LicenseData { MacAddress = macAddress, ExpiryDate = expiryDate };
string json = JsonConvert.SerializeObject(data);
string encryptedJson = AesEncryption.Encrypt(json);
// 创建许可证文件对象
LicenseFile licenseFile = new LicenseFile { Data = encryptedJson };
// 写入文件
File.WriteAllText(filePath, JsonConvert.SerializeObject(licenseFile));
}
static bool ValidateLicense(string filePath)
{
try
{
// 读取文件
string json = File.ReadAllText(filePath);
LicenseFile licenseFile = JsonConvert.DeserializeObject<LicenseFile>(json);
// 解密
string decryptedJson = AesEncryption.Decrypt(licenseFile.Data);
LicenseData licenseData = JsonConvert.DeserializeObject<LicenseData>(decryptedJson);
// 检查过期日期
if (DateTime.Now > licenseData.ExpiryDate)
{
Console.WriteLine("许可证已过期!");
return false;
}
// 检查MAC地址
string currentMacAddress = GetMacAddress();
if (currentMacAddress != licenseData.MacAddress)
{
Console.WriteLine("MAC地址不匹配!");
return false;
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"验证许可证时出错: {ex.Message}");
return false;
}
}
}
```
注意:
- 这里在程序第一次运行时生成许可证文件,实际应用中应该在购买后由用户运行一个激活程序生成。
- 密钥硬编码不安全,可以考虑使用更安全的方式存储密钥(比如使用ProtectedData类,或者由用户设置口令)。
### 相关问题
§§相关问题§§:
1. 如何改进加密密钥的安全性,避免硬编码在代码中?
2. 如果用户修改系统日期绕过时间检查,应如何应对?
3. 如何支持多网卡环境,允许用户选择绑定的网卡?
4. 如何防止许可证文件被复制到其他电脑上使用?</think># C# 加密软件实现:基于MAC地址和时间的许可证系统
下面我将实现一个C#加密软件,它使用电脑的MAC地址和系统日期生成加密许可证。程序启动时会验证许可证的有效性,只有在有效期内才能运行。
```csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Windows.Forms;
namespace LicenseProtectionSystem
{
public partial class MainForm : Form
{
private const string LicenseFileName = "license.key";
private readonly string LicenseFilePath = Path.Combine(Application.StartupPath, LicenseFileName);
public MainForm()
{
InitializeComponent();
CheckLicense();
}
private void CheckLicense()
{
// 1. 检查许可证文件是否存在
if (!File.Exists(LicenseFilePath))
{
CreateNewLicense();
return;
}
// 2. 读取并验证许可证
try
{
var license = LoadLicense();
if (IsLicenseValid(license))
{
lblStatus.Text = "许可证验证成功!程序已激活";
lblExpiry.Text = $"有效期至: {license.ExpiryDate:yyyy-MM-dd}";
return;
}
}
catch (CryptographicException)
{
MessageBox.Show("许可证文件已损坏!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show($"验证失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// 3. 许可证无效时退出程序
MessageBox.Show("许可证无效或已过期!程序将退出。", "许可证错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
private void CreateNewLicense()
{
try
{
// 获取系统信息
var macAddress = GetPrimaryMacAddress();
var creationDate = DateTime.Now;
var expiryDate = creationDate.AddMonths(1); // 默认有效期1个月
// 创建许可证对象
var license = new LicenseData
{
MachineId = macAddress,
CreationDate = creationDate,
ExpiryDate = expiryDate,
AdditionalInfo = "系统自动生成的许可证"
};
// 加密并保存许可证
SaveLicense(license);
MessageBox.Show("已创建新的许可证文件!", "信息",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"创建许可证失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
}
private LicenseData LoadLicense()
{
var encryptedJson = File.ReadAllText(LicenseFilePath);
var decryptedJson = AesDecrypt(encryptedJson);
return JsonSerializer.Deserialize<LicenseData>(decryptedJson);
}
private void SaveLicense(LicenseData license)
{
var json = JsonSerializer.Serialize(license);
var encryptedJson = AesEncrypt(json);
File.WriteAllText(LicenseFilePath, encryptedJson);
}
private bool IsLicenseValid(LicenseData license)
{
// 1. 检查MAC地址是否匹配
var currentMac = GetPrimaryMacAddress();
if (license.MachineId != currentMac)
{
MessageBox.Show($"MAC地址不匹配!\n注册地址: {license.MachineId}\n当前地址: {currentMac}",
"许可证错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// 2. 检查有效期
if (DateTime.Now > license.ExpiryDate)
{
MessageBox.Show($"许可证已过期!过期日期: {license.ExpiryDate:yyyy-MM-dd}",
"许可证错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// 3. 检查创建日期是否晚于当前日期(防篡改)
if (license.CreationDate > DateTime.Now)
{
MessageBox.Show("许可证创建日期无效!", "许可证错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return true;
}
#region 加密方法
// 使用AES加密算法
private static string AesEncrypt(string plainText)
{
using var aes = Aes.Create();
aes.Key = DeriveKey();
aes.IV = new byte[16]; // 固定IV,实际应用中应使用随机IV
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var sw = new StreamWriter(cs))
{
sw.Write(plainText);
}
return Convert.ToBase64String(ms.ToArray());
}
private static string AesDecrypt(string cipherText)
{
var buffer = Convert.FromBase64String(cipherText);
using var aes = Aes.Create();
aes.Key = DeriveKey();
aes.IV = new byte[16]; // 固定IV
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using var ms = new MemoryStream(buffer);
using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
using var sr = new StreamReader(cs);
return sr.ReadToEnd();
}
// 基于机器特征派生密钥
private static byte[] DeriveKey()
{
using var sha = SHA256.Create();
var machineInfo = $"{Environment.MachineName}_{GetPrimaryMacAddress()}";
return sha.ComputeHash(Encoding.UTF8.GetBytes(machineInfo)).Take(32).ToArray();
}
#endregion
#region 系统信息获取
private static string GetPrimaryMacAddress()
{
try
{
// 获取所有活动的、非虚拟的、非环回的网卡
var nics = NetworkInterface.GetAllNetworkInterfaces()
.Where(nic => nic.OperationalStatus == OperationalStatus.Up &&
nic.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
nic.NetworkInterfaceType != NetworkInterfaceType.Tunnel &&
!nic.Description.Contains("Virtual") &&
!nic.Description.Contains("Pseudo"));
// 返回第一个物理网卡的MAC地址
return nics.FirstOrDefault()?.GetPhysicalAddress().ToString() ?? "Unknown";
}
catch
{
return "Unknown";
}
}
#endregion
#region UI事件
private void btnShowInfo_Click(object sender, EventArgs e)
{
try
{
var license = LoadLicense();
var info = $"机器ID: {license.MachineId}\n" +
$"创建日期: {license.CreationDate:yyyy-MM-dd}\n" +
$"过期日期: {license.ExpiryDate:yyyy-MM-dd}\n" +
$"附加信息: {license.AdditionalInfo}";
MessageBox.Show(info, "许可证信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"读取许可证失败: {ex.Message}", "错误",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
}
// 许可证数据结构
public class LicenseData
{
public string MachineId { get; set; } // 机器标识(MAC地址)
public DateTime CreationDate { get; set; } // 创建日期
public DateTime ExpiryDate { get; set; } // 过期日期
public string AdditionalInfo { get; set; } // 附加信息
}
}
```
## 代码解释
### 1. 系统架构
- **许可证文件**:存储在应用程序目录下的`license.key`文件
- **验证流程**:
1. 检查许可证文件是否存在
2. 存在则加载并验证
3. 不存在则创建新许可证
4. 验证失败则退出程序
### 2. 关键技术实现
#### 机器标识获取
```csharp
private static string GetPrimaryMacAddress()
{
// 获取所有活动的、非虚拟的网卡
var nics = NetworkInterface.GetAllNetworkInterfaces()
.Where(nic => nic.OperationalStatus == OperationalStatus.Up &&
nic.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
!nic.Description.Contains("Virtual"));
return nics.FirstOrDefault()?.GetPhysicalAddress().ToString() ?? "Unknown";
}
```
此方法获取物理网卡的MAC地址作为机器唯一标识。
#### 加密算法
```csharp
private static string AesEncrypt(string plainText)
{
using var aes = Aes.Create();
aes.Key = DeriveKey(); // 基于机器特征派生密钥
aes.IV = new byte[16];
// 执行AES加密
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
// ...
}
```
使用AES-256加密算法保护许可证数据,密钥基于机器特征派生。
#### 密钥派生
```csharp
private static byte[] DeriveKey()
{
using var sha = SHA256.Create();
var machineInfo = $"{Environment.MachineName}_{GetPrimaryMacAddress()}";
return sha.ComputeHash(Encoding.UTF8.GetBytes(machineInfo)).Take(32).ToArray();
}
```
结合计算机名和MAC地址生成唯一密钥,增强安全性。
#### 许可证验证
```csharp
private bool IsLicenseValid(LicenseData license)
{
// 检查MAC地址匹配
if (license.MachineId != GetPrimaryMacAddress()) return false;
// 检查有效期
if (DateTime.Now > license.ExpiryDate) return false;
// 检查创建日期合理性
if (license.CreationDate > DateTime.Now) return false;
return true;
}
```
三重验证确保许可证有效性。
### 3. 安全特性
- **机器绑定**:使用MAC地址绑定特定机器
- **时间限制**:许可证包含有效期限
- **防篡改**:加密存储防止手动修改
- **密钥派生**:基于机器特征生成唯一密钥
- **完整性检查**:验证创建日期合理性
### 4. 使用说明
1. 首次运行时自动生成许可证文件
2. 每次启动验证许可证有效性
3. 许可证存储在应用程序目录的`license.key`文件中
4. 默认有效期1个月(可修改代码调整)
## 增强建议
1. **添加许可证延期功能**:允许用户输入新密钥延长有效期
2. **增加在线验证**:连接服务器验证许可证状态
3. **使用非对称加密**:改用RSA加密增强安全性
4. **添加硬件指纹**:结合更多硬件信息生成机器ID
5. **实现许可证吊销**:通过黑名单机制禁用特定许可证