programa - ¿Hay una forma más fácil de firmar un documento XML en Java?
validar firma digital xml online (3)
Estoy tratando de firmar digitalmente un documento XML utilizando Java. Tengo una implementación que trabaja con algunas referencias que he encontrado que usan varias implementaciones en el paquete javax.xml.crypto.dsig
.
Sin embargo, mi implementación actual es como many de los examples que he visto: es bastante detallada e implica el uso de no menos de 23 clases API diferentes de java.xml.crypto.dsig
, javax.xml.transform
y java.security
Paquetes, entre otros. Se siente como si hubiera ingresado a Factory Factory Factory Factory, y me tomó varias horas solo para averiguar qué estaba pasando.
Mi pregunta es, ¿hay una manera más fácil de hacer esto? Si tengo archivos de clave pública / privada y quiero agregar una <Signature/>
a un documento XML, ¿hay alguna biblioteca por ahí que me permita llamar a algo como:
OutputStream signFile(InputStream xmlFile, File privateKey)
... sin todas las locuras de XMLSignatureFactory / CanonicalizationMethod / DOMSignContext?
No estoy muy versado en criptografía, y la API proporcionada por Java parece bastante desalentadora para los desarrolladores como yo que intentan familiarizarse con la firma digital. Si todo esto es necesario o si actualmente no hay una API más amigable, está bien y estoy dispuesto a aceptarlo como respuesta. Solo me gustaría saber si estoy tomando el camino difícil innecesariamente aquí.
Construyendo a partir del ejemplo de Apache Santuario CreateSignature
, la cosa más corta que se me ocurre es esto. Sin el main()
y la output()
acompaña, son 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);
}
}
}
Echa un vistazo a la seguridad XML de Apache . Para usar el paquete para generar y verificar una firma, src_samples/org/apache/xml/security/samples/signature/
las muestras en src_samples/org/apache/xml/security/samples/signature/
.
Miré todas las opciones para firmar archivos XML y decidí usar un enfoque no estándar. Los estándares eran demasiado verbosos. 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.