2010-01-12 13 views
17

Próbuję użyć digitally sign an XML document używając Java. Mam implementację działającą z niektórymi referencjami, które znalazłem, które używają różnych implementacji w pakiecie javax.xml.crypto.dsig.Czy jest łatwiejszy sposób podpisywania dokumentu XML w Javie?

Jednak moja obecna implementacja jest jak many z examples Sprawdziliśmy - to raczej gadatliwy i polega na wykorzystaniu nie mniej niż 23 różnych klas API z java.xml.crypto.dsig, javax.xml.transform i java.security pakietów, między innymi. Czuję się tak, jakbym wszedł na teren factory factory factory i zajęło mi to kilka godzin, aby dowiedzieć się, co się dzieje.

Moje pytanie brzmi, jest łatwiejszy sposób to zrobić? Jeśli mam/prywatnych plików klucz publiczny i chcę dodać <Signature/> do dokumentu XML, jest tam biblioteki, które obecnie nie tylko pozwala mi nazwać coś takiego:

OutputStream signFile(InputStream xmlFile, File privateKey) 

... bez wszystkich szaleństwo XMLSignatureFactory/CanonicalizationMethod/DOMSignContext?

Nie jestem zbyt dobrze zaznajomiony z kryptografią, a interfejs API dostarczony przez Java wydaje się dość zniechęcający dla programistów takich jak ja, próbujących zapoznać się z cyfrowym podpisywaniem. Jeśli wszystko to jest konieczne lub nie ma obecnie przyjaznego interfejsu API, jest to w porządku i jestem gotów zaakceptować to jako odpowiedź. Chciałbym tylko wiedzieć, czy niepotrzebnie biorę tutaj trudną drogę.

+0

jako alternatywne rozwiązanie, udało mi się nawiązać połączenie jednym systemie-liner i używać http://www.aleksey.com/xmlsec/ do podpisania XML. Nie umieszczam tego jako odpowiedzi, ponieważ nie mieści się ona w granicach pytania (tj. "W Javie"). –

Odpowiedz

3

Przejrzałem wszystkie opcje podpisywania plików XML i zdecydowałem się na podejście niestandardowe. Standardy były zbyt szczegółowe. Ponadto nie potrzebowałem zgodności ze standardami --- potrzebowałem tylko podpisów na bloku XML.

Prawdopodobnie najłatwiejszym sposobem "podpisania" bloku XML jest użycie GPG z odłączonym podpisem.

+0

Niezły pomysł, prawdopodobnie najprostszy z dotychczasowych. Nie oferuje on wszystkich możliwości konfiguracji javax.xml.crypto lub Santuario Apache, ale to właśnie sprawia, że ​​te inne są tak skomplikowane. –

+0

Dzięki. Jeden system, który to robi, przyjmuje takie podejście, pobiera blok XML, oblicza podpis przy użyciu klucza publicznego RSA i OpenSSL, a następnie osadza ten podpis na końcu bloku XML w pliku tekstowym. Kod można znaleźć w moim afsign.cpp programie, który jest częścią AFFLIB, który można pobrać z http://afflib.org/ – vy32

+0

Zaakceptowanie tego, ponieważ jest to prostsze rozwiązanie. Rozwiązanie Pascala jest również rozsądne, chociaż nadal wymaga dużej ilości kodu. –

10

Zobacz na Apache XML Security. Aby użyć pakietu do wygenerowania i zweryfikowania podpisu, pobierz próbki w postaci src_samples/org/apache/xml/security/samples/signature/.

+0

Dziękuję, Pascal. Naprawiono pewne problemy z działaniem tych przykładów, a mianowicie "java.io.IOException: Invalid keystore format", chociaż udało mi się stworzyć własne keystore.pks i uruchomić go. – Kirby

+0

Link już nieaktualny –

8

Budowanie z przykładu Apache Santuario CreateSignature, najkrótszą rzeczą, jaką mogłem wymyślić, jest to. Bez main() i jej towarzyszące output(), to 20 linii

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

Próbowałem Twojej propozycji, ale wydaje mi się, że nie mogę zmienić aliasu C14N z Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS na Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS. Nawet gdy zmieniam to w kodzie, wyjściowy podpis XML nadal pokazuje CanonicalizationMethod jako . Jakieś pomysły na to, co może się tu nie udać? – mithrandir

Powiązane problemy