Как создать подпись XML без пробелов и линейных перерывов на Java?

StackOverflow https://stackoverflow.com/questions/4728300

Вопрос

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

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

Мы случайно используем Apache XMLSignature И я не могу создать неограниченную подпись.

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

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

Я не смог найти опцию или параметр в 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>

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

Удачи!

:) :)

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

Однако это не ожидаемое решение: замена API Apache на javax.xml.crypto 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 производит подпись без пробелов между тегами вообще.

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

Нам просто нужно установить значение «true» для параметра «невероятной линии», причина «значение по умолчанию является fals

Вот код, чтобы избежать или удалить строки

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

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

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

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

Можешь попробовать:

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

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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top