用java數(shù)字簽名提供xml安全
眾所周知,xml在產(chǎn)品和項目開發(fā)中起著非常重要的作用。通過xml文檔可以獲取很多信息,還可以使用xml文件進行crud(增加、查詢、更新和 刪除)操作。然而值得注意的是,我們?nèi)绾未_保xml中的數(shù)據(jù)是來自經(jīng)過認(rèn)證的可信和可靠的來源。關(guān)于xml文件數(shù)據(jù)的可靠性和真實性存在很多問題。通常的 情況是,開發(fā)者直接處理xml文件而不去考慮數(shù)據(jù)的可靠性。有一些情況提出了上面的所有問題。現(xiàn)實生活中,每當(dāng)我們從郵局收到一封信件時我們?nèi)绾未_定這封 信是來自我們的朋友?依據(jù)可能是他/她的習(xí)慣用語、用詞或者郵件詳細(xì)地址。也可能是他/她的個性簽名。如今,我們收到的信件可能被某人進行了篡改,添加了 其他內(nèi)容。基于上述原因,通常我們會驗證朋友的手寫簽名。當(dāng)然這些是關(guān)于來自郵局的普通郵件。電子消息又該如何?我們?nèi)绾悟炞C電子消息的真實性?這種情況 我們會采用數(shù)字簽名。本文會對保證數(shù)據(jù)完整性的xml數(shù)字簽名技術(shù)進行簡要介紹,并且展示如何為xml文件附加電子簽名及其驗證過程。
使用的技術(shù)
過去幾年里,xml數(shù)字簽名取得了快速發(fā)展,在金融領(lǐng)域尤其如此。在開始討論之前,讓我們考慮一個典型場景:想象一下,某個組織將所有雇員的薪資內(nèi) 容用xml文件發(fā)送給所得稅部門。那么現(xiàn)在的問題是:所得稅部門如何驗證這份xml文件?這就是說,it部門需要驗證該組織的敏感信息。it部門需要確保 xml文件的來源可信,并且在it部門收到之前沒有經(jīng)過篡改——也就是說文檔的內(nèi)容沒有在傳遞中被修改。首先,我們需要理解數(shù)字簽名的概念。數(shù)字簽名是一 種用來驗證文檔發(fā)自可信方的電子簽名。它確保了文檔的原始內(nèi)容在傳輸中沒有受到修改。數(shù)字簽名可以用于任何加密和非加密消息,因此接收方可以識別發(fā)送者的 身份,并確認(rèn)消息沒有被其他人修改。根據(jù)維基百科的定義:“數(shù)字簽名是一種驗證數(shù)字信息或文檔的數(shù)學(xué)方法”。一個有效的數(shù)字簽名可以讓接收者確認(rèn)收到的消 息來自已知發(fā)送方,發(fā)送者不能否認(rèn)自己發(fā)送了此消息(提供認(rèn)證和不可否認(rèn)性)并且此消息在傳輸中未經(jīng)修改(提供完整性)。數(shù)字簽名通常被用在軟件發(fā)布、金 融事務(wù)和其他需要檢測偽造或篡改的重要場合。
下面讓我們來看完整的一個帶有數(shù)字簽名的xml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
<?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> |
上面是一個帶有簽名的xml文件,該文件可以隨時進行驗證。文件中包了含雇員名稱、帳號和薪資信息。然而,實際的數(shù)字簽名通 過<signature></signature>標(biāo)記進行附加。<signature> 標(biāo)記中的信息提供了文檔的真實性。正如你看到的那樣,雖然你可以隨意修改其中的數(shù)據(jù),但是這種修改會在隨后的簽名驗證中被查到。
基本上數(shù)字簽名有三種類型:
- 封內(nèi)簽名
- 封外簽名
- 分離簽名
封內(nèi)簽名
這種簽名是將簽名作為xml對象的子信息,也就是說 <signature>是郵件中xml文件的子標(biāo)簽。封內(nèi)數(shù)字簽名的結(jié)構(gòu)如下:
1
2
3
4
5
|
<rootelement> <signature> …… </signature> </ rootelement> |
本文會介紹如何創(chuàng)建xml封內(nèi)數(shù)字簽名。
封外簽名
這種簽名將xml文檔包含到signature對象,也就是說<signature>標(biāo)簽是簽名xml文件的根元素。封外簽名結(jié)構(gòu)如下:
1
2
3
4
5
|
<signature > < myxmldocument > …… </ myxmldocument > </signature> |
分離簽名
這種情況下,簽名是獨立生成的不作為xml的一部分。也就是說你會擁有兩個xml文件:一個待簽名的xml文件,另一個是xml簽名。下面是分離簽名的xml結(jié)構(gòu):
1
2
3
|
<signature> …… </signature> |
xml數(shù)字簽名文件結(jié)構(gòu)如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<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個子標(biāo)簽,結(jié)構(gòu)如下:
1
2
3
4
5
|
<signature> <signedinfo></signedinfo> <signaturevalue></signaturevalue> <keyinfo></keyinfo> </signature> |
這里<signature>是xml數(shù)字簽名的根元素,這一點由w3c建議并且必須遵守。<signedinfo>元素是你的簽名信息;<signaturevalue>包含了實際的簽名以及使用base64加密的內(nèi)容;最后<keyinfo>表示公鑰。讓我們再看一下<signedinfo>標(biāo)簽,結(jié)構(gòu)如下:
1
2
3
4
5
6
7
8
9
10
11
|
<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ù)字簽名時,signedinfo對象被用來在數(shù)字簽名的signature標(biāo)簽內(nèi)創(chuàng)建元素。這也是w3c建議的xml簽名標(biāo)準(zhǔn)中的一部分。
xml標(biāo)簽<keyinfo>的結(jié)構(gòu)如下:
1
2
3
4
5
6
7
8
|
<keyinfo> <keyvalue> <rsakeyvalue> <modulus></modulus> <exponent></exponent> </rsakeyvalue> </keyvalue> </keyinfo> |
keyinfo>標(biāo)記包含了需要數(shù)學(xué)計算的相關(guān)信息,主要有公鑰的系數(shù)和指數(shù)。
要創(chuàng)建xml數(shù)字簽名可以遵循下列步驟:
- 生成一組私鑰和公鑰。
- 獲得原始xml文件。
- 通過java api使用私鑰和公鑰為原始的xml文件簽名,生成帶有xml簽名的文檔。
讓我們看看使用java生成xml簽名的相關(guān)代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public void generatexmldigitalsignature(string originalxmlfilepath, string destnsignedxmlfilepath, string privatekeyfilepath, string publickeyfilepath) { // 獲取xml文檔對象 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 { // 對文檔簽名 xmlsignature.sign(domsignctx); } catch (marshalexception ex) { ex.printstacktrace(); } catch (xmlsignatureexception ex) { ex.printstacktrace(); } // 存儲簽名過的文檔 storesigneddoc(doc, destnsignedxmlfilepath); } |
xml簽名驗證
數(shù)字簽名的驗證包含以下操作:
驗證數(shù)字簽名
- 計算<signedinfo>元素摘要。
- 使用公鑰解密<signaturevalue>元素。
- 比較上面兩個值。
- 計算引用摘要
- 重新計算<signedinfo>元素引用摘要。
- 將它們與<digestvalue>中的摘要比較。
為了驗證xml簽名文檔,需要完成下列步驟
- 得到xml文檔和公鑰。
- 驗證<signedinfo> 元素的數(shù)字簽名。
- 計算<signedinfo> 元素的摘要并對值進行比較。
讓我們看看下面這段xml數(shù)字簽名示例代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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簽名可以通過重新計算<signedinfo>的摘要值進行驗證,驗證算法由 <signaturemethod>元素指定;使用公鑰可以驗證<signedinfo>摘要中 的<signaturevalue>值是否正確。 引用摘要會在<signedinfo>元素中重新計算,并與<reference> 元素中對應(yīng)的<digestvalue> 進行比對。接下來,讓我們熟悉一下xml數(shù)字簽名相關(guān)的java組件。
xmlsignaturefactory
xmlsignaturefactory是生成xml文檔數(shù)字簽名的工廠對象。對象的創(chuàng)建如下列代碼所示:
1
|
xmlsignaturefactory factory = xmlsignaturefactory.getinstance( "dom" ); |
domsigncontext
domsigncontext對象用來生成dom樹。在創(chuàng)建數(shù)字簽名的過程中,dom樹會被附上xml數(shù)字簽名。domsigncontext對象要求輸入私鑰和xml文檔的根元素。
reference
reference對象用來在signature 標(biāo)記的signedinfo內(nèi)部創(chuàng)建xml數(shù)字簽名。對象創(chuàng)建的遵循“w3c xml簽名文法和處理”規(guī)則。reference的基本結(jié)構(gòu)如下:
1
2
3
4
5
6
7
|
<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對象可以在數(shù)字簽名的signature標(biāo)記內(nèi)部創(chuàng)建元素。創(chuàng)建的規(guī)則同樣遵循“w3c xml數(shù)字簽名協(xié)議”。signedinfo的基本結(jié)構(gòu)如下:
1
2
3
4
5
6
7
8
9
10
11
|
<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對象用來創(chuàng)建xml文檔的封面簽名。按照w3c的建議,簽名對象應(yīng)該作為xml數(shù)字簽名的根元素。
完整的結(jié)構(gòu)如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<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> |
為了有一個完成的理解,可以從這里下載完整的netbeans項目代碼。
可以用你最喜歡的java ide對項目進行配置;也可以在source文件夾下運行程序。這個項目已經(jīng)包含了公鑰和私鑰。如果想要自己生成,可以運行 “testgeneratekeys”類生成一對公鑰和私鑰。通過指定自己的xmi文件,還可以查看xml簽名的生成過程。
以上就是本次我們給大家整理的內(nèi)容的全部,感謝大家對服務(wù)器之家的支持,如果大家還有不明白的可以在下方留言區(qū)討論。
原文鏈接:http://weistar.iteye.com/blog/1982979