版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/chinahuyong/article/details/8533831
推薦+1置頂+1(分享、讨論、實作)
通用軟體注冊功能之建立有效的軟體保護機制
衆所周知,一些共享軟體往往提供給使用者的是一個功能不受限制的限時使用版,在試用期内使用者可以無限制的使用軟體的全部功能(隻是可能會出現提示使用者注冊的視窗),試用期一過部分(或全部)功能失效,要想繼續使用隻能向作者索取注冊碼(或注冊檔案)完成對軟體的合法注冊,注冊後的軟體将解除一切使用限制。如果您也開發出一個有價值的作品,是否也希望為自己的軟體增加一個這樣的功能呢?目前對于.NET反編譯的問題不在本文讨論之内,相關文章已經很多!本文我們就一起探讨軟體注冊功能的實作。
實作軟體的注冊功能方法很多,最需要考慮的就是不能輕易的讓使用者破解,在這裡,我就談談“.NET快速開發整合架構(RDIFramework.NET)”中平台注冊功能的實作方法。在RDIFramework.NET中,注冊功能主要方法就是對計算機唯一硬體資訊進行RSA數字簽名達到軟體注冊和保護的功能,該方法實作簡單,安全性相應較高。
計算機唯一硬體資訊(我們知道計算機中的關鍵部件如CPU,主機闆等在全球範圍内都有一個獨一無二的産品序列号,使用者通過注冊子產品擷取這些産品序列号(即傳統所說的:機器嗎)并将它發送給軟體開發商要求進行RSA資料簽名,軟體開發商獲得這些機器碼後利用手中的私鑰對這些資訊進行RSA數字簽名,生成的簽名資訊(即注冊碼)發回給使用者,使用者将收到的注冊碼輸入注冊子產品的注冊碼框,軟體即可利用公鑰執行簽名驗證,如果輸入的注冊碼被證明就是經過開發商數字簽名的機器碼,則完成注冊過程。
注冊功能項目結構圖如下所示:
圖1 注冊功能項目結構
平台服務端注冊碼生成主界面如下所示:
圖2 注冊檔案管理器
通過“注冊檔案管理器”,我們就可以根據使用者提供的資訊來生成軟體的注冊檔案。
用戶端的注冊主要就是根據我們提供的注冊檔案與公鑰,來驗證注冊檔案是否為目前客戶的有效注冊檔案,如果有效,注冊成功,無效則注冊失敗!用戶端注冊功能設計參考如下所示:
圖3 平台注冊
使用者單擊“注冊”按鈕,成功注冊提示:
圖4注冊成功
服務端注冊碼生成核心代碼:
一、 生成公/私鑰檔案:
1 private void btnGenerateKey_Click(object sender, EventArgs e)
2 {
3 if (MessageBox.Show("确定生成生成公/私鑰對嗎(是/否)?", "詢問資訊", MessageBoxButtons.OKCancel, MessageBoxIcon.Question)
4 == System.Windows.Forms.DialogResult.Cancel)
5 {
6 return;
7 }
8
9 RSACryptoServiceProvider crypt = new RSACryptoServiceProvider();
10
11 string publicKey = crypt.ToXmlString(true);
12 string privateKey = crypt.ToXmlString(false);
13 crypt.Clear();
14
15 //生成公鑰
16 using (StreamWriter sw = new StreamWriter(KeyPath + "RDIFrameworkkey.key", false, UTF8Encoding.UTF8))
17 {
18 sw.Write(SecretHelper.AESEncrypt(publicKey));
19 sw.Flush();
20 }
21
22 //生成私鑰
23 using (StreamWriter sw = new StreamWriter(KeyPath + "RDIFrameworkPrivateKey.key", false, UTF8Encoding.UTF8))
24 {
25 sw.Write(SecretHelper.AESEncrypt(privateKey));
26 sw.Flush();
27 }
28
29 MessageBox.Show("成功生成公/私鑰對!","提示資訊",MessageBoxButtons.OK,MessageBoxIcon.Information);
30 }
二、 生成注冊檔案:
1 private void btnGenerateRegisterFile_Click(object sender, EventArgs e)
2 {
3 if (string.IsNullOrEmpty(txtUserEmail.Text.Trim()))
4 {
5 MessageBox.Show("使用者郵箱不能為空!", "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
6 txtUserEmail.Focus();
7 return;
8 }
9 else
10 {
11 if (!RegexValidatorHelper.IsMatch(txtUserEmail.Text.Trim(), Pattern.EMAIL))
12 {
13 MessageBox.Show("郵箱格式不正确!", "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
14 txtUserEmail.SelectAll();
15 return;
16 }
17 }
18
19 if (string.IsNullOrEmpty(txtCPUSerialNo.Text.Trim()))
20 {
21 MessageBox.Show("CPU序列号不能為空!", "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
22 return;
23 }
24
25 if (!string.IsNullOrEmpty(txtUseLimited.Text.Trim()))
26 {
27 if (!RegexValidatorHelper.IsMatch(txtUseLimited.Text.Trim(), Pattern.INTEGER))
28 {
29 MessageBox.Show("使用次數應該為數值型!", "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Warning);
30 txtUseLimited.SelectAll();
31 return;
32 }
33 }
34
35 //讀取私鑰
36 StreamReader sr = new StreamReader(KeyPath + "RDIFrameworkPrivateKey.key", UTF8Encoding.UTF8);
37 string keypair = sr.ReadToEnd();
38 sr.Close();
39
40 //用私鑰參數初始化RSACryptoServiceProvider類的執行個體crypt。
41 RSACryptoServiceProvider crypt = new RSACryptoServiceProvider();
42
43 crypt.FromXmlString(SecretHelper.AESDecrypt(keypair));
44
45 UTF8Encoding enc = new UTF8Encoding();
46
47 string trialTime = "30";//試用次數(預設:30數,0:表示永久)
48 if (!string.IsNullOrEmpty(txtUseLimited.Text.Trim()))
49 {
50 trialTime = txtUseLimited.Text.Trim();
51 }
52 string regInfo = txtUserEmail.Text.Trim() + ";" + txtMAC.Text.Trim() + ";" + txtCPUSerialNo.Text.Trim() + ";" + trialTime;
53
54 byte[] bytes = enc.GetBytes(regInfo);//格式:郵箱位址;MAC;CPU序列号;試用時間
55 //對使用者資訊加密
56 bytes = crypt.Encrypt(bytes, false);
57
58 //生成注冊資料,對二進制位元組進行Base64編碼,但采用注冊檔案的形式的進修也可以不做此轉化。
59 string encrytText = System.Convert.ToBase64String(bytes, 0, bytes.Length);
60
61 //将注冊碼寫入檔案
62 using (StreamWriter sw = new StreamWriter(KeyPath + "RDIFramework_reg_file.lic", false, UTF8Encoding.UTF8))
63 {
64 sw.Write(encrytText);
65 sw.Flush();
66 }
67
68 MessageBox.Show("注冊檔案:RDIFramework_reg_file.lic生成成功!", "提示資訊", MessageBoxButtons.OK, MessageBoxIcon.Information);
69 }
三、 驗證注冊檔案:
1 private void btnCheckRegistr_Click(object sender, EventArgs e)
2 {
3 //讀取注冊資料檔案
4 StreamReader sr = new StreamReader(KeyPath + "RDIFramework_reg_file.lic", UTF8Encoding.UTF8);
5 string encrytText = sr.ReadToEnd();
6 sr.Close();
7
8
9 //讀取公鑰
10 StreamReader srPublickey = new StreamReader(KeyPath + "RDIFrameworkkey.key", UTF8Encoding.UTF8);
11 string publicKey = srPublickey.ReadToEnd();
12 srPublickey.Close();
13
14 //用公鑰初化始RSACryptoServiceProvider類執行個體crypt。
15 RSACryptoServiceProvider crypt = new RSACryptoServiceProvider();
16 crypt.FromXmlString(SecretHelper.AESDecrypt(publicKey));
17 UTF8Encoding enc = new UTF8Encoding();
18 byte[] decryptByte;
19 try
20 {
21 byte[] newBytes;
22 newBytes = System.Convert.FromBase64CharArray(encrytText.ToCharArray(), 0, encrytText.Length);
23 decryptByte = crypt.Decrypt(newBytes, false);
24 string decrypttext = enc.GetString(decryptByte);
25 //
26 //TODO:在此處添加驗證邏輯
27 //
28 MessageBox.Show(decrypttext);
29 }
30 catch(Exception ex)
31 {
32 MessageBox.Show(ex.Message);
33 }
34 }
至此,軟體的注冊功能就完成了,當然還有其他很多方法,比如:
一、 采用加密狗的方式(最安全的方式)。
二、 線上驗證注冊資訊(使用者需能上網),這種方式也比較可靠。
三、 其他方法,歡迎大家讨論。
四、 ......
作者: EricHu
出處: http://blog.csdn.net/chinahuyong
微網誌: 騰訊
Email: [email protected]
QQ 交流:406590790
平台部落格: 【CSDN】http://blog.csdn.net/chinahuyong
【CNBLOGS】http://www.cnblogs.com/huyong
關于作者:進階工程師、資訊系統項目管理師、DBA。專注于微軟平台項目架構、管理和企業解決方案,多年項目開發與管理經驗,曾多次組織并開發多個大型項目,精通DotNet,DB(SqlServer、Oracle等)技術。熟悉Java、Delhpi及Linux作業系統,有紮實的網絡知識。在面向對象、面向服務以及資料庫領域有一定的造詣。現從事DB管理與開發、WinForm、WCF、WebService、網頁資料抓取以及ASP.NET等項目管理、開發、架構等工作。
如有問題或建議,請多多賜教!
本文版權歸作者和CNBLOGS部落格共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,如有問題,可以通過郵箱或QQ 聯系我,非常感謝。