Created
March 9, 2018 08:52
-
-
Save rqx110/fab27f12988cac3c31da7fd8c10cca46 to your computer and use it in GitHub Desktop.
软件注册序列号设计
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LicenseInfo | |
{ | |
public LicenseInfo() | |
{ | |
KeySn = ""; | |
Date = DateTime.MinValue; | |
RegLimitDays = 0; | |
UseLimitDays = 0; | |
Offset = 0; | |
} | |
public string KeySn { get; set; } | |
public DateTime Date { get; set; } | |
public int RegLimitDays { get; set; } | |
public int UseLimitDays { get; set; } | |
public int Offset { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LicenseManage | |
{ | |
/// <summary> | |
/// 序列号字典,把数字和字母容易混淆的字符去掉。所产生的25位序列号从这个字典中产生。 | |
/// </summary> | |
private static string _Dictionary = "JCB8EF2GH7K6MVP9QR3TXWY4"; | |
/// <summary> | |
/// 可以自定义字典字符串 | |
/// </summary> | |
public static string Dictionary | |
{ | |
get { return _Dictionary; } | |
set | |
{ | |
if (value.Length < 9) | |
{ | |
throw new ArgumentOutOfRangeException("设置的字典长度不能小于9个字符"); | |
} | |
_Dictionary = value; | |
} | |
} | |
/// <summary> | |
/// 生成序列号 | |
/// </summary> | |
/// <param name="key">关键字,一般为CPU号、硬盘号、网卡号,用于与序列号绑定,实现一机一码</param> | |
/// <param name="now">现在的时间</param> | |
/// <param name="regLimitDays">注册天数限制,超过此天数,再进行注册,序列号就失效了,不能再使用了</param> | |
/// <param name="useLimitDays">使用天数限制,超过此天数,可以设置软件停止运行等操作</param> | |
/// <returns>返回序列号,例如:xxxxx-xxxxx-xxxxx-xxxxx-xxxxx</returns> | |
public static string BuildSn(string key, DateTime now, int regLimitDays, int useLimitDays) | |
{ | |
if (regLimitDays < 0 || regLimitDays > 9) | |
{ | |
throw new ArgumentOutOfRangeException("注册天数限制范围为0-9"); | |
} | |
if (useLimitDays < 0 || useLimitDays > 99999) | |
{ | |
throw new ArgumentOutOfRangeException("使用天数限制范围为0-99999"); | |
} | |
/* | |
*关键字用MD5加密后,取后5个字符作为序列号第1组字符 | |
*/ | |
string md5 = Safety.MD5(key); | |
string x1 = md5.Substring(md5.Length - 5); | |
/* | |
* 生成随机偏移量 | |
*/ | |
Random rand = new Random(); | |
int offset = rand.Next(1, Dictionary.Length - 1); | |
/* | |
* 第5组的第1个字符保存偏移量字符,其余4个字符随机生成,作为保留位 | |
*/ | |
string x5 = Dictionary[offset].ToString(); | |
for (int i = 0; i < 4; i++) | |
{ | |
x5 += Dictionary[rand.Next(0, Dictionary.Length - 1)].ToString(); | |
} | |
/* | |
* 以注册时间(yyyyMMdd)和注册时间限制生成第2组和第3组序列号,一共10位字符串 | |
*/ | |
string dateSn = GetDateSn(now, offset); | |
string regLimitSn = GetRegLimitSn(regLimitDays, offset); | |
string x2 = dateSn.Substring(0, 5); | |
string x3 = dateSn.Substring(dateSn.Length - 3) + regLimitSn; | |
/* | |
*以使用时间限制生成第4组序列号,一共5位字符串 | |
*/ | |
string x4 = GetUseLimitSn(useLimitDays, offset); | |
return String.Format("{0}-{1}-{2}-{3}-{4}", x1, x2, x3, x4, x5); | |
} | |
/// <summary> | |
/// 注册序列号 | |
/// </summary> | |
/// <param name="key">关键字,一般为CPU号、硬盘号、网卡号,用于与序列号绑定,实现一机一码</param> | |
/// <param name="sn">序列号</param> | |
/// <param name="desc">描述信息</param> | |
/// <returns>注册状态,成功:0</returns> | |
internal static int RegSn(string key, string sn, ref string desc) | |
{ | |
if (String.IsNullOrEmpty(key) || String.IsNullOrEmpty(sn)) | |
{ | |
throw new ArgumentNullException("参数为空"); | |
} | |
LicenseInfo regInfo = GetRegInfo(sn); | |
string md5 = Safety.MD5(key); | |
if (String.CompareOrdinal(md5.Substring(md5.Length - 5), regInfo.KeySn) != 0) | |
{ | |
desc = "关键字与序列号不匹配"; | |
return -1;//关键字与序列号不匹配 | |
} | |
if (regInfo.Date == DateTime.MaxValue || regInfo.Date == DateTime.MinValue || regInfo.Date > DateTime.Now.Date) | |
{ | |
desc = "序列号时间有错误"; | |
return -2;//序列号时间有错误 | |
} | |
TimeSpan ts = DateTime.Now.Date - regInfo.Date; | |
if (ts.TotalDays > 9 || ts.TotalDays < 0) | |
{ | |
desc = "序列号失效"; | |
return -3;//序列号失效 | |
} | |
if (regInfo.UseLimitDays <= 0) | |
{ | |
desc = "使用期限受限"; | |
return -4;//使用期限受限 | |
} | |
//保存至注册表等 | |
MediaTypeNames.Application.UserAppDataRegistry.SetValue("SN", sn); | |
desc = "注册成功"; | |
return 0; | |
} | |
/// <summary> | |
/// 检测序列号,试用于时钟定时调用 | |
/// </summary> | |
/// <param name="key">关键字,一般为CPU号、硬盘号、网卡号,用于与序列号绑定,实现一机一码</param> | |
/// <param name="desc">描述信息</param> | |
/// <returns>检测状态,成功:0</returns> | |
internal static int CheckSn(string key, ref string desc) | |
{ | |
if (String.IsNullOrEmpty(key)) | |
{ | |
throw new ArgumentNullException("参数为空"); | |
} | |
object val = Application.UserAppDataRegistry.GetValue("SN"); ; | |
if (val == null) | |
{ | |
desc = "未检测到本机的序列号"; | |
return -1; | |
} | |
string sn = val.ToString(); | |
LicenseInfo regInfo = GetRegInfo(sn); | |
string md5 = Safety.MD5(key); | |
if (String.CompareOrdinal(md5.Substring(md5.Length - 5), regInfo.KeySn) != 0) | |
{ | |
desc = "关键字与序列号不匹配"; | |
return -2;//关键字与序列号不匹配 | |
} | |
if ((DateTime.Now.Date - regInfo.Date).TotalDays > regInfo.UseLimitDays) | |
{ | |
desc = "序列使用到期"; | |
return -3;//关键字与序列号不匹配 | |
} | |
desc = "序列号可用"; | |
return 0; | |
} | |
/// <summary> | |
/// 获得剩余天数 | |
/// </summary> | |
/// <param name="key">关键字,一般为CPU号、硬盘号、网卡号,用于与序列号绑定,实现一机一码</param> | |
/// <returns>剩余天数</returns> | |
internal static int GetRemainDays(string key) | |
{ | |
if (String.IsNullOrEmpty(key)) | |
{ | |
throw new ArgumentNullException("参数为空"); | |
} | |
object val = Application.UserAppDataRegistry.GetValue("SN"); ; | |
if (val == null) | |
{ | |
return 0; | |
} | |
string sn = val.ToString(); | |
LicenseInfo regInfo = GetRegInfo(sn); | |
string md5 = Safety.MD5(key); | |
if (String.CompareOrdinal(md5.Substring(md5.Length - 5), regInfo.KeySn) != 0) | |
{ | |
return 0;//关键字与序列号不匹配,不能使用。 | |
} | |
//<=0的情况,证明不可以使用。 | |
return regInfo.UseLimitDays - (int)(DateTime.Now.Date - regInfo.Date).TotalDays; | |
} | |
/// <summary> | |
/// 根据序列号,反推注册信息 | |
/// </summary> | |
/// <param name="sn">序列号</param> | |
/// <returns>注册信息</returns> | |
private static LicenseInfo GetRegInfo(string sn) | |
{ | |
LicenseInfo reg = new LicenseInfo(); | |
string[] splitSn = sn.Split('-'); | |
if (splitSn.Length != 5) | |
{ | |
throw new FormatException("序列号格式错误,应该带有'-'字符"); | |
} | |
reg.KeySn = splitSn[0]; | |
reg.Offset = Dictionary.IndexOf(splitSn[4][0]); | |
reg.Date = GetDate(splitSn[1] + splitSn[2].Substring(0, 3), reg.Offset); | |
reg.RegLimitDays = GetRegLimitDays(splitSn[2].Substring(3, 2), reg.Offset); | |
reg.UseLimitDays = GetUseLimitDays(splitSn[3], reg.Offset); | |
return reg; | |
} | |
/// <summary> | |
/// 以当前时间和偏移量生成当前时间对应的字符串 | |
/// </summary> | |
/// <param name="now">当前时间</param> | |
/// <param name="offset">偏移量</param> | |
/// <returns>返回日期对应的字符串,8位字符串</returns> | |
private static string GetDateSn(DateTime now, int offset) | |
{ | |
string dateSn = ""; | |
string date = now.ToString("yyyyMMdd"); | |
string newDic = Dictionary; | |
for (int i = 0; i < date.Length; i++) | |
{ | |
newDic = GetNewDictionaryString(newDic, offset, LicenseOffset.Left); | |
int num = int.Parse(date[i].ToString()); | |
dateSn += newDic[num].ToString(); | |
} | |
return dateSn; | |
} | |
/// <summary> | |
/// 根据注册时间序列号反推注册时间 | |
/// </summary> | |
/// <param name="dateSn">时间字符串</param> | |
/// <param name="offset">偏移量</param> | |
/// <returns>时间</returns> | |
private static DateTime GetDate(string dateSn, int offset) | |
{ | |
string dateStr = ""; | |
string newDic = Dictionary; | |
for (int i = 0; i < dateSn.Length; i++) | |
{ | |
newDic = GetNewDictionaryString(newDic, offset, LicenseOffset.Left); | |
int num = newDic.IndexOf(dateSn[i]); | |
dateStr += num; | |
} | |
return new DateTime(int.Parse(dateStr.Substring(0, 4)), int.Parse(dateStr.Substring(4, 2)), int.Parse(dateStr.Substring(6, 2))); | |
} | |
/// <summary> | |
/// 以注册时间限制和偏移量生成对应的字符串 | |
/// </summary> | |
/// <param name="regLimitDays"></param> | |
/// <param name="offset"></param> | |
/// <returns>返回对应的注册时间限制的字符串,2位字符串</returns> | |
private static string GetRegLimitSn(int regLimitDays, int offset) | |
{ | |
string regLimitSn = ""; | |
string regLimitStr = regLimitDays.ToString("00"); | |
string newDic = Dictionary; | |
for (int i = 0; i < regLimitStr.Length; i++) | |
{ | |
newDic = GetNewDictionaryString(newDic, offset, LicenseOffset.Left); | |
int num = int.Parse(regLimitStr[i].ToString()); | |
regLimitSn += newDic[num].ToString(); | |
} | |
return regLimitSn; | |
} | |
/// <summary> | |
/// 根据注册时间限制字符串反推注册时间限制 | |
/// </summary> | |
/// <param name="regLimitSn">注册时间限制字符串</param> | |
/// <param name="offset">偏移量</param> | |
/// <returns>注册时间限制</returns> | |
private static int GetRegLimitDays(string regLimitSn, int offset) | |
{ | |
string regLimitStr = ""; | |
string newDic = Dictionary; | |
for (int i = 0; i < regLimitSn.Length; i++) | |
{ | |
newDic = GetNewDictionaryString(newDic, offset, LicenseOffset.Left); | |
int num = newDic.IndexOf(regLimitSn[i]); | |
regLimitStr += num; | |
} | |
return int.Parse(regLimitStr); | |
} | |
/// <summary> | |
/// 以使用时间限制和偏移量生成对应的字符串 | |
/// </summary> | |
/// <param name="useLimitDays">使用时间限制</param> | |
/// <param name="offset">偏移量</param> | |
/// <returns>使用时间限制对应字符串,5位字符串</returns> | |
private static string GetUseLimitSn(int useLimitDays, int offset) | |
{ | |
string useLimitSn = ""; | |
string useLimitStr = useLimitDays.ToString("00000"); | |
string newDic = Dictionary; | |
for (int i = 0; i < useLimitStr.Length; i++) | |
{ | |
newDic = GetNewDictionaryString(newDic, offset, LicenseOffset.Left); | |
int num = int.Parse(useLimitStr[i].ToString()); | |
useLimitSn += newDic[num].ToString(); | |
} | |
return useLimitSn; | |
} | |
/// <summary> | |
/// 根据使用时间限制字符串反推使用时间限制 | |
/// </summary> | |
/// <param name="regLimitSn">使用时间限制字符串</param> | |
/// <param name="offset">偏移量</param> | |
/// <returns>使用时间限制</returns> | |
private static int GetUseLimitDays(string useLimitSn, int offset) | |
{ | |
string useLimitStr = ""; | |
string newDic = Dictionary; | |
for (int i = 0; i < useLimitSn.Length; i++) | |
{ | |
newDic = GetNewDictionaryString(newDic, offset, LicenseOffset.Left); | |
int num = newDic.IndexOf(useLimitSn[i]); | |
useLimitStr += num; | |
} | |
return int.Parse(useLimitStr); | |
} | |
/// <summary> | |
/// 根据字典、偏移量和偏移类型生成新的字典 | |
/// </summary> | |
/// <param name="dic"></param> | |
/// <param name="offset"></param> | |
/// <param name="offsetType"></param> | |
/// <returns></returns> | |
private static string GetNewDictionaryString(string dic, int offset, LicenseOffset offsetType) | |
{ | |
StringBuilder sb = new StringBuilder(dic); | |
if (offsetType == LicenseOffset.Left) | |
{ | |
for (int i = 0; i < offset; i++) | |
{ | |
string head = sb[0].ToString(); | |
sb.Remove(0, 1); | |
sb.Append(head); | |
} | |
} | |
else if (offsetType == LicenseOffset.Right) | |
{ | |
for (int i = 0; i < offset; i++) | |
{ | |
string end = sb[dic.Length - 1].ToString(); | |
sb.Remove(dic.Length - 1, 1); | |
sb.Insert(0, end); | |
} | |
} | |
return sb.ToString(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal enum LicenseOffset | |
{ | |
Left, | |
Right | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Safety | |
{ | |
public static string MD5(string str) | |
{ | |
string strResult = ""; | |
MD5 md5 = System.Security.Cryptography.MD5.Create(); | |
byte[] bData = md5.ComputeHash(Encoding.Unicode.GetBytes(str)); | |
for (int i = 0; i < bData.Length; i++) | |
{ | |
strResult = strResult + bData[i].ToString("X"); | |
} | |
return strResult; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
refrence: http://www.cnblogs.com/lsjwq/p/5105183.html