net加密基礎(chǔ)3-數(shù)據(jù)完整性(散列)
2012-02-15 22:04 海不是藍(lán) 閱讀(1588) 評(píng)論(3) 收藏 舉報(bào)|
數(shù)據(jù)完整性概念 |
為什么需要數(shù)據(jù)完整性?
之前的數(shù)據(jù)加密技術(shù)可以保護(hù)信息不被第三方獲取,但是不能阻止惡意用戶(hù)對(duì)信息的篡改。
假如:用戶(hù)A發(fā)送一個(gè)加密信息給解密用戶(hù)B,很長(zhǎng)的時(shí)間都是正常運(yùn)行,但是突然有一天用戶(hù)A的電腦被用戶(hù)C獲取了,但是用戶(hù)C不知道密鑰,所以無(wú)法和用戶(hù)B進(jìn)行溝通,但是用戶(hù)C可以修改包含惡意數(shù)據(jù)的信息發(fā)給用戶(hù)B。
用戶(hù)B無(wú)法解密這些信息,這些惡意的信息還可能對(duì)用戶(hù)B的電腦進(jìn)行攻擊。
所以驗(yàn)證數(shù)據(jù)的完整性和正確性就很重要了,特別是一些涉及到金融和對(duì)安全性要求高的項(xiàng)目,對(duì)數(shù)據(jù)的完整性和正確性要求就更加嚴(yán)格了!
|
散列算法 |
什么是散列算法?
散列算法可以使用任意數(shù)量的數(shù)據(jù),并且用它生成該信息的唯一較小的散列碼。
散列算法的特點(diǎn)
1.單向
如果你想要根據(jù)散列碼來(lái)還原文檔是完全不可能的(除非你具有逆天的能力!),因?yàn)樯⒘胁话臋n的所有信息。
2.最小的沖突
幾乎是不可能創(chuàng)建出2個(gè)散列碼一樣的文檔(如果你無(wú)意成功了,請(qǐng)去把你所有家當(dāng)拿去買(mǎi)彩票吧,記得支援我點(diǎn)!)。

以上這些原因使散列成為驗(yàn)證數(shù)據(jù)完整性的最佳算法。
|
NET中的散列類(lèi) |
NET中的散列算法類(lèi)使用的3層繼承模型與NET中的加密算法模型相同。
NET中所有的散列算法都繼承HashAlgorithm抽象類(lèi)。
|
算法 |
抽象散列算法類(lèi) |
實(shí)現(xiàn)類(lèi) |
散列長(zhǎng)度(位) |
|
MD5 |
MD5 |
MD5CryptoServiceProvider |
128 |
|
SHA-1 |
SHA1 |
SHA1CryptoServiceProvider |
160 |
|
SHA-256 |
SHA256 |
SHA256Managed |
256 |
|
SHA-384 |
SHA384 |
SHA384Managed |
384 |
|
SHA512 |
SHA512 |
SHA512Managed |
512 |
散列長(zhǎng)度和安全性
對(duì)于大部分散列算法來(lái)說(shuō),散列的長(zhǎng)度越長(zhǎng),散列碼抵抗蠻力攻擊的能力就越強(qiáng),發(fā)現(xiàn)相同散列文檔也就越難。
如果一個(gè)散列有256位,那么它就有2^256次方這么多種不同的散列碼,貌似宇宙也只有10^66種原子!現(xiàn)在知道你是在逆天了吧!
下面我們使用MD5實(shí)現(xiàn)個(gè)簡(jiǎn)單的數(shù)據(jù)散列
View Code
static void Main(string[] args)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
Console.WriteLine("MD5 Hash size:{0} bits", md5.HashSize);
Console.WriteLine("MD5 Hash size:{0} bytes", md5.HashSize / 8);
Console.WriteLine();
byte[] data = { 0x13, 0x45, 0x5A, 0xB6 };
//計(jì)算data的散列碼
byte[] HashBytes = md5.ComputeHash(data);
Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length);
Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes));
Console.Read();
}

多次運(yùn)行這個(gè)程序,如果不修改byte數(shù)組的數(shù)據(jù),那么生成的散列碼都是相同的。
對(duì)于不同的MD5對(duì)象去計(jì)算相同的數(shù)據(jù)散列,其結(jié)果是相同的。
View Code
static void Main(string[] args)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
MD5CryptoServiceProvider md51 = new MD5CryptoServiceProvider();
byte[] data = { 0x13, 0x45, 0x5A, 0xB6 };
//計(jì)算data的散列碼
byte[] HashBytes = md5.ComputeHash(data);
Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length);
Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes));
byte[] HashBytes1 = md5.ComputeHash(data);
Console.WriteLine("HashBytes1 size:{0} bytes", HashBytes1.Length);
Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes1));
Console.Read();
}

散列對(duì)數(shù)據(jù)流的支持
ComputeHash有個(gè)重載的方法,接受一個(gè)流對(duì)象,然后從流的當(dāng)前位置讀取完整的流數(shù)據(jù)并且對(duì)其計(jì)算散列。
View Code
static void Main(string[] args)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] data = { 0x13, 0x45, 0x5A, 0xB6 };
MemoryStream ms = new MemoryStream();
ms.Write(data, 0, data.Length);
//計(jì)算data的散列碼
byte[] HashBytes = md5.ComputeHash(ms);
Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length);
Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes));
Console.Read();
}
請(qǐng)注意散列以后的流的內(nèi)部指針位置,如果需要重新使用該流,那么請(qǐng)重新設(shè)置該流的位置。
散列CryptoStream中的數(shù)據(jù)
回憶之前我們對(duì)稱(chēng)加密算法中對(duì)CryptoStream的使用,
DES des = DESCryptoServiceProvider.Create();
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
這里des.CreateEncryptor()方法返回的是ICryptoTransform,我們?cè)賮?lái)看看MD5的基類(lèi)實(shí)現(xiàn)。
public abstract class MD5 : HashAlgorithm
public abstract class HashAlgorithm : ICryptoTransform, IDisposable
所以我們可以把散列對(duì)象傳入CryptoStream中(注意CryptoStream必須是在寫(xiě)的模式下)。
View Code
static void Main(string[] args)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] data = { 0x13, 0x45, 0x5A, 0xB6 };
MemoryStream ms = new MemoryStream();
ms.Write(data, 0, data.Length);
MemoryStream ms1 = new MemoryStream();
CryptoStream cry = new CryptoStream(ms1, md5, CryptoStreamMode.Write);
cry.Write(data, 0, data.Length);
cry.FlushFinalBlock();
ms1.Close();
byte[] HashBytes = md5.Hash;
Console.WriteLine("HashBytes size:{0} bytes", HashBytes.Length);
Console.WriteLine("Hash:{0}", BitConverter.ToString(HashBytes));
Console.Read();
}
介紹完了散列的基本用法,下面介紹下散列的應(yīng)用
|
加密散列碼 |
使用散列就是為了驗(yàn)證數(shù)據(jù)的完整性,那么如果非法用戶(hù)向服務(wù)器發(fā)送了一條錯(cuò)誤的信息,并且對(duì)其生成了正確的散列碼,那么服務(wù)器驗(yàn)證發(fā)現(xiàn)散列碼是正確的,但是卻無(wú)法對(duì)數(shù)據(jù)正確的解密,而且那些數(shù)據(jù)很有可能是包含了惡意的攻擊代碼。
所以在實(shí)際的應(yīng)用中一般是把明文信息和散列碼一起加密發(fā)送到解密方,但是這樣的方法還是有個(gè)缺點(diǎn),就是如果非法用戶(hù)獲取了公鑰,那么還是可以偽造密文和加密散列。
散列加密文件
這里我們使用前面介紹到的net中的散列知識(shí)對(duì)一個(gè)文件進(jìn)行散列然后對(duì)散列碼和原文進(jìn)行加密,然后再解密驗(yàn)證散列碼。
(具體看代碼吧,啰嗦太累了)
static void Main(string[] args)
{
//只是圖方便,實(shí)際項(xiàng)目別這樣不規(guī)范
Encryptor();
Decryptor();
Console.Read();
}
public static void Encryptor()
{
CryptoStream cry = null; ;
FileStream FileInput = null;
FileStream FileOut = null;
//判斷是否應(yīng)該關(guān)閉CryptoStream
bool cryb = true;
try
{
FileInput = new FileStream(@"c:\123.xml", FileMode.Open);
FileOut = new FileStream(@"c:\456.xml", FileMode.Create);
byte[] key = Encoding.ASCII.GetBytes("ujd87yr4");
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
cry = new CryptoStream(FileOut, des.CreateEncryptor(key, key),
CryptoStreamMode.Write);
//考慮到加密xml文件可能很大
byte[] bytes = new byte[1024];
int read = 0;
do
{
//讀取到緩沖區(qū)
read = FileInput.Read(bytes, 0, 1024);
//寫(xiě)入加密流
cry.Write(bytes, 0, read);
}
while (read > 0);
//注意重新設(shè)置流的位置
FileInput.Position = 0;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
md5.ComputeHash(FileInput);
cry.Write(md5.Hash, 0, md5.Hash.Length);
cry.FlushFinalBlock();
Console.WriteLine("完成...");
}
catch (IOException ex)
{
Console.WriteLine("IOException:{0}", ex.Message);
}
catch (CryptographicException ex1)
{
cryb = false;
Console.WriteLine("CryptographicException:{0}", ex1.Message);
}
finally
{
if (FileInput != null)
FileInput.Close();
if (FileOut != null)
FileOut.Close();
if (cryb && cry != null)
cry.Close();
}
}
public static void Decryptor()
{
CryptoStream cry = null; ;
FileStream FileOut = null;
MemoryStream ms = null;
//判斷是否應(yīng)該關(guān)閉CryptoStream
bool cryb = true;
try
{
FileOut = new FileStream(@"c:\456.xml", FileMode.Open);
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
byte[] key = Encoding.ASCII.GetBytes("ujd87yr4");
des.Key = des.IV = key;
cry = new CryptoStream(FileOut, des.CreateDecryptor(), CryptoStreamMode.Read);
ms = new MemoryStream();
//考慮到解密xml文件可能很大
byte[] bytes = new byte[1024];
Int32 read = 0;
do
{
read = cry.Read(bytes, 0, 1024);
ms.Write(bytes, 0, read);
} while (read > 0);
//設(shè)置內(nèi)存流位置
ms.Position = 0;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
Int32 HashSize = md5.HashSize / 8;
//獲取xml解密數(shù)據(jù)
byte[] FileBytes = new byte[ms.Length - HashSize];
ms.Read(FileBytes, 0, FileBytes.Length);
//獲取md5散列碼
byte[] Md5Bytes = new byte[HashSize];
//不出問(wèn)題那么內(nèi)存流當(dāng)前位置就是我們期待的散列碼了
ms.Read(Md5Bytes, 0, Md5Bytes.Length);
md5.ComputeHash(FileBytes);
//驗(yàn)證數(shù)據(jù)完整性
bool b = true;
for (Int32 i = 0; i < md5.Hash.Length; i++)
{
if (md5.Hash[i] != Md5Bytes[i])
{
b = false;
break;
}
}
if (b)
{
Console.WriteLine("數(shù)據(jù)完整");
Console.WriteLine(Encoding.UTF8.GetString(FileBytes));
}
else
Console.WriteLine("數(shù)據(jù)不完整");
}
catch (IOException ex)
{
Console.WriteLine("IOException:{0}", ex.Message);
}
catch (CryptographicException ex1)
{
cryb = false;
Console.WriteLine("CryptographicException:{0}", ex1.Message);
}
finally
{
if (ms != null)
ms.Close();
if (FileOut != null)
FileOut.Close();
if (cryb && cry != null)
cry.Close();
}
}

|
作者:海不是藍(lán) 博客:http://www.rzrgm.cn/hailan2012/ 郵箱:hailan2012@sina.com 本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。 |

浙公網(wǎng)安備 33010602011771號(hào)