2012-05-01 12 views
25

Necesito enviar un archivo XML firmado a una agencia gubernamental en Brasil. El problema es que el digesto calcula el código de Java (usando el Java XML Digital Signature API es diferente de la generada con otra herramienta como XMLSECValor de resumen incorrecto para las firmas xml con Java XML Signature API

Aquí está el código que utilizo para generar una firma XML para algún nodo XML:.

private synchronized void sign(XmlObject obj) throws Exception { 
     initKeystore(); 
     XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); 
     List<Transform> transformList = new ArrayList<Transform>(); 
     Transform envelopedTransform = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null); 
     Transform c14NTransform = fac.newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", 
       (TransformParameterSpec) null); 
     transformList.add(envelopedTransform); 
     transformList.add(c14NTransform); 
     Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA1, null), 
       Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, 
       null); 
     SignedInfo si = fac.newSignedInfo(
       fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), 
       fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref)); 
     KeyStore ks = KeyStore.getInstance("PKCS12"); 
     ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")), 
       System.getProperty("javax.net.ssl.keyStorePassword").toCharArray()); 
     KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry("entry", 
       new KeyStore.PasswordProtection(System.getProperty("javax.net.ssl.keyStorePassword").toCharArray())); 

     X509Certificate cert = (X509Certificate) keyEntry.getCertificate(); 

     // Create the KeyInfo containing the X509Data. 
     KeyInfoFactory kif = fac.getKeyInfoFactory(); 
     X509Data xd = kif.newX509Data(Collections.singletonList(cert)); 
     KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd)); 
     // Instantiate the document to be signed. 

     Element el = (Element) obj.getDomNode().getFirstChild(); 
     String id = el.getAttribute("Id"); 

     DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), el); 
     // Create the XMLSignature, but don't sign it yet. 
     XMLSignature signature = fac.newXMLSignature(si, ki); 
     // Marshal, generate, and sign the enveloped signature. 
     signature.sign(dsc); 

    } 

Si intento para validar el XML generado con xmlsec, me sale el siguiente error:

$ xmlsec1 --verify consulta.xml 
func=xmlSecOpenSSLEvpDigestVerify:file=digests.c:line=229:obj=sha1:subj=unknown:error=12:invalid data:data and digest do not match 
FAIL 

Pero si trato de firmar el mismo archivo (consult.xml) con xmlsec (utilizando la misma clave privada), ese error va distancia:

xmlsec1 --sign --output doc-signed.xml --privkey-pem cert.pem consulta.xml 

Las diferencias entre consult.xml y doc-signed.xml (generado por xmlsec) son los contenidos de las etiquetas SignatureValue y DigestValue:

consulta.xml:

<DigestValue>Ajn+tfX7JQc0HPNJ8KbTy7Q2f8I=</DigestValue> 
... 
<SignatureValue>Q1Ys0Rtj8yL2SA2NaQWQPtmNuHKK8q2anPiyLWlH7mOIjwOs0GEcD0WLUM/BZU0Q 
T0kSbDTuJeTR2Ec9wu+hqXXbJ76FpX9/IyHrdyx2hLg0VhB5RRCdyBEuGlmnsFDf 
XCyBotP+ZyEzolbTCN9TjCUnXNDWtFP1YapMxAIA0sth0lTpYgGJd8CSvFlHdFj+ 
ourf8ZGiDmSTkVkKnqDsj8O0ZLmbZfJpH2CBKicX+Ct7MUz2sqVli4XAHs6WXX+E 
HJpbOKthS3WCcpG3Kw4K50yIYGTkTbWCYFxOVsMfiVy4W/Qz15Vxb8chD8LM58Ep 
m/szmvnTAESxv/piDr7hyw==</SignatureValue> 

doc-signed.xml:

<DigestValue>w6xElXJrZw3G86OsNkWav+pcKJo=</DigestValue> 
... 
<SignatureValue>YmUsnlnAY9uLhlfVBLhB8K8ArxMOkOKZJoQ6zgz55ggU6vJCO9+HWJCKQJp6Rvn/w5PCAFY0KJRb 
r6/WhHML0Z+Q6TSuIL8OTvJ3iPoROAK6uy07YAflKOUklqk4uxgfMkR+hWMCyfITJVCVZo/MXmPy 
g7YwmztoSlGH+p6+ND5n2u47Y2k6SpIvw3CUxwAVQkD0Hsj3G58cbUbrFCoyPVGOe4zJ9c1HPsMW 
KzBEFe3QETzPJ8I1B7EEVi5oDvzXE2rMTH4K7zvNGnXpBNGwnSjEOticlqKVP5wyUD7CPwgF1Wgy 
Z0njvlaW3K8YmAY8fc70v/+wSO6Fu+0zj18Xeg==</SignatureValue> 

que no va a publicar el resto del archivo, ya sea porque son iguales y haría esta publicación aún más detallada.

Por lo que puedo deducir, la aplicación web que recibe este archivo XML es una aplicación .NET y calcula un resumen de firma diferente que mi código Java (al igual que xmlsec). ¿Algunas ideas?

+0

Lo sentimos, pero ¿está seguro de que el algoritmo de resumen de resumen es SHA1? Puede ser otra cosa y la firma aún puede ser RSA_SHA1 (mientras leo su código). – esej

+0

Eso es lo que le digo a la API de Java que haga. Una cosa que noté es que si guardo el documento xmls en un archivo, leo ese archivo y firmo lo que leo, el resumen se calcula correctamente. Así que estoy pensando que tal vez los espacios en blanco sean de alguna manera considerados en el lado de Java o el de XMLSEC. Eso resolvería mi problema si tuviera que firmar el xml solo una vez; el problema es que necesito hacerlo al menos dos veces ... – Andre

+8

¿Has marcado el '\ n'? –

Respuesta

1

Si no es demasiado tarde para responder:

Creas 2 Transformaciones en el código (envelopedTransform y c14NTransform), pero no los usa.

Crea la referencia con una sola Transform.ENVELOPED nueva. http://www.w3.org/TR/2001/REC-xml-c14n-20010315 (C14N) no se aplica la transformación.

Ahora, no estoy seguro de lo que el estándar de seguridad XML dice que el comportamiento debería ser en este caso. Quizás otras herramientas apliquen automáticamente la transformación C14N también.

Sé con certeza si NO especifica ninguna transformación JDK aplicará al menos la transformación C14N.

Básicamente cambia esa fac.newReference ("", ...) y pasa transformList a ella en lugar de Collections.singletonList().

0

Lo ideal es que el elemento DigestValue contenga el valor de resumen real codificado en base64 en la API de firma XML de Java. ¿Podría verificar que el valor de resumen creado a partir de XMLSec también está codificado en base64?

Cuestiones relacionadas