2010-01-12 19 views
17

Estoy tratando de digitally sign an XML document usando Java. Tengo una implementación que funciona con algunas referencias que he encontrado que usan varias implementaciones en el paquete javax.xml.crypto.dsig.¿Existe alguna manera más fácil de firmar un documento XML en Java?

Sin embargo, mi implementación actual es como many del examples He mirado - que es bastante detallado e implica el uso de no menos de 23 clases diferentes de las API java.xml.crypto.dsig, javax.xml.transform y java.security paquetes, entre otros. Se siente como si hubiera ingresado al terreno factory factory factory, y me tomó varias horas solo para descubrir qué estaba pasando.

Mi pregunta es, ¿hay una manera más fácil de hacer esto? Si Tengo archivos de claves públicas/privadas y quiero añadir un <Signature/> a un documento XML, hay una biblioteca por ahí que simplemente me permite llamar algo así como:

OutputStream signFile(InputStream xmlFile, File privateKey) 

... sin todos la locura de XMLSignatureFactory/CanonicalizationMethod/DOMSignContext?

No soy muy versado en criptografía, y la API proporcionada por Java parece bastante desalentadora para los desarrolladores como yo tratando de familiarizarnos con la firma digital. Si todo esto es necesario o no existe actualmente una API más amigable, está bien y estoy dispuesto a aceptar eso como una respuesta. Solo me gustaría saber si estoy tomando innecesariamente el camino difícil aquí.

+0

Como una solución alternativa, que era capaz de hacer una llamada de sistema de una sola línea y utilizar http://www.aleksey.com/xmlsec/ para firmar el XML. No estoy publicando esto como una respuesta, ya que no está dentro de los límites de la pregunta (es decir, "en Java"). –

Respuesta

3

Miré todas las opciones para firmar archivos XML y decidí ir con un enfoque no estándar. Los estándares eran demasiado detallados. Además, no necesitaba compatibilidad con los estándares --- solo necesitaba firmas en un bloque de XML.

Probablemente la forma más fácil de "firmar" un bloque de XML es usar GPG con una firma separada.

+0

Buena idea, probablemente la más simple proporcionada hasta el momento. No ofrece toda la configurabilidad de javax.xml.crypto o Santuario de Apache, pero eso es realmente lo que hace que esos otros sean tan complejos. –

+0

Gracias. Un sistema que hace esto toma este enfoque toma el bloque XML, calcula una firma usando una clave pública RSA y OpenSSL, y luego inserta esa firma al final del bloque XML en el archivo de texto. Puede encontrar el código en mi programa afsign.cpp que es parte de AFFLIB, que se puede descargar desde http://afflib.org/ – vy32

+0

Aceptando este ya que es la solución más simple. La solución de Pascal también es razonable, aunque aún requiere una buena cantidad de código. –

10

Miren Apache XML Security. Para usar el paquete para generar y verificar una firma, revise las muestras en src_samples/org/apache/xml/security/samples/signature/.

+0

Gracias, Pascal. Tuve algunos problemas para que esos ejemplos funcionaran, a saber, "java.io.IOException: formato de almacén de claves no válido", aunque pude crear mi propio keystore.pks y ponerlo en funcionamiento. – Kirby

+0

El enlace ya no es válido –

8

Construyendo desde el Apache Santuario CreateSignature ejemplo, lo más corto que se me ocurrió es esto. Sin la main() y su acompañamiento output(), es 20 líneas

import java.io.*; 
import java.security.Key; 
import java.security.KeyStore; 
import java.security.cert.X509Certificate; 
import javax.xml.parsers.DocumentBuilderFactory; 

import org.apache.commons.io.IOUtils; 
import org.apache.xml.security.Init; 
import org.apache.xml.security.c14n.Canonicalizer; 
import org.apache.xml.security.signature.XMLSignature; 
import org.apache.xml.security.transforms.Transforms; 
import org.apache.xml.security.utils.Constants; 
import org.apache.xml.security.utils.ElementProxy; 
import org.w3c.dom.Document; 

public class CreateSignature { 

    private static final String PRIVATE_KEY_ALIAS = "test-alias"; 
    private static final String PRIVATE_KEY_PASS = "test"; 
    private static final String KEY_STORE_PASS = "test"; 
    private static final String KEY_STORE_TYPE = "JKS"; 

    public static void main(String... unused) throws Exception { 
     final InputStream fileInputStream = new FileInputStream("test.xml"); 
     try { 
      output(signFile(fileInputStream, new File("keystore.jks")), "signed-test.xml"); 
     } 
     finally { 
      IOUtils.closeQuietly(fileInputStream); 
     } 
    } 

    public static ByteArrayOutputStream signFile(InputStream xmlFile, File privateKeyFile) throws Exception { 
     final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile); 
     Init.init(); 
     ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, ""); 
     final KeyStore keyStore = loadKeyStore(privateKeyFile); 
     final XMLSignature sig = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA); 
     final Transforms transforms = new Transforms(doc); 
     transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE); 
     sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1); 
     final Key privateKey = keyStore.getKey(PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASS.toCharArray()); 
     final X509Certificate cert = (X509Certificate)keyStore.getCertificate(PRIVATE_KEY_ALIAS); 
     sig.addKeyInfo(cert); 
     sig.addKeyInfo(cert.getPublicKey()); 
     sig.sign(privateKey); 
     doc.getDocumentElement().appendChild(sig.getElement()); 
     final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
     outputStream.write(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(doc)); 
     return outputStream; 
    } 

    private static KeyStore loadKeyStore(File privateKeyFile) throws Exception { 
     final InputStream fileInputStream = new FileInputStream(privateKeyFile); 
     try { 
      final KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE); 
      keyStore.load(fileInputStream, KEY_STORE_PASS.toCharArray()); 
      return keyStore; 
     } 
     finally { 
      IOUtils.closeQuietly(fileInputStream); 
     } 
    } 

    private static void output(ByteArrayOutputStream signedOutputStream, String fileName) throws IOException { 
     final OutputStream fileOutputStream = new FileOutputStream(fileName); 
     try { 
      fileOutputStream.write(signedOutputStream.toByteArray()); 
      fileOutputStream.flush(); 
     } 
     finally { 
      IOUtils.closeQuietly(fileOutputStream); 
     } 
    } 
} 
+0

Estaba probando su propuesta, sin embargo, parece que no puedo cambiar el algoritmo C14N de Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS a Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS. Incluso cuando cambio esto en el código, la firma XML de salida todavía muestra el método Canonicalization como . ¿Alguna idea sobre qué podría estar yendo mal aquí? – mithrandir

Cuestiones relacionadas