我們進入資料傳輸安全後,陸續複習了簡單雜湊(Hash)及訊息驗證(authication check),而除了這兩天複習的訊息驗證碼(MAC)可以強化雜湊驗證的識別性外,還有一種數位簽章(Digital Signature)也可以確保我們在檔案傳輸時的完整性及身份識別。
數位簽章(Digital Signature)也是出道很久的安全技術,她是一種電子式的簽名機制,概念是根據檔案內容運算出一個加密後的雜湊驗證值讓資料接收端驗證,可以確保檔案是由正確的來源(避免身分偽冒)而且內容有完整的傳遞。
數位簽章主要使用雜湊與加密演算法的組合,雜湊(hash)可以確保完整,透過安全的金鑰加密雜湊則可以確保正確的識別。
數位簽章與訊息驗證碼(MAC)雖然功能類似,但在金鑰的使用以及演算法上還是有些不同的地方:
C#中可選擇的Library
這邊我們舉RSA的演算法為範例。
為了方便我們聚焦在數位簽章的使用,先把寫出測試檔並回傳檔案內容的功能撰寫好。
public string getFile()
{
    List<string> datas = new List<string>()
    {
        "4567-1111-2222-3333,高雄台鋁生活商場,1000",
        "4567-1111-2222-3333,台北微風信義分店,2000",
        "5567-1111-2222-3333,台南南紡購物中心,3000",
        "END,3,6000"
    };
    StringBuilder sb = new StringBuilder();
    using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\temp\transaction.txt", false))
    {
        foreach (var data in datas)
        {
            sb.AppendLine(data);
            byte[] bdata = Encoding.Default.GetBytes(data);
            file.WriteLine($"{data}");
        }
    }
    return sb.ToString();
}
1.請款廠商建立一組RSA的公鑰及私鑰。
2.檔案內容先經過雜湊(這邊假設我們選擇SHA256)後,取出雜湊值。
3.然後使用私鑰以RSA加密演算法加密。
4.加密後的結果合併在原始檔案之後。
簡單畫一個加密流程圖:
雖然這次數位簽章使用的是RSA加密演算法,不過這邊在加密時並不是使用檔案接收端公開出去的公鑰,而是請款檔案的廠商的私鑰。
好!來寫程式測試了。
[TestMethod]
public void TestEncrypt()
{
    //建立RSA演算法物件
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    
    //顯示公開金鑰
    Console.WriteLine($"public key:{rsa.ToXmlString(false)}");
    //顯示私有金鑰
    Console.WriteLine($"private key:{rsa.ToXmlString(true)}");
    //讀取本文資料
    byte[] plainText = Encoding.Unicode.GetBytes(getFile());
    //進行簽章(假設我們使用SHA256)
    byte[] signText = rsa.SignData(plainText, new SHA256CryptoServiceProvider());
    //寫出簽章檔案
    using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\temp\transaction.txt", true))
    {
        file.WriteLine("\t" + Convert.ToBase64String(signText));
    }
}
加密後的檔案驗證結果:
確保檔案是請款廠商給的,而且檔案未遭竄改。
我們在測試時先簡單建立RSA演算法所使用的參數,但實際操作時請必須把金鑰儲存在憑證中,我們只要在建立RSA演算法物件帶入csp參數並指定金鑰容器及儲存位置。
CspParameters param = new CspParameters();
param.KeyContainerName = "TestDigitalSignature";
param.Flags = CspProviderFlags.UseMachineKeyStore;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(param);
1.取得請款廠商的公開金鑰(或是安裝廠商的憑證)。
2.將檔案內容以分隔符號拆解成原始檔案內容及加密後的雜湊值兩部分。
3.原始檔案內容先經過雜湊後只取出雜湊值,然後再以RSA加密演算法解密。
4.然後比較加密後的雜湊值是否與步驟3相符。
簡單畫一個解密驗證流程圖:
 [TestMethod]
 public void TestVerify()
 {
     //建立RSA演算法物件
     RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
     //假設我們已經安裝了這家商店的憑證,也就是我們本來就有公鑰
     //public key
     string pk = @"<RSAKeyValue>
                   <Modulus>qRE+tUrBv52zLWcVmB24uR3dDkLrhmdzxKy9eJCKW7cuFz+DTOrJKqF+l0ek+JJELVx8VbzSLsFP5s/uVMpSZXN3r23EHC8cQ5nfUfT2C5kT6URdhB6Ucx9TB4TlVLwv3iaLXfdAtnoUpDVhmIXOScvewRAlwV+e5zlIMsOzG2U=</Modulus>
                     <Exponent>AQAB</Exponent>
                   </RSAKeyValue>";
     //載入公開金鑰
     rsa.FromXmlString(pk);     //讀取公開金鑰
     //讀取本文資料
     string AllText = System.IO.File.ReadAllText(@"C:\temp\transaction.txt");
     //使用分隔符號拆解
     if (!AllText.Contains("\t"))
     {
         Console.WriteLine("明文與簽章資料缺少分隔符號\t");
         return;
     }
     byte[] DataText = Encoding.Unicode.GetBytes(AllText.Split('\t')[0]);
     byte[] signText = Convert.FromBase64String(AllText.Split('\t')[1]);
     //驗證數位簽章正確性
     if (rsa.VerifyData(DataText, new SHA256CryptoServiceProvider(), signText))
     {
         Console.WriteLine("驗證成功!");
     }
     else
     {
         Console.WriteLine("驗證失敗!");
     }
 }
驗證成功!
數位簽章希望全世界都能確認自己的身份,而一般RSA加密演算法則希望別人給自己資料時的機密性,所以在公開金鑰與私有金鑰的使用有很大的差異。
DSACryptoServiceProvider
https://msdn.microsoft.com/zh-tw/library/system.security.cryptography.dsacryptoserviceprovider(v=vs.110).aspx
RSACryptoServiceProvider
https://msdn.microsoft.com/zh-tw/library/system.security.cryptography.rsacryptoserviceprovider(v=vs.110).aspx
余小章 @ 大內殿堂 [C#.NET] 字串及檔案,利用 RSA 演算法加解密
https://dotblogs.com.tw/yc421206/archive/2012/06/25/73041.aspx
每天早上4.5點起來很拚,來掛上新竹馬拉松的完賽御守加持。
