網路上大多數共享軟體的註冊碼(序列號)設計往往存在漏洞,容易被破解者編寫註冊機大量生成有效碼。本文介紹一種基於 RSA 非對稱加密演算法的註冊碼生成與驗證方案,若無私鑰幾乎無法構建註冊機,部分商業軟體已採用此類技術保護授權體系。
RSA 使用一組數學關聯的金鑰:公鑰與私鑰。由公鑰推導私鑰(或反向)在計算上不可行,其安全性建立在大整數分解的困難度之上。利用此特性設計註冊流程如下:
(E, N)、私鑰 (D, N),其中 N 為模數。M(經編碼為大整數)以私鑰 D 加密,得到密文 C 作為註冊碼。C = (M ^ D) mod NM 及註冊碼(經解碼還原為密文 C)。E 解密:M' = (C ^ E) mod N。M' == M 則註冊碼合法,否則拒絕。破解者可從程式碼中提取公鑰 E 與模數 N,但無法逆推私鑰 D,故無法偽造任意使用者的有效註冊碼。
模數 N 過短易被分解。以當前運算能力,建議 N ≥ 512-bit(現代建議 2048-bit 以上)。此舉會增加註冊碼長度,需權衡使用者輸入便利性。實作時必須引入大數運算庫(如 Crypto++、MIRACL、GMP、OpenSSL 等)。
生成大質數 P、Q 時,必須使用密碼學級亂數產生器(CSPRNG),否則即使模數夠長,若亂數可預測,N 仍可能被分解。
若改由公鑰加密、私鑰解密(即註冊機用公鑰加密,軟體用私鑰解密),則公鑰 E 不可 使用常見固定值(如 3、65537)。一旦 E 被猜測,破解者即可從軟體中提取私鑰 D 並編寫註冊機。
攻擊者仍可修改程式二進位碼(如將 JNE 改為 JE)繞過驗證。進階防護:以註冊碼中的部分資料加密關鍵程式碼段或資料,執行時解密,若註冊碼錯誤則解密失敗導致程式崩潰。
將驗證邏輯中的「使用者名稱」替換為機器唯一識別碼(CPU ID、主機板序號、MAC 位址組合等),實現「一機一碼」。注意:硬體資訊必須難以偽造且穩定,否則正版使用者換機器會失效。
採用上述方案後,擁有至少一組合法(姓名、註冊碼)配對的攻擊者,理論上可嘗試離線暴力破解私鑰(視模數長度而定),或直接散發該組配對。結合硬體綁定可緩解此問題。
以下示範 64-bit RSA(僅供教學,實務請用 ≥2048-bit):
大質數 P = A57F2B33
大質數 Q = E7C441B3
模數 N = 95D49FD119EF27A9
私鑰 D = 76D2A6E2AC86CC99
公鑰 E = 65537
#ifndef WIN32
#define WIN32
#endif
#include "lip.h" // Freelip 標頭
// 專案需加入 lip.c
char UserName[] = "4E6574677579"; // "Netguy" 的 ASCII 轉十六進位
char SerialNumber[256];
verylong N = 0, D = 0, M = 0, C = 0;
zhsread(UserName, &M); // M = 使用者名稱十六進位值
zhsread("95D49FD119EF27A9", &N); // 載入模數
zhsread("76D2A6E2AC86CC99", &D); // 載入私鑰
zexpmod(M, D, N, &C); // C = (M^D) mod N
zswrite(SerialNumber, C); // 輸出十進位字串作為註冊碼
char UserNameString[] = "4E6574677579"; // 使用者輸入姓名
char SerialNumber[] = "1876542098762625173846272838"; // 使用者輸入註冊碼
verylong N = 0, E = 0, C = 0, UserName = 0, DecryptedUserName = 0;
zhsread(SerialNumber, &C); // 還原密文
zhsread("95D49FD119EF27A9", &N); // 模數
zsread("65537", &E); // 公鑰
zexpmod(C, E, N, &DecryptedUserName); // M' = (C^E) mod N
zhsread(UserNameString, &UserName); // 輸入姓名轉大數
if (zcompare(UserName, DecryptedUserName) != 0) {
// 註冊碼錯誤
} else {
// 註冊碼正確
}
結語:RSA 非對稱機制大幅提升註冊碼抗逆向難度,配合硬體綁定、關鍵碼加密、反除錯技術,可構建相對完善的共享軟體授權體系。但請記住:沒有絕對無法破解的客戶端保護,目標是提高攻擊成本超過軟體價值。