前文 描述了.NET 为 Shareware 程序开发提供了许可证检测机制,使得开发者可以集中精力到如何验证许可证。
把一个信息经由不可信任的途径传达给接受者的时候,就需要进行加密。对于信息接受者来说,则需要确认信息的来源是来自正确的,就需要进行数字签名。Shareware 程序拥有者要告诉远在用户机器上的自己的程序用户有权使用这么个信息,是要加密的。因为你不想让其他人知道这个信息是如何表达的,更不能让人家如法炮制出相同的信息。
.NET 提供了加密和数字签名等现在用在网络安全方面的技术用到 Shareware 上是绝对好的。下面是用 .NET RSACryptoServiceProvider 的一个办法。
RSA 是一种非对称加密技术 Asymmetric Cryptography,即加密用的密码和解密用的密码是分开,相互不能推演的。如果知道了解密用的密码,无法知道加密用的密码。这比对称加密安全性好,因为 Shareware 总是需要一个密码用来解注册码,用对称加密的情况下,如果被人从程序中分析到了解密的密码,有可能复制注册机。而用非对称加密,即使解密的密码泄漏,也无法做出注册机。
1、生成和保存密码
创建个新的 RSACryptoServiceProvider 对象,.NET 就随机生成了密码。
RSACryptoServiceProvider 对象可以把密码输出成 xml。
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
Console.WriteLine(rsa.ToXmlString(true));
这个 xml 是要好好保存起来的秘密。其中包含了用来做注册机的加密用的 private key 和 将来在程序中用来解密的用 public key。
<RSAKeyValue><Modulus>...</Modulus>
<Exponent>...</Exponent>
<P>...</P>
<Q>...</Q>
<DP>...</DP>
<DQ>...</DQ>
<InverseQ>...</InverseQ>
<D>...</D>
</RSAKeyValue>
2、注册机
根据上面的密码 xml,可以这样生成注册机所需要的 RSACryptoServiceProvider 对象。然后用数字签名的办法生成注册码。
SignData 是做数字签名。它与单纯的加密解密有区别。
如果要变成注册码是这样一句话:某人于某时某地购买了我的软件。单纯的加密后的解密你会看到这句原话(从内存里什么地方挖出来)。而用数字签名办法,解密后看不到原话,而是一个单向计算出来的 Hash 值。所以对所传送的信息多了一层保护。RSACryptoServiceProvider 提供了直接的生成数字签名和验证数字签名的方法。
private static byte[]CreateKey(){
byte[] digestInput = fileEncoding.GetBytes(GetRegistrationInfo());
RSACryptoServiceProvider RSAalg= new RSACryptoServiceProvider();
RSAalg.ImportParameters(GetPrivateKeyParam());
return RSAalg.SignData(digestInput, new SHA1CryptoServiceProvider());
}
private static string GetRegistrationInfo()
{
return GetValue("NAME")+GetValue("PURCHASE_TIME")+GetValue("FROM");
}
private static RSAParametersGetPrivateKeyParam()
{
RSAParameters param = new RSAParameters();
param.D= Convert.FromBase64String("...");
param.DP = Convert.FromBase64String("...");
param.DQ = Convert.FromBase64String("...");
param.Q = Convert.FromBase64String("...");
param.P = Convert.FromBase64String("...");
param.Exponent= Convert.FromBase64String("...");
param.Modulus= Convert.FromBase64String("...");
param.InverseQ= Convert.FromBase64String("...");
return param;
}
从 XML 中生成 private key 来制作数字签名,需要用到全部参数:D, DP, DQ, P, Q, Exponent, Modulus, InverseQ.
3、验证数字签名
在 Shareware 程序中,从密码 xml 文件生成验证数字签名所需要的 SACryptoServiceProvider 对象,然后请用户输入他的名字、以及购买软件的时间地点。组成一个句子:某人于某时某地购买了我的软件,用 SACryptoServiceProvider 验证一下它的数字签名是否符合注册机生成注册码。
private static bool VerifyKey(byte[]digestInput, byte[] signed)
{
RSACryptoServiceProviderRSAalg = new RSACryptoServiceProvider();
RSAalg.ImportParameters(GetPublicKeyParam());
return RSAalg.VerifyData(digestInput, new SHA1CryptoServiceProvider(),signed);
}
private static RSAParametersGetPublicKeyParam()
{
RSAParameters param = new RSAParameters();
param.Exponent = Convert.FromBase64String("...");
param.Modulus = Convert.FromBase64String("...");
return param;
}
从 XML 中生成 public key 来验证数字签名,只要用两个参数:Exponent, Modulus. 别人即使从你散发出去的程序本身破解得到了public key,没有其它多大用处。
当然,破解不仅仅是要得到密码。如果来个暴力破解,把你的程序反编译出来,把你的 public key 换成他的,与他自己的注册机配合的,那你不就被抢劫一空了,或者反编译出来删掉前文所述的 LicenseProvdier,或者 .......,还有很多办法。
.NET 提供的许可证监测机制,非对称加密是否能真正发挥,还得看程序本身的加扰程度。下一篇文章介绍。