我已经成功地完成了这个任务。它并不太难,而且效果很好。我用它来激活产品的许可证。最重要的是,您真正控制客户端和服务器,没有人能从客户端代码中提取您的私钥。
步骤1:创建一个MVC控制器动作方法,该方法不带参数:
[HttpPost] public ActionResult Activate() { ... }
步骤2:在控制器中,只需使用HttpRequest.InputStream获取来自客户端发送的字节即可。
var stream = this.HttpContext.Request.InputStream;
步骤3:创建CryptoStream进行反序列化。
我在这里提供了加密和解密示例。sharedSecret是一个足够长(512个字节)的随机字节的byte[] - 这就是您要保护的内容!
public CryptoStream CreateEncryptionStream(Stream writeStream)
public CryptoStream CreateDecryptionStream(Stream readStream)
步骤四:使用CryptoStream和另一个流读取器进行解密。
我使用XmlReader,以便我的现有序列化代码可以在明文(读写磁盘或服务器上的数据库时)或加密(传输时)的情况下工作。
using (var reader = XmlReader.Create(decryptionStream, settings)) { ... }
步骤5:在您的控制器中制定一个安全响应。
这是对步骤1-4的反向操作,用于加密您的响应对象。然后,您只需将加密后的响应写入内存流,并将其作为文件结果返回。下面,我展示了如何为我的许可证响应对象执行此操作。
var responseBytes = GetLicenseResponseBytes(licenseResponse);
return File(responseBytes, "application/octet-stream");
private byte[] GetLicenseResponseBytes(LicenseResponse licenseResponse)
{
if (licenseResponse != null)
{
using (MemoryStream memoryStream = new MemoryStream())
{
this._licenseResponseSerializer.Write(memoryStream, licenseResponse);
return memoryStream.ToArray();
}
}
return null;
}
第六步:实现客户端请求响应。
您可以使用 HttpWebRequest 或 WebClient 类来构建请求。以下是我使用的代码中的一些示例。
byte[] postBytes = GetLicenseRequestBytes(licenseRequest);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(licenseServerUrl);
request.Method = "POST";
request.ContentType = "application/octet-stream";
request.Proxy = WebRequest.DefaultWebProxy;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(postBytes, 0, postBytes.Length);
}
return request;
private LicenseResponse ProcessHttpResponse(HttpWebResponse response)
{
if ((response.StatusCode == HttpStatusCode.OK) && response.ContentType.Contains("application/octet-stream"))
{
var stream = response.GetResponseStream();
if (stream != null)
{
var licenseResponse = this._licenseResponseSerializer.Read(stream);
return licenseResponse;
}
}
return new LicenseResponse(LicensingResult.Error);
}
总结和建议
- 在客户端和服务器中使用请求/响应流来通信二进制八位组数据
- 使用CryptoStream以及一个加密算法(考虑使用最强的加密方式)和一个好的私钥,在序列化/反序列化数据时进行加密。
- 确保检查所有传入数据的大小和格式(避免缓冲区溢出并尽早抛出异常)。
- 如果可能,使用混淆来保护客户端上的私钥(可以查看DeepSea混淆器)。