JAVA中的XML安全与数字签名(包含代码示例)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

原文链接:
https://www.javacodegeeks.com/2013/10/xml-security-with-digital-signature-in-java.html

XML 在产品或项目开发中扮演着重要角色,但如何确保 XML 文档中的数据真实性和完整性是一个令人担忧的问题。开发人员通常会忽略数据真实性,但在某些情况下需要考虑。

现实世界中,我们如何验证一封信是否来自朋友?我们会根据信中的内容、用词和地址细节来判断。如果信件被修改,我们会验证朋友的手写签名。电子邮件也是如此,我们可以使用数字签名技术来验证电子邮件的真实性。

本文将简要介绍 XML 数字签名,并演示如何使用数字签名来保护 XML 文档。

一、技术细节

1、数字签名原理

  • 数字签名是用于验证文档的电子签名。
  • 它使用加密算法来生成一个唯一的标识符,该标识符与文档和发送者关联。
  • 接收者可以使用相同的算法来验证标识符,以确保文档没有被修改过。

2、数字签名的优势

  • 确保文档的真实性:数字签名可以确保文档是由发送者创建的。
  • 确保文档的完整性:数字签名可以确保文档在传输过程中没有被修改过。
  • 防止伪造和篡改:数字签名可以防止恶意用户伪造或篡改文档。

3、数字签名应用

  • 软件分发:数字签名可用于确保软件是从可信来源下载的。
  • 金融交易:数字签名可用于确保金融交易是合法的。
  • 电子邮件:数字签名可用于确保电子邮件没有被修改过。

4、XML示例

下面我们来看一个带有数字签名的完整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>

上述的 XML 文件是一个数字签名文件,可以用于验证文件的真实性和完整性。文件中包含了员工姓名、账号和薪水金额等信息。

数字签名是附加在文件中的,位于标签内。标签内的信息提供了文件的真实性。

因为你可以看到里面的数据,所以你可以随意的进行篡改。但是,一旦你篡改了里面的数据,那么数字签名将会失效,导致验证不通过,进而达到了防篡改的目的。

二、签名的类型

XML的签名基本可分为三种:

  • 封装式签名(Enveloped Signature)
  • 包围式签名(Enveloping Signature)
  • 独立式签名(Detached Signatures)

1.封装式签名

在这种情况下,签名是被签署的XML对象的子元素。这意味着是邮件XML文档中的一个子XML标签。以下是封装数字签名的结构。

<RootElement>
	<Signature>
		……………………
	</Signature>
</RootElement>

2.包围式签名

在这种情况下,XML文档仍然保留在Signature对象内部。这意味着标签成为签名XML文档的根元素。以下是包含数字签名的结构。

<Signature >
	<MyXMLDocument ></MyXMLDocument >
</Signature>

该处使用的url网络请求的数据。

3.独立式签名

在这种情况下,数字签名是独立生成的,不是XML文档的一部分。这意味着您将拥有两个XML文件,一个是要签名的XML文件,另一个是XML签名。让我们来看一下XML文档的基本结构。

<Signature>
…………..
</Signature>

4、典型的XML骨架结构

现在让我们来看一个典型的XML数字签名的骨架结构。

<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标签基本上包含3个子标签。可以看作是:

<Signature>
	<SignedInfo></SignedInfo>
	<SignatureValue></SignatureValue>
	<KeyInfo></KeyInfo>
</Signature>

这里的 <Signature>是XML数字签名概念的根元素,它是一个必须按照W3C的指示遵循的协议。<SignedInfo> 元素是我们签名的信息。<SignatureValue>包含了实际的签名,其中包含Base64编码的内容,最后 <KeyInfo>指示了公钥。再次让我们来看一下<SignedInfo>标签。下面给出了<SignedInfo>的结构:

<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>

在使用Java创建XML数字签名时,使用SignedInfo对象在数字签名的Signature标签内创建一个元素。这也是W3C建议的XML数字签名协议的一部分。
现在让我们来看一下下面的XML标签的结构:

<KeyInfo>
	<KeyValue>
		<RSAKeyValue>
			<Modulus></Modulus>
			<Exponent></Exponent>
		</RSAKeyValue>
	</KeyValue>
</KeyInfo>

<KeyInfo>标签包含了通过数学计算得出的信息,基本上包括了公钥的模数和指数。

5、签名步骤

为了创建一个XML数字签名,请按照以下步骤进行:

1、生成一对称为私钥和公钥的密钥。
2、获取原始的XML文档。
3、获取原始的XML文档。

让我们来看一下生成XML数字签名的简短Java代码片段:

    public void generateXMLDigitalSignature(String originalXmlFilePath,
                                            String destnSignedXmlFilePath, String privateKeyFilePath, String publicKeyFilePath) {
        // Get the XML Document object
        Document doc = getXmlDocument(originalXmlFilePath);
        // Create XML Signature Factory
        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();
        }
        // Pass the Public Key File Path
        KeyInfo keyInfo = getKeyInfo(xmlSigFactory, publicKeyFilePath);
        // Create a new XML Signature
        XMLSignature xmlSignature = xmlSigFactory.newXMLSignature(signedInfo, keyInfo);
        try {
        // Sign the document
            xmlSignature.sign(domSignCtx);
        } catch (MarshalException ex) {
            ex.printStackTrace();
        } catch (XMLSignatureException ex) {
            ex.printStackTrace();
        }
        // Store the digitally signed document inta a location
        storeSignedDoc(doc, destnSignedXmlFilePath);
    }

三、XML签名验证

1、验证数字签名步骤

XML数字签名验证涉及以下操作:

  • 计算 <SignedInfo>元素的摘要
  • 使用公钥对<SignatureValue>元素进行解签名
  • 比较以上两个数值
  • 计算引用的摘要
  • 重新计算<SignedInfo>元素中引用的摘要
  • 与<DigestValue>中指定的摘要值进行比较

2、java示例

为了验证一个已签名的XML文档,请按照以下步骤进行:

  1. 获取已签名的XML文档和公钥。
  2. 验证<SignedInfo>元素的数字签名。
  3. 计算<SignedInfo>元素的摘要并比较数值。

让我们来看一下XML数字签名验证的简短Java代码片段:

    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签名可以通过使用<SignatureMethod>元素指定的摘要算法重新计算<SignedInfo>元素的摘要值,并使用公钥验证<SignatureValue>元素的值是否与<SignedInfo>元素的摘要值匹配来进行验证。同时,还要在<SignedInfo>元素内重新计算引用的摘要,并将它们与每个<Reference>元素对应的<DigestValue>元素中包含的摘要值进行比较。
让我们再熟悉一些负责XML数字签名的Java组件。

XMLSignatureFactory

这是一个使用“DOM”机制类型为XML文档生成数字签名的工厂对象。该对象是按照以下方式创建的:

XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");

DOMSignContext

DOMSignContext对象用于创建数字签名时附加XML数字签名的DOM树。该对象接受私钥和XML文档的根元素。

Reference

参考对象用于在XML数字签名的主签名标签SignedInfo元素内创建一个元素。该对象创建一个标签,作为W3C XML签名语法和处理规则的一部分。参考元素的基本结构如下所示:

<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对象用于在数字签名的Signature标签内创建一个元素。这也是W3C建议的XML数字签名协议的一部分。
基本结构如下:

<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对象以形成签名标签,该标签作为附加到XML文档的信封使用。根据W3C指南,这是XML数字签名的根元素。
完整的结构如下所示:

<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项目,其中包含完整的源代码。

Demo源码

你可以从该网站下载Java中关于XML数字签名的完整项目,或者您可以从下面的Dropbox链接下载:

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

如果想省事,那可以花5积分下载:

https://download.youkuaiyun.com/download/Ciel_7521/87273816

你可以在你喜欢的Java IDE中配置此项目,并运行位于测试源文件夹中的独立程序。该项目已经包含了私钥和公钥。如果您想要生成新的密钥对,请运行Java类“TestGenerateKeys”。您还可以提供您自己的XML文件路径,以查看如何生成XML数字签名。

资源和参考

http://en.wikipedia.org/wiki/XML_Signature
http://msdn.microsoft.com/en-us/library/ms996502.aspx
http://www.xml.com/pub/a/2001/08/08/xmldsig.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值