服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|JavaScript|易语言|

服务器之家 - 编程语言 - Java教程 - 详解Java数字签名提供XML安全

详解Java数字签名提供XML安全

2021-05-27 13:22weistar Java教程

在本篇文章中我们给大家整理了关于Java数字签名提供XML安全的知识点内容,有需要的朋友们可以学习下。

用java数字签名提供xml安全

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

使用的技术

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

下面让我们来看完整的一个带有数字签名的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文件,该文件可以随时进行验证。文件中包了含雇员名称、帐号和薪资信息。然而,实际的数字签名通 过<signature></signature>标记进行附加。<signature> 标记中的信息提供了文档的真实性。正如你看到的那样,虽然你可以随意修改其中的数据,但是这种修改会在随后的签名验证中被查到。
基本上数字签名有三种类型:

  1. 封内签名
  2. 封外签名
  3. 分离签名

封内签名

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

?
1
2
3
4
5
<rootelement>
 <signature>
 ……
 </signature>
</ rootelement>

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

封外签名

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

?
1
2
3
4
5
<signature >
  < myxmldocument >
  ……
  </ myxmldocument >
</signature>

分离签名

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

?
1
2
3
<signature>
……
</signature>

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

?
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个子标签,结构如下:

?
1
2
3
4
5
<signature>
  <signedinfo></signedinfo>
  <signaturevalue></signaturevalue>
  <keyinfo></keyinfo>
</signature>

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

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

当使用java创建xml数字签名时,signedinfo对象被用来在数字签名的signature标签内创建元素。这也是w3c建议的xml签名标准中的一部分。

xml标签<keyinfo>的结构如下:

?
1
2
3
4
5
6
7
8
<keyinfo>
  <keyvalue>
    <rsakeyvalue>
      <modulus></modulus>
      <exponent></exponent>
    </rsakeyvalue>
  </keyvalue>
</keyinfo>

keyinfo>标记包含了需要数学计算的相关信息,主要有公钥的系数和指数。
要创建xml数字签名可以遵循下列步骤:

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

让我们看看使用java生成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
public void generatexmldigitalsignature(string originalxmlfilepath,
string destnsignedxmlfilepath, string privatekeyfilepath, string publickeyfilepath) {
  // 获取xml文档对象
  document doc = getxmldocument(originalxmlfilepath);
  
  // 创建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);
  
  // 创建新的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签名验证

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

验证数字签名

  • 计算<signedinfo>元素摘要。
  • 使用公钥解密<signaturevalue>元素。
  • 比较上面两个值。
  • 计算引用摘要
  • 重新计算<signedinfo>元素引用摘要。
  • 将它们与<digestvalue>中的摘要比较。

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

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

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

?
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> 元素中对应的<digestvalue> 进行比对。接下来,让我们熟悉一下xml数字签名相关的java组件。

xmlsignaturefactory

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

?
1
xmlsignaturefactory factory = xmlsignaturefactory.getinstance("dom");

domsigncontext

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

reference

reference对象用来在signature 标记的signedinfo内部创建xml数字签名。对象创建的遵循“w3c xml签名文法和处理”规则。reference的基本结构如下:

?
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对象可以在数字签名的signature标记内部创建元素。创建的规则同样遵循“w3c xml数字签名协议”。signedinfo的基本结构如下:

?
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对象用来创建xml文档的封面签名。按照w3c的建议,签名对象应该作为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
<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文件夹下运行程序。这个项目已经包含了公钥和私钥。如果想要自己生成,可以运行 “testgeneratekeys”类生成一对公钥和私钥。通过指定自己的xmi文件,还可以查看xml签名的生成过程。

以上就是本次我们给大家整理的内容的全部,感谢大家对服务器之家的支持,如果大家还有不明白的可以在下方留言区讨论。

原文链接:http://weistar.iteye.com/blog/1982979

延伸 · 阅读

精彩推荐