安全第一:.NET加密技術指南(2)
2024-07-10 13:00:01
供稿:網友
四、執行加密/解密
.net加密技術要求密鑰有確定的長度,例如,des(data encryption standard)函數要求密鑰的長度是64位,rijndael則要求128、192或256位長度的密鑰。密鑰越長,加密強度越高。對于des之外的加密算法,查詢legalkeysizes屬性即可得到它允許的密鑰長度,包括minsize(支持的最小密鑰長度)、maxsize(最大密鑰長度)、skipsize(增量)。skipsize表示密鑰最大長度和最小長度之間可用長度的間隔,例如,rijndael算法的skipsize值是64位。
利用下面的代碼可以得到密鑰的長度信息:
' 創建des加密對象
dim des as new descryptoserviceprovider()
dim fd() as keysizes
fd = des.legalkeysizes() 'tells us the size(s), in bits
msgbox("加密類型=" & des.tostring() & chr(13) & "minsize = " & fd(0).minsize & chr(13) & _
"maxsize = " & fd(0).maxsize & chr(13) & "skipsize = " & fd(0).skipsize)
運行上面的代碼,得到的結果是64、64、0。如果把加密對象的聲明改成tripledescryptoserviceprovider(),得到的結果是128、192、64。
說明:des算法要求輸入一個8字節的密碼,但實際使用的密鑰只有56位(7個字節),每一個字節的最后一位不用(它作為校驗位使用,但不用于實際的加密過程)。
下面的代碼開始生成本文示例程序的密鑰:
public class form1
inherits system.windows.forms.form
' 保存密鑰的8字節的數組
private thekey(7) as byte
' 在向量中放入一些隨機數據
private vector() as byte = {&h12, &h44, &h16, &hee, &h88, &h15, &hdd, &h41}
首先,代碼定義了保存密鑰和初始向量(請參見稍后的詳細說明)的兩個變量。向量的初值這里用隨機數據填充,當然,通過密碼和hash算法也可以獲得向量的初值。下面的過程從用戶輸入的密碼創建出密鑰:
sub createkey(byval strkey as string)
' 保存密鑰的字節數組
dim arrbyte(7) as byte
dim ascencod as new asciiencoding()
dim i as integer = 0
ascencod.getbytes(strkey, i, strkey.length, arrbyte, i)
' 獲得密碼的hash值
dim hashsha as new sha1cryptoserviceprovider()
dim arrhash() as byte = hashsha.computehash(arrbyte)
' 將hash值保存到密鑰
for i = 0 to 7
thekey(i) = arrhash(i)
next i
end sub
用戶的密碼(strkey)傳入到createkey過程,分解成一組ascii值保存到一個字節數組。把這個字節數組傳遞給sha1cryptoserviceprovider類的computehash方法,返回一個hash值。把這個hash值保存到thekey數組,供以后的加密/解密過程使用(注意sha1cryptoserviceprovider實際能夠支持160位,但本文示例程序只用到64位)。
那么,初始向量究竟起什么作用呢?這個字節數組有8個元素,就象密鑰一樣,但向量和密鑰的作用是不同的,向量用來避免des之類的算法一個特有的問題。在des之類的算法中,原始數據被分成8字節一塊然后分別處理。des在加密一塊數據時,要用到前一塊數據的模式,也就是說,如果改動了原始數據中第一塊的某個字符,所有后繼的塊的內容都將隨之改變,從而避免了一系列相連接的塊中出現重復塊的問題。
例如,假設你一時高興,發了一個郵件,內容只有幾個重復的單詞“melanie! melanie! melanie! melanie!”,在密鑰和塊序列中前一塊的共同作用下,加密之后的密文不會出現重復現象。然而,進一步考慮這個加密過程可以發現,如果用同一個密鑰加密多個郵件,且郵件開頭的問候語都相同,則郵件開頭的一部分很容易受到攻擊。由于這個原因,我們用初始向量來模擬前一個塊。
本文加密/解密工具中的下面這段代碼示范了如何加密文件:
sub encrypt(byval inname as string , byval outname as string )
try
' 創建緩沖區
dim storage(4096) as byte
' 已經寫入的字節數量
dim totalbyteswritten as long = 8
' 每次寫入的字節數量
dim packagesize as integer
' 聲明文件流
dim fin as new filestream(inname, filemode.open, fileaccess.read)
dim fout as new filestream(outname, filemode.openorcreate, fileaccess.write)
fout.setlength(0)
' 源文件的大小
dim totalfilelength as long = fin.length
' 創建加密對象
dim des as new descryptoserviceprovider()
dim crstream as new cryptostream(fout, _
des.createencryptor(thekey, vector), _
cryptostreammode.write)
' 輸出加密后的文件
while totalbyteswritten < totalfilelength
packagesize = fin.read(storage, 0, 4096)
crstream.write(storage, 0, packagesize)
totalbyteswritten = convert.toint32(totalbyteswritten + _
packagesize / des.blocksize * des.blocksize)
end while
crstream.close()
catch e as exception
msgbox(e.message)
end try
end sub
注意這段代碼創建了三個文件流:fin,表示明文形式的原始文件;fout,加密結果文件;crstream,加密流,用來把des加密的結果轉入輸出文件fout。增加一個crstream流的好處是不必把結果保存到臨時文件或緩沖區。
加密過程與解密過程的唯一重要區別是,執行解密時,我們將使用descryptoserviceprovider對象的另一個方法createdecryptor,除此之外,兩者其余的處理步驟(包括參數,文件流,等等)基本相同。
五、防止破解
黑客和密碼專家破解加密文件的辦法主要有兩個,第一是搜索密文是否有重復現象,第二是用暴力破解法獲得密鑰。首先我們考慮一下初始向量如何防止重復現象,然后再探討一下防止暴力破解的關鍵問題。
破解密文的第一種方式是搜索樣本——特別是重復的樣本。人們在寫信的時候總是喜歡用同樣的文字開頭,例如“親愛的xxx”、“dear sir”等,如果多個郵件的開頭文字相同且通過同一密鑰加密,則每個密文信件的開頭也相同。假設antonio寫給melanie的所有加密信件都有相同的問候語“@4^f (2$@fx”,解密者就會首先檢查開頭的幾個單詞是不是“dear melanie”。解密機密文件的一個重要步驟就是猜測文件中應當會出現的幾個單詞,所以我們不應該給解密者提供這種方便。在本文的示例中,初始向量的內容被附加到文件的開頭,從而防止了出現重復現象。只有信件的開頭才容易受到此類攻擊。
計算機的運算速度和精度要遠遠超過人,特別擅長處理一些重復的任務,例如嘗試每一種可能的密鑰組合最終破解密鑰。des加密算法本身是不安全的,這種加密算法早在70年代就已經公之于眾。而且,破解者如果想要讓搜索密鑰的過程自動化,同樣可以方便地運用.net的descryptoserviceprovider類。
對于一個128位、結合運用密鑰/初始向量的加密方案,計算機嘗試每一種可能的密鑰組合要花多少時間?專家們的看法并不一致,有人認為需要數月,也有人認為裝有專用硬件的價值6位數的計算機每秒能夠驗證數十億個密鑰,破解des密文只需數小時。如果你的機密值得花數月時間去破解,那么最好改用tripledes或其他加密算法。從tripledes的名字也可以猜出,這種加密方式采用三重數據加密標準算法,所以密鑰的長度是192位,而不是64位的des密鑰。記住,在其他條件相同的情況下,密鑰越長,安全程度越高。
結束語:現在你已經了解了.net des加密算法的使用過程,接下去可以研究.net的其他安全功能,包括極具吸引力的公用密鑰加密方案。雖然公用密鑰加密方案執行起來速度慢一些,但加密效果一般要比tripledes好。本人沒有什么機密值得運用des之外的算法,不過你的要求可能有所不同。