Как создать XML-подпись без пробелов и разрывов строк в Java? - PullRequest
7 голосов
/ 18 января 2011

Я работаю с бразильским проектом " Nota Fiscal Eletronica ", в котором они определяют стандартный способ подписи документов XML.

Недавно они начали требовать, чтобы между тегами не было абсолютно никаких пробелов, включая теги подписи (*).

Нам довелось использовать XMLSignature от Apache, и я не могу создать не подписанную подпись.

Если я удаляю пробелы после подписи, подпись нарушается.

Я также не могу изменить набор канонизаторов / трансформаторов, поскольку они предопределены.

Не удалось найти параметр или параметр в API XMLSignature для управления отступами или пробелами.

Ниже приведен код:

    // 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>

потому что в последнем случае пробелы (разрывы строк) находятся между двумя тегами или, другими словами, помещаются внутри тега только для элемента.

Ответы [ 7 ]

8 голосов
/ 04 июня 2012

Вы можете просто установить -Dorg.apache.xml.security.ignoreLineBreaks = true для отключения '\ n' при генерации XML. оригинальная почта

описание ошибки

4 голосов
/ 18 января 2011

блоки подписи кодируют двоичную информацию как Base64, которая должна следовать за некоторым форматированием, включая разрывы строк (см. http://en.wikipedia.org/wiki/Base64). Так что вы просто не можете удалить их без изменения информации.

Лучший способ перенаправить сетевой трафик - использовать сжатие перед отправкой данных.

2 голосов
/ 18 января 2011

К счастью XMLSignature с открытым исходным кодом , поэтому я думаю, вам придется получить исходный код и взломать его самостоятельно.

Возможно, ваше решение поможет другим в будущем, поэтому создайте патч и отправьте его обратно в проект.

Удачи!

:) :)

1 голос
/ 30 июня 2016

Вы можете попробовать:

System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
1 голос
/ 12 мая 2016

Нам просто нужно установить «истинное» значение для параметра «ignoreLineBreaks», потому что «значение по умолчанию равно false, и это позволяет API-интерфейсу подписи добавлять LineBreaks

, вот код, которого следует избегатьудалите LineBreaks

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

, тогда мы сможем убедиться, что новое значение соответствует следующей строке кода

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

У меня была та же проблема, и это сработало для меня.

1 голос
/ 19 января 2011

Я нашел (позорное) решение.

Однако это не ожидаемое решение: замена API-интерфейса Apache на API-интерфейс javax.xml.crypto.

Вот измененный код:

// 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 создает подпись без пробелов между тегами вообще.

Тем не менее, хотелось бы увидеть решение для API Apache, так как этот код уже очень развит, и мы не хотели бы рисковать так сильно, как изменение всей реализации сигнатуры.

0 голосов
/ 19 января 2011

Подпись XML подписывает часть документа XML, начиная с заданного элемента (т. Е. Поддерева в DOM), после того как он нормализован с помощью алгоритма C14N.Используемый вами стандартный алгоритм C14N сохраняет разрывы строк и пробелы (см. http://www.w3.org/TR/xml-c14n#Example-WhitespaceInContent).

Таким образом, все разрывы строк в подписанной части исходного документа (в том числе между последним тегом данных и <Signature>)тег и между </Signature> и следующим закрывающим тегом) * необходимо сохранить , чтобы не изменять сигнатуру. Разрывы строк и пробелы в самом элементе 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>
...