如何在 Java 中生成没有空格和换行符的 XML 签名?

发布于 10-12 19:18 字数 5002 浏览 9 评论 0原文

我与巴西的“Nota Fiscal Eletronica”项目合作,其中他们定义签署 XML 文档的标准方法。

最近,他们开始要求标签之间绝对不能有空格,包括签名标签(*)。

我们碰巧使用 apache 的 XMLSignature 和我似乎无法生成无缩进的签名。

如果我在签名后删除空格,签名就会被破坏。

我也无法更改规范化器/变压器集,因为它们是预定义的。

我在 XMLSignature API 中找不到用于控制缩进或空格的选项或参数。

下面是代码:

    // the element where to insert the signature
    Element element = ...;
    X509Certificate cert = ...;
    PrivateKey privateKey = ...;

    XMLSignature signer =
            new XMLSignature(doc, "http://xml-security",
            XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);

    element.appendChild(signer.getElement());

    Transforms transforms = new Transforms(doc);

    // Define as regras de transformação e canonicalização do documento
    // XML, necessário para fazer a verificação do parsing e da
    // assinatura pelos destinatários
    transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE); //, xpath.getElementPlusReturns());

    transforms.addTransform(Transforms.TRANSFORM_C14N_OMIT_COMMENTS); //,xpath.getElementPlusReturns());

    String id = "";

    id = ((Element) element.getElementsByTagName("infNFe").item(0)).getAttributeNode("Id").getNodeValue();

    signer.addDocument("#" + id, transforms, 
                       Constants.ALGO_ID_DIGEST_SHA1);
    signer.addKeyInfo(cert);
    signer.sign(privateKey);

下面是生成的签名(片段):

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#NFe43110189716583000165550010000076011492273645">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>fas0ra5uRskQgRHSrIYhEjFEjKQ=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
2RGltUZy0HfNoiKtVanAeN+JUPyglWDuQNnMudSgA7kESoHBZ/q/GMbc+xMSN1eV8u7+2PxSKl1T
Zl592FWmCSAkL8pwMujDxJ4iTLU20Hf0dNF7oGcyB+g9GgbipW2udq0kwJLz6HzXUD/Evf/0y+3T
NtsXeIaA6A29ttD/UEs=
</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
MIIFqTCCBJGgAwIBAgIEQeNSuzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJicjETMBEGA1UE
ChMKSUNQLUJyYXNpbDEgMB4GA1UECxMXQ2FpeGEgRWNvbm9taWNhIEZlZGVyYWwxFDASBgNVBAMT
C0FDIENBSVhBIFBKMB4XDTEwMDYwODE5MjQwNVoXDTExMDYwODE5NTQwNVowgYQxCzAJBgNVBAYT
AmJyMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSAwHgYDVQQLExdDYWl4YSBFY29ub21pY2EgRmVkZXJh
bDEUMBIGA1UECxMLQUMgQ0FJWEEgUEoxKDAmBgNVBAMTH0EgQlVITEVSIFNBIENVUlRVTUU6NDA5
NDI0OTAwMTAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOFxgvG35RQWgXec4zVrzoUHolnJ
fP76rpO2Vo40593W9Gf0WwHt36gVmli0ZeQitFmzFSoE5KhgXQGZg6RpV3WJUFcIrPBHPdqOSfiB
988kf962P+j8fZ38BNmo7TV9H9hMBkV9bD/QOe73wFDc+rT6/9io++Z+7/wup/3glKntAgMBAAGj
ggLOMIICyjAOBgNVHQ8BAf8EBAMCBeAwVwYDVR0gBFAwTjBMBgZgTAECAQkwQjBABggrBgEFBQcC
ARY0aHR0cDovL2ljcC5jYWl4YS5nb3YuYnIvcmVwb3NpdG9yaW8vZHBjYWNjYWl4YXBqLnBkZjAp
BgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwgbYGA1UdEQSBrjCBq4EV
YnVobGVyQGFidWhsZXIuY29tLmJyoD4GBWBMAQMEoDUEMzE0MDkxOTQ2NDA5NDI0OTAwMTAxMDg0
NDcwODE3NTAwMDAwODAzMjkyMjM1NlNTUCBSU6AeBgVgTAEDAqAVBBNOQUlSIEJVSExFUiBTQ0hO
RUNLoBkGBWBMAQMDoBAEDjg5NzE2NTgzMDAwMTY1oBcGBWBMAQMHoA4EDDAwMDAwMDAwMDAwMDCC
ATIGA1UdHwSCASkwggElMIGuoIGroIGohjJodHRwOi8vaWNwLmNhaXhhLmdvdi5ici9yZXBvc2l0
b3Jpby9BQ0NBSVhBUEoxLmNybIY0aHR0cDovL2ljcDIuY2FpeGEuZ292LmJyL3JlcG9zaXRvcmlv
Mi9BQ0NBSVhBUEoxLmNybIY8aHR0cDovL3JlcG9zaXRvcmlvLmljcGJyYXNpbC5nb3YuYnIvbGNy
L2NhaXhhL0FDQ0FJWEFQSjEuY3JsMHKgcKBupGwwajELMAkGA1UEBhMCYnIxEzARBgNVBAoTCklD
UC1CcmFzaWwxIDAeBgNVBAsTF0NhaXhhIEVjb25vbWljYSBGZWRlcmFsMRQwEgYDVQQDEwtBQyBD
QUlYQSBQSjEOMAwGA1UEAxMFQ1JMNDEwHwYDVR0jBBgwFoAUjkAvCv4T1ao5oHZ0htO8fcfx5c8w
CQYDVR0TBAIwADAZBgkqhkiG9n0HQQAEDDAKGwRWNy4xAwIDqDANBgkqhkiG9w0BAQUFAAOCAQEA
nZHUvdnZsiCIDjKm1zHehbtuDtDJha4O4FZ03J74Y+AxyAFs/4JED+xUvZ5jFuEsdqgA0V/dxUFy
Uz/ca10Ievd578GQdGwYl1GFhRtO/SlxeaOEf7eDdGOWXO3VmUA3NmNo0X8RRTIoifnhpDXu7RbN
5sijyH/uXyRFWX9XH2N0U/r3oJtNKXsvoUlbDrkalgkuLzLKsaEj0TkwisXO3cmMoWGuBpAZC+46
e4x/2vTqOvYkzZO+O9NLi0YWSYY7OJKiKBjMC6MzdlPM9VTkIwO9WvWEMdbU0/jhO2cMcVMzNZc1
r6ZmdTDrwqV3elSTkQtJ0RIZNgMJUn+Y8c7Aog==
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>

注意(不需要的)换行符。

任何帮助将不胜感激。

预先非常感谢。

(*) 说明:新规则禁止纯元素标签之间存在空格(或任何其他文本)。例如,这将是允许

<a><b>
  text
  inside
  tag
</b></a>

而这将是禁止

<a>
<b>text</b>
</a>

因为在后一种情况下,空格(换行符)位于两个标签之间,或者,换句话说,放置在纯元素标签内。

I work with the brazilian "Nota Fiscal Eletronica" project, in which they define a standart way to sign XML documents.

Recently, they started to require that there are absolutely no whitespaces between tags, including signature tags (*).

We happen to use apache's XMLSignature and I can't seem to produce an unindented signature.

If I remove the whitespaces after signing, the signature gets broken.

I cannot change the canonicalizer / transformers set either, since they're predefined.

I couldn't find an option or parameter in the XMLSignature API to control indentation or whitespaces.

Below is the code:

    // the element where to insert the signature
    Element element = ...;
    X509Certificate cert = ...;
    PrivateKey privateKey = ...;

    XMLSignature signer =
            new XMLSignature(doc, "http://xml-security",
            XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);

    element.appendChild(signer.getElement());

    Transforms transforms = new Transforms(doc);

    // Define as regras de transformação e canonicalização do documento
    // XML, necessário para fazer a verificação do parsing e da
    // assinatura pelos destinatários
    transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE); //, xpath.getElementPlusReturns());

    transforms.addTransform(Transforms.TRANSFORM_C14N_OMIT_COMMENTS); //,xpath.getElementPlusReturns());

    String id = "";

    id = ((Element) element.getElementsByTagName("infNFe").item(0)).getAttributeNode("Id").getNodeValue();

    signer.addDocument("#" + id, transforms, 
                       Constants.ALGO_ID_DIGEST_SHA1);
    signer.addKeyInfo(cert);
    signer.sign(privateKey);

And below is the resulting signature (snippet):

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#NFe43110189716583000165550010000076011492273645">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>fas0ra5uRskQgRHSrIYhEjFEjKQ=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
2RGltUZy0HfNoiKtVanAeN+JUPyglWDuQNnMudSgA7kESoHBZ/q/GMbc+xMSN1eV8u7+2PxSKl1T
Zl592FWmCSAkL8pwMujDxJ4iTLU20Hf0dNF7oGcyB+g9GgbipW2udq0kwJLz6HzXUD/Evf/0y+3T
NtsXeIaA6A29ttD/UEs=
</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
MIIFqTCCBJGgAwIBAgIEQeNSuzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJicjETMBEGA1UE
ChMKSUNQLUJyYXNpbDEgMB4GA1UECxMXQ2FpeGEgRWNvbm9taWNhIEZlZGVyYWwxFDASBgNVBAMT
C0FDIENBSVhBIFBKMB4XDTEwMDYwODE5MjQwNVoXDTExMDYwODE5NTQwNVowgYQxCzAJBgNVBAYT
AmJyMRMwEQYDVQQKEwpJQ1AtQnJhc2lsMSAwHgYDVQQLExdDYWl4YSBFY29ub21pY2EgRmVkZXJh
bDEUMBIGA1UECxMLQUMgQ0FJWEEgUEoxKDAmBgNVBAMTH0EgQlVITEVSIFNBIENVUlRVTUU6NDA5
NDI0OTAwMTAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOFxgvG35RQWgXec4zVrzoUHolnJ
fP76rpO2Vo40593W9Gf0WwHt36gVmli0ZeQitFmzFSoE5KhgXQGZg6RpV3WJUFcIrPBHPdqOSfiB
988kf962P+j8fZ38BNmo7TV9H9hMBkV9bD/QOe73wFDc+rT6/9io++Z+7/wup/3glKntAgMBAAGj
ggLOMIICyjAOBgNVHQ8BAf8EBAMCBeAwVwYDVR0gBFAwTjBMBgZgTAECAQkwQjBABggrBgEFBQcC
ARY0aHR0cDovL2ljcC5jYWl4YS5nb3YuYnIvcmVwb3NpdG9yaW8vZHBjYWNjYWl4YXBqLnBkZjAp
BgNVHSUEIjAgBggrBgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcUAgIwgbYGA1UdEQSBrjCBq4EV
YnVobGVyQGFidWhsZXIuY29tLmJyoD4GBWBMAQMEoDUEMzE0MDkxOTQ2NDA5NDI0OTAwMTAxMDg0
NDcwODE3NTAwMDAwODAzMjkyMjM1NlNTUCBSU6AeBgVgTAEDAqAVBBNOQUlSIEJVSExFUiBTQ0hO
RUNLoBkGBWBMAQMDoBAEDjg5NzE2NTgzMDAwMTY1oBcGBWBMAQMHoA4EDDAwMDAwMDAwMDAwMDCC
ATIGA1UdHwSCASkwggElMIGuoIGroIGohjJodHRwOi8vaWNwLmNhaXhhLmdvdi5ici9yZXBvc2l0
b3Jpby9BQ0NBSVhBUEoxLmNybIY0aHR0cDovL2ljcDIuY2FpeGEuZ292LmJyL3JlcG9zaXRvcmlv
Mi9BQ0NBSVhBUEoxLmNybIY8aHR0cDovL3JlcG9zaXRvcmlvLmljcGJyYXNpbC5nb3YuYnIvbGNy
L2NhaXhhL0FDQ0FJWEFQSjEuY3JsMHKgcKBupGwwajELMAkGA1UEBhMCYnIxEzARBgNVBAoTCklD
UC1CcmFzaWwxIDAeBgNVBAsTF0NhaXhhIEVjb25vbWljYSBGZWRlcmFsMRQwEgYDVQQDEwtBQyBD
QUlYQSBQSjEOMAwGA1UEAxMFQ1JMNDEwHwYDVR0jBBgwFoAUjkAvCv4T1ao5oHZ0htO8fcfx5c8w
CQYDVR0TBAIwADAZBgkqhkiG9n0HQQAEDDAKGwRWNy4xAwIDqDANBgkqhkiG9w0BAQUFAAOCAQEA
nZHUvdnZsiCIDjKm1zHehbtuDtDJha4O4FZ03J74Y+AxyAFs/4JED+xUvZ5jFuEsdqgA0V/dxUFy
Uz/ca10Ievd578GQdGwYl1GFhRtO/SlxeaOEf7eDdGOWXO3VmUA3NmNo0X8RRTIoifnhpDXu7RbN
5sijyH/uXyRFWX9XH2N0U/r3oJtNKXsvoUlbDrkalgkuLzLKsaEj0TkwisXO3cmMoWGuBpAZC+46
e4x/2vTqOvYkzZO+O9NLi0YWSYY7OJKiKBjMC6MzdlPM9VTkIwO9WvWEMdbU0/jhO2cMcVMzNZc1
r6ZmdTDrwqV3elSTkQtJ0RIZNgMJUn+Y8c7Aog==
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>

Notice the (unwanted) line-breaks.

Any help would be greatly appreciated.

Thanks a lot in advance.

(*) Clarification: the new rule forbids whitespaces (or any other text) between element-only tags. As an example, this would be allowed:

<a><b>
  text
  inside
  tag
</b></a>

while this would be forbidden:

<a>
<b>text</b>
</a>

because on the latter case, the whitespaces (line-breaks) are between two tags, or, in other words, placed inside an element-only tag.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

在风中等你2024-10-19 19:18:28

您只需设置 -Dorg.apache.xml.security.ignoreLineBreaks=true 即可在 XML 生成中禁用“\n”。
原始邮件

错误描述

You can simply set -Dorg.apache.xml.security.ignoreLineBreaks=true for disabling '\n' in XML generation.
original mail

bug description

终陌2024-10-19 19:18:28

您可以尝试:

static {
    System.setProperty("com.sun.org.apache.xml.internal.security.ignoreLineBreaks", "true");
    com.sun.org.apache.xml.internal.security.Init.init();
}

或者

static {
    System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
    org.apache.xml.security.Init.init();
}

将其添加到执行签名工作的类中。

You can try:

static {
    System.setProperty("com.sun.org.apache.xml.internal.security.ignoreLineBreaks", "true");
    com.sun.org.apache.xml.internal.security.Init.init();
}

Or

static {
    System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
    org.apache.xml.security.Init.init();
}

Add this to the class which does signature job.

栀子花开つ2024-10-19 19:18:28

签名块将二进制信息编码为 Base64,它必须遵循一些格式,包括换行符(请参阅http://en.wikipedia.org/wiki/Base64)。因此,您根本无法在不更改信息的情况下删除它们。

减少网络流量的更好方法是在发送数据之前使用压缩。

the signature blocks are encoding binary information as Base64, which must follow some formating including line breaks (see http://en.wikipedia.org/wiki/Base64). So you simply can't remove them without alter the information.

a better way to redure network traffic, is to use comression before sending data.

风吹过旳痕迹2024-10-19 19:18:28

我们只需要将“ignoreLineBreaks”参数设置为“true”值即可,
因为'默认值为 false,这允许签名 API 添加 LineBreaks

这里是避免或删除 LineBreaks 的代码

Field f = XMLUtils.class.getDeclaredField("ignoreLineBreaks");
f.setAccessible(true);
f.set(null, Boolean.TRUE);

,然后,我们可以确保新值在下一个代码行中为 true

System.err.println(XMLUtils.ignoreLineBreaks());

我遇到了同样的问题这对我有用。

We just need to set the "true" value to the "ignoreLineBreaks" parameter,
cause' the default value is false and this allows to the signature API to add LineBreaks

here is the code to avoid or remove LineBreaks

Field f = XMLUtils.class.getDeclaredField("ignoreLineBreaks");
f.setAccessible(true);
f.set(null, Boolean.TRUE);

then, we'll can make sure that the new value is true with the next code line

System.err.println(XMLUtils.ignoreLineBreaks());

I had the same problem and this worked for me.

零度℉2024-10-19 19:18:28

幸运的是 XMLSignature 是开源的,所以我想你必须获取源代码并破解它自己。

也许您的解决方案将来会帮助其他人,因此,创建一个补丁并将其发送回项目。

祝你好运!

:) :)

Fortunately XMLSignature is opensource, so I guess you'll have to get the source code and hack it your self.

Probably your solution will help others in the future so, create a patch and send it back to the project.

Good luck!

:) :)

空城旧梦2024-10-19 19:18:28

XML 签名在使用 C14N 算法规范化后,对 XML 文档的一部分进行签名,该元素从给定元素(即 DOM 中的子树)开始。您使用的标准 C14N 算法保留换行符和空格(请参阅 http://www .w3.org/TR/xml-c14n#Example-WhitespaceInContent)。

因此,原始文档签名部分中的所有换行符(包括数据的最后一个标签与 标签之间,以及 与下一个结束标记)*必须保留,以免更改签名。 Signature 元素本身中的换行符和空格并不重要,可以在不改变签名的情况下删除它们。

这里有一个例子:

<root id="signedpart">
  <data>
     ...
  </data>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
     <SignedInfo>
       <Reference URI="#signedpart">
          ...
       </Reference>
     </SignedInfo>
  </Signature>
</root> 

以下是您可能的选择:

  1. 定义您自己的 C14N 算法,该算法将自行删除空格和换行符。我不鼓励这样做,因为另一方也必须使用这种非标准 C14N 算法。

  2. 签名之前从 XML 中删除换行符和空格(之后可能会删除签名中的空格)

例如,这将为您提供以下签名的 XML:

<root id="signedpart"><data>...</data><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
       <Reference URI="#signedpart">
          ...
       </Reference>
     </SignedInfo>
  </Signature></root>

删除签名中的空格后

<root id="signedpart"><data>...</data><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><Reference URI="#signedpart">...</Reference></SignedInfo></Signature></root>

XML Signature signs part of an XML Document starting with a given element (i.e. a sub tree in DOM) after it is normalized with a C14N algorithm. The standard C14N algorithm you use preserves line breaks and white spaces (see http://www.w3.org/TR/xml-c14n#Example-WhitespaceInContent).

So all line breaks in the signed part of the original document (including between last tag of data and the <Signature> tag, and between </Signature> and the next closing tag) *must be preserved so as not to alter the signature. The line breaks and spaces in the Signature element itself are not important and may be removed without altering the signature.

Here an example:

<root id="signedpart">
  <data>
     ...
  </data>
  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
     <SignedInfo>
       <Reference URI="#signedpart">
          ...
       </Reference>
     </SignedInfo>
  </Signature>
</root> 

Here are your possible options:

  1. define your own C14N algorithm that will remove spaces and line breaks by it self. I would discourage this as the other side must also use this non standard C14N algorithm.

  2. remove line breaks an spaces from you XML before signing it (and potentially remove spaces in signature afterwards)

with the example this will give you the following signed XML:

<root id="signedpart"><data>...</data><Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
       <Reference URI="#signedpart">
          ...
       </Reference>
     </SignedInfo>
  </Signature></root>

and after removing spaces in signature

<root id="signedpart"><data>...</data><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><Reference URI="#signedpart">...</Reference></SignedInfo></Signature></root>
∞梦里开花2024-10-19 19:18:28

您可以尝试:

System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");

You can try:

System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
山川志2024-10-19 19:18:28

我找到了一个(可耻的)解决方案。

但这不是预期的解决方案:用 javax.xml.crypto API 替换 apache 的 API。

以下是更改后的代码:

// the element where to insert the signature
Element element = ...;
X509Certificate cert = ...;
PrivateKey privateKey = ...;
// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a Reference to the enveloped document (in this case,
// you are signing the whole document, so a URI of "" signifies
// that, and also specify the SHA1 digest algorithm and
// the ENVELOPED Transform.
List<Transform> transformList = new ArrayList<Transform>();
TransformParameterSpec tps = null;
Transform envelopedTransform;
try {
    envelopedTransform = fac.newTransform(Transform.ENVELOPED,
            tps);
    Transform c14NTransform = fac.newTransform(
            "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", tps);

    transformList.add(envelopedTransform);
    transformList.add(c14NTransform);
} catch (NoSuchAlgorithmException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
} catch (InvalidAlgorithmParameterException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
}

// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List<Serializable> x509Content = new ArrayList<Serializable>();
x509Content.add(cert);
javax.xml.crypto.dsig.keyinfo.X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));

// Obtem elemento do documento a ser assinado, será criado uma
// REFERENCE para o mesmo
Element el = (Element) element.getElementsByTagName(subTag).item(0);
String id = el.getAttribute("Id");

// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.

Reference ref;
javax.xml.crypto.dsig.SignedInfo si;
try {
    ref = fac.newReference("#" + id, fac.newDigestMethod(
            DigestMethod.SHA1, null), transformList, null, null);

    // Create the SignedInfo.
    si = fac.newSignedInfo(fac.newCanonicalizationMethod(
            CanonicalizationMethod.INCLUSIVE,
            (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
            Collections.singletonList(ref));
} catch (NoSuchAlgorithmException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
} catch (InvalidAlgorithmParameterException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
}

// Create the XMLSignature, but don't sign it yet.
javax.xml.crypto.dsig.XMLSignature signature = fac.newXMLSignature(si, ki);

// Marshal, generate, and sign the enveloped signature.
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element.
DOMSignContext dsc = new DOMSignContext(privateKey, element);
signature.sign(dsc);

此 API 生成的签名在标签之间根本没有空格。

仍然希望看到 apache 的 API 的解决方案,因为这段代码已经非常成熟,我们不想冒险改变整个签名实现。

I found a (shameful) solution.

It's not the expected solution, though: replacing apache's API with javax.xml.crypto API.

Here's the changed code:

// the element where to insert the signature
Element element = ...;
X509Certificate cert = ...;
PrivateKey privateKey = ...;
// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

// Create a Reference to the enveloped document (in this case,
// you are signing the whole document, so a URI of "" signifies
// that, and also specify the SHA1 digest algorithm and
// the ENVELOPED Transform.
List<Transform> transformList = new ArrayList<Transform>();
TransformParameterSpec tps = null;
Transform envelopedTransform;
try {
    envelopedTransform = fac.newTransform(Transform.ENVELOPED,
            tps);
    Transform c14NTransform = fac.newTransform(
            "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", tps);

    transformList.add(envelopedTransform);
    transformList.add(c14NTransform);
} catch (NoSuchAlgorithmException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
} catch (InvalidAlgorithmParameterException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
}

// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List<Serializable> x509Content = new ArrayList<Serializable>();
x509Content.add(cert);
javax.xml.crypto.dsig.keyinfo.X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));

// Obtem elemento do documento a ser assinado, será criado uma
// REFERENCE para o mesmo
Element el = (Element) element.getElementsByTagName(subTag).item(0);
String id = el.getAttribute("Id");

// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.

Reference ref;
javax.xml.crypto.dsig.SignedInfo si;
try {
    ref = fac.newReference("#" + id, fac.newDigestMethod(
            DigestMethod.SHA1, null), transformList, null, null);

    // Create the SignedInfo.
    si = fac.newSignedInfo(fac.newCanonicalizationMethod(
            CanonicalizationMethod.INCLUSIVE,
            (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
            Collections.singletonList(ref));
} catch (NoSuchAlgorithmException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
} catch (InvalidAlgorithmParameterException e) {
    throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
}

// Create the XMLSignature, but don't sign it yet.
javax.xml.crypto.dsig.XMLSignature signature = fac.newXMLSignature(si, ki);

// Marshal, generate, and sign the enveloped signature.
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element.
DOMSignContext dsc = new DOMSignContext(privateKey, element);
signature.sign(dsc);

This API produces the signature with no whitespaces between tags at all.

Still would like to see a solution for apache's API, since this code was very mature already, and we wouldn't like to risk as much as changing the entire signature implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文