net加密基礎(chǔ)2-非對(duì)稱加密
2012-02-09 20:31 海不是藍(lán) 閱讀(2541) 評(píng)論(6) 收藏 舉報(bào)|
非對(duì)稱密碼算法 |
非對(duì)稱密碼算法使用2個(gè)不同但在數(shù)學(xué)上卻相關(guān)的密鑰。用于加密數(shù)據(jù)的密鑰不能用于解密。
非對(duì)稱密碼技術(shù)統(tǒng)稱稱為“公鑰加密技術(shù)”,沒(méi)有雙方必要保密的單個(gè)密鑰。
公鑰加密技術(shù)只有一個(gè)必須由一方保密的私鑰,第二個(gè)密鑰就是公鑰,任何想與他們通信的人都可以隨意使用它。
安全web通信就是采用的這種技術(shù)。該技術(shù)可以用于電子商務(wù)。
|
非對(duì)稱和對(duì)稱的區(qū)別 |
對(duì)稱加密和非對(duì)稱加密的密鑰類型分別是私鑰和公鑰。
對(duì)稱加密使用的是私鑰加密,也就是密鑰只能是加密者和解密者知道,第3方不能知道。
而非對(duì)稱加密情況完全不一樣,非對(duì)稱加密有2個(gè)不同的密鑰,一個(gè)是公共的密鑰,一個(gè)是私有的密鑰,公鑰是可以讓外人知道的,因?yàn)楣€只用來(lái)加密數(shù)據(jù),而私鑰只能由解密方知道,只有私鑰能解密!
官方的啰嗦!
公鑰加密使用一個(gè)必須對(duì)未經(jīng)授權(quán)的用戶保密的私鑰和一個(gè)可以對(duì)任何人公開(kāi)的公鑰。公鑰和私鑰都在數(shù)學(xué)上相關(guān)聯(lián);用公鑰加密的數(shù)據(jù)只能用私鑰解密,而用私鑰簽名的數(shù)據(jù)只能用公鑰驗(yàn)證。公鑰可以提供給任何人;公鑰用于對(duì)要發(fā)送到私鑰持有者的數(shù)據(jù)進(jìn)行加密。兩個(gè)密鑰對(duì)于通信會(huì)話都是唯一的。公鑰加密算法也稱為不對(duì)稱算法,原因是需要用一個(gè)密鑰加密數(shù)據(jù)而需要用另一個(gè)密鑰來(lái)解密數(shù)據(jù)。
|
經(jīng)典的小紅和小明 |
雙方(小紅和小明)可以按照下列方式使用公鑰加密。首先,小紅生成一個(gè)公鑰/私鑰對(duì)。如果小明想要給小紅發(fā)送一條加密的消息,他將向她索要她的公鑰。小紅通過(guò)不安全的網(wǎng)絡(luò)將她的公鑰發(fā)送給小明,小明接著使用該密鑰加密消息。(如果小明在不安全的信道如公共網(wǎng)絡(luò)上收到小紅的密鑰,則小明必須同小紅驗(yàn)證他具有她的公鑰的正確副本。)小明將加密的消息發(fā)送給小紅,而小紅使用她的私鑰解密該消息。
但是,在傳輸小紅的公鑰期間,未經(jīng)授權(quán)的代理可能截獲該密鑰。而且,同一代理可能截獲來(lái)自小明的加密消息。但是,該代理無(wú)法用公鑰解密該消息。該消息只能用小紅的私鑰解密,而該私鑰沒(méi)有被傳輸。小紅不使用她的私鑰加密給小明的答復(fù)消息,原因是任何具有公鑰的人都可以解密該消息。如果小紅想要將消息發(fā)送回小明,她將向小明索要他的公鑰并使用該公鑰加密她的消息。然后,小明使用與他相關(guān)聯(lián)的私鑰來(lái)解密該消息。
在一個(gè)實(shí)際方案中,小紅和小明使用公鑰(不對(duì)稱)加密來(lái)傳輸私(對(duì)稱)鑰,而對(duì)他們的會(huì)話的其余部分使用私鑰加密。
|
NET中的非對(duì)稱加密算法類 |
||||
|
算法 |
抽象算法類 |
默認(rèn)實(shí)現(xiàn)類 |
有效密鑰大小(位) |
默認(rèn)密鑰大小(位) |
|
RSA |
RSA |
RSACryptoServiceProvider |
364-16384 |
1024 |
|
DSA |
DSA |
DSACryptoServiceProvider |
364-512 |
1024 |
注意:公鑰加密算法使用固定的緩沖區(qū)大小,而私鑰加密算法使用長(zhǎng)度可變的緩沖區(qū)。公鑰算法無(wú)法像私鑰算法那樣將數(shù)據(jù)鏈接起來(lái)成為流,原因是它只可以加密少量數(shù)據(jù)。因此,不對(duì)稱操作不使用與對(duì)稱操作相同的流模型。
所有的非對(duì)稱加密算法都是由AsymmetricAlgorithm類派生。

|
非對(duì)稱RSA加密算法 |
實(shí)際使用中很少直接用RSA類來(lái)實(shí)現(xiàn)加密算法,因?yàn)樗鼈儾捎貌贿M(jìn)行填充的原始計(jì)算。而是使用RSACryptoServiceProvider實(shí)現(xiàn)類的加密和解密方法
public byte[] Decrypt(byte[] rgb, bool fOAEP);
public byte[] Encrypt(byte[] rgb, bool fOAEP);
創(chuàng)建個(gè)rsa算法的對(duì)象
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
我們?cè)趺传@取公鑰和私鑰信息?
之前的對(duì)稱算法可以獲取算法的key和IV信息。
但是非對(duì)稱算法不提供可以用于檢索密鑰信息的任何屬性!如果需要保存密鑰信息供以后使用,就只能用ToXmlString()方法輸出密鑰對(duì)的信息。
public override string ToXmlString(bool includePrivateParameters);
這個(gè)布爾參數(shù)如果設(shè)置為true,那么就會(huì)輸出帶私鑰的xml,如果是false,那么就只輸出帶公鑰的xml。
嘗試輸出完整的密鑰對(duì)
rsa.ToXmlString(true)
----------------------------完整的密鑰對(duì)信息-----------------------------
<RSAKeyValue>
<Modulus>yvGBq09qcWhIi+u3UHGK0EH5zav018FVGM+BPiXK23nFZeRky9HLrOH9DP5Bc7NXyBxoE0MC3YmaG68tr6nHtLhI7
TVshAsfMX1E2BjTfLq1wHhcFwTJSvyNpwnR9NHMZsDE166XgRQ5vHkfFwBWvk7F8YVcACKp3Y6OcvAcp1s=</Modulus>
<Exponent>AQAB</Exponent>
<P>7+9YUu/6SFlOW4RrON/Lie+bMSmOZB/wfqj9AAdmX+n8joPQ/WLA69/CtetzIZ6KIOkD2fd8MFiFrjoHSXmpUQ==</P>
<Q>2IgZma4hTV3EWFP3TXasajl23oRoLBeuWZldu+Q2pyeCwuBE6Mmp2aTbXauPGw3ptUAX/8pw0QyI/6i0b3wa6w==</Q>
<DP>F4AW45Czr/BnV1lh8yEgW3NHfQo38yCZup4soZsX8N8HKKJKjvbkNHYnKRBVp35SwyRvhyLRXB7fgRAX9J9g8Q==</DP>
<DQ>SSgCbje0rKznb2g+/37+1YzAqoFVqL//eeolDxwVkvf4Z9rZrUSlDBF0w/r4iI10znXvJc7Buv9fMfFPtPLbLQ==</DQ>
<InverseQ>pJ37e7nw8Joy2dmnrYf5urx88NQz1TBKZF/xGReSeTiBsbkugqkoHWvX6GoWV8TL7Gx9ZRmAma/rs3YWQbk00Q==</InverseQ>
<D>PrjbibW2wSwo183XTy54Z5sseIt/1brz8QIZALsvchu1jaNEH9ZMa7dAvWZLllXEeJ2G8QUR+qRPk+TVaug/RydliAPdf11h+7nnIoNOzk4LJtBFmNROPXTOLNwap+GJWIQM1Hd72V4kDiv1He3HUNLsLgGmUelDWekISUzrYOE=</D>
</RSAKeyValue>
上面的xml信息可以在我們需要使用的時(shí)候?qū)氲剿惴ɡ锩妗?/span>
只輸出公鑰信息
rsa.ToXmlString(false)
----------------------------只包含公鑰信息-----------------------------
<RSAKeyValue>
<Modulus>igBXA4/oLVLNP+r9jxdx1lLfqNNkSUOe0ApKql8/jmUGc/dIzSMF6bgBcvw5dBxXncYVXxrtk7
AEilemz8PQui07M0NbJ4IeGQzRcJJNlAsQJvGWTydaoH/xLm4I0wH13RG2V9
UjcOYPMOHBEapB4Flkst44qqclw8SVuh55OWk=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
一個(gè)簡(jiǎn)單的RSA例子
RSA簡(jiǎn)單例子
static void Main()
{
Byte[] StrByte = Encoding.UTF8.GetBytes("我是明文");
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string str = RSAEncryptor(rsa.ToXmlString(false), StrByte);
Console.WriteLine("密文:{0}", str);
Encoding en = Encoding.UTF8;
Console.WriteLine("明文:{0}", RSADecryptor(rsa.ToXmlString(true), Convert.FromBase64String(str), en));
Console.Read();
}
public static string RSAEncryptor(string XMLKey, byte[] Str)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(XMLKey);
return Convert.ToBase64String(rsa.Encrypt(Str, false));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
public static string RSADecryptor(string XMLKey, byte[] Str, Encoding en)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(XMLKey);
return en.GetString(rsa.Decrypt(Str, false));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}

如果給解密的RSA對(duì)象傳輸個(gè)不包含私鑰的XML,那么就會(huì)異常了,
你可以講上面調(diào)用解密的代碼修改成下面這樣,就能得到異常了
Console.WriteLine("明文:{0}", RSADecryptor(rsa.ToXmlString(false), Convert.FromBase64String(str), en));
|
使用參數(shù)來(lái)傳遞密鑰信息 |
RSAParameters
RSACryptoServiceProvider有2個(gè)方法可以導(dǎo)出RSAParameters類型的參數(shù)對(duì)象。
在使用加密解密算法的時(shí)候我們就只需要傳遞RSAParameters對(duì)象
將上面的代碼成使用RSAParameters
使用RSAParameters
static void Main()
{
Byte[] StrByte = Encoding.UTF8.GetBytes("使用RSAParameters");
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string str = RSAEncryptor(rsa.ExportParameters(false), StrByte);
Console.WriteLine("密文:{0}", str);
Encoding en = Encoding.UTF8;
Console.WriteLine("明文:{0}", RSADecryptor(rsa.ExportParameters(true), Convert.FromBase64String(str), en));
Console.Read();
}
public static string RSAEncryptor(RSAParameters KeyPar, byte[] Str)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(KeyPar);
return Convert.ToBase64String(rsa.Encrypt(Str, false));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
public static string RSADecryptor(RSAParameters KeyPar, byte[] Str, Encoding en)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(KeyPar);
return en.GetString(rsa.Decrypt(Str, false));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}

|
使用容器存儲(chǔ)CSP密鑰 |
之前使用的導(dǎo)出XML密鑰信息的做法并不妥當(dāng),推薦使用密鑰容器。
有關(guān)密鑰容器的更多信息,請(qǐng)參見(jiàn)位于 http://www.microsoft.com/china/msdn 上的 Platform SDK 文檔中的“CryptoAPI”一節(jié)。
我們這里使用的密鑰容器是CspParameters類,當(dāng)我們創(chuàng)建個(gè)CspParameters對(duì)象時(shí),會(huì)生成隨機(jī)的密鑰對(duì)信息。我們只需要使用這個(gè)對(duì)象,便能方便的把密鑰信息傳遞給相關(guān)的加密解密方法。
不使用密鑰容器創(chuàng)建2個(gè)不同的RSA對(duì)象
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider();
string xml1 = rsa1.ToXmlString(true);
RSACryptoServiceProvider rsa2 = new RSACryptoServiceProvider();
string xml2 = rsa2.ToXmlString(true);
Console.WriteLine(xml1 == xml2);
輸出False
使用密鑰容器創(chuàng)建2個(gè)不同的RSA對(duì)象
CspParameters csp = new CspParameters();
csp.KeyContainerName = "MyCspParameters";
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);
string xml1 = rsa1.ToXmlString(true);
RSACryptoServiceProvider rsa2 = new RSACryptoServiceProvider(csp);
string xml2 = rsa2.ToXmlString(true);
Console.WriteLine(xml1 == xml2);
輸出True
|
RSA加密大數(shù)據(jù)對(duì)象 |
RSA算法的一個(gè)弱點(diǎn)是只能加密117字節(jié)數(shù)據(jù),前面的實(shí)例都是比較小的數(shù)據(jù)。
所以加密大數(shù)據(jù)對(duì)象的時(shí)候我們只能使用分塊加密,講數(shù)據(jù)分成多次加密。
(注意RSA加密后的Byte數(shù)組長(zhǎng)度固定為128,117和128都是在默認(rèn)密鑰大小下的規(guī)定,如果您使用的不是默認(rèn)密鑰大小,那么就會(huì)不一樣)
解密的思維也是將加密后的字節(jié)數(shù)組分多次來(lái)解密,這里每次最大每次解密128字節(jié)數(shù)據(jù)。
static void Main()
{
Byte[] StrByte = Encoding.UTF8.GetBytes("王祖賢(1967年1月31日出生),英文名Joey Wong,身高1.72米。出生于臺(tái)灣臺(tái)北市,祖籍安徽舒城。臺(tái)灣出道后于香港發(fā)展之電影女演員。17歲時(shí)拍攝第一部電影《今年的湖畔會(huì)很冷》并獲得金馬獎(jiǎng)三項(xiàng)提名。1987年以《倩女幽魂》一片開(kāi)始走紅于亞洲各地。2009年7月7日傳王祖賢因感情不順在加拿大削發(fā)為尼。王祖賢本人對(duì)此表示否認(rèn),王父也通過(guò)王祖賢干爹傅達(dá)仁否認(rèn)傳言。");
Console.WriteLine(StrByte.Length);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string str = RSAEncryptor(rsa.ExportParameters(false), StrByte);
Console.WriteLine("密文:{0}", str);
Encoding en = Encoding.UTF8;
Console.WriteLine("明文:{0}", RSADecryptor(rsa.ExportParameters(true), Convert.FromBase64String(str), en));
Console.Read();
}
public static string RSAEncryptor(RSAParameters par, byte[] Str)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(par);
//獲取RSA最大加密的長(zhǎng)度
Int32 KeySize = rsa.KeySize / 8;
Int32 BlockSize = KeySize - 11;
if (Str.Length > BlockSize)
{
//分配加密次數(shù)
Int32 EncryptIndex = 0;
if (Str.Length % BlockSize != 0)//不能整除
{
EncryptIndex = Str.Length / BlockSize + 1;
}
else
{
EncryptIndex = Str.Length / BlockSize;
}
//分配存儲(chǔ)已加密的byte數(shù)組,請(qǐng)注意下這里分配的大小
//每次只能加密117長(zhǎng)度的數(shù)據(jù),但是每次加密117長(zhǎng)度的數(shù)據(jù)要生成128長(zhǎng)度的密文。
Byte[] EncryptByte = new Byte[EncryptIndex * KeySize];
for (Int32 i = 0; i < EncryptIndex; i++)
{
Byte[] Block = new Byte[BlockSize];
if (i == (EncryptIndex - 1))
{
//最后一次加密
Array.Copy(Str, i * BlockSize, Block, 0, Str.Length % BlockSize);
}
else
{
Array.Copy(Str, i * BlockSize, Block, 0, BlockSize);
}
Byte[] Block1 = rsa.Encrypt(Block, false);
//將加密過(guò)的Byte數(shù)組保存到EncryptByte中
Array.Copy(Block1, 0, EncryptByte, i * KeySize, KeySize);
}
return Convert.ToBase64String(EncryptByte);
}
else
{
byte[] b = rsa.Encrypt(Str, false);
return Convert.ToBase64String(b);
}
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
public static string RSADecryptor(RSAParameters par, byte[] Str, Encoding en)
{
try
{
Byte[] Block1;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(par);
Int32 KeySize = rsa.KeySize / 8;
Int32 BlockSize = KeySize - 11;
if (Str.Length == KeySize)
{
return en.GetString(rsa.Decrypt(Str, false));
}
else
{
//這個(gè)變量用來(lái)記錄所有解密后的Byte數(shù)組的長(zhǎng)度
Int32 Len = 0;
//保存每次解密后的Byte數(shù)組
List<Byte[]> list = new List<Byte[]>();
//計(jì)算解密需要的次數(shù)
Int32 EncryptIndex = Str.Length / KeySize;
for (Int32 i = 0; i < EncryptIndex; i++)
{
Byte[] Block = new Byte[KeySize];
Array.Copy(Str, i * KeySize, Block, 0, KeySize);
Block1 = rsa.Decrypt(Block, false);
Len += Block1.Length;
list.Add(Block1);
}
Byte[] EncryptByte = new Byte[Len];
for (Int32 i = 0; i < list.Count; i++)
{
Array.Copy(list[i], 0, EncryptByte, i * BlockSize, BlockSize);
}
return en.GetString(EncryptByte);
}
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}

| RSA的優(yōu)點(diǎn)和缺點(diǎn) |
優(yōu)點(diǎn)
1.安全性高,幾乎不會(huì)被攻破。
缺點(diǎn)
1.速度慢,數(shù)學(xué)原理復(fù)雜。
2.單次加密數(shù)據(jù)小,加密大數(shù)據(jù)處理復(fù)雜
|
作者:海不是藍(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)