我需要对XML文档进行数字签名。给出的要求是输入是XML文件和私钥。签名应使用SHA256 / RSA-2048,签名应为Enveloping。我想出了以下方法来做到这一点。在测试时,KeyPair使用KeyPairGenerator类生成,RSA作为算法,密钥大小设置为2048.传递的输入流是FileInputStream对象,输出流是FileOutputStream对象。该方法似乎工作正常。
public void sign(InputStream inputStream, OutputStream outputStream, KeyPair kp) throws Exception {
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
Reference ref = fac.newReference("#object", fac.newDigestMethod(DigestMethod.SHA256, null));
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
org.w3c.dom.Document doc = dbf.newDocumentBuilder().parse(inputStream);
XMLStructure content = new DOMStructure(doc.getDocumentElement());
XMLObject obj = fac.newXMLObject(Collections.singletonList(content), "object", null, null);
SignatureMethod signatureMethod = fac.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null);
CanonicalizationMethod canonicalizationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec)null);
SignedInfo si = fac.newSignedInfo(canonicalizationMethod, signatureMethod, Collections.singletonList(ref));
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
XMLSignature signature = fac.newXMLSignature(si, ki, Collections.singletonList(obj), null, null);
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc);
signature.sign(dsc);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(outputStream));
}
我有一些问题:
我没有在代码中指定RSA应该在签名中使用,那么它是如何知道的?另外,据我所知,在加密时,还有模式和填充。同样,这些也没有指定,那么它们会是什么?
我真的不知道XML的规范化是如何运作的。 CanonicalizationMethod.INCLUSIVE和CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS都生成相同的输出(两者都具有相同的DigestValue值)。这些不同的方法意味着什么?
对象标记中的封装消息似乎与输入XML文件内容完全相同。封装消息应该与原始XML相同还是应该是规范化版本?我知道规范化版本用于摘要,但不确定封装版本是否应该是规范化的。我的原始XML有注释标记,但无论是否包含INCLUSIVE或INCLUSIVE_WITH_COMMENTS,注释标记将始终存在于输出中。
是否需要公钥?给我的要求是私钥是输入,但我不确定是否应该询问公钥。似乎KeyValue对象需要公钥。
提前致谢。
答案 0 :(得分:0)
1)使用newSignatureMethod(“http://www.w3.org/2001/04/xmldsig-more#rsa-sha256”)
从您使用的URI推断出签名算法2)您没有为newReference中的Reference指定任何规范化算法。因此,在计算XML数据的摘要值之前,将使用默认值(没有注释的Canonical XML)。您的canonicalizationMethod与SignedInfo一起使用:在计算签名值之前,SignedInfo元素将使用此算法进行规范化。
要为签名的XML数据指定规范化算法,您需要在创建参考时说出来:
Transform tr = fac.newTransform(CanonicalizationMethod.INCLUSIVE, (TransformParameterSpec) null);
fac.newReference("#object", fac.newDigestMethod(DigestMethod.SHA256, null), Collections.singletonList(tr), null, null);
此代码将在Reference中添加一个带有规范化算法的Transform,引用将被散列的#object。
3)您将在Object元素中添加与其相同的内容。在计算摘要值之前,将对此内容应用规范化(或任何其他转换)。
4)KeyValue是Optional,是提示签名的验证者。通常的签名将在X509Certificate中使用证书而不是密钥值。
希望这会有所帮助。 Moez