Pregunta

He escrito el siguiente método .NET Framework 3.5 C# que toma la ubicación de un documento XML y una representación de objeto de un certificado digital X509 (con una clave privada) y devuelve el documento XML como objeto con la firma XML (XMLDSIG) integrada como primer elemento secundario de la raíz.

La cuestión es que necesito poder hacer exactamente el mismo procedimiento con Java SE 6, pero no he escrito Java en años y no tengo idea de dónde comenzar.

¿Alguien puede proporcionar el método equivalente en el código Java que produzca el exacto ¿La misma salida XML?

private static XmlDocument SignXmlDocument(string xmlFilePath, X509Certificate2 certificate)
{
    // load xml from disk preserving whitespaces
    XmlDocument xmlDocument = new XmlDocument { PreserveWhitespace = true };
    xmlDocument.Load(xmlFilePath);

    // create signed xml with a same-document reference containing an enveloped-signature transform
    SignedXml signedXml = new SignedXml(xmlDocument) { SigningKey = certificate.PrivateKey };
    Reference reference = new Reference { Uri = "" };
    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();            
    reference.AddTransform(env);
    signedXml.AddReference(reference);

    // embed public key information for signature validation purposes
    KeyInfo keyInfo = new KeyInfo();
    KeyInfoX509Data keyInfoX509Data = new KeyInfoX509Data(certificate, X509IncludeOption.ExcludeRoot);
    keyInfo.AddClause(keyInfoX509Data);
    signedXml.KeyInfo = keyInfo;

    // compute and retreive the signature xml
    signedXml.ComputeSignature();          
    XmlElement xmldsigXmlElement = signedXml.GetXml();

    // insert the signature xml into the xml document as first child of the root element
    xmlDocument.DocumentElement.PrependChild(xmlDocument.ImportNode(xmldsigXmlElement, true));

    return xmlDocument;
}
¿Fue útil?

Solución

Lo siguiente hace lo mismo en Java. Requiere un archivo de certificado PKCS12 en el disco.

import java.util.*;
import java.io.*;
import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.cert.X509Certificate;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.crypto.dsig.spec.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

public class XMLSigner {

    public static void signXmlDocumentOnDisk(String fileToBeSignedPath, String signedFileSavePath, String pkcs12CertificateFilePath, String password) throws Exception {
        XMLSignatureFactory fac = getXMLSignatureFactory();
        Reference ref = getSHA1WholeDocumentEnvelopedTransformReference(fac);
        SignedInfo si = getSignedInfo(fac, ref);
        PrivateKeyEntry keyEntry = loadPKCS12KeyStoreAndGetSigningKeyEntry(pkcs12CertificateFilePath, password);
        KeyInfo ki = getKeyInfoWithX509Data(keyEntry, fac);
        Document doc = instantiateDocumentToBeSigned(fileToBeSignedPath);
        signDocumentAndPlaceSignatureAsFirstChildElement(doc, keyEntry, fac, si, ki);
        writeResultingDocument(doc, signedFileSavePath);
    }

    private static XMLSignatureFactory getXMLSignatureFactory() {
        return XMLSignatureFactory.getInstance("DOM");
    }

    private static Reference getSHA1WholeDocumentEnvelopedTransformReference(XMLSignatureFactory fac) throws Exception {
        return 
            fac.newReference(
                "", 
                fac.newDigestMethod(DigestMethod.SHA1, null),
                Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
                null, 
                null
            );
    }

    private static SignedInfo getSignedInfo(XMLSignatureFactory fac, Reference ref) throws Exception {
        return 
            fac.newSignedInfo(
                fac.newCanonicalizationMethod(
                    CanonicalizationMethod.INCLUSIVE, 
                    (C14NMethodParameterSpec) null
                ),
                fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
                Collections.singletonList(ref)
            );
    }

    private static PrivateKeyEntry loadPKCS12KeyStoreAndGetSigningKeyEntry(String pkcs12CertificateFilePath, String password) throws Exception {
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(new FileInputStream(pkcs12CertificateFilePath), password.toCharArray());    
        return (PrivateKeyEntry)ks.getEntry(ks.aliases().nextElement(), new KeyStore.PasswordProtection(password.toCharArray()));           
    }

    private static KeyInfo getKeyInfoWithX509Data(PrivateKeyEntry keyEntry, XMLSignatureFactory fac) {
        X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
        KeyInfoFactory kif = fac.getKeyInfoFactory();
        List x509Content = new ArrayList();
        x509Content.add(cert.getSubjectX500Principal().getName());
        x509Content.add(cert);
        X509Data xd = kif.newX509Data(x509Content);
        return kif.newKeyInfo(Collections.singletonList(xd));
    }

    private static Document instantiateDocumentToBeSigned(String fileToBeSignedPath) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        return dbf.newDocumentBuilder().parse(new FileInputStream(fileToBeSignedPath));
    }

    private static void signDocumentAndPlaceSignatureAsFirstChildElement(Document doc, PrivateKeyEntry keyEntry, XMLSignatureFactory fac, SignedInfo si, KeyInfo ki) throws Exception {
        DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement(), doc.getDocumentElement().getFirstChild());
        XMLSignature signature = fac.newXMLSignature(si, ki);
        signature.sign(dsc);
    }

    private static void writeResultingDocument(Document doc, String signedFileSavePath) throws Exception {
        OutputStream os = new FileOutputStream(signedFileSavePath);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer();
        trans.transform(new DOMSource(doc), new StreamResult(os));
    }
}

Otros consejos

Echa un vistazo a la API de firma digital Java XML: https://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top