▲点击“RingCentral铃盛软件”并设为【星标】,及时获取RingCentral的资讯
本文是XML数字签名原理篇的姐妹篇,在“原理篇”中我们从理论上探讨了XML数字签名的原理和签名的处理过程,以及验证数字签名的步骤。胡适先生说过科学的精神就是大胆假设,小心求证,所以这篇文章的目标就是从应用层面针对“原理篇”中介绍的内容进行验证,实践是检验真理的唯一标准,尤其是编程这门实践性非常强的学科。
本文分成两部分,第一部分以一个第三方系统生成的一份XML签名文档为例,应用Java代码进行签名的验证,第二部分则通过对一份简单的原始XML文档进行签名来演示生成XML数字签名的过程。
说明:完整的代码可以从github下载,代码基于JDK 8+以上测试。
R 验证XML数字签名我们以第三方权威系统(Okta)签发的一份XML测试文档为例,使用从Okta获取的可靠证书,分步骤演示如何验证这份文档的签名是否有效,最后再给出一个综合的例子。读者可以点击链接查看或下载XML签名文档。为了更好的从原理上验证签名的处理过程,除了DOM节点的查找和规范化处理使用了Apache的Santuario库,其他步骤不借助任何XML数字签名的类库。
第一步:验证签名信息
在原理篇我们谈到,XML的数字签名是针对整个”SignedInfo"子节点规范化的内容进行签名的,为了验证签名,我们需要取得整个“SignedInfo”子节点的规范化内容,签名的方法和签名信息,以及验证的公钥,我们一步一步来分析如何获取这些信息:
首先加载被验证的XML文档
Document doc = XMLHelper.loadXML("xml/signed-xml-example.xml");
其次加载验证签名的证书
X509Certificate verifyCert = SecurityHelper.loadCert("certs/test-okta-public-key.pem");
有了XML文档和证书,就可以着手进行签名的验证了
//获取签名节点
Element signatureNode = getSignatureNode(doc.getDocumentElement());
//获取SignedInfo子节点
Element signInfoNode = XMLHelper.getNextElement(signatureNode.getFirstChild());
//获取Base64解码后的签名信息
byte[] signatureValue = getSignatureValue(signatureNode);
//取得规范化处理的算法
String methodUrl = getCanonicalizationMethodURI(signInfoNode);
//获取SignedInfo规范化的内容
Canonicalizer canon = Canonicalizer.getInstance(methodUrl);
canon.setSecureValidation(true);
byte[] canonSignInfo = canon.canonicalizeSubtree(signInfoNode);
//创建RSA-SHA256签名实例
Signature sig = Signature.getInstance("SHA256withRSA");
//使用公钥验证签名