前置文章: webservice 概述
了解XML安全体系参考
XML签名将定义一系列可以嵌入任何XML文档或以其他方式附属于任何XML文档的XML元素。它将允许接收方验证消息未被发送者的意图修改。
它可以提供端到端的消息完整性保证,还可以提供有关消息发件人的验证信息。为了达到较好的效果,签名必须是应用程序数据的一部分,这样可以在创建消息时生成签名,并可以在最终使用和处理消息时对签名进行验证。
xml加密(XML Encryption)是w3c加密xml的标准。加密后的文档仍然是xml格式。我们使用非对称和对称算法来加密xml,对称算法用于加密数据,非对称算法用于加密对称算法中的密钥,加密后的数据被保存在EncryptedData元素下。EncryptedData元素包含着一些列用于描述算法的子元素,同时也包含着密钥信息。
安全体系有两种: 公钥加密和数字签名:参考
公钥和私钥是成对的,它们互相解密。
公钥加密,私钥解密为公钥,私钥加密验证。
私钥加密,公钥验证则为数字签名验证。(先整体生成HASH值形成摘要signature, 再进行加密)
公私钥加密算法是一种非对称加密算法, 具有更高的安全度,
非对称加密算法
是指一对加密密钥与解密密钥,这两个密钥是数学相关,用某用户密钥加密后所得的信息,只能用该用户的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。称公开的密钥为公钥;不公开的密钥为私钥。
目前最公用的公钥,私钥加密
另外几种不入流的 对称加密或HASH加密就不介绍了。
公钥就是通过私钥生成的, 目前最常用的算法是RSA算法:
RSA是目前最有影响力的公钥加密算法,它能够 抵抗到目前为止已知的所有密码攻击,已被ISO推荐为公钥数据加密标准。RSA算法基于一个十分简单的数论事实:将两个大素数相乘十分容易。
已知的任意生成的私钥, 可以通过RSA算法, 生成公钥, 从而形成RSA密钥对。
RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。
以下主要针对RSA算法生成的公私钥进行加密验证和加签验签进行讲解:
XML公私钥加密解密的实现:
/**
* 公钥加密
* @param data待加密数据
* @param key 密钥
* @return byte[] 加密数据
* */
public static byte[] encryptByPublicKey(byte[] data,byte[] key) throws Exception{
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//初始化公钥
//密钥材料转换
X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);
//产生公钥
PublicKey pubKey=keyFactory.generatePublic(x509KeySpec);
//数据加密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return cipher.doFinal(data);
}
/**
* 私钥解密
* @param data 待解密数据
* @param key 密钥
* @return byte[] 解密数据
* */
public static byte[] decryptByPrivateKey(byte[] data,byte[] key) throws Exception{
//取得私钥
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//生成私钥
PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//数据解密
Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
}
XML数字签名的格式及实现:参考
基本上数字签名有三种类型:
封内签名: 这种签名是将签名作为XML对象的子信息,
封外签名: 这种签名将XML文档包含到Signature对象
分离签名: 这种情况下,签名是独立生成的不作为XML的一部分。也就是说你会拥有两个XML文件
目前主要的签名形式是封内签名, 本文主要探讨封内签名:
不同的数字签名命名空间有着不同的数字签名格式, 签名格式标准由W3指定, 目前使用广泛的是2000/09标准, 在xml加签部分中申明该命名空间:
图片.png
在(2000/09)命名空间下,XML签名包含了Signature元素。它的基本结构如下:
<Signature>
<SignedInfo>
<CanonicalizationMethod/> 规范化方法,用于去除空格和其他格式
<SignatureMethod/>
(<Reference (URI)?>
(<Transforms/>)?
<DigestMethod/>
<DigestValue/>
</Reference>)+
</SignedInfo>
<SignatureValue/>
(<KeyInfo/>)?
(<Object ID?/>)*
</Signature>
从上往下它们的复杂性是依次递增的接下来讲一下各个属性的作用:
[SignedInfo]
该元素的子元素包含有关所签名的内容以及签名方式的所有信息。签名算法实际上应用于该元素及其所有子元素以生成签名。
[CanonicalizationMethod]
该元素指定了用于SignedInfo元素以便将XML规范化的规范化(C14N)算法。
[SignatureMethod]
该元素指定了该签名的签名算法。这里的签名算法是带有RSA的SHA-256。
[Reference]
指定了将要签名的数据以及在哈希运算之前应当如何对该数据进行处理。
Reference- URL 的这个可选 URI 属性标识要签名的数据对象。 在一个 Signature 中,至多可以对一个Reference省略该属性。(为了确保明确地匹配引用和对象, 要强加这个限制。)
该标识与transforms一起是签名者提供的描述, 其内容有关它们如何获得已编摘形式的已签名数据对象(即,已编摘的内容)。验证者还可能以另一种方法获得已编摘的内容,只要摘要验证这种方法。
[Transforms]
每个Reference元素都可以具有零个或更多个为它指定的转换。这些转换按照它们在XML中列出的顺序应用于该Reference的数据。转换使您可以在对Reference的数据进行哈希运算之前对该数据进行筛选或修改。在该示例中,我们将使用包封式签名转换,该转换选择了包含文档中除Signature元素以外的所有XML。我们必须从将被签名的数据中移除Signature元素,否则,当我们存储签名值时,可能会修改我们尝试签名的数据。
[DigestMethod]
DigestMethod 是在应用 Transforms(如果已经指定它)之后对数据应用以产生 DigestValue 的算法。DigestValue 的签名是将资源内容与签名者密钥绑定的机制。
[SignatureValue]
该元素包含通过签名SignedInfo元素及其所有子元素而计算得到的签名值。
[KeyInfo]
KeyInfo表示用于验证签名的密钥。标识机制可以包括证书、密钥名称和密钥协议算法。KeyInfo是可选的有两个原因。首先,签名者可能不希望向所有文档处理方披露任何密钥信息。为什么总要告诉人家?其次,该信息在应用程序上下文中可能是已知的,并且不需要明确表示。 由于KeyInfo在SignedInfo之外,所以如果签名者希望将密钥信息与签名绑定,那么Reference可以容易地将KeyInfo作为签名的一部分标识并将其包括在内。
注意:
转换的内容将取决于 Algorithm 属性,即用Algorithm指定W3标准中的属性值。
例如:
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>的形式。
另外,可以为xml的加密部分再声明一个ds
的命名空间,使得整体更加规范。
加密整体示例:
<license>
<test>hello world</test>
<ds:Signature
xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>d+F/TTTxoF2Fk/knoTwHLqC7gqa+ETnTn84sNwx4c0Y=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>KdSti.....6OA==</ds:SignatureValue>
<ds:KeyInfo>
<ds:KeyValue>
<ds:RSAKeyValue>
<ds:Modulus>nM95A0J.....+GQ==</ds:Modulus>
<ds:Exponent>AQAB</ds:Exponent>
</ds:RSAKeyValue>
</ds:KeyValue>
</ds:KeyInfo>
</ds:Signature>
</license>
加签的实现:
原文链接需翻墙范围, 原文的项目下载链接已经失效,需要完整jar包可以评论留言提供
主要思路:
Java生成XML签名的相关代码:
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);
}
XMLSignatureFactory
XMLSignatureFactory是生成XML文档数字签名的工厂对象。对象的创建如下列代码所示:
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
DOMSignContext
DOMSignContext对象用来生成DOM树。在创建数字签名的过程中,DOM树会被附上XML数字签名。DOMSignContext对象要求输入私钥和XML文档的根元素。
关于验证数字签名:
可以使用java第三方的--KryptoUtil类进行验证 验证
如果想要将整体的加密环境打包成jar包,使其可以通过jar包运行, 可以使用github的一个开源项目: jar包资源管理
如果想要jar包在服务器上不通过tomcat就可运行, 可以安装jdk,并且使用服务器脚步执行java -c 命令编译jar包,输入参数并生产加密文件。
网友评论