本文使用RSA非對稱加密和Base64簡單地實現離線注冊碼的生成與驗證功能。
主要思路就是提供者持有密鑰,通過RSA加密客戶機標識或時間標識,再通過Base64加密成不太難看的注冊碼,然后分發給客戶機。
客戶機解Base64后,通過持有的公鑰來驗證注冊碼是否與本機標識或時間標識相符。
一、生成公密鑰
RSACryptoServiceProvider cryptor = new RSACryptoServiceProvider(); File.WriteAllText("PrivateKey.xml", cryptor.ToXmlString(true)); File.WriteAllText("PublicKey.xml", cryptor.ToXmlString(false));
為了方便長期保存這里就直接存入文件了。
為了避免客戶機公鑰丟失,我比較傾向於將公鑰直接編譯到驗證程序中,但是這樣也就意味着如果更換了密鑰,老的驗證程序就驗不了新生成的注冊碼了。
二、生成注冊碼
1 static string CreateRegCode(string mac, DateTime date) 2 { 3 RSACryptoServiceProvider cryptor = new RSACryptoServiceProvider(); 4 cryptor.FromXmlString(File.ReadAllText("PrivateKey.xml")); 5 string signature = String.Format("[{0}][{1}]", mac, date.ToString("yyyy-MM-dd")); 6 byte[] regCodeBytes = cryptor.SignData( 7 Encoding.UTF8.GetBytes(signature), 8 "SHA1"); 9 return Convert.ToBase64String(regCodeBytes); 10 }
這個方法是通過加密MAC和日期的組合來生成注冊碼,需要注意幾點:
1.參數中的MAC是客戶機的地址2.第四行的文件是上一步生成的密鑰文件
3.由於只考慮驗證,所以客戶機還必須知道參數中的date
三、驗證注冊碼
1 static bool Verify(string regCode) 2 { 3 const string PUBLIC_KEY = ""; 4 try 5 { 6 RSACryptoServiceProvider cryptor = new RSACryptoServiceProvider(); 7 cryptor.FromXmlString(PUBLIC_KEY); 8 byte[] signedData = Convert.FromBase64String(regCode); 9 10 bool today = cryptor.VerifyData( 11 Encoding.UTF8.GetBytes(String.Format("[{0}][{0}]", DateTime.Now.ToString("yyyy-MM-dd"))), 12 "SHA1", signedData); 13 bool machineToday = cryptor.VerifyData( 14 Encoding.UTF8.GetBytes(String.Format("[{0}][{1}]", MAC, DateTime.Now.ToString("yyyy-MM-dd"))), 15 "SHA1", signedData); 16 bool forever = cryptor.VerifyData( 17 Encoding.UTF8.GetBytes(String.Format("[{0}][{1}]", MAC, Environment.MachineName)), 18 "SHA1", signedData); 19 return today || machineToday || forever; 20 } 21 catch 22 { 23 return false; 24 } 25 }
這個方法驗證了三種類型的注冊碼:當天可用、本機當天可用和永久可用。需要注意:
1.第三行的公鑰就是第一步的PublicKey.xml中的內容
2.十四和十七行的MAC是客戶機的物理地址,至於怎么獲取不是本文的重點,請各位看官自行百度
3.考慮到客戶機填寫的注冊碼有可能不是合法的Base64文本,需要捕獲解析時異常
其實RSACryptoServiceProvider也提供了解密的方法,這樣就可以驗證更多種類的驗證碼了。