ASP.NET 加密 Part.1(简介)

       .NET 提供了丰富的 CryptoAPI 以满足很大范围内的加密任务。比如,创建不同类型的散列字符串(MD5、SHA1 等)以及实现最重要的对称和非对称加密算法。

       .NET Framework 还提供了单独的功能来保护本地计算机上或以每个用户为基础的秘密数据,这些工作都是通过 WindowsDPAPIData Protection API,数据保护 API)的完全被托管的封装类进行的。

 

 

1. 加密数据:机密性问题

       你知道,可以使用散列码来保护密码。使用散列时,存储的是原数据的数字指纹,而不是数据本身。结果,你没有办法倒转散列过程来获得原来的数据。你所能做的就是对一个新的数据进行散列并对其进行比较

       散列是一个不可逆的过程,用来保护密码是最安全的方案。但当你想保护敏感数据并在随后对其解密时,这个方案就没用了。比如,存储用户信用卡信息时,你的程序需要读取到信用卡的相关信息。

       使用纯文本的方式存储敏感数据并不安全。有时你会认为,既然数据存储在一个安全的服务器端的存储位置,就没有必要做额外的工作来加密它们。但是专家知道事实并非如此。不使用加密,黑客可能仅需花费几分钟甚至几秒钟就可以获得每个用户的密码或者信用卡帐号。不好的管理策略、脆弱的管理员口令或者服务器上其他可利用的软件都会导致安全方面的隐患和漏洞,甚至硬件维护也会导致问题。

 

 

2. .NET 加密命名空间

       在 System.Security.Cryptography 命名空间中,你可以找到程序中用来加密和解密的类。此外,你还能找到所有创建不同类型的散列的基类如果引用额外的 System.Security.dll 程序集,甚至可以访问到更多高级的安全功能,比如一个用来修改 Windiws ACL 的 API(System.Security.AccessControl 命名空间)、DPAPI 和用来创建密钥散列信息验证代码的类(HMAC)。

 

System.Security.Cryptography 命名空间中与安全相关的类的分类

加密算法 包含最重要的散列和加密算法以及用来创建数字签名的类
辅助类 这些辅助类可以创建真实加密的随机数,它们和底层的 Windows 加密系统(CryptoAPI)交
X509 证书 在 System.Security.Cryptography.X509Certificates 命名空间中,包含所有使用 X509 证书必需的类和用于访问 Windows 证书存储的类
XML 签名和加密 在 System.Security.Cryptography.Xml 命名空间中包含对 XML 签名和加密标准的类。它们按 W3C 发布的标准对 XML 文档进行加密和签名
CMS/PKCS#7 这个框架具备直接创建 CMS/PKCS 包装的信息的托管支持,而无需使用非托管调用(CMS,Crtptographic Message Syntax)(PKCS,Public-Key Crtptography Standard)

 

       在 Web 世界中,X509 证书扮演着非常重要的角色。它们建立 SSL 通信并执行证书验证以保证 Web 服务器和客户端之间的通信安全。X509 证书是一个二进制标准,用来将非对称加密算法的密钥和一个已发布了证书的特殊组织(证书授权)的签名封装在一起。

       如果是简单的 SSL 连接,你不需要访问证书存储。但如果你想在另一台机器上的代码中调用 Web 服务或者 Web 程序,你需要使用一个 X509 证书进行验证。你的程序需要从 Windows 证书存储中读取证书,然后在实际发送请求之前将这个证书添加到 Web 请求(或 Web 服务代理)中。

       System.Security.Cryptography.X509Certificates 命名空间包括如下几个可用的类:

  • X509Certificate 和 X509Certificate2:这些类封装 X509 证书。让你能够从各种存储(如文件系统)中加载证书并访问证书的属性。前者是一个版本的 .NET Framework 就开始提供的原始类;后者是对前者的扩展
  • X509Store:让你能访问 Windows 证书存储。对于每个用户来说,Windows 都创建一个这样的存储(通过 StoreLocation.CurrentUser 访问);而对于机器,只创建一个存储(通过 StoreLocation.LocalMachine 访问)。用户存储只对创建的用户可访问,而机器存储保存的证书则对机器上所有客户可用
  • X509CertificateCollection:这是一个表示 X509Certificate 和 X509Certificate2 实例(它们表示单个证书)的集合的简单类。让你能够取到一组证书或者根据一个唯一(比如证书的主题键、主题名或散列)的标识取得一个证书

 

       例如,象下面这样从存储中读取证书信息并将其赋给一个 Web 请求:

X509Certificate2 certificate = null;
 
// Read the certificate from the store
// X509Store: 表示 X.509 存储区,该存储区是保留和管理证书的物理存储区
// StoreName: 指定要打开的 X.509 证书存储区的名称
// StoreLocation: 指定 X.509 证书存储区的位置
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
try
{
    // Try to find the certificate based on this common name
    X509Certificate2Collection results = store.Certificates.Find(
        X509FindType.FindBySubjectDistinguishedName, "CN=Mario, CN=Szpuszta", false);
    if (results.Count == 0)
    {
        throw new Exception("Unable to find certicate!");
    }
    else
    {
        certificate = results[0];
    }
}
finally
{
    store.Close();
}

       这段代码使用 X509Store 类打开本地机器的个人证书存储,然后在存储中查找主题名为 "CN=Mario, CN=Szpuszta" 的证书。此处用的语法是一般的命名语法,你也可以从 LDAP 目录系统了解到。

 

       Windows 提供了好几种类型的证书存储,称作 存储位置。比如,本地机器的存储对于所有运行于本机上且拥有适当权限的程序都是可以访问的。你可以为每一个 Windows 服务创建单独的存储,并且每一个用户都有一个单独的证书存储,证书在这些存储中被安全地保存。

       当本地机器的存储使用本地安全授权机构管理的密钥进行加密时,用户存储会使用存储在用户配置文件中的密钥进行加密。在一个存储位置中,Windows 为了不同的目的而使用不同的存储。最重要的存储是 Personal("my") 存储和 Trusted Root Certification Authorities

       通常,"my" 存储包含了程序所使用的所有证书(如果是一个用户存储,则包含被用户所使用的证书)。Trusted Root Certification Authorities 存储则包含发型证书的授权机构的证书。VeriSign 是一个著名的授权机构的例子,从它那你可以购买证书。如果你在 Trusted Root Certification Authorities 存储中放置了一个证书,就表明任何由这个机构发布的证书都是可信的,因此证书就可以被任何程序放心的使用。默认情况下,其他证书都是不被信任的,因此被做了一个特殊的标记。

       在 ASP.NET Web 应用程序中,你需要使用一个本地机器存储或者一个服务账号的存储。因此,前面介绍过的代码通过 StoreLocation.LocalMachine 标志打开数据库。这个选项的第二个可选标志就是 StoreLocation.CurrentUser,它会打开一个当前用户或者服务账户的存储。因为这个证书是一个“用法”证书,所以你需要从个人存储中读取它。

       你可以打开一个微软的管理控制台来查看存储的证书,然后添加证书管理单元。启动管理控制台(mmc.exe)-> 文件 -> 增加/删除管理单元。对话框中选择证书,并添加至管理单元列表中。选中的账户会显示所有存储和在该存储中保存的所有证书。

image

       你可以通过 makecert.exe 命令创建测试证书。例如,在本地机器上的个人存储中创建一个证书:

makecert.exe -ss my -sr LocalMachine -n "CN=Mario, CN=Szpuszta"

       一旦有了存储中的证书,当通过 SSL 将请求发送到一个需要证书验证的服务器时,就可以象下面的例子这样使用证书:

// Now Create the web request
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ClientCertificates.Add(certificate);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

       这段代码有意义的情形包括:应用程序需要从其他 Web 应用程序获取数据或使用 HTTP GET/POST 请求向其他 Web 应用程序发送数据而其他应用程序需要通过证书进行验证。

 

       另外一个关于安全的用例是用来创建强随机数的类。当你想存储加盐值(Salted)的密码散列时,这个类对于创建随机密钥值或者盐值(Salt)是非常重要的。一个加盐值的密码散列是从一个密码和一个所谓的盐值创建而来的。盐值是一个随机值。这样可以保证即使两个用户选择了相同的密码,在后台存储中的结果看起来也是不同的,因为随机的盐值和密码一起进行了散列。

       下面这个例子来显示如何使用 System.Security.Cryptography.RandomNumberGenerator 类创建随机数字:

byte[] randomValue = new byte[16];
RandomNumberGenerator rndGen = RandomNumberGenerator.Create();
rndGen.GetBytes(randomValue);
txtBase64.Text = Convert.ToBase64String(randomValue);

 

 

3. 理解 .NET 加密类

       在使用 .NET 执行加密之前,需要理解更多关于底层信息的知识。.NET 加密类分为 3 层。

       第一层,是一套抽象的基类,用于完成加密任务:

  • AsymmetricAlgorithm:提供非对称的加密算法,使用一个公钥/私钥对。使用其中一个密钥进行加密的数据只可以使用另外一个密钥解密。
  • SymmetricAlgorithm:提供对称加密算法,使用一个共享的密码值进行加密。使用密钥加密的值只能使用相同的密钥解密。
  • HashAlgorithm:提供散列的生成算法以及验证机制。散列也称为单向加密算法。即只能加密,不能解密。这可以保证数据不会被篡改。

       第二层,包括代表一个指定的加密算法的类。它们从加密基类中继承而来,但它们仍是抽象的类。比如,DES 算法类派生自 SymmetricAlgorithm,代表 DESData Encryption Syandard,数据加密标准)算法。

       第三层,一系列加密的实现类。每一个实现的类都从某个加密算法类中继承。这意味着一个指定的加密算法(比如 DES)可以有多个实现类。

 

      一些 .NET Framework 加密类完全是在托管代码中实现的,而大部分实际上是 CryptoAPI 库的若干薄封装。封装 CryptoAPI 功能的类在它们的名字中都含有 CryptoServiceProvider(比如,DESCryptoServiceProvider);而托管类在名字中通常含有 Managed(比如 RijndaelManaged)。

       基本上,托管类在 CLR 监控下执行工作,而非托管类则调用非托管 CryptoAPI 库。看似像是一个限制,但实际上是对现有技术的有效重用。

       CryptoAPI 的技术无可挑剔,只是它的编程接口较难使用。下图显示了 System.Security.Cryptography 命名空间中的类:

       image   image 

 

       image 

       这种 3 层的组织几乎可以不受限制地扩展。你可以通过继承一个现存的算法类创建一种新的实现。

 

 

4. 对称加密算法

       对称加密算法通常使用同样的密钥进行加密和解密,加密和解密的速度比较快。

.NET 支持的对称算法

抽象算法

默认实现

有效密钥长度

最大密钥长度

DES DESCryptoServiceProvider 64 64
TripleDES TripleDESCryptoServiceProvider 128、192 192
RC2 RC2CryptoServiceProvider 40 - 128 128
Rijndael RijndaelManaged 128、192、256 256

       加密的强度和密钥的长度有关。密钥越长,攻击者实施攻击就越困难,因为有太多的密钥值需要测试。当然,较长的对称加密密钥会导致信息增多,加密时间变长。对大多应用程序来讲,一个比较标准的选择是 Rijndael,它提供了一个可靠的性能,同时也支持长的密钥。

       对称加密算法最大的好处是它的性能,而对于安全系统来讲是不够的。另外,如果在两个不同部分的应用程序之间使用对称加密算法进行数据交换,你必须使用一种安全的方式交换密钥。

 

 

5. 非对称加密

       非对称加密基于数学方法,加密和解密需要不同的密钥。通常把用来加密的密钥称为公钥,你可以将公钥发给任何想传递加密信息给你的人。另一方面,私钥是唯一可以用来解密数据的密钥。因此,如果你是唯一可以访问私钥的人,你就是唯一可以解密信息的人。

 

.NET 支持的非对称算法

抽象算法

默认实现

有效密钥长度

默认密钥长度

RSA RSACryptoServiceProvider 384 - 16384 (8 位增量) 1024
DSA DSACryptoServiceProvider 512 - 1024 (64 位增量) 1024

       RSA(其名字来自该算法的发明者 Ron Rivest、Adi Shamir 和 Leonard Adleman)支持直接的加密和解密。

       DSA(Digital Signature Algorithm,数字签名算法)只能用于对信息进行签名以及确认签名。

       非对称加密算法比加密算法更慢(看你要加密的数据的大小),如果需要在很多请求中交换数据,它会影响系统性能。因此,像 SSL 这样的技术,在开始建立连接会话时使用非对称加密算法。客户端和服务器之间的传输通过非对称加密算法进行安全保证(客户端使用公钥加密而服务器端使用私钥解密)。之后,两端就可以安全的传递一个对称的密钥了。这样这个对称密钥就可以通过对称加密来保证后续的通信。

       这将对称加密和非对称加密的优点都结合在一起了。当然,你须要找一个安全的地方来存放私钥,保证未经授权的人没有机会对它进行访问。

 

 

6. 抽象加密类

       抽象加密类通常有两个目的。首先,它们定义了加密实现类需要支持的基本成员。其次,它们通过静态的 Create() 方法提供了一些功能,这样你可以用来间接创建一个类实例。通过这种方式,你可以创建某个具体的实现类而无需知道它是如何实现的。

       比如,看一下下面的代码:

DES cryp = DES.Create();

       静态方法返回了一个 DES 实现类的一个实例。这种情况下,这个类就是 DESCryptoServiceProvider(默认实现)。这种技术的优点在于你可以编写同样的代码,而不需要创建一个指定实现的依赖。此外,如果微软更新了框架,而且默认的 DES 实现类也发生了改变,你的程序可以无缝地继承这些改动。如果你正在使用 CryptoAPI 类,这特别有用,因为它可以在将来用一个等价的托管类进行替换。

       实际上,甚至还可以在一个更高的层次上工作。比如,考虑下面的代码:

SymmetricAlgorithm crypt = SymmetricAlgorithm.Create();

       无论什么类型的加密算法被定义为默认的对称加密算法,这段代码都会创建一个实例。在这种情况下,它不是 DES,而是 Rijndael。

 

 

7. ICryptoTransform 接口

       .NET 使用一个基于流的架构来实现加密和解密,通过这种方式可以非常容易地加密或者解密来自不同类型的数据。这种架构可以非常容易地实时实现连续的多重加密操作,而和加密算法的底层细节无关。

       为了理解这是如何工作的,需要考虑最核心的类型:ICryptoTransform 接口和 CryptoStream 类。

       ICryptoTransform 接口实现了基于块的加密转换。这可能是加密、解密、散列、Base64 编码和解码或者格式化操作。为一个指定的算法创建一个 ICryptoTransform 对象,你可以在加密算法类实例中使用 CreateEncryptor() 和 CreateDecryptor()。

       下面的代码段创建 DES 加密算法加密的 ICryptoTransform 接口:

DES crypt = DES.Create();
ICryptoTransform transform = crypt.CreateEncryptor();

       各种加密任务都以同样的方式执行,即使实际执行转换的加密函数可能会不同。每一个加密操作都需要数据在处理之前被分为固定大小的块。你可以直接使用一个

ICryptoTransform 实例,但在大多数情况下,你可以选择一个更简单的方式:将它传递给另外一个类:CryptoStream。

 

 

8. CryptoStream 类

       CryptoStream 包装了一个普通流,并使用 ICryptoTransform 在后台执行它的工作。最重要的优点是 CryptoStream 使用缓冲访问,因此你可以执行自动加密而不用担心加密算法所要求的块的大小。另一个优点是它可以封装普通的继承于 .NET 流的类,所以它可以非常容易的在其他操作之上使用。比如文件访问(通过 FileStream)、内存访问(通过 MemoryStream)、低层的网络调用(通过 NetworkStream)等等。

       要创建 CryptoStream,需要 3 个信息:底层的数据流、模式(读或写)、ICryptoTransform。例如下面代码段使用 DES 算法实现类创建 ICryptoTransform,然后使用它和一个现存的数据流创建 CryptoStream:

DES crypt = DES.Create();
ICryptoTransform transform = crypt.CreateEncryptor();
CryptoStream cs = new CryptoStream(fileStream, transform, CryptoStreamMode.Write);

posted on 2013-04-24 17:03  SkySoot  阅读(594)  评论(0编辑  收藏  举报

导航