作者:代恩
工作單位:武漢英思工程科技有限公司
日期:
關鍵字:
電子簽章(Electronic Signature)、數字簽章(Digital Signature)、加密簽名、公鑰私鑰、非對稱加密、哈希加密、C#、MD5
摘要:
電子簽章(Electronic Signature)泛指所有以電子形式存在,依附在電子文件並與其邏輯相關,可用以辨識電子文件簽署者身份,保證文件的完整性,並表示簽署者同意電子文件所陳述事項的內容。包括數字簽章技術和逐漸普及的用於身份驗證的生物識別技術如指紋、面紋、DNA技術等。本文從本人蔘與OA系統項目中數字簽章技術的設計來講述如何使用微軟公司的編程平臺Vs.Net中的C#語言來實現數字簽章技術。
正文:
1、緒論
經過10年的高速發展,互聯網已經慢慢地滲透到我們的日常生活中,從各個方面不經意地改變着人類的生活。電子商務越來越接近理想中的樣子了;電子政務也正在一步步地走向我們……這一系列新興網絡行業發展的同時也帶來了許多法律問題,如網絡通訊的安全與隱私保護問題、知識產權問題、電子支付問題、合同問題、交易認證問題等等。
爲解決這些問題,國家在2004年8月28日通過《中華人民共和國電子簽名法》,此法令從法律意義上肯定了電子簽章的作用,並明確了電子簽章的用途及範圍,爲電子簽章的進一步發展提供了法律上的保證。
所謂電子簽章就是用於電子文件之上,與傳統的手寫簽名、蓋章具有完全相同功能的技術。有了電子簽章,任何信息都可以放心地通過網絡以電子文件的形式傳輸,因此,電子簽章問題是電子商務和電子政務建設中必須首先解決的核心問題。
2、引言
傳統的交易行爲和政府間的公文傳送,必須要用書面的文件來完成,爲了保證文件是某個當事人或者機關簽發的,並且文件沒有被篡改,還必須要有簽發人的手寫簽字或者公章。
網絡環境中,文件的傳遞採取電子的方式,比如E-mail。這種方式最大的優點就是速度快,文件可以在數秒中到達遠在萬里之外的客戶手中。在“時間就是金錢、效率就是生命”的生意場上,這種優勢無疑成爲廣大廠商的首選。
電子文件並非只用於合同的簽訂和政府文件的傳遞,而是適用於所有信息的傳遞。可以理想化地認爲,未來的信息傳遞,可以不藉助於任何紙張。
但如何保證一項文件或者一條信息是某個人發出的?某個人發出文件或者信息後,如果發現對自己不利抵賴怎麼辦?如果文件或者信息在網絡傳輸過程中被他人截取,並被篡改了怎麼辦?許多人都知道,我們平時發送和接收的電子郵件是不加密的,對於某些人比如網管員來說,看這些文件甚至比看沒有封上的書面信件更容易。
3、 實例
某信息公司要求研發一套辦公自動化系統,其中就包括了對電子公文進行電子簽章的功能,本人有幸參與並設計了電子簽章部份。
目前最成熟的電子簽章技術就是“數字簽章(Digital Signature)”,它是以公鑰及私鑰的“非對稱型”密碼技術製作的電子簽章。使用原理大致爲:由計算機程序將密鑰和需傳送的文件濃縮成信息摘要予以運算,得出數字簽章,將數字簽章並同原交易信息傳送給對方,後者可用公鑰來驗證該信息確實由前者傳送、並可查驗文件在傳送過程是否遭他人篡改,並防止對方抵賴。由於數字簽章技術採用的是單向不可逆運算方式,要想對其破解,以目前的計算機速度以及技術而言,幾乎是不可能的。文件傳輸可選擇明文或密文進行傳輸。因此,從某種意義上講,使用電子文件和數字簽章,甚至比使用經過簽字蓋章的書面文件安全得多。
設計之初,我就先提出了需要解決的幾個問題:
如何保證電子文件在傳輸過程中不被篡改?
如何保證電子文件合法性及不可抵賴?
一般來說要保證電子文件在傳輸過程中不被篡改,一是對電子文件進行私鑰加密。在傳輸方使用私鑰對電子文件進行明文加密,接收方再用私鑰對傳輸過來的密文進行解密,以此來達到電子文件不被篡改。但此方法需要傳輸方與接收方都擁有私鑰而且出現問題後雙方都可能指責對方將私鑰解密,因此單純使用此方法,一般都只用於安全性要求不高的應用,不值得推薦。二是傳輸方對電子文件創建哈希,將電子文件與哈希一同發給接收方,接收方對接受到的電子文件也創建哈希,然後對比哈希值,以此來達到電子文件不被篡改。這是本人所選用的方案。下面是根據本人電子簽章設計所寫的一個DEMO片段,以供參考。
以下是對電子文件創建哈希的具本實現(使用了MD5加密):
2 /// 得到指定電子文件的哈希
3 /// </summary>
4 /// <param name="filePath">電子文件地址</param>
5 /// <returns>哈希值</returns>
6 public static byte[] GetFileHash(string filePath)
7 {
8 try
9 {
10 FileStream objFile = File.OpenRead(filePath);
11 HashAlgorithm MD5 = HashAlgorithm.Create("MD5");
12 byte[] Hashbyte = MD5.ComputeHash(objFile);
13 objFile.Close();
14 return Hashbyte;
15 }
16 catch
17 {
18 return null;
19 }
20 }
要實現合法性及不可抵賴就必需使用公鑰私鑰非對稱加密方式並結合哈希加密方式。
公鑰及私鑰是一對很有意思的組合,它們是成對使用的,一一對應的,使用私鑰加密的數據,可以使用公鑰來證明是否是由此公鑰所對應的私鑰所加密的,將電子文件的哈希用私鑰加密,接收人就算出電子文件的哈希,然後用公鑰來驗證電子文件是否是傳輸者所確認的文件。
生成公鑰及私鑰:
2 /// 得到公鑰與私鑰
3 /// </summary>
4 /// <param name="ContainerName">私鑰容器名</param>
5 /// <param name="privatekey">真爲得到私鑰,假爲得到公鑰</param>
6 /// <returns>公鑰或私鑰</returns>
7 public static string GetKeyFromContainer(string ContainerName,bool privatekey)
8 {
9 CspParameters cp = new CspParameters();
10 cp.KeyContainerName = ContainerName;
11 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
12 return rsa.ToXmlString(privatekey);
13 }
對電子文件的哈希進行數字簽名:
2 /// 對哈希進行數字簽名
3 /// </summary>
4 /// <param name="privateKey">私鑰</param>
5 /// <param name="fileHash">電子文件哈希</param>
6 /// <returns></returns>
7 public static byte[] EncryptHash(string privateKey,byte[] fileHash)
8 {
9 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
10
11 RSA.FromXmlString(privateKey);
12
13 RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
14
15 RSAFormatter.SetHashAlgorithm("MD5");
16
17 return RSAFormatter.CreateSignature(fileHash);
18 }
19
對數字簽名用公鑰進行驗證
2 /// 對數字簽名用公鑰進行驗證
3 /// </summary>
4 /// <param name="publicKey">公鑰</param>
5 /// <param name="fileHash">接收到的電子文件的哈希</param>
6 /// <param name="electronicSignature">數字簽名</param>
7 /// <returns>數字簽名有效爲真,數字簽名無效爲假</returns>
8 public static bool DecryptHash(string publicKey,byte[] fileHash,byte[] electronicSignature)
9 {
10 RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
11
12 RSA.FromXmlString(publicKey);
13
14 RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);
15
16 RSADeformatter.SetHashAlgorithm("MD5");
17
18 return RSADeformatter.VerifySignature(fileHash,electronicSignature);
19 }
主體程序
2 {
3 // 生成電子文件
4 string filePath = "C://公文.txt";
5 StreamWriter sw = File.CreateText(filePath);
6 sw.Write("測試公文");
7 sw.Close();
8
9 // 對電子文件進行哈希
10 byte[] fileHash = GetFileHash(filePath);
11 // 取得公鑰
12 string publicKey = GetKeyFromContainer("公文",false);
13 // 取得私鑰
14 string privateKey = GetKeyFromContainer("公文",true);
15
16 Console.WriteLine("發送方:A");
17 Console.WriteLine("電子文件地址:");
18 Console.WriteLine(filePath);
19
20 Console.WriteLine("哈希:");
21 Console.WriteLine(ConvertBytesToString(fileHash));
22
23 Console.WriteLine("使用私鑰進行數字簽名:");
24 // 取得電子簽名
25 byte[] ElectronicSignature = EncryptHash(privateKey,fileHash);
26 Console.WriteLine(ConvertBytesToString(ElectronicSignature));
27
28 Console.WriteLine("傳送給接收方。");
29 Console.WriteLine("");
30 string fileCopyPath = "C://公文接收.txt";
31 File.Copy(filePath,fileCopyPath,true);
32
33 Console.WriteLine("是否篡改文件?(Y/N)");
34 string sRe = string.Empty;
35 do
36 {
37 sRe = Console.ReadLine();
38 }
39 while(sRe != "Y" && sRe != "N");
40
41 byte[] fileCopyHash;
42 if(sRe == "N")
43 {
44 Console.WriteLine("接收方收到電子文件。");
45
46 Console.WriteLine("接收方:B");
47 Console.WriteLine("接收文件地址:");
48 Console.WriteLine(fileCopyPath);
49
50 fileCopyHash = GetFileHash(fileCopyPath);
51 Console.WriteLine("哈希:");
52 Console.WriteLine(ConvertBytesToString(fileCopyHash));
53 }
54 else
55 {
56 Console.WriteLine("正在篡改文件。");
57 sw = new StreamWriter(fileCopyPath);
58 sw.WriteLine("公文已被篡改。");
59 sw.Close();
60
61 Console.WriteLine("接收方收到電子文件。");
62
63 Console.WriteLine("接收方:");
64 Console.WriteLine("接收文件地址:");
65 Console.WriteLine(fileCopyPath);
66
67 fileCopyHash = GetFileHash(fileCopyPath);
68 Console.WriteLine("哈希:");
69 Console.WriteLine(ConvertBytesToString(fileCopyHash));
70 }
71
72 Console.WriteLine("公鑰:");
73 Console.WriteLine(publicKey);
74
75 //使用公鑰進行驗證
76 Console.WriteLine("使用公鑰進行驗證:");
77 if(DecryptHash(publicKey,fileCopyHash,ElectronicSignature))
78 {
79 Console.WriteLine("通過驗證,電子文件合法有效。");
80 }
81 else
82 {
83 Console.WriteLine("未通過驗證,電子文件非法或被人篡改過。");
84 }
85
86 Console.Read();
87 }
4、 問題的解決
由主體程序我們可以得出這樣的結論,當對電子文件進行數字簽名時,此時電子文檔是得到了簽名者的認可的,也證明了此時電子文檔的合法性與不可抵賴。如果此電子文檔在傳輸過程中被篡改後,則數字簽名失效,並可證明被篡改的電子文檔與進行數字簽名時被認可的電子文檔不一致。這樣就保護了雙方利益。
而此實現也楔合了《中華人民共和國電子簽名法》第五條第二款,“能夠可靠地保證自最終形成時起,內容保持完整、未被更改。但是,在數據電文上增加背書以及數據交換、儲存和顯示過程中發生的形式變化不影響數據電文的完整性。”
5、 結論及展望
當然,根據《中華人民共和國電子簽名法》第十三條,本例只完成了其中第三第四二條款,其第一第二二個條款還需完善。以下是第十三條:
(一)電子簽名製作數據用於電子簽名時,屬於電子簽名人專有;
(二)簽署時電子簽名製作數據僅由電子簽名人控制;
(三)簽署後對電子簽名的任何改動能夠被發現;
(四)簽署後對數據電文內容和形式的任何改動能夠被發現。
參考文獻:
。。。。。。
本來是不想使用論文這種形式來寫這篇,但爲了鍛鍊自己寫論文的能力,還是斗膽用論文寫出來了,如有何不確之處,望勉之。
完整代碼如下:
using System.IO;
using System.Security.Cryptography;
namespace oozinoz1
{
/**//// <summary>
/// Class5 的摘要說明。
/// </summary>
public class Class5
{
static void Main()
{
// 生成電子文件
string filePath = "C://公文.txt";
StreamWriter sw = File.CreateText(filePath);
sw.Write("測試公文");
sw.Close();
// 對電子文件進行哈希
byte[] fileHash = GetFileHash(filePath);
// 取得公鑰
string publicKey = GetKeyFromContainer("公文",false);
// 取得私鑰
string privateKey = GetKeyFromContainer("公文",true);
Console.WriteLine("發送方:A");
Console.WriteLine("電子文件地址:");
Console.WriteLine(filePath);
Console.WriteLine("哈希:");
Console.WriteLine(ConvertBytesToString(fileHash));
Console.WriteLine("使用私鑰進行數字簽名:");
// 取得電子簽名
byte[] ElectronicSignature = EncryptHash(privateKey,fileHash);
Console.WriteLine(ConvertBytesToString(ElectronicSignature));
Console.WriteLine("傳送給接收方。");
Console.WriteLine("");
string fileCopyPath = "C://公文接收.txt";
File.Copy(filePath,fileCopyPath,true);
Console.WriteLine("是否篡改文件?(Y/N)");
string sRe = string.Empty;
do
{
sRe = Console.ReadLine();
}
while(sRe != "Y" && sRe != "N");
byte[] fileCopyHash;
if(sRe == "N")
{
Console.WriteLine("接收方收到電子文件。");
Console.WriteLine("接收方:B");
Console.WriteLine("接收文件地址:");
Console.WriteLine(fileCopyPath);
fileCopyHash = GetFileHash(fileCopyPath);
Console.WriteLine("哈希:");
Console.WriteLine(ConvertBytesToString(fileCopyHash));
}
else
{
Console.WriteLine("正在篡改文件。");
sw = new StreamWriter(fileCopyPath);
sw.WriteLine("公文已被篡改。");
sw.Close();
Console.WriteLine("接收方收到電子文件。");
Console.WriteLine("接收方:");
Console.WriteLine("接收文件地址:");
Console.WriteLine(fileCopyPath);
fileCopyHash = GetFileHash(fileCopyPath);
Console.WriteLine("哈希:");
Console.WriteLine(ConvertBytesToString(fileCopyHash));
}
Console.WriteLine("公鑰:");
Console.WriteLine(publicKey);
//使用公鑰進行驗證
Console.WriteLine("使用公鑰進行驗證:");
if(DecryptHash(publicKey,fileCopyHash,ElectronicSignature))
{
Console.WriteLine("通過驗證,電子文件合法有效。");
}
else
{
Console.WriteLine("未通過驗證,電子文件非法或被人篡改過。");
}
Console.Read();
}
/**//// <summary>
/// 將Byte[]轉換成十六進制字符串
/// </summary>
/// <param name="bytes">要轉換的Byte[]</param>
/// <returns>十六進制字符串</returns>
public static string ConvertBytesToString(byte[] bytes)
{
string bytestring = string.Empty;
if(bytes != null && bytes.Length > 0)
{
for(int i = 0;i<bytes.Length;i++)
{
bytestring += bytes[i].ToString("X") + " ";
}
}
return bytestring;
}
/**//// <summary>
/// 得到指定電子文件的哈希
/// </summary>
/// <param name="filePath">電子文件地址</param>
/// <returns>哈希值</returns>
public static byte[] GetFileHash(string filePath)
{
try
{
FileStream objFile = File.OpenRead(filePath);
HashAlgorithm MD5 = HashAlgorithm.Create("MD5");
byte[] Hashbyte = MD5.ComputeHash(objFile);
objFile.Close();
return Hashbyte;
}
catch
{
return null;
}
}
/**//// <summary>
/// 得到公鑰與私鑰
/// </summary>
/// <param name="ContainerName">私鑰容器名</param>
/// <param name="privatekey">真爲得到私鑰,假爲得到公鑰</param>
/// <returns>公鑰或私鑰</returns>
public static string GetKeyFromContainer(string ContainerName,bool privatekey)
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = ContainerName;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
return rsa.ToXmlString(privatekey);
}
/**//// <summary>
/// 對哈希進行數字簽名
/// </summary>
/// <param name="privateKey">私鑰</param>
/// <param name="fileHash">電子文件哈希</param>
/// <returns></returns>
public static byte[] EncryptHash(string privateKey,byte[] fileHash)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.FromXmlString(privateKey);
RSAPKCS1SignatureFormatter RSAFormatter = new RSAPKCS1SignatureFormatter(RSA);
RSAFormatter.SetHashAlgorithm("MD5");
return RSAFormatter.CreateSignature(fileHash);
}
/**//// <summary>
/// 對數字簽名用公鑰進行驗證
/// </summary>
/// <param name="publicKey">公鑰</param>
/// <param name="fileHash">接收到的電子文件的哈希</param>
/// <param name="electronicSignature">數字簽名</param>
/// <returns>數字簽名有效爲真,數字簽名無效爲假</returns>
public static bool DecryptHash(string publicKey,byte[] fileHash,byte[] electronicSignature)
{
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.FromXmlString(publicKey);
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(RSA);
RSADeformatter.SetHashAlgorithm("MD5");
return RSADeformatter.VerifySignature(fileHash,electronicSignature);
}
}
}