羅偉雄++時東曉++曾紀霞++劉嵐++郭柏喬



摘要:隨著數據加密技術的不斷發展,計算機處理能力的不斷增強,以往許多被認為比較安全的數據加密算法,都相繼被破解。針對這些情況,此文首先詳細分析了私鑰加密算法、公鑰加密算法、數字簽名技術和哈希函數的特點以及在使用過程中需要注意的問題,然后提出了一種綜合使用私鑰、公鑰、數字簽名等技術的增強型加密服務架構。此架構利用公鑰算法的特點為私鑰算法生成密鑰,有效降低了因私鑰泄露而導致的威脅,利用數字簽名技術可有效降低數據被非法篡改的可能性。
關鍵詞:數據加密;私鑰加密;公鑰加密;數字簽名;哈希算法
中圖分類號:TP309
文獻標識碼:A
DOI: 10.3969/j.issn.1003-6970.2015.08.012
0 引言
當在軟件開發過程中往往會用到許多數據加密技術,例如使用哈希函數加密密碼,使用數字簽名技術驗證文本是否被篡改等等。在.NET Framework下,系統提供了一套相對完整可靠的加密服務。但是隨著數據加密技術的不斷發展,計算機處理能力的不斷增強,以往被認為比較安全的數據加密算法,都紛紛暴露其缺陷,有些甚至已經宣布被破解。例如2004年我國密碼學家王小云教授利用碰撞方法實現了對MD5、SHA-1和RIPEMD的破解。那么在.NET Framework下,該如何更為安全地使用這些加密服務,如何構建相對安全的加密服務架構,是軟件開發過程中必須考慮和深究的問題。
1 加密服務類型
在使用.NET Framework平臺的開發過程中最常用到的是以下四種加密服務:
(1)私鑰加密。又稱為對稱加密,它使用單個共享的密鑰來加解密數據。
(2)公鑰加密。又稱為非對稱加密,它使用兩個密鑰來分別對數據進行加解密。
(3)數字簽名。它的作用是驗證收到的數據是否被非法篡改。
(4)哈希加密。其作用是生成數據的摘要信息。
2 私鑰加密算法
在.NET Framework 3.5及以上版本中支持DES,RC2,Rij ndael,TripleDES和AES等私鑰加密算法,這些都屬于分組加密算法。表1列出了這些算法的基本信息。
其中DES算法是使用較為廣泛的私鑰加密算法,但是由于DES算法的密鑰長度只有64位,以現代計算機的計算能力來說,一天左右的時間就可以破解,因此在實際的應用中應該使用安全性相對較高的算法,如AES算法。由于AES算法支持128位以上長度的密鑰,其安全性比DES要高,目前該算法已經成為替代DES算法的新標準。
2.1 塊密碼模式
以上的私鑰加密算法在使用時可以選擇不同的塊密碼模式并設置初始向量IV,其目的是提高系統的安全性。塊密碼在一定程度上決定了加密的強度。當前.NET Framework只支持CBC、CFB和ECB三種模式。
CBC模式:密碼塊鏈模式。該模式引入了反饋。每個純文本塊在加密前,通過按位“異或”操作與前一個塊的密碼文本結合。這樣確保了即使純文本包含許多相同的塊,這些塊中的每一個也會加密為不同的密碼文本塊。在加密塊之前,初始化向量通過按位“異或”操作與第一個純文本塊結合。
CFB模式:密碼反饋模式。該模式將少量遞增的純文本處理成密碼文本,而不是一次處理整個塊。
ECB模式:電子密碼本模式。該模式是分別加密每個塊。這意味著任何純文本塊只要相同,并且在同一消息中,或者在用相同的密鑰加密的不同消息中,都將被轉換成同樣的密碼文本塊。
通過上面的比較可以看到,ECB模式安全性相對較差,所以在實際應用中盡量不要選擇該模式,另外在.NET Framework中AES算法不支持CFB模式。
2.2 填充模式
對于分組加密算法,由于是將明文分成固定長度的多個塊再進行處理,如果最后一個塊的字節數小于塊的長度,則需要進行填充。在.NET Framework中可選的填充模式有五種,而默認使用的是PKCS7模式。這五種模式分別是:
ANSIX923:該模式下填充字符串由一個字節序列組成,此字節序列的最后一個字節填充字節序列的長度,其余字節均填充數字零。
IS010126:該模式下填充字符串由一個字節序列組成,此字節序列的最后一個字節填充字節序列的長度,其余字節填充隨機數據。
None:不填充。
PKCS7:該模式下填充字符串由一個字節序列組成,每個字節填充該字節序列的長度。
Zeros:該模式下填充字符串由設置為零的字節組成。
2.3 加解密流程
在.NET Framework中使用私鑰加密算法就是把數據寫入使用了某種私鑰算法的加密流中;而解密則是從加密流中讀出數據,算法類型的選擇通過接口ICryptoTransform指定。私鑰加密算法的加密流程如圖1所示。
(1)定義密鑰Key和初始向量IV;
(2)構建內存流;
(3)創建加密接口;
(4)使用加密接口創建加密流并套接在內存流上;
(5)創建流編寫器并套接在加密流上;
(6)通過流編寫器把明文寫入加密流中;
(7)從內存流中讀取密文;
(8)、清空敏感數據
以AES算法為例,其加密的核心代碼如下:
Aes aes= new AesManaged();//創建AES加密服務對象
aes.Mode=cipMode;//設置塊密碼模式
aes.Key= key;//設置密鑰
aes.IV= iv;//設置初始向量
aes.Padding= PaddingMode.PKCS7; //設置填充模式
MemoryStream ms= new MemoryStream();//創建:內存流
ICryptoTransform tran= aes.CreateEncryptor();//創建加密接口
CryptoStream cs=new CryptoStream(ms, tran, CryptoStreamMode.Write);//創建加密轉換流
StreamWriter sw=new StreamWriter(cs);//創建編寫器
sw.Write(m);//把明文寫入流進行加密
sw.Flush();
cs.FlushFinalBlock();
string enString=Convert.ToBase64String(ms.ToArray());//轉換為Base64字符串
aes.Clear();//清空敏感數據
解密操作與加密類似,也是先設置塊密碼模式、初始向量、填充模式、密鑰等基本信息,然后再進行解密,其流程如圖2所示。
(1)定義密鑰Key和初始向量IV;
(2)構建內存流;
(3)把密文寫入內存流中;
(4)創建解密接口;
(5)使用解密接口創建解密流并套接在內存流上;
(6)創建流讀取器并套接在解密流上;
(7)通過流讀取器從解密流中讀取明文;
(8)清空敏感數據。
以AES算法為例,其解密的核心代碼如下:
MemoryStream ms= new MemoryStream();//創建;內存流
ms.Write(enByte,0,enByte.Length);//把密文寫入內存流中
ms.Position=0://重新定位內存流
ICryptoTransform tran= aes.CreateDecryptor();//創建解密接口
CryptoStream cs=new CryptoStream(ms,tran,Crypto StreamMode.Read);//創建解密轉換流
StreamReader sr= new StreamReader(cs);//創建讀取器
string deString=sr.ReadToEnd();//從流中讀取明文
aes.Clear()//清空敏感數據
這里要注意的是,加解密完成后必須使用對應的Clear方法清空內存中的敏感信息,否則攻擊者有可能通過讀取內存數據的方法實施破解。另外對象清零后,要調用Dispost方法以釋放與對象關聯的所有資源。
3 公鑰加密算法
在.NET Framework中有三種公鑰加密算法,但只有RSA算法能用于數據加密,而DSA算法只能用于數字簽名,DiffieHellman算法只能用于生成密鑰。
公鑰加密算法的使用最困難的是配置密鑰對,而密鑰從一定層面上決定了算法的安全性。在.NETFramework下,公鑰加密算法的密鑰一般由系統生成,然后把密鑰保存到密鑰文件中,需要時再從密鑰文件中導人。例如下面的代碼演示了生成RSA密鑰的過程。
RSACryptoServiceProvider rsa= new RSACryptoServiceProvider();//構建RSA加密服務對象
Console.WriteLine(rsa.ToXmlString(true》;//輸出系統生成的公鑰和私鑰
在公鑰加密算法中,如果明文信息比較短則容易受到攻擊,為了增強其安全性,算法通過為短信息填充偽數據來加大破解的難度。在.NET Framework中RSA算法支持OAEP和直接加密兩種填充模式。
使用RSA算法進行數據加密的流程一般是:
(1)創建公鑰加密算法服務對象;
(2)導人或生成公開密鑰;
(3)把明文轉換為字節數組;
(4)調用加密函數加密數據;
(5)清空敏感數據。
其核心代碼如下:
RSACryptoServiceProvider rsa= new RSACryptoServiceProvider();//構建:RSA加密服務對象
rsa.FromXmlString(pubKey);//導人公開密鑰
byte[] dataToEncrypt= Encoding.UTF8.GetBytes(m);//把明文轉換為字節數組
byte[] enByte=rsa.Encrypt(dataToEncrypt, DoOAEPPadding);//設置填充模式并加密
string enString=Convert.ToBase64String(enByte);//把字節數組轉換為Base64編碼的字符串
rsa.Clear()//清空敏感數據
其中加密函數Encrypt的第二個參數DoOAEPPadding,是用于設置填充模式的,此值為true,則表示使用OAEP填充,否則直接加密。
解密的流程與加密類似,只是調用的方法不同,一般為:
(1)創建公鑰加密算法服務對象;
(2)導人私密密鑰;
(3)把密文轉換為字節數組;
(4)調用解密函數解密數據;
(5)清空敏感數據。
其核心代碼如下:
RSACryptoServiceProvider rsa= new RSACryptoServiceProvider();//構建RSA加密服務對象
rsa.FromXmlString(priKey);//導人私密密鑰
byte[] dataToDecrypt= Convert.FromBase64String(enString);//把Base64編碼的密文字符串轉換為密文字節數組
byte[] deByte=rsa.Decrypt(dataToDecrypt, DoOAEPPadding);//設置填充模式并解密
string deString= Encoding.UTF8.GetString(deByte);//把明文字節數組轉換為明文字符串
rsa.Clear()//清空敏感數據
同樣Decrypt函數的第二個參數DoOAEPPadding也是用于設置填充模式的。
4 數字簽名算法
在.NET Framework中可以使用RSA、DSA和ECD算法進行數字簽名,這三種算法的數字簽名函數均為SignData和SignHash。前者是計算指定數據的哈希值并對其簽名,而后者則是計算指定哈希值的簽名。兩者對應的驗證函數分別是VerifyData和VerifyHash。
其中ECD算法是橢圓曲線數字簽名算法,其安全性比其他兩種要高。另外在密鑰管理方面ECD算法與其他兩種算法有所不同。ECD算法的密鑰是由系統統一管理的,不需要用戶自行存儲,這樣可以減少因私密密鑰泄露而導致的威脅。ECD算法的密鑰由類CngKey管理,該類也提供了對密鑰的導出,但是如果要導出私密密鑰,則要在創建CngKey對象實例時傳人CngKeyCreationParameters對象實例,并設置其ExportPolicy屬性為AllowPlaintextArchiving或AllowPlaintextExport。另外使用帶密鑰名稱的方式創建密鑰后,系統會將其自動保存,待需要時可以使用CngKey.Open方法打開,如果要刪除,則可以使用Delete方法刪除。下面的代碼演示了密鑰的創建和導出。
CngKeyCreationParameters creationParameters= new CngKeyCreationParameters();//密鑰的高級屬性
creationParameters.KeyCreationOptions=CngKeyCreationOptions.OverwriteExistingKey;
creationParameters.ExportPolicy=CngExportPolicies.AllowPlaintextExport;,/設置策略允許私鑰以純文本形式導出多次
CngKey cngKey=CngKey.Create(CngAlgorithm.ECDsaP256,”ECDKey”,creationParameters);//創建密鑰
byte[] privateKey=cngKey.Export(CngKeyBlobFormat.EccPrivateBlob);//導出私密密鑰
byte[] publicKey=cngKey.Export( CngKeyBlobFormat.EccPublicBlob);//導出公開密鑰
cngKey.Delete();//刪除系統存儲的密鑰
使用ECD算法進行數字簽名的流程一般為:
(1)構建ECD加密服務對象;
(2)導人或打開私密密鑰;
(3)把文本轉換為字節數組;
(4)對文本進行數字簽名;
(5)清空敏感數據。
其核心代碼如下所示:
CngKey cngkey=CngKey.Import(privateKey,CngKeyBlobFormat.EccPrivateBlob);//導人私有密鑰
ECDsaCng ecd= new ECDsaCng(cngkey);//構建ECD加密服務對象
byte[] dataToSign= Encoding,UTF8.GetBytes(m);//把明文轉換為字節數組
byte[] enByte=ecd.SignData(dataToSign);//數字簽名
ecd.Clear()∥清空敏感數據
而驗證簽名時對方需要先導人公開密鑰,然后再進行驗證。其核心代碼如下。
CngKey cngkey=CngKey.Import(publicKey,CngKeyBlobFo rmat.EccPublicBlob);//導人公開密鑰
ECDsaCng ecd= new ECDsaCng(cngkey);//構建ECD加密服務對象
byte[] dataToSign= Encoding.UTF8.GetBytes(m);//把明文轉換為字節數組
bool verify=ecd.VerifyData(dataTo Sign,dataToVerify);//驗證數字簽名
ecd.Clear()//清空敏感數據
另外ECD算法還提供了SignHash和VerifyHash方法對明文的摘要進行簽名和驗證,其使用方式與SignData和VerifyData類似,只是傳人的是明文的摘要而不是明文,這里就不再贅述。
5 哈希算法
在.NET Framework中提供了MD5、SHA-1、SHA-2和RIPEMD等多種哈希算法,其計算哈希值的函數名稱均為ComputeHash。2004年我國密碼學家王小云教授已經宣布MD5、SHA-1和RIPEMD被破解,因此這些哈希算法已經是不安全的,所以在實際開發中建議使用SHA-2等算法。
生成哈希值的流程是:首先創建Hash轉換器,然后調用ComputeHash方法。下面是使用SHA-2生成哈希值的核心代碼。
byte[] dataToHash= Encoding.UTF8.GetBytes(m);//轉換明文為字節數組
SHA256 sha=SHA256.Create()∥構建Hash轉換器
byte[] hash= sha.ComputeHash(dataToHash);//計算哈希值
由于相同的明文使用相同的哈希算法產生的哈希值都是相同的,因此極容易遭受已知密文的攻擊。例如,在許多系統中,用戶的密碼大部分都是用其哈希值存儲,如果不做特殊處理,兩個相同密碼存儲的哈希值也必定相同。基于安全考慮,用戶更希望即使密碼相同,但存儲的哈希值卻不同。要實現這種效果,可以在計算過程中做一些特殊處理。例如在明文中添加一些附加信息,然后把明文和附加信息作為整體,一起計算其哈希值。而這些附加信息可以使用與用戶相關的數據,如用戶名、身份證號等。當然為了提高系統的安全性,可以使用由系統隨機產生的數據作為附加信息。在.NET Framework下可以使用RNGCryptoServiceProvider類來產生隨機數。該類所生成的隨機數安全性較高,其輸出的可推算概率不高于50%;即任何推算下一個輸出位的方法的成功概率低于隨機猜測。下面是使用RNGCryptoServiceProvider生成隨機數的代碼。
byte[] randomBytes=new byte[size];//創建字節數組
RNGCryptoServiceProvider.Create().GetBytes(randomBytes);//生成隨機數6增強型加密服務架構
由于私鑰算法的加密速度要比公鑰加密算法快,特別是對大數據量來說更是如此,所以在實際數據傳輸過程中以私鑰加密算法使用居多。下面筆者以私鑰加密算法為基礎,提出如何結合公鑰加密算法和數字簽名算法構建安全的加密服務架構。
通過前面的分析可以看到,私鑰加密算法的安全性很大程度上取決于對密鑰和初始向量的保護。所以在.NET Framework中如何更加安全地使用私鑰加密算法就轉換為如何保護好私密密鑰和初始向量。為此增強型的加密服務架構如下。
首先通訊雙方都已經知道對方的RSA公鑰和ECD公鑰,這里分別記為RA、RB、EA和EB。通訊前,用戶A會使用DiffieHellman算法生成公鑰Al和A2,然后使用用戶B的RSA公鑰RB以及RSA算法對Al和A2進行加密,然后使用ECD算法對加密結果進行數字簽名,接著把加密的結果和簽名發給用戶B。用戶B收到后,首先使用用戶A的ECD公鑰EA以及ECD算法驗證簽名是否正常,正常后再使用RSA算法解密出公鑰Al和A2,然后使用DiffieHellman算法生成自己的公開密鑰Bl和B2,并結合密鑰Al和A2生成相應的兩個私密密鑰,分別作為私鑰算法的Key和初始向量IV,接著把Bl和B2使用用戶A的RSA公鑰RA以及RSA算法加密并使用ECD算法對加密結果進行數字簽名,然后把加密結果和簽名發給用戶A。用戶A收到后以同樣的方法進行數字簽名的驗證并解密出密鑰Bl和B2,然后使用DiffieHellman算法并結合Bl和B2生成兩個私密密鑰,這兩個密鑰和用戶B所生成的兩個私密密鑰是相同的。同樣這兩個密鑰分別作為私鑰算法的Key和初始向量IV。然后用戶A和用戶B使用該Key和IV以及AES算法對數據進行加密,然后使用ECD算法進行簽名,接著發送給對方。對方收到后首先使用ECD算法進行簽名驗證,然后使用AES算法進行解密得到明文數據。整個流程如圖3所示。
其中使用DiffieHellman算法生成密鑰的流程一般是:
(1)構建ECDiffieHellman對象;
(2)設置生成密鑰的參數;
(3)獲取自己的公開密鑰;
(4)傳輸自己的公開密鑰給對方;
(5)接收對方的公開密鑰;
(6)根據對方的公開密鑰生成私密密鑰;
(7)清空敏感數據。
其核心代碼如下:
ECDiffieHellmanCng ecd= new ECDiffieHellmanCng();//構建ECDiffieHellman對象
ecd.KeyDerivationFunction= ECDiffieHellmanKeyDerivationFunction.Hash;//設置密鑰派生函數
ecd.HashAlgorithm= CngAlgorithm.Sha256;,/設置生成密鑰所使用的哈希算法
byte[] senderPublicKey=ecd.PublicKey.ToByteArray();//獲取自己的公開密鑰
byte[] receiverPublicKey=SendKey(senderPublicKey);//傳輸自己的公開密鑰給對方并獲取對方的公開密鑰
byte[] senderPrivateKey=ecd. DeriveKeyMaterial (CngKey. Import receiverPublicKey, CngKeyBlobFormat.EccPublicBlob));//根據對方的公開密碼生成私密密鑰
代碼中要設置生成密鑰所使用的哈希算法,這里建議使用SHA-2算法較為安全。另外要注意DiffieHellman算法生成的密鑰長度與AES算法的密鑰和初始向量的長度匹配問題,一般可通過截取或重復疊加的方式處理。
此架構中,私密密鑰和初始向量都是由DiffieHellman算法生成的,并沒有在信道上傳輸,這樣可有效降低因私密密鑰泄露而導致的威脅。另外,由于對應的私密密鑰要結合雙方的密鑰信息方可生成,因此安全性較高。而且在傳輸公鑰過程中,系統還對密鑰進行了加密和數字簽名,這樣有效降低了密鑰被非法竊取和篡改的可能。同樣在數據傳輸過程中,系統對信息進行了加密和數字簽名,這樣有效提高了數據的安全性和可靠性。整個架構綜合運用了私鑰加密算法、公鑰加密算法和數字簽名技術各自的優點,取長補短,有效提高了系統的安全性和可靠性。
7 結論
本文對在NET Framework中如何更加安全可靠地使用私鑰加密算法、公鑰加密算法、數字簽名算法和哈希算法做了詳細的分析和介紹,并且提出了一種綜合運用私鑰加密算法、公鑰加密算法和數字簽名算法的增強型加密服務架構。該架構充分利用這些算法的優點,有效降低了因私密密鑰泄露而導致的威脅,降低了信息被非法竊取和篡改地可能,有效提高了系統的安全性和可靠性。