封装自己的对称加密模块

说明

通过对 Enterprise Library 5.0的学习,提取并修改一个适合自己使用的加密解密库;

需要封装的库能支持大部分对称加密解密算法,使用DPAPI保证key的安全性,并且简单易懂方便使用; 


本文技术含量不高,基本只是抽取 Enterprise Library 5.0的几个类,对代码稍作修改,并且给出使用演示示例!

 

一,对称加密解密算法的了解,System.Security.Cryptography;


查看.net framework 可知很多对称加密解密算法都被封装到了Cryptography空间下,

包括有:AesDES RC2RSARijndaelTripleDESSHA等等等。。。。

 

SymmetricAlgorithm;

表示所有对称算法的实现都必须从中继承的抽象基类。 

如:SymmetricAlgorithm symmetricAlgorithm = Activator.CreateInstance(algorithmType) as SymmetricAlgorithm;

 

二,DPAPI的了解,ProtectedData

DPAPI

您必须了解的背景知识

在开始学习本章之前,您应该知道:

Windows 2000 操作系统和更高版本的操作系统提供了用于加密和解密数据的 Win32® 数据保护 API (DPAPI)。

DPAPI 是加密 API (Crypto API) 的一部分并且是在 crypt32.dll 中实现的。它包含两个方法:CryptProtectData 和 CryptUnprotectData

DPAPI 特别有用,因为它能够消除使用密码的应用程序所带来的密钥管理问题。虽然加密能确保数据安全,但您必须采取额外的步骤来确保密钥的安全。DPAPI 使用与 DPAPI 函数的调用代码关联的用户帐户的密码,以便派生加密密钥。因此,是操作系统(而非应用程序)管理着密钥。

DPAPI 能够与计算机存储或用户存储(需要一个已加载的用户配置文件)配合使用。DPAPI 默认情况下用于用户存储,但您可以通过将 CRYPTPROTECT_LOCAL_MACHINE 标志传递给 DPAPI 函数来指定使用计算机存储。

这种用户配置文件方式提供了一个额外的安全层,因为它限制了哪些用户能访问机密内容。只有加密该数据的用户才能解密该数据。但是,当通过 ASP.NET Web 应用程序使用 DPAPI 时,使用用户配置文件需要您执行额外的开发工作,因为您需要采取明确的步骤来加载和卸载用户配置文件(ASP.NET 不会自动加载用户配置文件)。

计算机存储方式更容易开发,因为它不需要管理用户配置文件。但是,除非使用一个附加的熵参数,否则并不安全,因为该计算机的任何用户都可以解密数据。(熵是一个设计用来使解密机密内容更为困难的随机值)。使用附加的熵参数出现的问题在于它必须由应用程序安全地存储起来,这带来了另一个密钥管理问题。

注意:如果您将 DPAPI 和计算机存储一起使用,那么加密字符串仅适用于给定的计算机,因此您必须在每台计算机上生成加密数据。不要在场或群集中将加密数据从一台计算机复制到另一台计算机。

如果将 DPAPI 和用户存储一起使用,则可以用一个漫游的用户配置文件在任何一台计算机上解密数据。

 

ProtectedData类实现了DPAPI,提供保护数据和取消数据保护的方法。无法继承此类。 

 

三,封装我们的加密解密库

直接从微软企业库提取出以下3个类:

CryptographyUtility,此类包含一些通用的处理方法,如byte[]比较,byte[]与十六进制的转换,数据流到加密解密流的转换等

SymmetricCryptographer,此类就是实现我们对称加密解密的服务类,通过SymmetricAlgorithm基类获取一个对称加密解密算法的实例对象,进行加密解密

ProtectedKey,此封装了使用DPAPI对对称算法的key进行保护


下面帖上完整代码+注释,如有不对的地方欢迎批评指正!

ExpandedBlockStart.gif SymmetricCryptographer
using  System;
using  System.IO;
using  System.Security.Cryptography;
namespace  CryptBlock
{
    
public   class  SymmetricCryptographer : IDisposable
    {
        
private  SymmetricAlgorithm algorithm;  // 表示一个对称加密算法的对象,在Cryptography空间下所有对称加密算法的基类
         private  ProtectedKey key;    // key=ProtectedKey.CreateFromPlaintextKey()  key对象通过CreateFromPlaintextKey方法或CreateFromEncryptedKey方法返回

        
public   byte [] EnKey
        {
            
get  
            {
                
return  key.EncryptedKey;    // 由DPAPI加密过的密钥
            }
        }
        
public   byte [] Key
        {
            
get
            {
                
return  key.DecryptedKey;  // 返回由DPAPI解密出来 ,对称加密算法的key
            }
        }
        
///   <summary>
        
///  [构造函数]   创建一个对称加密实例
        
///   </summary>
        
///   <param name="algorithmType"></param>
        
///   <param name="key"></param>
         public  SymmetricCryptographer(Type algorithmType, ProtectedKey key)
        {
            
if  (algorithmType  ==   null throw   new  ArgumentNullException( " algorithmType Null " );
            
if  ( ! typeof (SymmetricAlgorithm).IsAssignableFrom(algorithmType))  throw   new  ArgumentException( " algorithmType IsAssignable Error " );
            
if  (key  ==   null throw   new  ArgumentNullException( " key Null " );
            
this .key  =  key;
            
this .algorithm  =  GetSymmetricAlgorithm(algorithmType);
        }

        
///   <summary>
        
///  Finalizer for  <see cref="SymmetricCryptographer"/>
        
///   </summary>
         ~ SymmetricCryptographer()
        {
            Dispose(
false );
        }

        
///   <summary>
        
///  Releases all resources for this instance.
        
///   </summary>
         public   void  Dispose()
        {
            Dispose(
true );
            System.GC.SuppressFinalize(
this );
        }

        
///   <summary>
        
///  Override to customize resources to be disposed.
        
///   </summary>
        
///   <param name="disposing"> Unused. </param>
         protected   virtual   void  Dispose( bool  disposing)
        {
            
if  (algorithm  !=   null )
            {
                algorithm.Clear();
                algorithm 
=   null ;
            }
        }

        
///   <summary>
        
///  加密
        
///   </summary>
        
///   <param name="plaintext"></param>
        
///   <returns></returns>
         public   byte [] Encrypt( byte [] plaintext)
        {
            
byte [] output  =   null ;
            
byte [] cipherText  =   null ;

            
this .algorithm.Key  =  Key;  // 加密算法对象的key值,此Key由DPAPI的解密返回

            
using  (ICryptoTransform transform  =   this .algorithm.CreateEncryptor())  // [CreateEncryptor]:用当前的 Key 属性和初始化向量 (IV) 创建对称加密器对象
            {
                cipherText 
=  Transform(transform, plaintext);
            }

            output 
=   new   byte [IVLength  +  cipherText.Length];
            Buffer.BlockCopy(
this .algorithm.IV,  0 , output,  0 , IVLength); // [BlockCopy]:将指定数目的字节从起始于特定偏移量的源数组复制到起始于特定偏移量的目标数组。
            Buffer.BlockCopy(cipherText,  0 , output, IVLength, cipherText.Length);

            CryptographyUtility.ZeroOutBytes(
this .algorithm.Key);  // 把密钥内存清0 防止被恶意读取

            
return  output;
        }

        
///   <summary>
        
///   <para> Decrypts bytes with the initialized algorithm and key. </para>
        
///   </summary>
        
///   <param name="encryptedText"><para> The text which you wish to decrypt. </para></param>
        
///   <returns><para> The resulting plaintext. </para></returns>
         public   byte [] Decrypt( byte [] encryptedText)
        {
            
byte [] output  =   null ;
            
byte [] data  =  ExtractIV(encryptedText);   // 先去除IV数据

            
this .algorithm.Key  =  Key;

            
using  (ICryptoTransform transform  =   this .algorithm.CreateDecryptor())    // 创建解密对象
            {
                output 
=  Transform(transform, data);   // 用解密对象将数据解密为解密流读出的bytes
            }

            CryptographyUtility.ZeroOutBytes(
this .algorithm.Key);

            
return  output;
        }

        
private   static   byte [] Transform(ICryptoTransform transform,  byte [] buffer)
        {
            
return  CryptographyUtility.Transform(transform, buffer);
        }

        
private   int  IVLength
        {
            
get
            {
                
if  ( this .algorithm.IV  ==   null )
                {
                    
this .algorithm.GenerateIV();    // 生成用于该算法的随机初始化向量 (IV)
                }
                
return   this .algorithm.IV.Length;
            }
        }
        
///   <summary>
        
///  返回除去IV之后的数据
        
///   </summary>
        
///   <param name="encryptedText"></param>
        
///   <returns></returns>
         private   byte [] ExtractIV( byte [] encryptedText) 
        {
            
byte [] initVector  =   new   byte [IVLength];

            
if  (encryptedText.Length  <  IVLength  +   1 )
            {
                
throw   new  CryptographicException( " ExceptionDecrypting " );
            }

            
byte [] data  =   new   byte [encryptedText.Length  -  IVLength];

            Buffer.BlockCopy(encryptedText, 
0 , initVector,  0 , IVLength);    // 从encryptedText中将 IVLength长度的数据复制到 initVector
            Buffer.BlockCopy(encryptedText, IVLength, data,  0 , data.Length); 

            
this .algorithm.IV  =  initVector;

            
return  data;
        }

        
///   <summary>
        
///  根据加密类型获取一个SymmetricAlgorithm对象
        
///   </summary>
        
///   <param name="algorithmType"></param>
        
///   <returns></returns>
         private   static  SymmetricAlgorithm GetSymmetricAlgorithm(Type algorithmType)
        {
            SymmetricAlgorithm symmetricAlgorithm 
=  Activator.CreateInstance(algorithmType)  as  SymmetricAlgorithm;
            
return  symmetricAlgorithm;
        }
        
// [SymmetricAlgorithm]:表示所有对称算法的实现都必须从中继承的抽象基类。  
    }
}

 

ExpandedBlockStart.gif ProtectedKey
using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.ComponentModel;
using  System.Security.Cryptography; 
namespace  CryptBlock
{
    
///   <summary>
    
///  Represents an encrypted cryptographic key and the DPAPI  <see cref="DataProtectionScope"/>  used to encrypt it.
    
///   </summary>
     public   class  ProtectedKey
    {
        
private   byte [] protectedKey;
        
private  DataProtectionScope protectionScope;

        
///   <summary>
        
///  Constructor method use to create a new  <see cref="ProtectedKey"></see>  from a plaintext symmetric key. The caller of this method
        
///  is responsible for clearing the byte array containing the plaintext key after calling this method.
        
///   </summary>
        
///   <param name="plaintextKey"> Plaintext key to protect. The caller of this method is responsible for 
        
///  clearing the byte array containing the plaintext key after calling this method. </param>
        
///   <param name="dataProtectionScope"><see cref="DataProtectionScope"></see></param>  used to protect this key.
        
///   <returns> Initialized  <see cref="ProtectedKey"></see>  containing the plaintext key protected with DPAPI. </returns>
         public   static  ProtectedKey CreateFromPlaintextKey( byte [] plaintextKey, DataProtectionScope dataProtectionScope)
        {
            
// [ProtectedData]:提供保护数据和取消数据保护的方法。无法继承此类。  DPAPI
             byte [] encryptedKey  =  ProtectedData.Protect(plaintextKey,  null , dataProtectionScope);
            
return   new  ProtectedKey(encryptedKey, dataProtectionScope);
        }

        
///   <summary>
        
///  Constructor method used to create a new  <see cref="ProtectedKey"/>  from an already encrypted symmetric key.
        
///   </summary>
        
///   <param name="encryptedKey"> Encrypted key to protect. </param>
        
///   <param name="dataProtectionScope"><see cref="DataProtectionScope"></see></param>  used to protect this key.
        
///   <returns> Initialized  <see cref="ProtectedKey"></see>  containing the plaintext key protected with DPAPI. </returns>
         public   static  ProtectedKey CreateFromEncryptedKey( byte [] encryptedKey, DataProtectionScope dataProtectionScope)
        {
            
return   new  ProtectedKey(encryptedKey, dataProtectionScope);
        }

        
///   <summary>
        
///  Gets the encrypted key contained by this object.
        
///   </summary>
         public   byte [] EncryptedKey
        {
            
get
            {
                
return  ( byte [])protectedKey.Clone();
            }
        }

        
///   <summary>
        
///  Gets the decrypted key protected by this object. It is the responsibility of the caller of this method 
        
///  to clear the returned byte array.
        
///   </summary>
         public   byte [] DecryptedKey
        {
            
get  {  return  Unprotect(); }
        }

        
///   <summary>
        
///  Returns the decrypted symmetric key.
        
///   </summary>
        
///   <returns> Unencrypted symmetric key. It is the responsibility of the caller of this method to
        
///  clear the returned byte array. </returns>
         public   virtual   byte [] Unprotect()
        {
            
return  ProtectedData.Unprotect(protectedKey,  null , protectionScope);
        }

        
private  ProtectedKey( byte [] protectedKey, DataProtectionScope protectionScope)
        {
            
this .protectionScope  =  protectionScope;
            
this .protectedKey  =  protectedKey;
        }
    }
}

 

ExpandedBlockStart.gif CryptographyUtility
using  System;
using  System.Collections.Specialized;
using  System.Diagnostics;
using  System.Globalization;
using  System.Security.Cryptography;
using  System.Text;
using  System.IO;
namespace  CryptBlock
{
    
///   <summary>
    
///   <para> Common Cryptography methods. </para>
    
///   </summary>
     public   static   class  CryptographyUtility
    {
        
///   <summary>
        
///   <para> Determine if two byte arrays are equal. </para>
        
///   </summary>
        
///   <param name="byte1">
        
///   <para> The first byte array to compare. </para>
        
///   </param>
        
///   <param name="byte2">
        
///   <para> The byte array to compare to the first. </para>
        
///   </param>
        
///   <returns>
        
///   <para><see langword="true"/>  if the two byte arrays are equal; otherwise  <see langword="false"/> . </para>
        
///   </returns>
         public   static   bool  CompareBytes( byte [] byte1,  byte [] byte2)
        {
            
if  (byte1  ==   null   ||  byte2  ==   null )
            {
                
return   false ;
            }
            
if  (byte1.Length  !=  byte2.Length)
            {
                
return   false ;
            }

            
bool  result  =   true ;
            
for  ( int  i  =   0 ; i  <  byte1.Length; i ++ )
            {
                
if  (byte1[i]  !=  byte2[i])
                {
                    result 
=   false ;
                    
break ;
                }
            }

            
return  result;
        }

        
///   <summary>
        
///   <para> Returns a byte array from a string representing a hexidecimal number. </para>
        
///   </summary>
        
///   <param name="hexidecimalNumber">
        
///   <para> The string containing a valid hexidecimal number. </para>
        
///   </param>
        
///   <returns><para> The byte array representing the hexidecimal. </para></returns>
         public   static   byte [] GetBytesFromHexString( string  hexidecimalNumber)
        {
            
if  (hexidecimalNumber  ==   null throw   new  ArgumentNullException( " hexidecimalNumber " );

            StringBuilder sb 
=   new  StringBuilder(hexidecimalNumber.ToUpperInvariant());

            
if  (sb[ 0 ].Equals( ' 0 ' &&  sb[ 1 ].Equals( ' X ' ))
            {
                sb.Remove(
0 2 );
            }

            
if  (sb.Length  %   2   !=   0 )
            {
                
throw   new  ArgumentException( " InvalidHexString " );
            }

            
byte [] hexBytes  =   new   byte [sb.Length  /   2 ];
            
try
            {
                
for  ( int  i  =   0 ; i  <  hexBytes.Length; i ++ )
                {
                    
int  stringIndex  =  i  *   2 ;
                    hexBytes[i] 
=  Convert.ToByte(sb.ToString(stringIndex,  2 ),  16 );
                }
            }
            
catch  (FormatException ex)
            {
                
throw  ex;
            }

            
return  hexBytes;
        }

        
///   <summary>
        
///   <para> Returns a string from a byte array represented as a hexidecimal number (eg: 0F351A). </para>
        
///   </summary>
        
///   <param name="bytes">
        
///   <para> The byte array to convert to forat as a hexidecimal number. </para>
        
///   </param>
        
///   <returns>
        
///   <para> The formatted representation of the bytes as a hexidcimal number. </para>
        
///   </returns>
         public   static   string  GetHexStringFromBytes( byte [] bytes)
        {
            
if  (bytes  ==   null throw   new  ArgumentNullException( " bytes Null " );
            
if  (bytes.Length  ==   0 throw   new  ArgumentException( " bytes.Length=0 " );

            StringBuilder sb 
=   new  StringBuilder(bytes.Length  *   2 );
            
for  ( int  i  =   0 ; i  <  bytes.Length; i ++ )
            {
                sb.Append(bytes[i].ToString(
" X2 " , CultureInfo.InvariantCulture));
            }
            
return  sb.ToString();
        }

        
///   <summary>
        
///   <para> Combines two byte arrays into one. </para>
        
///   </summary>
        
///   <param name="buffer1"><para> The prefixed bytes. </para></param>
        
///   <param name="buffer2"><para> The suffixed bytes. </para></param>
        
///   <returns><para> The combined byte arrays. </para></returns>
         public   static   byte [] CombineBytes( byte [] buffer1,  byte [] buffer2)
        {
            
if  (buffer1  ==   null throw   new  ArgumentNullException( " buffer1 " );
            
if  (buffer2  ==   null throw   new  ArgumentNullException( " buffer2 " );

            
byte [] combinedBytes  =   new   byte [buffer1.Length  +  buffer2.Length];
            Buffer.BlockCopy(buffer1, 
0 , combinedBytes,  0 , buffer1.Length);
            Buffer.BlockCopy(buffer2, 
0 , combinedBytes, buffer1.Length, buffer2.Length);

            
return  combinedBytes;
        }

        
///   <summary>
        
///  Creates a cryptographically strong random set of bytes.
        
///   </summary>
        
///   <param name="size"> The size of the byte array to generate. </param>
        
///   <returns> The computed bytes. </returns>
         public   static   byte [] GetRandomBytes( int  size)
        {
            
byte [] randomBytes  =   new   byte [size];
            GetRandomBytes(randomBytes);
            
return  randomBytes;
        }

        
///   <summary>
        
///   <para> Fills a byte array with a cryptographically strong random set of bytes. </para>
        
///   </summary>
        
///   <param name="bytes"><para> The byte array to fill. </para></param>
         public   static   void  GetRandomBytes( byte [] bytes)
        {
            RNGCryptoServiceProvider.Create().GetBytes(bytes);
        }

        
///   <summary>
        
///   <para> Fills  <paramref name="bytes"/>  zeros. </para>
        
///   </summary>
        
///   <param name="bytes">
        
///   <para> The byte array to fill. </para>
        
///   </param>
         public   static   void  ZeroOutBytes( byte [] bytes)
        {
            
if  (bytes  ==   null )
            {
                
return ;
            }
            Array.Clear(bytes, 
0 , bytes.Length);
        }

        
///   <summary>
        
///  Transforms an array of bytes according to the given cryptographic transform.
        
///   </summary>
        
///   <param name="transform"><see cref="ICryptoTransform" />  used to transform the given  <paramref name="buffer" /> . </param>
        
///   <param name="buffer"> Buffer to transform. It is the responsibility of the caller to clear this array when finished. </param>
        
///   <returns> Transformed array of bytes. It is the responsibility of the caller to clear this byte array
        
///  if necessary. </returns>
         public   static   byte [] Transform(ICryptoTransform transform,  byte [] buffer)
        {
            
if  (buffer  ==   null throw   new  ArgumentNullException( " buffer null " );

            
byte [] transformBuffer  =   null ;

            
using  (MemoryStream ms  =   new  MemoryStream())
            {
                CryptoStream cs 
=   null ;             // 定义将数据流链接到加密转换的流
                 try
                {
                    cs 
=   new  CryptoStream(ms, transform, CryptoStreamMode.Write);
                    cs.Write(buffer, 
0 , buffer.Length);
                    cs.FlushFinalBlock();
                    transformBuffer 
=  ms.ToArray();
                }
                
finally
                {
                    
if  (cs  !=   null )
                    {
                        cs.Close();
                        ((IDisposable)cs).Dispose();
                    } 
//  Close is not called by Dispose
                }
            }

            
return  transformBuffer;
        }


    }
}

 

 

四,实例演示 

可能微软的企业库封装的东西太多,代码也比较多,一下看起来就比较没耐心了,所以可能很多同学还不能真正了解怎么用,以及怎么一个原理!

希望通过下面实例演示,用我的理解来为没有耐心的同学解惑,高手们就不要拍我了!

看代码,写了自己认为比较详细的注释:

ExpandedBlockStart.gif Tester
 [STAThread]
        
static   void  Main( string [] args)
        {
            ProtectedKey key;
            Rijndael rj 
=  Rijndael.Create(); // 创建Rijndael 加密算法对象
             if (File.Exists( " key2.dat " ))
            {
                
// 解密由DPAPI加密过的Key数据, DPAPI模式为机器模式,只有对此Key进行加密的计算机才可以使用
                 using  (FileStream fs  =   new  FileStream( " key2.dat " , FileMode.Open, FileAccess.Read))
                {
                    
byte [] keybytes  =   new   byte [fs.Length];
                    fs.Read(keybytes, 
0 , ( int )fs.Length);
                    key 
=  ProtectedKey.CreateFromEncryptedKey(keybytes, DataProtectionScope.LocalMachine);
                    
try {
                        Console.WriteLine(
" Decode Key2:{0} " , Encoding.Default.GetString(key.DecryptedKey));  
                    }
                    
catch (Exception e)
                    {
                        
// 捕捉由不是创建加密key的机器来解密key时产生的异常
                        Console.WriteLine( " Error DPAPI.DecryptedKey:{0} " , e.Message);
                    }
                }
                
            }
            
else
            {
                rj.GenerateKey();
// 生成随机的rj算法的key
                key  =  ProtectedKey.CreateFromPlaintextKey(rj.Key, DataProtectionScope.LocalMachine);
                
using  (FileStream fs  =   new  FileStream( " key.dat " , FileMode.CreateNew, FileAccess.Write))
                {
                    fs.Write(rj.Key, 
0 , rj.Key.Length);  // 把key保存出来在key.dat文件
                }
                Console.WriteLine(
" Key:{0} " , Encoding.Default.GetString(rj.Key));  
            }
            SymmetricCryptographer crypt 
=   new  SymmetricCryptographer(rj.GetType(), key);  // 实例化一个对称加密的对象

            
string  str  =   " 加密演示! " ;
            
byte [] encode = crypt.Encrypt(Encoding.Default.GetBytes(str));

            
// 加密解密的key来自 DPAPI.DecryptedKey 即crypt对象的Key属性
            Console.WriteLine( " Encode:{0} " ,Encoding.Default.GetString(encode) );
            Console.WriteLine(
" Decode:{0} " , Encoding.Default.GetString(crypt.Decrypt(encode)));

            Console.WriteLine(
" press ESC to continue... " );
            
while  (Console.ReadKey().Key != ConsoleKey.Escape)
            {
                
// ...
            }
             
        }

 

 

需要说明一下的是,为了演示,所以SymmetricCryptographer类中的 Key属性在上面代码了改成了Public级别的,实际使用中还是需要隐藏此key!

 

 

个人理解及能力都有限,自己认为需要注意的地方都添加了注释,如有错误之处还望大家指正!

另外这么封装是为了我要在软件中添加远程认证时对传递的数据进行保护,具体怎么使用还要看自己的设计! 

转载于:https://www.cnblogs.com/cxwx/archive/2010/07/03/1770354.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值