.NET安全系列之五:数据保护API

从Windows 2000开始,Windows操作系统提供了一套密码学方面的API,称为DPAPI(Data Protection API,数据保护API)。系统中的这套API由crypt32.dll实现,它使用当前用户的登录用户名/密码对来管理密钥。它可以用于标识一个进程、Windows会话或目前使用的机器,从而在用户、进程、会话或机器级别上加密数据从而确保其机密性。同时使用DPAPI可以使我们不用再负责密钥的管理。

深入一些,这套API还可以管理对用户密码的修改。例如,如果为某个特定用户将数据加密保存后,即使该用户的密钥发生了变化,仍然可以使用那些数据。这项功能依靠保存过期密钥这一机制实现的。

 

使用System.Security.Cryptography.ProtectedData类

下面的例子演示了使用System.Security.Cryptography.ProtectedData类在用户级别保护数据。

using System.Security.Cryptography; 
class Program{ 
    static void Main() { 
        string sMsg = "The message to encrypt!"; 
        string sEnc, sDec; 
        System.Text.Encoding utf = new System.Text.UTF8Encoding(); 
        byte[] entropy = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; 
        { 
            byte[] bMsg = utf.GetBytes(sMsg); 
            byte[] bEnc = ProtectedData.Protect(bMsg , entropy , DataProtectionScope.CurrentUser); 
            sEnc = System.Convert.ToBase64String(bEnc); 
        } 
        { 
            byte[] bEnc = System.Convert.FromBase64String(sEnc); 
            byte[] bDec = ProtectedData.Unprotect(bEnc, entropy, DataProtectionScope.CurrentUser); 
            sDec = utf.GetString(bDec); 
        } 
        System.Console.WriteLine("Message : " + sMsg); 
        System.Console.WriteLine("Encrypted: " + sEnc); 
        System.Console.WriteLine("Decrypted: " + sDec); 
    } 
} 

​可以通过设置DataProtectionScope.LocalMachine值来实现在机器级别保护该数据。本例中为加密添加,这意味着,即使进程执行的上下文环境符合要求(即处在正确的用户或机器下),如果不知道加密使用的熵也是无法解密数据。即可以把熵值看成某种形式的辅助密钥。

 

使用System.Security.Cryptography.ProtectedMemory类

使用System.Security.Cryptography.ProtectedMemory类可以获得比ProtectedData类更加精确的保护数据的程度。枚举量MemoryProtectionScope提供了以下选项:

SameProcess: 表示只有处在同一进程中的代码才能将进过加密的数据解密。

SameLogon: 表示只有处在同一用户上下文的代码才能将经过加密的数据解密。

CrossProcess: 表示只要在加密完成后操作系统没有重启,那么在任何进程中执行的任何代码都能将经过加密的数据解密。

下面的示例演示了此类的使用。注意:被加密的数据的字节数必须是16的倍数。

using System.Security.Cryptography; 
class Program { 
    static void Main() { 
        string sMsg = "01234567890123456789012345678901"; 
        System.Text.Encoding utf = new System.Text.UTF8Encoding(); 
        System.Console.WriteLine("Message : " + sMsg); 
        byte[] bMsg = utf.GetBytes(sMsg); 
        ProtectedMemory.Protect( bMsg, MemoryProtectionScope.SameProcess ); 
        System.Console.WriteLine("Encrypted: " + utf.GetString(bMsg)); 
        ProtectedMemory.Unprotect( bMsg, MemoryProtectionScope.SameProcess ); 
        System.Console.WriteLine("Decrypted: " + utf.GetString(bMsg)); 
    } 
}

 

使用System.Security.SecureString类

字符串在内存中的存储是不加密的,而且由于垃圾回收机制,字符串这种引用类型在销毁之前有一段不受控制的时间。如果某些恶意的人能够访问到Windows进程的页面,将这些字符串暴露在外面是很危险的。

为了解决如上问题,.NET Framework提供了SecureString类,其采用DPAPI服务并且允许字符串以加密的形式保存在内存中。

可以通过一个字符数组(使用过后将其设为零)初始化一个SecureString类的实例,或者通过一个字符一个字符的将其构造出来。Marshal类提供了多个方法以供解密一个保存在SecureString类实例中的加密字符串。

下面示例展示了SecureString类的使用:

using System; 
using System.Security; 
using System.Runtime.InteropServices; 
class Program { 
    static void Main() {
        SecureString pwd = new SecureString(); 
        ConsoleKeyInfo nextKey = Console.ReadKey( true ); 
        while(nextKey.Key != ConsoleKey.Enter) { 
            pwd.AppendChar( nextKey.KeyChar ); 
            Console.Write( "*" ); 
            nextKey = Console.ReadKey( true ); 
        } 
        Console.WriteLine();
        pwd.MakeReadOnly(); 
        IntPtr bstr = Marshal.SecureStringToBSTR(pwd); 
        // Get an instance of the Sring class 
        // only for the example needs. 
        try{ Console.WriteLine( Marshal.PtrToStringAuto(bstr) ); } 
        finally{ Marshal.ZeroFreeBSTR(bstr); } 
    } 
} ​

使用中往往很难避免将一个SecureString在内存中解密,甚至存放在一个字符串。一定要注意需要在特定的内容区域执行此操作,而写使用完后要尽快销毁,以免出现前文提到的安全问题。

 

保护特殊文件中的数据

在应用程序的配置文件中常常包含需要加密的设置。.NET中这通过System.Configuration.Configuration类来实现。下面示例演示了在一个名为TagPassword的配置参数中保存经过加密的MyPassword字符串。

using System.Configuration; 
class Program { 
    static void Main() { 
        SavePwd("MyPassword"); 
        System.Console.WriteLine(LoadPwd()); 
    } 
    static void SavePwd(string pwd) { 
        Configuration cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None ); 
        cfg.AppSettings.Settings.Add("TagPassword", pwd ); 
        cfg.AppSettings.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider" ); 
        cfg.Save(); 
    } 
    static string LoadPwd() { 
        Configuration cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None ); 
        return cfg.AppSettings.Settings["TagPassword"].Value; 
    } 
}

如上,在保存操作的时候需要进行处理,以指定我们希望保存一个加密的版本,而在配置文件被读取的时候加密信息会自动被解密。

posted @ 2009-01-24 16:06  hystar  阅读(1774)  评论(3编辑  收藏  举报