C# 電子簽章的實現
電子簽章(Electronic Signature)、數字簽章(Digital Signature)、加密簽名、公鑰私鑰、非對稱加密、雜湊加密、C#、MD5
摘要:
電子簽章(Electronic Signature)泛指所有以電子形式存在,依附在電子檔案並與其邏輯相關,可用以辨識電子檔案簽署者身份,保證檔案的完整性,並表示簽署者同意電子檔案所陳述事項的內容。包括數字簽章技術和逐漸普及的用於身份驗證的生物識別技術如指紋、面紋、DNA技術等。本文從OA系統專案中數字簽章技術的設計來講述如何使用微軟公司的程式設計平臺Vs.Net中的C#語言來實現數字簽章技術。
正文:
1、所謂電子簽章就是用於電子檔案之上,與傳統的手寫簽名、蓋章具有完全相同功能的技術。有了電子簽章,任何資訊都可以放心地通過網路以電子檔案的形式傳輸,因此,電子簽章問題是電子商務和電子政務建設中必須首先解決的核心問題。
2、傳統的交易行為和政府間的公文傳送,必須要用書面的檔案來完成,為了保證檔案是某個當事人或者機關簽發的,並且檔案沒有被篡改,還必須要有簽發人的手寫簽字或者公章。電子檔案並非只用於合同的簽訂和政府檔案的傳遞,而是適用於所有資訊的傳遞。可以理想化地認為,未來的資訊傳遞,可以不借助於任何紙張。
3、 例項
目前最成熟的電子簽章技術就是“數字簽章(Digital Signature)”,它是以公鑰及私鑰的“非對稱型”密碼技術製作的電子簽章。使用原理大致為:由計算機程式將金鑰和需傳送的檔案濃縮成資訊摘要予以運算,得出數字簽章,將數字簽章並同原交易資訊傳送給對方,後者可用公鑰來驗證該資訊確實由前者傳送、並可查驗檔案在傳送過程是否遭他人篡改,並防止對方抵賴。由於數字簽章技術採用的是單向不可逆運算方式,要想對其破解,以目前的計算機速度以及技術而言,幾乎是不可能的。檔案傳輸可選擇明文或密文進行傳輸。因此,從某種意義上講,使用電子檔案和數字簽章,甚至比使用經過簽字蓋章的書面檔案安全得多。
設計之初,我就先提出了需要解決的幾個問題:
如何保證電子檔案在傳輸過程中不被篡改?
如何保證電子檔案合法性及不可抵賴?
一般來說要保證電子檔案在傳輸過程中不被篡改,一是對電子檔案進行私鑰加密。在傳輸方使用私鑰對電子檔案進行明文加密,接收方再用私鑰對傳輸過來的密文進行解密,以此來達到電子檔案不被篡改。但此方法需要傳輸方與接收方都擁有私鑰而且出現問題後雙方都可能指責對方將私鑰解密,因此單純使用此方法,一般都只用於安全性要求不高的應用,不值得推薦。二是傳輸方對電子檔案建立雜湊,將電子檔案與雜湊一同發給接收方,接收方對接受到的電子檔案也建立雜湊,然後對比雜湊值,以此來達到電子檔案不被篡改。這是本人所選用的方案。下面是根據本人電子簽章設計所寫的一個DEMO片段,以供參考。
以下是對電子檔案建立雜湊的具本實現(使用了MD5加密):
//// <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);
}
主體程式
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();
}
4、 問題的解決
由主體程式我們可以得出這樣的結論,當對電子檔案進行數字簽名時,此時電子文件是得到了簽名者的認可的,也證明了此時電子文件的合法性與不可抵賴。如果此電子文件在傳輸過程中被篡改後,則數字簽名失效,並可證明被篡改的電子文件與進行數字簽名時被認可的電子文件不一致。這樣就保護了雙方利益。
而此實現也楔合了《中華人民共和國電子簽名法》第五條第二款,“能夠可靠地保證自最終形成時起,內容保持完整、未被更改。但是,在資料電文上增加背書以及資料交換、儲存和顯示過程中發生的形式變化不影響資料電文的完整性。”
5、 結論及展望
當然,根據《中華人民共和國電子簽名法》第十三條,本例只完成了其中第三第四二條款,其第一第二二個條款還需完善。以下是第十三條:
(一)電子簽名製作資料用於電子簽名時,屬於電子簽名人專有;
(二)簽署時電子簽名製作資料僅由電子簽名人控制;
(三)簽署後對電子簽名的任何改動能夠被發現;
(四)簽署後對資料電文內容和形式的任何改動能夠被發現。