对于 Web 应用程序,我想创建一个简单但有效的许可系统。在 C# 中,这有点困难,因为任何安装了 Reflector 的人都可以查看我的解密方法。
在 C# 中加密文件有哪些相当防篡改的方法?
对于 Web 应用程序,我想创建一个简单但有效的许可系统。在 C# 中,这有点困难,因为任何安装了 Reflector 的人都可以查看我的解密方法。
在 C# 中加密文件有哪些相当防篡改的方法?
听起来您想使用公共/私有密码术来签署许可证令牌(例如 XML 片段或文件),以便检测篡改。处理它的最简单方法是执行以下步骤:
1) 为您的公司生成密钥对。您可以使用 SN 工具在 Visual Studio 命令行中执行此操作。语法是:
sn -k c:\keypair.snk
2) 使用密钥对来强命名(即签名)您的客户端应用程序。您可以使用应用程序属性页面中的签名选项卡进行设置
3) 为您的客户创建一个许可证,这应该是一个 XML 文档并使用您的私钥对其进行签名。这涉及简单地计算数字签名和完成它的步骤可以在以下位置找到:
http://msdn.microsoft.com/en-us/library/ms229745.aspx
4) 在客户端,在检查许可证时,您加载 XmlDocument 并使用您的公钥验证签名,以证明许可证未被篡改。有关如何执行此操作的详细信息,请访问:
http://msdn.microsoft.com/en-us/library/ms229950.aspx
要绕过密钥分发(即确保您的客户端使用正确的公钥),您实际上可以从签名程序集本身中提取公钥。因此确保您没有另一个密钥可以分发,即使有人篡改程序集,.net 框架也会因安全异常而死,因为强名称将不再与程序集本身匹配。
要从客户端程序集中提取公钥,您需要使用类似于以下的代码:
/// <summary>
/// Retrieves an RSA public key from a signed assembly
/// </summary>
/// <param name="assembly">Signed assembly to retrieve the key from</param>
/// <returns>RSA Crypto Service Provider initialised with the key from the assembly</returns>
public static RSACryptoServiceProvider GetPublicKeyFromAssembly(Assembly assembly)
{
if (assembly == null)
throw new ArgumentNullException("assembly", "Assembly may not be null");
byte[] pubkey = assembly.GetName().GetPublicKey();
if (pubkey.Length == 0)
throw new ArgumentException("No public key in assembly.");
RSAParameters rsaParams = EncryptionUtils.GetRSAParameters(pubkey);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParams);
return rsa;
}
我在 Snipt 上上传了一个示例类,其中包含许多有用的加密实用程序:http://snipt.net/Wolfwyrd/encryption-utilities/以帮助您上路。
我还在以下位置包含了一个示例程序:https ://snipt.net/Wolfwyrd/sign-and-verify-example/ 。该示例要求您将其添加到具有加密实用程序库的解决方案中,并提供一个测试 XML 文件和一个用于签名的 SNK 文件。该项目应设置为使用您生成的 SNK 进行签名。它演示了如何使用来自 SNK 的私钥对测试 XML 文件进行签名,然后通过程序集上的公钥进行验证。
更新
添加了最新的博客文章,其中包含许可证文件的详细介绍
使用签名的 XML 文件。使用密钥对的私钥部分对其进行签名,并使用软件中的公钥部分对其进行检查。这使您有机会检查许可证是否已被更改,并检查许可证文件是否有效。
MSDN 中记录了对已签名 XML 文件的签名和检查。
您在自己的公司签署许可证文件并将许可证文件发送给客户,然后客户将许可证文件放在一个文件夹中供您阅读,这当然是合乎逻辑的。
当然,人们可以剪切/破解您的分布式程序集并撕掉 xml 符号检查,但话又说回来,无论您做什么,他们总是能够这样做。
Wolfwyrd 的回答非常好,但我只是想提供一个更简单的 WolfwyrdGetPublicKeyFromAssembly
方法版本(它使用了 Wolfwyrd 慷慨提供的库中的许多辅助方法)。
在这个版本中,这是您需要的所有代码:
/// <summary>
/// Extracts an RSA public key from a signed assembly
/// </summary>
/// <param name="assembly">Signed assembly to extract the key from</param>
/// <returns>RSA Crypto Service Provider initialised with the public key from the assembly</returns>
private RSACryptoServiceProvider GetPublicKeyFromAssembly(Assembly assembly)
{
// Extract public key - note that public key stored in assembly has an extra 3 headers
// (12 bytes) at the front that are not part of a CAPI public key blob, so they must be removed
byte[] rawPublicKeyData = assembly.GetName().GetPublicKey();
int extraHeadersLen = 12;
int bytesToRead = rawPublicKeyData.Length - extraHeadersLen;
byte[] publicKeyData = new byte[bytesToRead];
Buffer.BlockCopy(rawPublicKeyData, extraHeadersLen, publicKeyData, 0, bytesToRead);
RSACryptoServiceProvider publicKey = new RSACryptoServiceProvider();
publicKey.ImportCspBlob(publicKeyData);
return publicKey;
}
为什么需要加密它?如果您害怕它被篡改(例如有人增加了用户数量),您是否可以只使用例如您组织的数字证书对其进行签名?
为许可提供某种安全性的唯一方法是针对您自己持有的凭据强制在线登录(实际上是抽象的口语)。
所有其他方法都需要更多的时间,因此需要更多的钱来实施,以破解和滥用您的软件,而不是购买许可证。
.NET 有一些很好的加密类,但正如您所提到的,如果您正在编写许可证的编码/解密,每个人都可以轻松地对其进行反编译。
似乎 MSDN 示例使用默认参数,这会导致特定于机器的签名,因此您无法在一台机器上签名并在任意其他机器上进行验证。我修改了这些示例中的逻辑,以使用签名机上我的 snk 中的密钥和验证机中我的程序集中的密钥。这是使用 MSDN 示例中的逻辑和上面链接的(非常出色的)ExcryptionUtils.cs 示例中的例程完成的。
为了签署文件,我使用了这个:
RSACryptoServiceProvider rsaKey = EncryptionUtils.GetRSAFromSnkFile(keyFilePath);
为了验证文件,我使用了这个:
RSACryptoServiceProvider rsaKey = EncryptionUtils.GetPublicKeyFromAssembly
(System.Reflection.Assembly.GetExecutingAssembly());
顺便说一句:我观察到 XML 签名验证会忽略 XML 注释。
或者您可能希望将许可证绑定到特定机器,例如通过根据 PC 的 c 驱动器的 md5 值生成许可证密钥。那么您的许可证将是特定于机器的。
当你实现你的签名/验证码时,注意不要把它放在一个单独的程序集中。代码访问安全防篡改现在很容易绕过,正如文章CAS 防篡改被破坏:软件许可的后果 中所解释的那样。
强制性免责声明和插件:我共同创立的公司生产OffByZero Cobalt 许可解决方案。
RE:不要在代码中包含整个密钥。使用 sn -p 将公共部分提取到文件中。在您分发的代码中使用它来验证许可证。
使用 MSDN 文章中的代码,我创建了一个小应用程序 (LicMaker) 来实现这一点。该应用程序使用完整的密钥对进行签名。输入是一个未签名的 .XML 许可证文件。输出是签名的 .LIC 文件中的原始 XML + 签名。我的产品也由相同的完整密钥对文件签名。产品验证 .LIC 文件未被篡改。效果很好。我没有使用 sn -p 这个解决方案。