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