馬上就要過年回村裡了,村裡沒有wifi,沒有4G,沒有流量,更加重要的是過幾天電腦就得賣掉換車票了,得趕緊寫幾篇博客。
數據安全的相關技術在現在愈來愈變得重要,因為人們對於自身的信息都有一種保護的欲望,不想被人獲取到自己的私密信息,加密幾乎已經是這個時代的關鍵詞了。在這個HTTPS盛行的時代,作為一個開發人員怎麼可能不去了解和學習呢。這篇博文就來給大家簡單介紹一個HTTPS在.NET種的應用和實現方法。
數字證書和數字簽名的實現主要是基於非對稱加密和數字摘要,數字簽名是數字證書不可或缺的一部分。這篇博客主要講解數字簽名、數字證書,以及數字簽名在.NET種的實現方法。
這裡首先來了解一些什麼叫做數字簽名,數字簽名是附加在數據單元上的一些數據,或是對數據單元所做的密碼變換。數字簽名是對非對稱加密和消息摘要的應用。數簽名的原理:使用非對稱密鑰將簽名函數添加到非對稱算法,創建一個“簽名”,另一方接收加密的信息,使用確認函數來驗證簽名。有如下圖:
說明:用戶A選擇一個非對稱簽名算法創建一對新密鑰,自己保留私鑰,公鑰發給B。用戶B使用用戶A的公鑰來驗證簽名。
將散列碼做為創建數字簽名,有如下圖:
將散列碼作為確認一個數字簽名,有如下圖:
第三方不能偽造用戶A的數字簽名;第三方不能重新使用用戶A的數字簽名;第三方不能改變簽名後的文件;用戶A無法否認自己的簽名文件。數字簽名能夠提供一種和物理簽名類似的合理機制。數字簽名的安全性和加密的其他方面是一樣的,他們都是基於可能的有效密鑰管理的。數字簽名只采用了非對稱密鑰加密算法,能保證發送信息的完整性、身份認證和不可以否認行,數字加密采用了對稱密鑰加密算法和非對稱密鑰加密算法相結合的方法,能夠保證發送信息的保密性。
對於HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer)很多開發人員都不會陌生,即使是普通用戶也是比較的熟悉。數字證書(公鑰證書):用於電子信息活動中電子文件行為主體的驗證和證明,並可實現電子文件保密性和完整性的電子數據。數字證書是一個經證書認證中心發行的證書。
數字證書:個人數字證書,單位數字證書、單位員工數字證書、服務器證書、VPN證書、WAP證書、代碼簽名證書和表單簽名證書等。
數字證書是一個經證書授權重心數字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件,最簡單的證書包含一個公開密鑰、名稱一劑證書授權中心的數字簽名。
數字證書的特點:信息的保密性;交易者身份的確定性;不可否認性、不可修改性。
數字證書的三種保存形式:帶有私鑰的證書;二進制編碼的證書;Base64編碼證書。
在.NET中包含兩種支持數字簽名的非對稱算法:RSA算法(為兩種數據加密和數字簽名定義了函數);DSA算法(支持數字簽名,不支持數據加密)。在.NET中使用RSA算法進行數字簽名使用RSACryptoServiceProvider類,使用DSA進行數字簽名的四個核心類如下圖:
DSA類:數字簽名算法DSA的基類;DSACryptoServiceProvider類:定義訪問DSA算法的加密服務提供程序實現的包裝對象;DSASignatureDeformatter類:驗證DSA簽名;DSASignatureFormatter類:創建DSA簽名;
接下來我們具體了解一下這些類:
1.RSACryptoServiceProvider類:
(1).SignData()方法:使用指定的哈希算法計算指定輸入流的哈希值,並對計算所得的哈希值簽名。
public byte[] SignData(Stream inputStream, object halg) { int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm); return this.SignHash(Utils.ObjToHashAlgorithm(halg).ComputeHash(inputStream), calgHash); }
該方法存在三個重載方法,三個重載方法的第一個參數不同,分別是Stream、byte[]兩個類型。由代碼可以看出,該方法接受兩個參數,inputStream是要計算其哈希值的輸入數據,halg用於創建哈希值的哈希算法。SignHash()通過用私鑰對其進行加密來計算指定哈希值的簽名。
(2).VerifyData():通過使用提供的公鑰確定簽名中的哈希值並將其與所提供數據的哈希值進行比較驗證數字簽名是否有效。
public bool VerifyData(byte[] buffer, object halg, byte[] signature) { int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm); return this.VerifyHash(Utils.ObjToHashAlgorithm(halg).ComputeHash(buffer), calgHash, signature); }
該方法沒有重載版本,有源碼可以看出該方法接收三個參數,分別是:buffer已簽名的數據,halg用於創建數據的哈希值的哈希算法名稱,signature要驗證的簽名數據。該方法返回一個布爾類型,如果簽名有效,則為 true;否則為 false。VerifyHash()通過使用提供的公鑰確定簽名中的哈希值並將其與提供的哈希值進行比較來驗證數字簽名是否有效。
2.DSA類解析:
(1).CreateSignature():創建指定數據的 Cryptography.DSA 簽名。
public abstract byte[] CreateSignature(byte[] rgbHash);
該方法為一個抽象方法,在派生類中重寫,接受一個字節數組表示要簽名的數據,返回指定數據的數字簽名。在使用CreateSignature方法時,必須自己創建SHA-1散列碼,返回一個用字節數組表示的DSA簽名。
(2).VerifySignature():驗證指定數據的 Cryptography.DSA 簽名。
public abstract bool VerifySignature(byte[] rgbHash, byte[] rgbSignature);
該方法接受字符數組表示的SHA-1散列碼和簽名來驗證。
3.DSACryptoServiceProvider類解析:
(1).ImportParameters():導入指定的 DSAParameters。該方法接受一個參數,Cryptography.DSA的參數。
(2).VerifyData():通過將指定的簽名數據與為指定數據計算的簽名進行比較來驗證指定的簽名數據。
public bool VerifyData(byte[] rgbData, byte[] rgbSignature) { return this.VerifyHash(this._sha1.ComputeHash(rgbData), (string) null, rgbSignature); }
該方法接受兩個參數,rgbData已簽名的數據;rgbSignature要驗證的簽名數據,如果簽名驗證為有效,則為 true;否則,為 false。VerifyHash()通過將指定的簽名數據與為指定哈希值計算的簽名進行比較來驗證指定的簽名數據,我們看一下VerifyHash()的實現代碼:
public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) { if (rgbHash == null) throw new ArgumentNullException("rgbHash"); if (rgbSignature == null) throw new ArgumentNullException("rgbSignature"); int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm); if (rgbHash.Length != this._sha1.HashSize / 8) { string key = "Cryptography_InvalidHashSize"; object[] objArray = new object[2]; int index1 = 0; string str1 = "SHA1"; objArray[index1] = (object) str1; int index2 = 1; // ISSUE: variable of a boxed type __Boxed<int> local = (ValueType) (this._sha1.HashSize / 8); objArray[index2] = (object) local; throw new CryptographicException(Environment.GetResourceString(key, objArray)); } this.GetKeyPair(); return Utils.VerifySign(this._safeKeyHandle, 8704, calgHash, rgbHash, rgbSignature); }
該方法接收三個參數,rgbHash要簽名的數據的哈希值,str用於創建數據的哈希值的哈希算法名稱,rgbSignature要驗證的簽名數據。
該類在System.Security.Cryptography.X509Certificates空間下,提供幫助你使用 X.509 v.3 證書的方法。
(1).LoadCertificateFromBlob():加載證書:
private void LoadCertificateFromBlob(byte[] rawData, object password, X509KeyStorageFlags keyStorageFlags) { if (rawData == null || rawData.Length == 0) throw new ArgumentException(Environment.GetResourceString("Arg_EmptyOrNullArray"), "rawData"); if (X509Utils.MapContentType(X509Utils._QueryCertBlobType(rawData)) == X509ContentType.Pfx && (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet) new KeyContainerPermission(KeyContainerPermissionFlags.Create).Demand(); uint dwFlags = X509Utils.MapKeyStorageFlags(keyStorageFlags); IntPtr num = IntPtr.Zero; RuntimeHelpers.PrepareConstrainedRegions(); try { num = X509Utils.PasswordToHGlobalUni(password); X509Utils._LoadCertFromBlob(rawData, num, dwFlags, (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) != X509KeyStorageFlags.DefaultKeySet, ref this.m_safeCertContext); } finally { if (num != IntPtr.Zero) Marshal.ZeroFreeGlobalAllocUnicode(num); } }
該方法是X509Certificate類構造函數等幾個方法加載證書的具體實現方法。
(2).Export():使用指定的格式和密碼將當前 X509Certificate對象導出到字節數組。
public virtual byte[] Export(X509ContentType contentType, SecureString password) { return this.ExportHelper(contentType, (object) password); }
該方法接受兩個參數,contentType描述如何設置輸出數據格式的 X509ContentType 值之一。password訪問 X.509 證書數據所需的密碼。返回表示當前 X509Certificate 對象的字節數組。
下面提供一個X509Certificate的操作方法實例:
public void EncryptXmlDocument(string arqXmlAssinar, string tagAssinatura, string tagAtributoId, X509Certificate2 x509Cert) { StreamReader sr = null; try { sr = System.IO.File.OpenText(arqXmlAssinar); var xmlString = sr.ReadToEnd(); sr.Close(); sr = null; XmlDocument doc = new XmlDocument { PreserveWhitespace = false }; doc.LoadXml(xmlString); if (doc.GetElementsByTagName(tagAssinatura).Count == 0) { throw new Exception(tagAssinatura.Trim()); } if (doc.GetElementsByTagName(tagAtributoId).Count == 0) { throw new Exception(tagAtributoId.Trim()); } XmlNodeList lists = doc.GetElementsByTagName(tagAssinatura); foreach (XmlNode nodes in lists) { foreach (XmlNode childNodes in nodes.ChildNodes) { if (!childNodes.Name.Equals(tagAtributoId)) continue; if (childNodes.NextSibling != null && childNodes.NextSibling.Name.Equals("Signature")) continue; Reference reference = new Reference { Uri = "" }; XmlElement childElemen = (XmlElement)childNodes; if (childElemen.GetAttributeNode("Id") != null) { var attributeNode = childElemen.GetAttributeNode("Id"); if (attributeNode != null) reference.Uri = "#" + attributeNode.Value; } else if (childElemen.GetAttributeNode("id") != null) { var attributeNode = childElemen.GetAttributeNode("id"); if (attributeNode != null) reference.Uri = "#" + attributeNode.Value; } XmlDocument documentoNovo = new XmlDocument(); documentoNovo.LoadXml(nodes.OuterXml); SignedXml signedXml = new SignedXml(documentoNovo) { SigningKey = x509Cert.PrivateKey }; XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(env); XmlDsigC14NTransform c14 = new XmlDsigC14NTransform(); reference.AddTransform(c14); signedXml.AddReference(reference); KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new KeyInfoX509Data(x509Cert)); signedXml.KeyInfo = keyInfo; signedXml.ComputeSignature(); XmlElement xmlDigitalSignature = signedXml.GetXml(); nodes.AppendChild(doc.ImportNode(xmlDigitalSignature, true)); } } var xmlDoc = doc; var stringXmlAssinado = xmlDoc.OuterXml; StreamWriter sw2 = System.IO.File.CreateText(arqXmlAssinar); sw2.Write(stringXmlAssinado); sw2.Close(); } catch (CryptographicException ex) { throw new CryptographicException(ex.Message); } catch (Exception e) { throw new Exception(e.Message); } finally { if (sr != null) sr.Close(); } }
上面是有關.NET數字證書的簡單介紹,如有寫的不對的地方還望多多見諒,在博文中有些類和方法沒有較多的列舉出來,有興趣的可以自己去深入的了解。我們學習一個知識時,已經從知識的結構了解開始,這樣有利於我們站在全局思考問題。