8

我想利用某种形式的“简单”加密,这种加密相当安全,但在对开发过程的影响方面摩擦非常小。

假设我在客户端 <> Web 服务情况下拥有对话的双方。我的应用程序是 windows phone/win8/silverlight/desktop 应用程序,服务器是 ASP.Net MVC 或 WebAPI。

在我看来,我想要一些简单的东西: -

<security encryption="off|sometype|someothertype">
    <privatekey>12345MyKey54321</privatekey>
</security>

作为客户端和服务器上的某种形式的配置参数。此外,身份验证例程将返回并存储某种形式的公钥。

这样做将启用“加密模式”,并使用提供的密钥以选定的方式对任何 http 请求进行加密和散列。最终结果是在本地、代理或远程机器上嗅探到的任何东西都无法在没有密钥和解密方法的情况下查看数据。在服务器上,数据在执行控制器操作之前使用相同的密钥进行解密。

除了将 HttpRequest/WebClient 调用换成 EncryptedHttpRequest 之类的调用并在 MVC/WebAPI 方面添加适当的钩子之外,所有其他客户端代码和控制器操作都将不知道数据已加密的事实。

我是否遗漏了什么或者设置不是这么简单?据我搜索,没有什么能提供这种简单程度的东西,所以我认为我的逻辑中遗漏了一些巨大的缺陷?

4

2 回答 2

19

您所寻找的一切都可以通过简单地使用 HTTPS 来实现。只需购买证书(或使用自签名证书),就可以进行加密。

不要重新发明轮子。

于 2012-08-16T11:23:49.930 回答
8

我已经成功地做到了。这不是太难并且效果很好。我用它来激活产品的许可证。最重要的是您真正控制客户端和服务器 - 没有人可以从客户端上的代码中提取您的私钥。

第 1 步:创建一个不带参数的 MVC 控制器操作方法:

[HttpPost]        public ActionResult Activate()        { ... }

第 2 步:在控制器中,只需使用 HttpRequest.InputStream 来获取从客户端发送的字节。

var 流 = this.HttpContext.Request.InputStream;

第 3 步:创建 CryptoStream 以进行反序列化。

我在这里包含了创建加密和解密示例。sharedSecret 是一个足够长(512 字节)的随机字节的 byte[] - 这就是您要保护的!

public CryptoStream CreateEncryptionStream(Stream writeStream)
{            
    TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();               
    PasswordDeriveBytes derivedBytes = new PasswordDeriveBytes(this._sharedSecret, null);                
    CryptoStream cryptoStream = new CryptoStream(writeStream, cryptoProvider.CreateEncryptor(derivedBytes.GetBytes(16), derivedBytes.GetBytes(16)), CryptoStreamMode.Write);            
    return cryptoStream;        
}        

public CryptoStream CreateDecryptionStream(Stream readStream)        
{            
    TripleDESCryptoServiceProvider cryptoProvider = new TripleDESCryptoServiceProvider();            
    PasswordDeriveBytes derivedBytes = new PasswordDeriveBytes(this._sharedSecret, null);                
    CryptoStream cryptoStream = new CryptoStream(readStream, cryptoProvider.CreateDecryptor(derivedBytes.GetBytes(16), derivedBytes.GetBytes(16)), CryptoStreamMode.Read);            
    return cryptoStream;        
}

第 4 步:使用您的 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;        
}

第 6 步:实施您的客户请求响应。

您可以使用 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 混淆器)
于 2012-08-16T17:43:49.523 回答