詳解Java數(shù)字簽名提供XML安全
用Java數(shù)字簽名提供XML安全
眾所周知,XML在產(chǎn)品和項(xiàng)目開(kāi)發(fā)中起著非常重要的作用。通過(guò)XML文檔可以獲取很多信息,還可以使用XML文件進(jìn)行CRUD(增加、查詢、更新和 刪除)操作。然而值得注意的是,我們?nèi)绾未_保XML中的數(shù)據(jù)是來(lái)自經(jīng)過(guò)認(rèn)證的可信和可靠的來(lái)源。關(guān)于XML文件數(shù)據(jù)的可靠性和真實(shí)性存在很多問(wèn)題。通常的 情況是,開(kāi)發(fā)者直接處理XML文件而不去考慮數(shù)據(jù)的可靠性。有一些情況提出了上面的所有問(wèn)題?,F(xiàn)實(shí)生活中,每當(dāng)我們從郵局收到一封信件時(shí)我們?nèi)绾未_定這封 信是來(lái)自我們的朋友?依據(jù)可能是他/她的習(xí)慣用語(yǔ)、用詞或者郵件詳細(xì)地址。也可能是他/她的個(gè)性簽名。如今,我們收到的信件可能被某人進(jìn)行了篡改,添加了 其他內(nèi)容?;谏鲜鲈颍ǔN覀儠?huì)驗(yàn)證朋友的手寫簽名。當(dāng)然這些是關(guān)于來(lái)自郵局的普通郵件。電子消息又該如何?我們?nèi)绾悟?yàn)證電子消息的真實(shí)性?這種情況 我們會(huì)采用數(shù)字簽名。本文會(huì)對(duì)保證數(shù)據(jù)完整性的XML數(shù)字簽名技術(shù)進(jìn)行簡(jiǎn)要介紹,并且展示如何為XML文件附加電子簽名及其驗(yàn)證過(guò)程。
使用的技術(shù)
過(guò)去幾年里,XML數(shù)字簽名取得了快速發(fā)展,在金融領(lǐng)域尤其如此。在開(kāi)始討論之前,讓我們考慮一個(gè)典型場(chǎng)景:想象一下,某個(gè)組織將所有雇員的薪資內(nèi) 容用XML文件發(fā)送給所得稅部門。那么現(xiàn)在的問(wèn)題是:所得稅部門如何驗(yàn)證這份XML文件?這就是說(shuō),IT部門需要驗(yàn)證該組織的敏感信息。IT部門需要確保 XML文件的來(lái)源可信,并且在IT部門收到之前沒(méi)有經(jīng)過(guò)篡改——也就是說(shuō)文檔的內(nèi)容沒(méi)有在傳遞中被修改。首先,我們需要理解數(shù)字簽名的概念。數(shù)字簽名是一 種用來(lái)驗(yàn)證文檔發(fā)自可信方的電子簽名。它確保了文檔的原始內(nèi)容在傳輸中沒(méi)有受到修改。數(shù)字簽名可以用于任何加密和非加密消息,因此接收方可以識(shí)別發(fā)送者的 身份,并確認(rèn)消息沒(méi)有被其他人修改。根據(jù)維基百科的定義:“數(shù)字簽名是一種驗(yàn)證數(shù)字信息或文檔的數(shù)學(xué)方法”。一個(gè)有效的數(shù)字簽名可以讓接收者確認(rèn)收到的消 息來(lái)自已知發(fā)送方,發(fā)送者不能否認(rèn)自己發(fā)送了此消息(提供認(rèn)證和不可否認(rèn)性)并且此消息在傳輸中未經(jīng)修改(提供完整性)。數(shù)字簽名通常被用在軟件發(fā)布、金 融事務(wù)和其他需要檢測(cè)偽造或篡改的重要場(chǎng)合。
下面讓我們來(lái)看完整的一個(gè)帶有數(shù)字簽名的XML文件:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><SalaryDeposit> <Organisation> <Name>DDLab Inc</Name> <AccountNo>SBC-12345789</AccountNo> </Organisation> <Employees> <Emp> <Name>John Abraham</Name> <AccountNo>SB-001</AccountNo> <Amount>1234</Amount> </Emp> <Emp> <Name>Bipasha Basu</Name> <AccountNo>SB-002</AccountNo> <Amount>2334</Amount> </Emp> <Emp> <Name>Vidya Balan</Name> <AccountNo>SB-003</AccountNo> <Amount>3465</Amount> </Emp> <Emp> <Name>Debadatta Mishra</Name> <AccountNo>SB-007</AccountNo> <Amount>5789</Amount> </Emp> <Emp> <Name>Priti Zinta</Name> <AccountNo>SB-009</AccountNo> <Amount>1234</Amount> </Emp> </Employees> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue> </Reference> </SignedInfo> <SignatureValue> aUEMrCT5dzeOfSNaznzoT0If8WZ8KQcMNXDqtoeseonVk3NqOk9ctcxrf3QVX3wP6810DDRPdI6l e8ccG64Ge0HjkO+aYC5+c2L/qKBzwtSbl/olJEuFU2DVxBQO+K29TTUJfxpVzC9Zf2pvT+1NRj0f 2/ofHujYZ01D6+YqI8c= </SignatureValue> <KeyInfo> <KeyValue> <RSAKeyValue> <Modulus> jfAd5uV38L36+lDZJrqfH9oLN86VJezXYfAeU+lrFoHlKAXVJLAi9hKvBHQRer4tPfdez6iSBKsl 6IHkPnVRAKt0xU99uxi5QpymsWAX3qnBqHlw9Z70PwyZ+Xysfw4Q2tK2HtSgUOhMuaUcIf9sbHvf gbvcRPgxDZZqfIzDmDU=</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue> </KeyValue> </KeyInfo> </Signature> </SalaryDeposit>
上面是一個(gè)帶有簽名的XML文件,該文件可以隨時(shí)進(jìn)行驗(yàn)證。文件中包了含雇員名稱、帳號(hào)和薪資信息。然而,實(shí)際的數(shù)字簽名通 過(guò)<Signature></Signature>標(biāo)記進(jìn)行附加。<Signature> 標(biāo)記中的信息提供了文檔的真實(shí)性。正如你看到的那樣,雖然你可以隨意修改其中的數(shù)據(jù),但是這種修改會(huì)在隨后的簽名驗(yàn)證中被查到。
基本上數(shù)字簽名有三種類型:
- 封內(nèi)簽名
- 封外簽名
- 分離簽名
封內(nèi)簽名
這種簽名是將簽名作為XML對(duì)象的子信息,也就是說(shuō) <Signature>是郵件中XML文件的子標(biāo)簽。封內(nèi)數(shù)字簽名的結(jié)構(gòu)如下:
<RootElement> <Signature> …… </Signature> </ RootElement>
本文會(huì)介紹如何創(chuàng)建XML封內(nèi)數(shù)字簽名。
封外簽名
這種簽名將XML文檔包含到Signature對(duì)象,也就是說(shuō)<Signature>標(biāo)簽是簽名XML文件的根元素。封外簽名結(jié)構(gòu)如下:
<Signature > < MyXMLDocument > …… </ MyXMLDocument > </Signature>
分離簽名
這種情況下,簽名是獨(dú)立生成的不作為XML的一部分。也就是說(shuō)你會(huì)擁有兩個(gè)XML文件:一個(gè)待簽名的XML文件,另一個(gè)是XML簽名。下面是分離簽名的XML結(jié)構(gòu):
<Signature> …… </Signature>
XML數(shù)字簽名文件結(jié)構(gòu)如下:
<Signature xmlns=""> <SignedInfo> <CanonicalizationMethod Algorithm="" /> <SignatureMethod Algorithm="" /> <Reference URI=""> <Transforms> <Transform Algorithm="" /> </Transforms> <DigestMethod Algorithm="" /> <DigestValue></DigestValue> </Reference> </SignedInfo> <SignatureValue></SignatureValue> <KeyInfo> <KeyValue> <RSAKeyValue> <Modulus></Modulus> <Exponent></Exponent> </RSAKeyValue> </KeyValue> </KeyInfo> </Signature>
XML中<Signature>有3個(gè)子標(biāo)簽,結(jié)構(gòu)如下:
<Signature> <SignedInfo></SignedInfo> <SignatureValue></SignatureValue> <KeyInfo></KeyInfo> </Signature>
這里<Signature>是XML數(shù)字簽名的根元素,這一點(diǎn)由W3C建議并且必須遵守。<SignedInfo>元素是你的簽名信息;<SignatureValue>包含了實(shí)際的簽名以及使用Base64加密的內(nèi)容;最后<KeyInfo>表示公鑰。讓我們?cè)倏匆幌?lt;SignedInfo>標(biāo)簽,結(jié)構(gòu)如下:
<SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue> </Reference> </SignedInfo>
當(dāng)使用Java創(chuàng)建XML數(shù)字簽名時(shí),SignedInfo對(duì)象被用來(lái)在數(shù)字簽名的Signature標(biāo)簽內(nèi)創(chuàng)建元素。這也是W3C建議的XML簽名標(biāo)準(zhǔn)中的一部分。
XML標(biāo)簽<KeyInfo>的結(jié)構(gòu)如下:
<KeyInfo> <KeyValue> <RSAKeyValue> <Modulus></Modulus> <Exponent></Exponent> </RSAKeyValue> </KeyValue> </KeyInfo>
KeyInfo>標(biāo)記包含了需要數(shù)學(xué)計(jì)算的相關(guān)信息,主要有公鑰的系數(shù)和指數(shù)。
要?jiǎng)?chuàng)建XML數(shù)字簽名可以遵循下列步驟:
- 生成一組私鑰和公鑰。
- 獲得原始XML文件。
- 通過(guò)Java API使用私鑰和公鑰為原始的XML文件簽名,生成帶有XML簽名的文檔。
讓我們看看使用Java生成XML簽名的相關(guān)代碼:
public void generateXMLDigitalSignature(String originalXmlFilePath, String destnSignedXmlFilePath, String privateKeyFilePath, String publicKeyFilePath) { // 獲取XML文檔對(duì)象 Document doc = getXmlDocument(originalXmlFilePath); // 創(chuàng)建XML簽名工廠 XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM"); PrivateKey privateKey = new KryptoUtil().getStoredPrivateKey(privateKeyFilePath); DOMSignContext domSignCtx = new DOMSignContext(privateKey, doc.getDocumentElement()); Reference ref = null; SignedInfo signedInfo = null; try { ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null), Collections.singletonList(xmlSigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null); signedInfo = xmlSigFactory.newSignedInfo( xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), xmlSigFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref)); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (InvalidAlgorithmParameterException ex) { ex.printStackTrace(); } // 傳入公鑰路徑 KeyInfo keyInfo = getKeyInfo(xmlSigFactory, publicKeyFilePath); // 創(chuàng)建新的XML簽名 XMLSignature xmlSignature = xmlSigFactory.newXMLSignature(signedInfo, keyInfo); try { // 對(duì)文檔簽名 xmlSignature.sign(domSignCtx); } catch (MarshalException ex) { ex.printStackTrace(); } catch (XMLSignatureException ex) { ex.printStackTrace(); } // 存儲(chǔ)簽名過(guò)的文檔 storeSignedDoc(doc, destnSignedXmlFilePath); }
XML簽名驗(yàn)證
數(shù)字簽名的驗(yàn)證包含以下操作:
驗(yàn)證數(shù)字簽名
- 計(jì)算<SignedInfo>元素摘要。
- 使用公鑰解密<SignatureValue>元素。
- 比較上面兩個(gè)值。
- 計(jì)算引用摘要
- 重新計(jì)算<SignedInfo>元素引用摘要。
- 將它們與<DigestValue>中的摘要比較。
為了驗(yàn)證XML簽名文檔,需要完成下列步驟
- 得到XML文檔和公鑰。
- 驗(yàn)證<SignedInfo> 元素的數(shù)字簽名。
- 計(jì)算<SignedInfo> 元素的摘要并對(duì)值進(jìn)行比較。
讓我們看看下面這段XML數(shù)字簽名示例代碼:
public static boolean isXmlDigitalSignatureValid(String signedXmlFilePath, String pubicKeyFilePath) throws Exception { boolean validFlag = false; Document doc = getXmlDocument(signedXmlFilePath); NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); if (nl.getLength() == 0) { throw new Exception("No XML Digital Signature Found, document is discarded"); } PublicKey publicKey = new KryptoUtil().getStoredPublicKey(pubicKeyFilePath); DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0)); XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); XMLSignature signature = fac.unmarshalXMLSignature(valContext); validFlag = signature.validate(valContext); return validFlag; }
如上面示例代碼所示,XML簽名可以通過(guò)重新計(jì)算<SignedInfo>的摘要值進(jìn)行驗(yàn)證,驗(yàn)證算法由 <SignatureMethod>元素指定;使用公鑰可以驗(yàn)證<SignedInfo>摘要中 的<SignatureValue>值是否正確。 引用摘要會(huì)在<SignedInfo>元素中重新計(jì)算,并與<Reference> 元素中對(duì)應(yīng)的<DigestValue> 進(jìn)行比對(duì)。接下來(lái),讓我們熟悉一下XML數(shù)字簽名相關(guān)的Java組件。
XMLSignatureFactory
XMLSignatureFactory是生成XML文檔數(shù)字簽名的工廠對(duì)象。對(duì)象的創(chuàng)建如下列代碼所示:
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
DOMSignContext
DOMSignContext對(duì)象用來(lái)生成DOM樹(shù)。在創(chuàng)建數(shù)字簽名的過(guò)程中,DOM樹(shù)會(huì)被附上XML數(shù)字簽名。DOMSignContext對(duì)象要求輸入私鑰和XML文檔的根元素。
Reference
Reference對(duì)象用來(lái)在Signature 標(biāo)記的SignedInfo內(nèi)部創(chuàng)建XML數(shù)字簽名。對(duì)象創(chuàng)建的遵循“W3C XML簽名文法和處理”規(guī)則。Reference的基本結(jié)構(gòu)如下:
<Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue> </Reference>
SignedInfo
類似的,SignedInfo對(duì)象可以在數(shù)字簽名的Signature標(biāo)記內(nèi)部創(chuàng)建元素。創(chuàng)建的規(guī)則同樣遵循“W3C XML數(shù)字簽名協(xié)議”。SignedInfo的基本結(jié)構(gòu)如下:
<SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue> </Reference> </SignedInfo>
XMLSignature
最后,XMLSignature對(duì)象用來(lái)創(chuàng)建XML文檔的封面簽名。按照W3C的建議,簽名對(duì)象應(yīng)該作為XML數(shù)字簽名的根元素。
完整的結(jié)構(gòu)如下:
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> <Reference URI=""> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue> </Reference> </SignedInfo> <SignatureValue>aUEMrCT5dzeOfSNaznzoT0If8WZ8KQcMNXDqtoeseonVk3NqOk9ctcxrf3QVX3wP6810DDRPdI6l e8ccG64Ge0HjkO+aYC5+c2L/qKBzwtSbl/olJEuFU2DVxBQO+K29TTUJfxpVzC9Zf2pvT+1NRj0f 2/ofHujYZ01D6+YqI8c=</SignatureValue> <KeyInfo> <KeyValue> <RSAKeyValue> <Modulus>jfAd5uV38L36+lDZJrqfH9oLN86VJezXYfAeU+lrFoHlKAXVJLAi9hKvBHQRer4tPfdez6iSBKsl 6IHkPnVRAKt0xU99uxi5QpymsWAX3qnBqHlw9Z70PwyZ+Xysfw4Q2tK2HtSgUOhMuaUcIf9sbHvf gbvcRPgxDZZqfIzDmDU=</Modulus> <Exponent>AQAB</Exponent> </RSAKeyValue> </KeyValue> </KeyInfo> </Signature>
為了有一個(gè)完成的理解,可以從這里下載完整的Netbeans項(xiàng)目代碼。
可以用你最喜歡的Java IDE對(duì)項(xiàng)目進(jìn)行配置;也可以在source文件夾下運(yùn)行程序。這個(gè)項(xiàng)目已經(jīng)包含了公鑰和私鑰。如果想要自己生成,可以運(yùn)行 “TestGenerateKeys”類生成一對(duì)公鑰和私鑰。通過(guò)指定自己的XMI文件,還可以查看XML簽名的生成過(guò)程。
以上就是本次我們給大家整理的內(nèi)容的全部,感謝大家對(duì)腳本之家的支持,如果大家還有不明白的可以在下方留言區(qū)討論。
- Java PDF 添加數(shù)字簽名的實(shí)現(xiàn)方法
- java遇到微信小程序 "支付驗(yàn)證簽名失敗" 問(wèn)題解決
- Java實(shí)現(xiàn)的數(shù)字簽名算法RSA完整示例
- Java方法簽名的獲取實(shí)例代碼
- Java加密解密和數(shù)字簽名完整代碼示例
- 純Java實(shí)現(xiàn)數(shù)字證書生成簽名的簡(jiǎn)單實(shí)例
- Java將日期類型Date時(shí)間戳轉(zhuǎn)換為MongoDB的時(shí)間類型數(shù)據(jù)
- Java獲取凌晨時(shí)間戳的方法分析
- Java獲取精確到秒的時(shí)間戳方法
- Java 給PDF簽名時(shí)添加可信時(shí)間戳的方法
相關(guān)文章
深入理解Java8新特性之Optional容器類的應(yīng)用
Optional<T> 類(java.util.Optional) 是一個(gè)容器類,代表一個(gè)值存在或不存在,原來(lái)用 null 表示一個(gè)值不存在,現(xiàn)在 Optional 可以更好的表達(dá)這個(gè)概念。并且可以避免空指針異常,需要的朋友可以參考下本文2021-11-11Spring Cloud Config 使用本地配置文件方式
這篇文章主要介紹了Spring Cloud Config 使用本地配置文件方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java使用NioSocket手動(dòng)實(shí)現(xiàn)HTTP服務(wù)器
本篇文章主要介紹了Java使用NioSocket手動(dòng)實(shí)現(xiàn)HTTP服務(wù)器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-05-05Java+MySql圖片數(shù)據(jù)保存與讀取的具體實(shí)例
之前一直沒(méi)有做過(guò)涉及到圖片存儲(chǔ)的應(yīng)用,最近要做的東東涉及到了這個(gè)點(diǎn),就做了一個(gè)小的例子算是對(duì)圖片存儲(chǔ)的初試吧2013-06-06Java中字節(jié)流和字符流的理解(超精簡(jiǎn)!)
Java通過(guò)稱為流的抽象來(lái)執(zhí)行I/O操作,下面這篇文章主要給大家介紹了關(guān)于Java中字節(jié)流和字符流理解,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07SpringBoot整合mybatis常見(jiàn)問(wèn)題(小結(jié))
這篇文章主要介紹了SpringBoot整合mybatis常見(jiàn)問(wèn)題(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12