用Java数字签名提供XML安全

文章源自:http://www.importnew.com/6271.html

用Java数字签名提供XML安全

众所周知,XML在产品和项目开发中起着非常重要的作用。通过XML文档可以获取很多信息,还可以使用XML文件进行CRUD(增加、查询、更新和 删除)操作。然而值得注意的是,我们如何确保XML中的数据是来自经过认证的可信和可靠的来源。关于XML文件数据的可靠性和真实性存在很多问题。通常的 情况是,开发者直接处理XML文件而不去考虑数据的可靠性。有一些情况提出了上面的所有问题。现实生活中,每当我们从邮局收到一封信件时我们如何确定这封 信是来自我们的朋友?依据可能是他/她的习惯用语、用词或者邮件详细地址。也可能是他/她的个性签名。如今,我们收到的信件可能被某人进行了篡改,添加了 其他内容。基于上述原因,通常我们会验证朋友的手写签名。当然这些是关于来自邮局的普通邮件。电子消息又该如何?我们如何验证电子消息的真实性?这种情况 我们会采用数字签名。本文会对保证数据完整性的XML数字签名技术进行简要介绍,并且展示如何为XML文件附加电子签名及其验证过程。

使用的技术

过去几年里,XML数字签名取得了快速发展,在金融领域尤其如此。在开始讨论之前,让我们考虑一个典型场景:想象一下,某个组织将所有雇员的薪资内 容用XML文件发送给所得税部门。那么现在的问题是:所得税部门如何验证这份XML文件?这就是说,IT部门需要验证该组织的敏感信息。IT部门需要确保 XML文件的来源可信,并且在IT部门收到之前没有经过篡改——也就是说文档的内容没有在传递中被修改。首先,我们需要理解数字签名的概念。数字签名是一 种用来验证文档发自可信方的电子签名。它确保了文档的原始内容在传输中没有受到修改。数字签名可以用于任何加密和非加密消息,因此接收方可以识别发送者的 身份,并确认消息没有被其他人修改。根据维基百科的定义:“数字签名是一种验证数字信息或文档的数学方法”。一个有效的数字签名可以让接收者确认收到的消 息来自已知发送方,发送者不能否认自己发送了此消息(提供认证和不可否认性)并且此消息在传输中未经修改(提供完整性)。数字签名通常被用在软件发布、金 融事务和其他需要检测伪造或篡改的重要场合。

下面让我们来看完整的一个带有数字签名的XML文件:

Xml代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?><SalaryDeposit>  
  2.        <Organisation>  
  3.               <Name>DDLab Inc</Name>  
  4.               <AccountNo>SBC-12345789</AccountNo>  
  5.        </Organisation>  
  6.        <Employees>  
  7.               <Emp>  
  8.                      <Name>John Abraham</Name>  
  9.                      <AccountNo>SB-001</AccountNo>  
  10.                      <Amount>1234</Amount>  
  11.               </Emp>  
  12.               <Emp>  
  13.                      <Name>Bipasha Basu</Name>  
  14.                      <AccountNo>SB-002</AccountNo>  
  15.                      <Amount>2334</Amount>  
  16.               </Emp>  
  17.               <Emp>  
  18.                      <Name>Vidya Balan</Name>  
  19.                      <AccountNo>SB-003</AccountNo>  
  20.                      <Amount>3465</Amount>  
  21.               </Emp>  
  22.               <Emp>  
  23.                      <Name>Debadatta Mishra</Name>  
  24.                      <AccountNo>SB-007</AccountNo>  
  25.                      <Amount>5789</Amount>  
  26.               </Emp>  
  27.               <Emp>  
  28.                      <Name>Priti Zinta</Name>  
  29.                      <AccountNo>SB-009</AccountNo>  
  30.                      <Amount>1234</Amount>  
  31.               </Emp>  
  32.        </Employees>  
  33.        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">  
  34.               <SignedInfo>  
  35.                      <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>  
  36.                      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>  
  37.                      <Reference URI="">  
  38.                            <Transforms>  
  39.                                   <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>  
  40.                            </Transforms>  
  41.                            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>  
  42.                            <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>  
  43.                      </Reference>  
  44.               </SignedInfo>  
  45.               <SignatureValue>  
  46. aUEMrCT5dzeOfSNaznzoT0If8WZ8KQcMNXDqtoeseonVk3NqOk9ctcxrf3QVX3wP6810DDRPdI6l  
  47.             e8ccG64Ge0HjkO+aYC5+c2L/qKBzwtSbl/olJEuFU2DVxBQO+K29TTUJfxpVzC9Zf2pvT+1NRj0f  
  48.             2/ofHujYZ01D6+YqI8c=  
  49.               </SignatureValue>  
  50.               <KeyInfo>  
  51.                      <KeyValue>  
  52.                            <RSAKeyValue>  
  53.                                   <Modulus>  
  54. jfAd5uV38L36+lDZJrqfH9oLN86VJezXYfAeU+lrFoHlKAXVJLAi9hKvBHQRer4tPfdez6iSBKsl  
  55.                        6IHkPnVRAKt0xU99uxi5QpymsWAX3qnBqHlw9Z70PwyZ+Xysfw4Q2tK2HtSgUOhMuaUcIf9sbHvf  
  56.                         gbvcRPgxDZZqfIzDmDU=</Modulus>  
  57.                                   <Exponent>AQAB</Exponent>  
  58.                            </RSAKeyValue>  
  59.                      </KeyValue>  
  60.               </KeyInfo>  
  61.        </Signature>  
  62. </SalaryDeposit>  

 

上面是一个带有签名的XML文件,该文件可以随时进行验证。文件中包了含雇员名称、帐号和薪资信息。然而,实际的数字签名通 过<Signature></Signature>标记进行附加。<Signature> 标记中的信息提供了文档的真实性。正如你看到的那样,虽然你可以随意修改其中的数据,但是这种修改会在随后的签名验证中被查到。

基本上数字签名有三种类型:

  • 封内签名
  • 封外签名
  • 分离签名

封内签名

这种签名是将签名作为XML对象的子信息,也就是说 <Signature>是邮件中XML文件的子标签。封内数字签名的结构如下:

Xml代码   收藏代码
  1. <RootElement>  
  2.     <Signature>  
  3.     ……  
  4.     </Signature>  
  5. </ RootElement>  

 

本文会介绍如何创建XML封内数字签名。

封外签名

这种签名将XML文档包含到Signature对象,也就是说<Signature>标签是签名XML文件的根元素。封外签名结构如下:

Xml代码   收藏代码
  1. <Signature >  
  2.     < MyXMLDocument >  
  3.     ……  
  4.     </ MyXMLDocument >  
  5. </Signature>  

 

分离签名

这种情况下,签名是独立生成的不作为XML的一部分。也就是说你会拥有两个XML文件:一个待签名的XML文件,另一个是XML签名。下面是分离签名的XML结构:

Xml代码   收藏代码
  1. <Signature>  
  2. ……  
  3. </Signature>  

 XML数字签名文件结构如下:

Xml代码   收藏代码
  1. <Signature xmlns="">  
  2.     <SignedInfo>  
  3.         <CanonicalizationMethod Algorithm="" />  
  4.         <SignatureMethod Algorithm="" />  
  5.         <Reference URI="">  
  6.             <Transforms>  
  7.                 <Transform Algorithm="" />  
  8.                 </Transforms>  
  9.             <DigestMethod Algorithm="" />  
  10.             <DigestValue></DigestValue>  
  11.         </Reference>  
  12.     </SignedInfo>  
  13.     <SignatureValue></SignatureValue>  
  14.     <KeyInfo>  
  15.         <KeyValue>  
  16.             <RSAKeyValue>  
  17.                 <Modulus></Modulus>  
  18.                 <Exponent></Exponent>  
  19.             </RSAKeyValue>  
  20.         </KeyValue>  
  21.     </KeyInfo>  
  22. </Signature>  

 XML中<Signature>有3个子标签,结构如下:

Xml代码   收藏代码
  1. <Signature>  
  2.     <SignedInfo></SignedInfo>  
  3.     <SignatureValue></SignatureValue>  
  4.     <KeyInfo></KeyInfo>  
  5. </Signature>  

 这里<Signature>是XML数字签名的根元素,这一点由W3C建议并且必须遵守。<SignedInfo>元素是你的签名信息;<SignatureValue>包含了实际的签名以及使用Base64加密的内容;最后<KeyInfo>表示公钥。让我们再看一下<SignedInfo>标签,结构如下:

Xml代码   收藏代码
  1. <SignedInfo>  
  2.     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>  
  3.     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>  
  4.     <Reference URI="">  
  5.         <Transforms>  
  6.             <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>  
  7.         </Transforms>  
  8.         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>  
  9.         <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>  
  10.     </Reference>  
  11. </SignedInfo>  

 

当使用Java创建XML数字签名时,SignedInfo对象被用来在数字签名的Signature标签内创建元素。这也是W3C建议的XML签名标准中的一部分。

XML标签<KeyInfo>的结构如下:

Xml代码   收藏代码
  1. <KeyInfo>  
  2.     <KeyValue>  
  3.         <RSAKeyValue>  
  4.             <Modulus></Modulus>  
  5.             <Exponent></Exponent>  
  6.         </RSAKeyValue>  
  7.     </KeyValue>  
  8. </KeyInfo>  

 

<KeyInfo>标记包含了需要数学计算的相关信息,主要有公钥的系数和指数。

要创建XML数字签名可以遵循下列步骤:

  1. 生成一组私钥和公钥。
  2. 获得原始XML文件。
  3. 通过Java API使用私钥和公钥为原始的XML文件签名,生成带有XML签名的文档。

让我们看看使用Java生成XML签名的相关代码:

Java代码   收藏代码
  1. public void generateXMLDigitalSignature(String originalXmlFilePath,  
  2. String destnSignedXmlFilePath, String privateKeyFilePath, String publicKeyFilePath) {  
  3.     // 获取XML文档对象  
  4.     Document doc = getXmlDocument(originalXmlFilePath);  
  5.    
  6.     // 创建XML签名工厂  
  7.     XMLSignatureFactory xmlSigFactory = XMLSignatureFactory.getInstance("DOM");  
  8.     PrivateKey privateKey = new KryptoUtil().getStoredPrivateKey(privateKeyFilePath);  
  9.     DOMSignContext domSignCtx = new DOMSignContext(privateKey, doc.getDocumentElement());  
  10.     Reference ref = null;  
  11.     SignedInfo signedInfo = null;  
  12.     try {  
  13.         ref = xmlSigFactory.newReference("", xmlSigFactory.newDigestMethod(DigestMethod.SHA1, null),  
  14.         Collections.singletonList(xmlSigFactory.newTransform(Transform.ENVELOPED,  
  15.         (TransformParameterSpec) null)), nullnull);  
  16.         signedInfo = xmlSigFactory.newSignedInfo(  
  17.         xmlSigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE,  
  18.         (C14NMethodParameterSpec) null),  
  19.         xmlSigFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null),  
  20.         Collections.singletonList(ref));  
  21.     } catch (NoSuchAlgorithmException ex) {  
  22.         ex.printStackTrace();  
  23.     } catch (InvalidAlgorithmParameterException ex) {  
  24.         ex.printStackTrace();  
  25.     }  
  26.    
  27.     // 传入公钥路径  
  28.     KeyInfo keyInfo = getKeyInfo(xmlSigFactory, publicKeyFilePath);  
  29.    
  30.     // 创建新的XML签名  
  31.     XMLSignature xmlSignature = xmlSigFactory.newXMLSignature(signedInfo, keyInfo);  
  32.     try {  
  33.         // 对文档签名  
  34.         xmlSignature.sign(domSignCtx);  
  35.     } catch (MarshalException ex) {  
  36.         ex.printStackTrace();  
  37.     } catch (XMLSignatureException ex) {  
  38.         ex.printStackTrace();  
  39.     }  
  40.    
  41.     // 存储签名过的文档  
  42.     storeSignedDoc(doc, destnSignedXmlFilePath);  
  43. }  

 

XML签名验证

数字签名的验证包含以下操作:

  • 验证数字签名
    • 计算<SignedInfo>元素摘要。
    • 使用公钥解密<SignatureValue>元素。
    • 比较上面两个值。
    • 计算引用摘要
      • 重新计算<SignedInfo>元素引用摘要。
      • 将它们与<DigestValue>中的摘要比较。

为了验证XML签名文档,需要完成下列步骤:

  1. 得到XML文档和公钥。
  2. 验证<SignedInfo> 元素的数字签名。
  3. 计算<SignedInfo> 元素的摘要并对值进行比较。

让我们看看下面这段XML数字签名示例代码:

Java代码   收藏代码
  1. public static boolean isXmlDigitalSignatureValid(String signedXmlFilePath, String pubicKeyFilePath) throws Exception {  
  2.     boolean validFlag = false;  
  3.     Document doc = getXmlDocument(signedXmlFilePath);  
  4.     NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");  
  5.     if (nl.getLength() == 0) {  
  6.         throw new Exception("No XML Digital Signature Found, document is discarded");  
  7.     }  
  8.    
  9.     PublicKey publicKey = new KryptoUtil().getStoredPublicKey(pubicKeyFilePath);  
  10.     DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0));  
  11.     XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");  
  12.     XMLSignature signature = fac.unmarshalXMLSignature(valContext);  
  13.     validFlag = signature.validate(valContext);  
  14.     return validFlag;  
  15. }  

 

如上面示例代码所示,XML签名可以通过重新计算<SignedInfo>的摘要值进行验证,验证算法由 <SignatureMethod>元素指定;使用公钥可以验证<SignedInfo>摘要中 的<SignatureValue>值是否正确。 引用摘要会在<SignedInfo>元素中重新计算,并与<Reference> 元素中对应的<DigestValue> 进行比对。接下来,让我们熟悉一下XML数字签名相关的Java组件。

XMLSignatureFactory

XMLSignatureFactory是生成XML文档数字签名的工厂对象。对象的创建如下列代码所示:

Java代码   收藏代码
  1. XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");  

 

DOMSignContext

DOMSignContext对象用来生成DOM树。在创建数字签名的过程中,DOM树会被附上XML数字签名。DOMSignContext对象要求输入私钥和XML文档的根元素。

Reference

Reference对象用来在Signature 标记的SignedInfo内部创建XML数字签名。对象创建的遵循“W3C XML签名文法和处理”规则。Reference的基本结构如下:

Xml代码   收藏代码
  1. <Reference URI="">  
  2.     <Transforms>  
  3.     <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>  
  4.     </Transforms>  
  5.     <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>  
  6.     <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>  
  7. </Reference>  

 

SignedInfo

类似的,SignedInfo对象可以在数字签名的Signature标记内部创建元素。创建的规则同样遵循“W3C XML数字签名协议”。SignedInfo的基本结构如下:

Xml代码   收藏代码
  1. <SignedInfo>  
  2.     <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>  
  3.     <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>  
  4.     <Reference URI="">  
  5.         <Transforms>  
  6.         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>  
  7.         </Transforms>  
  8.         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>  
  9.         <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>  
  10.     </Reference>  
  11. </SignedInfo>  

 

XMLSignature

最后,XMLSignature对象用来创建XML文档的封面签名。按照W3C的建议,签名对象应该作为XML数字签名的根元素。

完整的结构如下:

Xml代码   收藏代码
  1. <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">  
  2.     <SignedInfo>  
  3.         <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>  
  4.         <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>  
  5.         <Reference URI="">  
  6.             <Transforms>  
  7.             <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>  
  8.             </Transforms>  
  9.             <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>  
  10.             <DigestValue>bHS+6uf8KbJV4AGzoHNHLfnXvKM=</DigestValue>  
  11.         </Reference>  
  12.     </SignedInfo>  
  13.     <SignatureValue>aUEMrCT5dzeOfSNaznzoT0If8WZ8KQcMNXDqtoeseonVk3NqOk9ctcxrf3QVX3wP6810DDRPdI6l  
  14.     e8ccG64Ge0HjkO+aYC5+c2L/qKBzwtSbl/olJEuFU2DVxBQO+K29TTUJfxpVzC9Zf2pvT+1NRj0f  
  15.     2/ofHujYZ01D6+YqI8c=</SignatureValue>  
  16.     <KeyInfo>  
  17.         <KeyValue>  
  18.         <RSAKeyValue>  
  19.             <Modulus>jfAd5uV38L36+lDZJrqfH9oLN86VJezXYfAeU+lrFoHlKAXVJLAi9hKvBHQRer4tPfdez6iSBKsl  
  20.             6IHkPnVRAKt0xU99uxi5QpymsWAX3qnBqHlw9Z70PwyZ+Xysfw4Q2tK2HtSgUOhMuaUcIf9sbHvf  
  21.             gbvcRPgxDZZqfIzDmDU=</Modulus>  
  22.             <Exponent>AQAB</Exponent>  
  23.         </RSAKeyValue>  
  24.         </KeyValue>  
  25.     </KeyInfo>  
  26. </Signature>  

 

为了有一个完成的理解,可以从这里下载完整的Netbeans项目代码。

配置

可以从这个站点下载完整的XML数字签名项目;也可以从下面的链接下载:

  • https://www.dropbox.com/s/0k1iukhy0in6n8h/xmldigitalsignature1.zip

可以用你最喜欢的Java IDE对项目进行配置;也可以在source文件夹下运行程序。这个项目已经包含了公钥和私钥。如果想要自己生成,可以运行 “TestGenerateKeys”类生成一对公钥和私钥。通过指定自己的XMI文件,还可以查看XML签名的生成过程。

 

参考资料:http://www.ibm.com/developerworks/cn/xml/x-cn-java6xmlsignature/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 可以通过使用数字证书来实现电子签名。数字证书是由可信的证书机构颁发的电子文件,用于验证签名者的身份,并确保签名的真实性和完整性。 Java 提供了许多 API 用于实现数字签名,其中最常用的是 Java Security API。下面是一个简单的示例代码,用于使用 Java Security API 对数据进行数字签名: ```java import java.io.*; import java.security.*; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; public class SignatureExample { public static void main(String[] args) throws Exception { // 读取数字证书 CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream in = new FileInputStream("path/to/certificate.crt"); X509Certificate cert = (X509Certificate) cf.generateCertificate(in); in.close(); // 获取证书公钥 PublicKey publicKey = cert.getPublicKey(); // 读取要签名的数据 String data = "Hello, world!"; byte[] dataBytes = data.getBytes(); // 创建数字签名对象 Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(loadPrivateKey("path/to/privatekey.pem")); signature.update(dataBytes); // 对数据进行签名 byte[] signatureBytes = signature.sign(); // 验证签名 Signature verifier = Signature.getInstance("SHA256withRSA"); verifier.initVerify(publicKey); verifier.update(dataBytes); boolean verified = verifier.verify(signatureBytes); System.out.println("Signature verified: " + verified); } // 读取私钥 private static PrivateKey loadPrivateKey(String filename) throws Exception { BufferedReader br = new BufferedReader(new FileReader(filename)); StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { if (!line.startsWith("-")) { sb.append(line); } } br.close(); byte[] keyBytes = javax.xml.bind.DatatypeConverter.parseBase64Binary(sb.toString()); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(spec); } } ``` 在上面的示例代码中,我们首先读取数字证书,然后获取公钥。接下来,我们创建 `Signature` 对象并使用私钥对数据进行签名。最后,我们使用公钥验证签名的真实性。 需要注意的是,在上面的示例代码中,我们还需要实现 `loadPrivateKey` 方法来读取私钥。私钥应该存储在一个 PEM 格式的文件中,这个方法会读取文件并将其转换成 `PrivateKey` 对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值