关于 Java XML 数字签名的问题
Questions on Java XML Digital Signature
我需要对 XML 文档进行数字签名。给出的要求是输入是一个XML文件和一个私钥。签名应该使用SHA256/RSA-2048,签名应该是Enveloping。我想出了以下方法来做到这一点。在测试中,KeyPair 是使用 KeyPairGenerator class 生成的,其中 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 值)。这些不同的方法是什么意思?
object 标记中的封装消息似乎与输入的XML 文件内容完全相同。封装的消息应该与原始 XML 相同还是应该是规范化版本?我知道摘要中使用了规范化版本,但不确定是否应该对封装版本进行规范化。我原来的 XML 有评论标签,但评论标签将始终出现在输出中,无论我是否输入 INCLUSIVE 或 INCLUSIVE_WITH_COMMENTS.
是否需要 public 密钥?给我的要求是私钥是一个输入,但我不确定我是否应该也询问 public 密钥。似乎 KeyValue 对象需要 public 键。
提前致谢。
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);
此代码将在参考中添加一个带有规范化算法的转换,引用将被散列的#object。
3) 您将在 Object 元素中拥有您添加到其中的完全相同的内容。在计算摘要值之前,将对此内容应用规范化(或任何其他转换)。
4) KeyValue 是可选的,是对签名验证者的提示。通常的签名将在 X509Certificate 中包含证书而不是密钥值。
希望这对您有所帮助。
莫兹
我需要对 XML 文档进行数字签名。给出的要求是输入是一个XML文件和一个私钥。签名应该使用SHA256/RSA-2048,签名应该是Enveloping。我想出了以下方法来做到这一点。在测试中,KeyPair 是使用 KeyPairGenerator class 生成的,其中 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 值)。这些不同的方法是什么意思?
object 标记中的封装消息似乎与输入的XML 文件内容完全相同。封装的消息应该与原始 XML 相同还是应该是规范化版本?我知道摘要中使用了规范化版本,但不确定是否应该对封装版本进行规范化。我原来的 XML 有评论标签,但评论标签将始终出现在输出中,无论我是否输入 INCLUSIVE 或 INCLUSIVE_WITH_COMMENTS.
是否需要 public 密钥?给我的要求是私钥是一个输入,但我不确定我是否应该也询问 public 密钥。似乎 KeyValue 对象需要 public 键。
提前致谢。
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);
此代码将在参考中添加一个带有规范化算法的转换,引用将被散列的#object。
3) 您将在 Object 元素中拥有您添加到其中的完全相同的内容。在计算摘要值之前,将对此内容应用规范化(或任何其他转换)。
4) KeyValue 是可选的,是对签名验证者的提示。通常的签名将在 X509Certificate 中包含证书而不是密钥值。
希望这对您有所帮助。 莫兹