zoukankan      html  css  js  c++  java
  • 外业记录:在Windows Azure应用程序中使用基于证书的加密

    这是一系列文章中首次集中讨论关于从Windows Azure社区分享“现实世界”技术信息。该文章的作者是Walter Myers III,他是微软咨询服务的资深顾问。

    问题

    我浏览过各种Windows Azure相关的帖子,在这些帖子中开发人员选择了使用加密和解锁数据的对称密钥方案。一个重要的情形是,当开发者需要在SQL Azure中存储加密数据时,它将在一个Windows Azure应用程序中被解密,从而呈现给用户。另一种是数据同步的情形,非云端数据必须与SQL Azure里的数据保持同步,当部署到Windows Azure时这些数据就会被加密。

    开发人员可能会将加密密钥作为一个blob存储在Windows Azure里,只要存储密钥所涉及的Windows Azure存储是安全的,这种方式就是安全的;但是这不是最佳的做法,因为开发人员必须访问对称密钥,这可能在非云端不知不觉地泄露了对称密钥。此外,如果Windows Azure 应用程序被泄露,那么有可能密钥就被泄露了。本文提供了Windows Azure应用程序的基于证书的数据加密和解密的模型和代码。

    解决方案

    首先,让我提供一些背景。使用一个基于证书(不对称的密钥)的方法,最好的做法是遵循用来保护私钥的“关注分离”协议。这样,它将通过Windows Azure应用程序负责任何带有私钥并被作为服务证书上传到Windows Azure Management Portal,以供使用(Windows Azure应用程序可用的服务证书必须被上传到相应的托管服务)。开发人员被提供一个公钥,这个公钥只能在应用程序部署时用在开发机器上。当在开发fabric里测试的时候,开发人员必须使用一个他们利用IIS7通过自我认证而创建的证书。在部署时,他们将做一个简单的替换,用上传到Windows Azure的服务证书替换他们加密/解密代码中的thumbprint,并且利用他们的应用程序来部署服务证书的公钥。

    开发人员必须在他们的应用程序上部署公钥,当Windows Azure调用角色示例,它将使服务定义里的thumbprint与上传的服务证书匹配并将私钥部署到角色示例。私钥是有意设成的不可导出的.pfx格式,所以你不能通过RDC连接到一个角色实例来获取私钥。

    解决方案的实现

    到目前为止,我们讨论了一些理论,现在让我们来看看这些概念具体是怎样表现的。注意此解决方案使用了Visual Studio中为证书管理提供的功能。

    如果你尚未准备好,那就现在开始吧,将公钥证书安装到你的个人证书存储区。使用Local Computer而不是使用Current User存储区,因此你的代码应该与将要部署证书的Windows Azure的代码 一致。注意,为了看见证书,你不能只是启动certmgr.msc,因为它会带你来到Current User存储区。你还要启动mmc.exe 并选择File| Add/Remove Snap-In…菜单选项,添加证书,如下面的截图所示。

    因此你的证书控制台应该和下图类似:

    在部署应用程序之前,先让我们看一看它在Visual Studio 2010所展现的样子,然后看看在Windows Azure角色实例中证书控制台是什么样的。下面的截图来自于我的web角色里的Properties页,并让Certificates标签处在选择的状态。我在该截图上为Certificates添加了高亮文本并将其重新命名为EncryptDecrypt。注意存储单元是LocalMachine,存储名称是My,这就是我们想要的。

    一旦你在这里添加了证书,你马上可以进入到ServiceDefinition.csdef文件,类似于下图。你在ServiceDefinition.csdef里还会发现与thumbprint 一起的一个entry。

    部署应用程序之后,你可以为任意实例建立一个Remote Desktop Connection(RTC)(假设你在发布应用程序时已经配置了RDC)。以上面所示的相同的方式,启动mmc.exe 并为Local Computer 和 Current User添加 Certificates snap-ins。你的RDC窗口应类似于下图。

    请注意证书已经安装到Local Computer个人证书存储区,但是没有一个已安装到Current User个人存储区。这是将上传服务证书到托管服务与配置角色(它是Windows Azure需要在证书存储区安装证书的原因)证书结合到一起。现在,如果你右击certificate并试图导出,就像上面所讨论的那样,你将会看到私钥是不可导出的,这是期望中的事情,如下图所示。

    因此现在我们知道了即将用来加密/解密的证书是如何处理的。下一步,让我们看一看加密/解密的例程。

    public static class X509CertificateHelper
    {
        public static X509Certificate2 LoadCertificate(StoreName storeName, StoreLocation
        storeLocation, string thumbprint)
        {
            // The following code gets the cert from the keystore
            X509Store store = new X509Store(storeName, storeLocation);
            store.Open(OpenFlags.ReadOnly);
            X509Certificate2Collection certCollection =
                     store.Certificates.Find(X509FindType.FindByThumbprint,
                     thumbprint, false);
            X509Certificate2Enumerator enumerator = certCollection.GetEnumerator();
            X509Certificate2 cert = null;
            while (enumerator.MoveNext())
            {
                cert = enumerator.Current;
            }
            return cert;
        }
     
        public static byte[] Encrypt(byte[] plainData, bool fOAEP,
                 X509Certificate2 certificate)
        {
            if (plainData == null)
            {
                throw new ArgumentNullException("plainData");
            }
     
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }
     
            using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
            {
                provider.FromXmlString(GetPublicKey(certificate));
                // We use the public key to encrypt.
                return provider.Encrypt(plainData, fOAEP);
            }
        }
     
        public static byte[] Decrypt(byte[] encryptedData, bool fOAEP,
                 X509Certificate2 certificate)
        {
            if (encryptedData == null)
            {
                throw new ArgumentNullException("encryptedData");
            }
     
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }
     
            using (RSACryptoServiceProvider provider = (RSACryptoServiceProvider)
    certificate.PrivateKey)
            {
                // We use the private key to decrypt.
                return provider.Decrypt(encryptedData, fOAEP);
            }
        }
     
        public static string GetPublicKey(X509Certificate2 certificate)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }
            return certificate.PublicKey.Key.ToXmlString(false);
        }
     
        public static string GetXmlKeyPair(X509Certificate2 certificate)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException("certificate");
            }
     
            if (!certificate.HasPrivateKey)
            {
                throw new ArgumentException("certificate does not have a PK");
            }
            else
            {
                return certificate.PrivateKey.ToXmlString(true);
            }
        }
    }

    注意在上面的加密和解密例程中,我们需要为加密获取公钥但必须获取解密的私钥。这很重要,因为Public Key Infrastructure (PKI)使任何拥有公钥的人执行加密,但只有拥有私钥的人才有权限来解密加密字符串。一个显著的差异是,当我们获取密钥,我们可以将公钥导出到XML,如加密例程中所示的那样,但是我们在解密例程中不能将私钥导出到XML,因为证书是与私钥一起部署的,Windows Azure上私钥设为 non-exportable,这就是之前我们所讲到的。

    让我们来看看我写的一段代码,使用上面的X509 encrypt/decrypt helper class来加密和解密一个字符串:

    string myText = "Encrypt me.";
     
    X509Certificate2 certificate = X509CertificateHelper.LoadCertificate( 
             StoreName.My,
             StoreLocation.LocalMachine,
            "D3E6F7F969546ED620A255794CAB31D8C07E9F31"); 
    if (certificate == null) 
    {
             Response.Write("Certificate is null.");
             return;
    } 
    byte[] encoded = System.Text.UTF8Encoding.UTF8.GetBytes(myText)
    byte[] encrypted; 
    byte[] decrypted;
    try
    {
             encrypted = X509CertificateHelper.Encrypt(encoded, true, certificate); 
    } 
    catch (Exception ee) 
    { 
             Response.Write("Encrypt failed with error: " + ee.Message + "<br>"); 
             return;
    }
    try
    {
             decrypted = X509CertificateHelper.Decrypt(encrypted, true, certificate);
    }
    catch (Exception ed) 
    { 
             Response.Write("Decrypt failed with error: " + ed.Message + "<br>"); 
             return;
    }

    这样,在上面的代码中我装载了我的证书,使用个人存储区放在本地计算机上。X509 encrypt/decrypt class 的LoadCertificate方法的最后一个参数保存了角色的property页上Certificates标签里获取的thumbprint。作为练习,你可以编写一些代码从ServiceConfiguration.cscfg文件中检索此字符串。

    参考文献: http://www.josefcobonnin.com/post/2007/02/20/Encrypting-with-Certificates.aspx

    本文翻译自:http://blogs.msdn.com/b/windowsazure/archive/2011/09/07/field-note-using-certificate-based-encryption-in-windows-azure-applications.aspx

  • 相关阅读:
    DVWA--SQL Injection(SQL注入)
    DVWA--Insecure CAPTCHA(不安全的验证码)
    DVWA--File Upload(文件上传)+中国菜刀下载及使用
    DWVA--File Inclusion(文件包含)
    DVWA--CSRF(跨站请求伪造)
    DVWA--Command Injection(命令行注入)
    2020.08.06【省选B组】模拟 总结
    2020.08.05【省选B组】模拟 总结
    2020.08.04【省选B组】模拟 总结
    jzoj 5251. 【GDOI2018模拟8.11】决战
  • 原文地址:https://www.cnblogs.com/new0801/p/6176602.html
Copyright © 2011-2022 走看看