2011-01-18 16 views
5

Pracuję z brazylijskim projektem "Nota Fiscal Eletronica", w którym definiują standardowy sposób podpisywania dokumentów XML.Jak utworzyć podpis XML bez białych znaków i linii łamania w Javie?

Ostatnio zaczęto wymagać, aby absolutnie nie było żadnych białych znaków między znacznikami, w tym znaczników podpisu (*).

Zdarza się, że używamy apache'a o nazwie XMLSignature i nie wydaje mi się, aby tworzył sygnaturę, która nie ma żadnego pisma.

Po usunięciu białych znaków po podpisaniu podpis zostanie zerwany.

Nie mogę zmienić zestawu canonicalizer/transformers, ponieważ są one predefiniowane.

Nie można znaleźć opcji lub parametru w interfejsie API XMLSignature w celu sterowania wcięciem lub białymi znakami.

Poniżej znajduje się kod:

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

A poniżej otrzymany podpis (fragment):

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

Wskazówki te (niechciane) łamanie wierszy.

Każda pomoc zostanie bardzo doceniona.

Dziękuję bardzo z góry.

(*) Wyjaśnienie: nowa reguła zakazuje białych znaków (lub dowolnego innego tekstu) między tagami tylko elementów. Na przykład, byłoby to pozostawiono:

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

gdy będzie to zabronione:

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

ponieważ w tym ostatnim przypadku, odstępy (line-przerwy) są pomiędzy dwoma znacznikami lub, innymi słowy, umieszczone wewnątrz znacznika tylko elementu.

+6

Problem polega na tym, że ich wymaganie to nie ma sensu z XML i XMLDSig punktu widzenia. Najprawdopodobniej popełnili jakiś błąd w swoim kodzie, a teraz zamiast potwierdzać błąd i naprawiać kod, chcą, aby wszyscy inni radzili sobie z ich błędnym wdrożeniem. –

+0

Wspieram @Eugene Mayevski 'EldoS Corp. Obsługiwałem xml jak ten powyżej bez żadnych problemów. – Bozho

+0

Twierdzą oni, że podali tę zasadę, aby zmniejszyć ruch sieciowy (który jest rzeczywiście ogromny), ponieważ niektóre oprogramowanie zawierało nadużywanie ilości wcięć. Są w stanie z powodzeniem zweryfikować podpis. Jest to inna (niezwiązana i nowa) zasada, która jest stosowana w przypadku odrzucenia wiadomości. Zgadzam się jednak, że nie powinny nakładać tego ograniczenia na część dotyczącą podpisu. –

Odpowiedz

7

Możesz po prostu ustawić -Dorg.apache.xml.security.ignoreLineBreaks = true, aby wyłączyć '\ n' w generowaniu XML. original mail

bug description

+0

Nie mogłem przetestować tego rozwiązania, ponieważ przed udzieleniem odpowiedzi już zastosowałem rozwiązanie zastępcze, wybierając inny interfejs API podpisu. Ale zajrzałem do linków i wydaje mi się, że jest to proste i znacznie mniej traumatyczne rozwiązanie, którego szukałem. –

+0

Linki nie działają –

3

Bloki podpisu kodują informacje binarne jako Base64, które musi śledzić niektóre formaty, w tym linie podziału (patrz http://en.wikipedia.org/wiki/Base64). Więc po prostu nie możesz ich usunąć bez zmiany informacji.

lepszym sposobem na zmniejszenie ruchu w sieci, jest użycie kompresji przed wysłaniem danych. Na szczęście

+0

+1, bardzo dobra odpowiedź. Artykuł wikipedia stwierdza, że ​​istnieje maksymalna długość linii i separator linii. Użycie kompresji jest właściwą metodą naprawy. – Bozho

+0

Nowa reguła zakazuje białych znaków tylko pomiędzy tagami. Tekst wewnątrz znacznika liścia (taki jak informacje zakodowane w base64) może zawierać białe spacje. Zaktualizuję pytanie, aby to wyjaśnić. –

+0

Tęsknimy za nami. W takim przypadku możesz go uruchomić (zobacz moją drugą odpowiedź). – lweller

2

Na szczęście musisz pobrać kod źródłowy i zhakować go samodzielnie.

Prawdopodobnie Twoje rozwiązanie pomoże innym w przyszłości, utwórz poprawkę i wyślij ją z powrotem do projektu.

Powodzenia!

:) :)

0

podpisu XML znaki częścią dokumentu XML, wychodząc z danego elementu (tj poddrzewa DOM) po to znormalizowane algorytmu C14N. Standardowy algorytm C14N, z którego korzystasz, zachowuje podział linii i białe spacje (patrz http://www.w3.org/TR/xml-c14n#Example-WhitespaceInContent).

Więc wszystkie podziały wiersza w podpisanym części oryginalnego dokumentu (w tym między ostatnim tagiem danych i znacznika <Signature> oraz między </Signature> i następnego znacznika zamykającego) * musi być zachowane, aby nie zmieniać podpis . Linie łamane i spacje w samym elemencie Signature nie są ważne i mogą zostać usunięte bez zmiany sygnatury.

Oto przykład:

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

Oto twoi możliwe opcje:

  1. zdefiniować własny algorytm C14N który usunie spacje i podziały wiersza przez to samo. Zniechęciłbym to, ponieważ druga strona musi również użyć tego niestandardowego algorytmu C14N.

  2. usuń linia łamie obowiązuje od ciebie XML przed podpisanie (i potencjalnie usunąć spacje w podpisie potem)

z przykładu to daje następujące podpisany XML:

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

i po usunięciu spacji z podpisem

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

Opcja 1. Nie wolno mi tego zrobić, ponieważ konwertery kanoniczne i transformaty są predefiniowane i nie można ich zmienić. –

+0

Opcja 2. Jeśli usuniemy spacje z podpisu, podpis nieuchronnie zostanie zerwany. Myślę, że jedynym sposobem jest stworzenie podpisu bez łamania linii. –

+0

Przetestowałem to samodzielnie (z apache xmlsec 1.4.4) i usuwając linie podziału i spacje ** pomiędzy tagami ** podpisu nie zmieniają tego. – lweller

1

Znalazłem (haniebne) rozwiązanie.

Nie jest to jednak oczekiwane rozwiązanie: zastąpienie interfejsu API apache interfejsem API javax.xml.crypto.

Oto kod Zmieniono:

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

Ten interfejs API produkuje podpis bez białych znaków pomiędzy znacznikami w ogóle.

Nadal chciałbym znaleźć rozwiązanie dla interfejsu API apache, ponieważ kod ten był już bardzo dojrzały i nie chcielibyśmy ryzykować aż tak wiele, jak zmiana całej implementacji podpisu.

0

Po prostu trzeba ustawić "prawdziwą" wartość do "ignoreLineBreaks" parametru Bo”Wartością domyślną jest false, a to pozwala na API podpisu, aby dodać linebreaks

oto kod, aby uniknąć lub usunąć linebreaks

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

wtedy będziemy może upewnić się, że nowa wartość jest prawdą z kodem następnej linii

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

Miałem ten sam problem i to działało dla mnie.

0

Można spróbować:

System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true"); 
Powiązane problemy