2010-01-12 3 views
17

Sto provando a digitally sign an XML document utilizzando Java. Ho una implementazione che funziona con alcuni riferimenti che ho trovato che utilizzano varie implementazioni nel pacchetto javax.xml.crypto.dsig.C'è un modo più semplice per firmare un documento XML in Java?

Tuttavia, la mia attuale implementazione è come many del examples Ho guardato - è piuttosto prolisso e coinvolge utilizzando non meno di 23 diverse classi API dalle java.xml.crypto.dsig, javax.xml.transform, e java.security pacchetti, tra gli altri. Mi sembra di aver inserito il terreno factory factory factory e mi ci sono volute diverse ore per capire cosa stava succedendo.

La mia domanda è, c'è un modo più semplice per farlo? Se ho/file di chiave privata pubblici e voglio aggiungere un <Signature/> a un documento XML, c'è una biblioteca là fuori che permette di chiamare me qualcosa di simile:

OutputStream signFile(InputStream xmlFile, File privateKey) 

... senza tutti la follia XMLSignatureFactory/CanonicalizationMethod/DOMSignContext?

Non sono molto esperto di crittografia e l'API fornita da Java sembra piuttosto scoraggiante per sviluppatori come me che cercano di familiarizzare con la firma digitale. Se tutto ciò è necessario o se al momento non ci sono API più amichevoli, va bene e sono disposto ad accettarlo come risposta. Vorrei solo sapere se sto prendendo inutilmente la strada difficile qui.

+0

Come soluzione alternativa, ho potuto effettuare una chiamata di sistema one-liner e utilizzare http://www.aleksey.com/xmlsec/ di firmare la XML. Non sto postando questa risposta come risposta poiché non rientra nei limiti della domanda (ad esempio "in Java"). –

risposta

3

Ho esaminato tutte le opzioni per la firma di file XML e ho deciso di adottare un approccio non standard. Gli standard erano troppo verbosi. Inoltre, non avevo bisogno di compatibilità con gli standard --- avevo solo bisogno di firme su un blocco di XML.

Probabilmente il modo più semplice per "firmare" un blocco di XML è utilizzare GPG con una firma separata.

+0

Bella idea, probabilmente la più semplice fornita finora. Non offre tutta la configurabilità di javax.xml.crypto o di Santuario di Apache, ma questo è ciò che rende questi altri così complessi. –

+0

Grazie. Un sistema che fa questo approccio prende il blocco XML, calcola una firma usando una chiave pubblica RSA e OpenSSL e quindi incorpora quella firma alla fine del blocco XML nel file di testo. Puoi trovare il codice nel mio programma afsign.cpp che fa parte di AFFLIB, che può essere scaricato da http://afflib.org/ – vy32

+0

Accettando questo dato che è la soluzione più semplice. Anche la soluzione di Pascal è ragionevole, sebbene richieda ancora una buona quantità di codice. –

10

Dai un'occhiata a Apache XML Security. Per utilizzare il pacchetto per generare e verificare una firma, eseguire il checkout degli esempi in src_samples/org/apache/xml/security/samples/signature/.

+0

Grazie, Pascal. Ho avuto alcuni problemi nel far funzionare questi esempi, vale a dire "java.io.IOException: formato keystore non valido", sebbene sia stato in grado di creare il mio keystore.pks e farlo funzionare. – Kirby

+0

Link non valido –

8

Edificio da Apache Santuario CreateSignature esempio, la cosa più breve che ho potuto inventare è questa. Senza la main() e lo accompagna output(), è 20 linee

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

Stavo provando la tua proposta, ma non riesco a cambiare l'algo C14N da Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS a Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS. Anche quando lo cambio nel codice, la firma XML di output mostra ancora il metodo CanonicalizationMethod come . Qualche idea su cosa potrebbe andare storto qui? – mithrandir