关于 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));       
}

我有一些问题:

  1. 我在代码中没有指定签名要用RSA,它怎么知道的?另外,据我所知,在加密中,还有模式和填充。同样,这些也没有指定,那么它们会是什么?

  2. 我真的不知道 XML 的规范化是如何工作的。 CanonicalizationMethod.INCLUSIVE 和 CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS 产生相同的输出(都有相同的 DigestValue 值)。这些不同的方法是什么意思?

  3. object 标记中的封装消息似乎与输入的XML 文件内容完全相同。封装的消息应该与原始 XML 相同还是应该是规范化版本?我知道摘要中使用了规范化版本,但不确定是否应该对封装版本进行规范化。我原来的 XML 有评论标签,但评论标签将始终出现在输出中,无论我是否输入 INCLUSIVE 或 INCLUSIVE_WITH_COMMENTS.

  4. 是否需要 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 中包含证书而不是密钥值。

希望这对您有所帮助。 莫兹