無法破解的軟體註冊碼演算法
實現原代碼,包括一個Asp.Net分頁檔和對應的CS代碼檔案 - 3 Kb
Microsoft .Net的應用程式的代碼檔案,與Java產生的檔案類似,它們都沒有本地代碼,而是一種類似於彙編的代碼。這樣,只要有合適的工具,就可以完整的把別人寫出來的程式反編譯成自己需要的程式檔案。
我所知道的.Net下的反組譯工具是Salamander 和 Refelector 兩個工具,他們都可以對.Net的程式集反編譯成你需要的語言。
那麼,我們寫的程式,做的項目,如何進行正版的許可證管理,有許多方法。
最好的方案,是幾個方法的綜合。下面我說一下單獨的許可驗證方法。
最簡單的方法,就是使用許可儲存。方法是使用者輸入正版的註冊碼,通過程式中專門的演算法程式進行驗算,得出的結果與事先儲存在程式中的結果比對,比對一致表示輸入正確。然後把結果儲存在儲存中,如註冊表或者專門的許可檔案中,程式許可通過。
這個方法使用的人/公司最多,但是缺點也是最多的,只要使用上面的工具把驗算註冊碼的演算法給弄清楚,就可以自己寫一個產生序號的註冊機,這個註冊方法就形同虛設了。
還有一個比較好的方法,就是仿照WindowsXP的啟用機制,客戶的程式自動訪問互連網的一個專門設定的伺服器,通過Tcp/Ip或者WebService遠端存取伺服器上的許可程式,許可後把結果儲存在用戶端電腦上。這個方法的好處是許可驗證代碼儲存在開發人員控制的電腦上,用戶端無法擷取驗證演算法,而且可以通過資料庫系統管理使用者,非常方便。
但是這個方法也有缺點,首先是可靠的Internet串連。如果要防止使用者使用盜版,則必須在用戶端的程式中添加一個隨機訪問遠程同意伺服器驗證的功能,這樣不但需要一個24小時的Internet串連,而且經常進行驗證也會干擾程式的正常運行。還有就是如果有人通過研究用戶端的接收返回資訊的代碼,弄一個虛擬驗證伺服器,這個功能也會完蛋。
那麼,所有的焦點都聚集在用戶端的驗證演算法上,只要這個用戶端的驗證演算法被人弄清楚了,整個程式的許可可以說就不存在了,所以許多開發人員/開發公司費好大的力氣,弄一個足夠複雜的驗證演算法出來,用演算法的複雜度來抵抗破解。但是再複雜的演算法,只要有人寫得出來,就有人能破解得出來,這個道理我想大家都明白。
那是否有密碼編譯演算法與解密演算法不同的辦法呢?有。而且.Net內建的類庫裡面就有這個演算法。
這個演算法的原理是不對稱式加密的原理。不對稱式加密原理大家基本上都瞭解。加密的密碼(密鑰)分為兩個部分,公開金鑰和私密金鑰。通過私密金鑰加密的密文只能通過公開金鑰解密。根據這個特性,我們可以發現只要開發人員儲存好私密金鑰,即使演算法代碼被用戶端破解,因用戶端不知道儲存在開發人員處的私密金鑰,也無法產生註冊碼。
這個演算法就是 System.Security.Cryptography 名稱空間的 RSAPKCS1SignatureFormatter 類(用來產生註冊碼)和 RSAPKCS1SignatureDeformatter 類(用來在用戶端驗證註冊碼)。驗證過程如下:
首先,需要產生一個公開金鑰和私密金鑰對,當然,依靠人是無法產生的,我們可以通過 System.Security.Cryptography 名稱空間的RSACryptoServiceProvider 類來產生公開金鑰/私密金鑰對。 複製 儲存
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { // 公開金鑰 string pubkey = rsa.ToXmlString(false); // 私密金鑰 string prikey = rsa.ToXmlString(true); }
擷取私密金鑰以後,可以用 RSAPKCS1SignatureFormatter 類來產生註冊碼,代碼如下(引用名稱空間略) 複製 儲存
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(prikey); // 加密對象 RSAPKCS1SignatureFormatter f = new RSAPKCS1SignatureFormatter(rsa); f.SetHashAlgorithm("SHA1"); byte[] source = System.Text.ASCIIEncoding.ASCII.GetBytes(txtIn.Text); SHA1Managed sha = new SHA1Managed(); byte[] result = sha.ComputeHash(source); byte[] b = f.CreateSignature(result); msg.Text = Convert.ToBase64String(b); }
上面的代碼是一個樣本aspx頁面的代碼,頁麵包括一個id為msg的Label控制項,一個ID為txtIn的TextBox控制項,一個ID為btnOK的Button控制項,上面的代碼就是btnOK的事件處理常式的內容。大家可以非常清楚的看出處理流程,產生一個RsaCryptoServiceProvider類執行個體,然後把這個類執行個體的加密金鑰指定為包含私密金鑰的prikey字串因為加密解密的公開金鑰/私密金鑰必須是對應的。然後擷取txtIn輸入的內容,產生密鑰後在msg控制項上顯示。
下面是使用 RSAPKCS1SignatureDeformatter 類來驗證輸入: 複製 儲存
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(pubkey); RSAPKCS1SignatureDeformatter f = new RSAPKCS1SignatureDeformatter(rsa); f.SetHashAlgorithm("SHA1"); byte[] key = Convert.FromBase64String(txtKey.Text); SHA1Managed sha = new SHA1Managed(); byte[] name = sha.ComputeHash(ASCIIEncoding.ASCII.GetBytes(txtIn.Text)); if(f.VerifySignature(name,key)) msg.Text = "驗證成功"; else msg.Text = "不成功"; }
上面的代碼也很好理解,就是多了一個ID為txtKey的TextBox控制項,他通過同時擷取使用者名稱/加密金鑰來進行驗證。重點是RSA類的FromXmlString()方法,注意上面的這個方法擷取的是公開金鑰,表示這段驗證代碼是儲存在用戶端的,用戶端代碼是沒有私密金鑰的,即使有人把程式集的代碼反編譯了也沒有用。
上面兩段代碼需要注意的就是產生的公開金鑰/私密金鑰必須匹配,我使用RSA對象產生金鑰組後儲存成為字串常量,就可以解決這個問題。
上面這個方法仍然無法解決客戶使用ildasm反編譯後暴力修改IL代碼,只有靠可靠的強式名稱以及數位憑證來保證程式集不被修改了。