国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

[連載]《C#通訊(串口和網(wǎng)絡(luò))框架的設(shè)計(jì)與實(shí)現(xiàn)》-14.序列號的設(shè)計(jì),不重復(fù)的實(shí)現(xiàn)一機(jī)一碼

2019-11-14 13:35:45
字體:
供稿:網(wǎng)友

目       錄

第十四章     序列號的設(shè)計(jì)... 2

14.1        設(shè)計(jì)原則... 2

14.2        設(shè)計(jì)思想... 3

14.3        代碼實(shí)現(xiàn)... 4

14.4        代碼混淆... 18

14.5        代碼破解... 18

14.6        小結(jié)... 18

 

第十四章      序列號的設(shè)計(jì)

    序列號作為軟件使用授權(quán)方式之一,被廣泛使用在應(yīng)用軟件方面。主要考慮到這幾方面:1.對知識產(chǎn)權(quán)的保護(hù),畢竟付出來腦力勞動和體力勞動。2.商業(yè)競爭中增加防守的能力,防止被競爭對手盜取。3.增強(qiáng)合同的執(zhí)行效力,防止另一方由于各種原因破壞合作機(jī)制。

    基于上述方面,從保護(hù)、防守思維模式角度考慮,增加序列號功能是有必要的。每個作者或公司設(shè)計(jì)序列號的方式不一樣,就是因?yàn)椴灰粯樱圆拍苓_(dá)到我們增加該功能的效果。

14.1     設(shè)計(jì)原則

  1. 序列號長度盡可能短

    主要是從成本角度考慮的。例如用戶現(xiàn)場需要一個正版軟件的序列號,你把序列號信息通過什么方式傳遞給用戶呢?假設(shè)我們用對稱或非對稱方式生成一個很長的序列號,如果口述告訴對方的話,那么對方肯定要用紙和筆進(jìn)行記錄,最后輸入到軟件后還不一定正確;如果把序列號以文件的方式通過網(wǎng)絡(luò)傳遞給對方,那么需要占用網(wǎng)絡(luò)資源,另外對方的電腦不一定有網(wǎng)絡(luò)環(huán)境。不管如何,很長的序列號在生成和傳遞的過程中可能涉及到的成本包括:材料成本、流量成本、人力成本和時(shí)間成本等。

     如果一個字符可以表達(dá)序列號所需要的完整信息,那么是最理想的。但是,這是理想狀態(tài),是不可能實(shí)現(xiàn)的,至少以我現(xiàn)在的能力是無法完成的。所以,要以最佳的長度表達(dá)出序列號的全部信息。

  1. 避免出現(xiàn)容易混淆的字符生成一個序列號發(fā)給了用戶,這個序列號包括:數(shù)字0和字母O,數(shù)字1和字母l。難道讓用戶一遍一遍的試嘛,這樣的用戶體驗(yàn)太差了,雖然嘴上不說出來,至少感覺不太舒服。

14.2     設(shè)計(jì)思想

    設(shè)計(jì)的思想要看序列號要實(shí)現(xiàn)什么樣的功能和具備什么屬性。從功能角度考慮,包括:1.一個計(jì)算機(jī)一個序列號;2.盡管輸入的條件都一樣,每次生成的序列號都不一樣;3.對使用的時(shí)限進(jìn)行驗(yàn)證;4.序列號有注冊時(shí)限,超過規(guī)定的使用時(shí)間,序列號作廢,避免短時(shí)間多次注冊。從屬性角度考慮,包括:同樣的計(jì)算機(jī)、同樣的輸入條件生成的序列號都不一樣。

   我們把上述因素考慮進(jìn)去,序列號長度為25位字符,序列號生成格式和元素信息如下圖:

 

 X01-X05:為計(jì)算機(jī)的特征碼,5位字符串,獲得機(jī)器某個部件的ID,這個部件可能為CPU、網(wǎng)卡、硬盤等信息,把ID進(jìn)行md5加密后取前5個字符作為特征碼,來實(shí)現(xiàn)一機(jī)一碼。這種方式,特征碼有可能有相同的情況,但是機(jī)率很小。

 X06-X13:為生成序列號的日期,8位字符串,格式為:yyyyMMdd。與后邊的使用時(shí)間限制配合使用,來驗(yàn)證軟件的使用期限。

 X14-X15:為注冊時(shí)間限制,2位數(shù)字字符,從生成序列號日期算起,超過此注冊時(shí)間限制,序列號將無法正常進(jìn)行注冊操作。

 X16-X20:為使用時(shí)間限制,5位數(shù)字字符,與生成序列號日期配合使用來驗(yàn)證軟件使用期限。

 X21:為序列號的偏移量,1位字符,不管在什么場景下,每次生成序列號的偏移量都不一樣。

X22-X25:為保留數(shù)據(jù)位,暫時(shí)不使用。自定義一個序列號字典信息,例如:_Dictionary ="JCB8EF2GH7K6MVP9QR3TXWY4",把容易混淆的字符去掉,這個可以自定義。序列號的每個部分都是通過隨機(jī)生成的偏移量(X21),對字典進(jìn)行位移,根據(jù)輸入的數(shù)字信息對應(yīng)字典的下標(biāo)提取相應(yīng)的字符作為序列號的一個字符。

   生成序列號的大概過程:

  1. 在字典信息的長度范圍內(nèi)隨機(jī)生成一個偏移量數(shù)字。
  2. 根據(jù)偏移量數(shù)字對字典進(jìn)行左或右的循環(huán)移動。
  3. 根據(jù)輸入的數(shù)字信息,例如:2015中的2,作為下標(biāo),從字典信息中提取出相應(yīng)的字符。

反向解析大概過程類似,只需要根據(jù)X21字符,與字典的字符進(jìn)行匹配,對應(yīng)的下標(biāo)作為偏移量,就可以反向解析出各項(xiàng)信息。

14.3     代碼實(shí)現(xiàn)

1.MD5操作類:

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

2.注冊信息類:

public class RegInfo{       public RegInfo()       {              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; }}

3.偏移操作類型:

internal enum OffsetType{       Left,       Right}

4.        序列號管理類

public class LicenseManage    {        /// <summary>        /// 序列號字典,把數(shù)字和字母容易混淆的字符去掉。所產(chǎn)生的25位序列號從這個字典中產(chǎn)生。        /// </summary>        PRivate static string _Dictionary = "JCB8EF2GH7K6MVP9QR3TXWY4";        /// <summary>        /// 可以自定義字典字符串        /// </summary>        public static string Dictionary        {            get { return _Dictionary; }            set            {                if (value.Length < 9)                {                    throw new ArgumentOutOfRangeException("設(shè)置的字典長度不能小于9個字符");                }                _Dictionary = value;            }        }        /// <summary>        /// 生成序列號        /// </summary>        /// <param name="key">關(guān)鍵字,一般為CPU號、硬盤號、網(wǎng)卡號,用于與序列號綁定,實(shí)現(xiàn)一機(jī)一碼</param>        /// <param name="now">現(xiàn)在的時(shí)間</param>        /// <param name="regLimitDays">注冊天數(shù)限制,超過此天數(shù),再進(jìn)行注冊,序列號就失效了,不能再使用了</param>        /// <param name="useLimitDays">使用天數(shù)限制,超過此天數(shù),可以設(shè)置軟件停止運(yùn)行等操作</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("注冊天數(shù)限制范圍為0-9");            }            if (useLimitDays < 0 || useLimitDays > 99999)            {                throw new ArgumentOutOfRangeException("使用天數(shù)限制范圍為0-99999");            }            /*             *關(guān)鍵字用MD5加密后,取后5個字符作為序列號第1組字符             */            string md5 = Safety.MD5(key);            string x1 = md5.Substring(md5.Length - 5);            /*             * 生成隨機(jī)偏移量             */            Random rand = new Random();            int offset = rand.Next(1, Dictionary.Length - 1);            /*             * 第5組的第1個字符保存偏移量字符,其余4個字符隨機(jī)生成,作為保留位             */            string x5 = Dictionary[offset].ToString();            for (int i = 0; i < 4; i++)            {                x5 += Dictionary[rand.Next(0, Dictionary.Length - 1)].ToString();            }            /*             * 以注冊時(shí)間(yyyyMMdd)和注冊時(shí)間限制生成第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;            /*             *以使用時(shí)間限制生成第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">關(guān)鍵字,一般為CPU號、硬盤號、網(wǎng)卡號,用于與序列號綁定,實(shí)現(xiàn)一機(jī)一碼</param>        /// <param name="sn">序列號</param>        /// <param name="desc">描述信息</param>        /// <returns>注冊狀態(tài),成功:0</returns>        internal static int RegSn(string key, string sn, ref string desc)        {            if (String.IsNullOrEmpty(key) || String.IsNullOrEmpty(sn))            {                throw new ArgumentNullException("參數(shù)為空");            }            LicenseInfo regInfo = GetRegInfo(sn);            string md5 = Safety.MD5(key);            if (String.CompareOrdinal(md5.Substring(md5.Length - 5), regInfo.KeySn) != 0)            {                desc = "關(guān)鍵字與序列號不匹配";                return -1;//關(guān)鍵字與序列號不匹配            }            if (regInfo.Date == DateTime.MaxValue || regInfo.Date == DateTime.MinValue || regInfo.Date > DateTime.Now.Date)            {                desc = "序列號時(shí)間有錯誤";                return -2;//序列號時(shí)間有錯誤            }            TimeSpan ts = DateTime.Now.Date - regInfo.Date;            if (ts.TotalDays > 9 || ts.TotalDays < 0)            {                desc = "序列號失效";                return -3;//序列號失效            }            if (regInfo.UseLimitDays <= 0)            {                desc = "使用期限受限";                return -4;//使用期限受限            }            application.UserAppDataRegistry.SetValue("SN", sn);            desc = "注冊成功";            return 0;        }        /// <summary>        /// 檢測序列號,試用于時(shí)鐘定時(shí)調(diào)用        /// </summary>        /// <param name="key">關(guān)鍵字,一般為CPU號、硬盤號、網(wǎng)卡號,用于與序列號綁定,實(shí)現(xiàn)一機(jī)一碼</param>        /// <param name="desc">描述信息</param>        /// <returns>檢測狀態(tài),成功:0</returns>        internal static int CheckSn(string key, ref string desc)        {            if (String.IsNullOrEmpty(key))            {                throw new ArgumentNullException("參數(shù)為空");            }            object val = Application.UserAppDataRegistry.GetValue("SN");            if (val == null)            {                desc = "未檢測到本機(jī)的序列號";                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 = "關(guān)鍵字與序列號不匹配";                return -2;//關(guān)鍵字與序列號不匹配            }            if ((DateTime.Now.Date - regInfo.Date).TotalDays > regInfo.UseLimitDays)            {                desc = "序列使用到期";                return -3;//關(guān)鍵字與序列號不匹配            }            desc = "序列號可用";            return 0;        }        /// <summary>        /// 獲得剩余天數(shù)        /// </summary>        /// <param name="key">關(guān)鍵字,一般為CPU號、硬盤號、網(wǎng)卡號,用于與序列號綁定,實(shí)現(xiàn)一機(jī)一碼</param>        /// <returns>剩余天數(shù)</returns>        internal static int GetRemainDays(string key)        {            if (String.IsNullOrEmpty(key))            {                throw new ArgumentNullException("參數(shù)為空");            }            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;//關(guān)鍵字與序列號不匹配,不能使用。            }            //<=0的情況,證明不可以使用。            return regInfo.UseLimitDays - (int)(DateTime.Now.Date - regInfo.Date).TotalDays;        }        /// <summary>        /// 根據(jù)序列號,反推注冊信息        /// </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("序列號格式錯誤,應(yīng)該帶有'-'字符");            }            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>        /// 以當(dāng)前時(shí)間和偏移量生成當(dāng)前時(shí)間對應(yīng)的字符串        /// </summary>        /// <param name="now">當(dāng)前時(shí)間</param>        /// <param name="offset">偏移量</param>        /// <returns>返回日期對應(yīng)的字符串,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>        /// 根據(jù)注冊時(shí)間序列號反推注冊時(shí)間        /// </summary>        /// <param name="dateSn">時(shí)間字符串</param>        /// <param name="offset">偏移量</param>        /// <returns>時(shí)間</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>        /// 以注冊時(shí)間限制和偏移量生成對應(yīng)的字符串        /// </summary>        /// <param name="regLimitDays"></param>        /// <param name="offset"></param>        /// <returns>返回對應(yīng)的注冊時(shí)間限制的字符串,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>        /// 根據(jù)注冊時(shí)間限制字符串反推注冊時(shí)間限制        /// </summary>        /// <param name="regLimitSn">注冊時(shí)間限制字符串</param>        /// <param name="offset">偏移量</param>        /// <returns>注冊時(shí)間限制</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>        /// 以使用時(shí)間限制和偏移量生成對應(yīng)的字符串        /// </summary>        /// <param name="useLimitDays">使用時(shí)間限制</param>        /// <param name="offset">偏移量</param>        /// <returns>使用時(shí)間限制對應(yīng)字符串,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>        /// 根據(jù)使用時(shí)間限制字符串反推使用時(shí)間限制        /// </summary>        /// <param name="regLimitSn">使用時(shí)間限制字符串</param>        /// <param name="offset">偏移量</param>        /// <returns>使用時(shí)間限制</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>        /// 根據(jù)字典、偏移量和偏移類型生成新的字典        /// </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();        }    }

 

14.4     代碼混淆

   從安全角度來講,.NET程序如果不加混淆的話,很容易被反編譯出源代碼的。從專業(yè)角度來講,即使增加了序列號功能,也無濟(jì)于事,專業(yè)的人員分分鐘可以破解掉,盡管這樣干的人很少,但是存在這種可能性。如果一個軟件人員想了解一個很好的軟件,第一反映可能就是反編譯。

   對于公司或商業(yè)使用的軟件來講,增加混淆還是有必要的,盡管現(xiàn)在開源很流行。

14.5     代碼破解

    不管.NET程序如何進(jìn)行混淆,理論上都是可以破解的,理論的東西就不贅述了。通常接觸過的破解方式有兩種:注冊機(jī)方式和暴力方式。

    注冊機(jī)的方式,需要通過軟件的驗(yàn)證序列號的過程和機(jī)制反向推算出序列號的生成算法,根據(jù)反推的算法開發(fā)一個小軟件,用于生成脫離作者授權(quán)生成序列號。這種方式不會破壞程序本身的代碼,相對溫和。暴力的方式,就是找到序列號驗(yàn)證部分的代碼,通過刪除或繞過驗(yàn)證代碼等方式不讓代碼有效執(zhí)行。這種方式會對程序本身的代碼進(jìn)行改動,所以也存在一些風(fēng)險(xiǎn)。

14.6     小結(jié)

     實(shí)現(xiàn)序列號有多種方式,上述方式不一定最好,但是希望對開發(fā)者有一定幫助。

 

最終實(shí)現(xiàn)效果圖如下:

作者:唯笑志在

Email:504547114@QQ.com

QQ:504547114

.NET開發(fā)技術(shù)聯(lián)盟:54256083

文檔下載:http://pan.baidu.com/s/1pJ7lZWf

官方網(wǎng)址:http://www.bmpj.net


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 元谋县| 永善县| 宣恩县| 茂名市| 绥棱县| 台北县| 恩施市| 阿尔山市| 江城| 横山县| 孟村| 桦南县| 渑池县| 昌宁县| 陕西省| 廉江市| 中方县| 峨边| 陆良县| 龙岩市| 太保市| 平舆县| 大城县| 盐边县| 启东市| 饶平县| 乌兰县| 乌兰浩特市| 周口市| 临清市| 青阳县| 中方县| 龙川县| 平邑县| 连城县| 越西县| 灯塔市| 南乐县| 宾阳县| 巴马| 海林市|