验证XML文件中的签名(java)

时间:2016-02-02 07:42:18

标签: java xml signature

我在XML文件中验证数字签名时遇到问题。 它说核心验证失败了。 我知道有很多人问这个问题,因为我一直在寻找 也回答这个网站。大部分时间。 我几乎尝试了所有找到的方法,但结果总是一样的。 我希望你不要将这个问题标记为重复。 我将包含用于测试的文件(密钥库,xml文件源和签名的xml文件)。

签署xml文档的过程

守则:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.security.KeyStore;
    import java.security.PrivateKey;
    import java.security.cert.CertificateEncodingException;
    import java.security.cert.X509Certificate;

    import javax.xml.parsers.DocumentBuilderFactory;

    import org.bouncycastle.util.encoders.Base64;
    import org.w3c.dom.Document;

    import com.sun.org.apache.xml.internal.security.Init;
    import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
    import com.sun.org.apache.xml.internal.security.signature.XMLSignature;
    import com.sun.org.apache.xml.internal.security.transforms.Transforms;
    import com.sun.org.apache.xml.internal.security.utils.Constants;
    import com.sun.org.apache.xml.internal.security.utils.ElementProxy;

    public class SignDocuments {

        public static void main(String[] args) throws Exception {
            File originFile = new File("");
            File keyStore = new File("");
            String alias = "";
            String pass = "";

            int rc = signDocument(keyStore, originFile, alias, pass);
            System.out.println("RC: " + rc);
        }

        private static int signDocument(File keyStore, File originFile, String alias, String pass) throws Exception {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(originFile);
            Init.init();
            ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, "");
            KeyStore keyStorez = KeyStore.getInstance("JKS");
            keyStorez.load(new FileInputStream(keyStore), pass.toCharArray());

            XMLSignature sig = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1);
            Transforms transforms = new Transforms(doc);
            transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
            sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);

            PrivateKey privateKey = (PrivateKey)keyStorez.getKey(alias, pass.toCharArray());
            X509Certificate cert = (X509Certificate)keyStorez.getCertificate(alias);

            String zzzz = convertToPem(cert);

            FileOutputStream foz = new FileOutputStream(new File("E:\\Innova\\new mayapada\\mayapadaX.cer"));
            foz.write(zzzz.getBytes());
            foz.close();

            sig.addKeyInfo(cert);
            sig.addKeyInfo(cert.getPublicKey());
            sig.sign(privateKey);
            doc.getDocumentElement().getElementsByTagName("SOAP-ENV:Header").item(0).appendChild(sig.getElement());
            FileOutputStream fos = new FileOutputStream("E:\\Innova\\mayapada\\523097000990_BS_I_10030029993_20151019_140850SOAPSIGNED.xml");
            fos.write(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(doc));
            fos.close();
            return 0;
        }

        private static String convertToPem(X509Certificate certificate) {
            String certBegin = "-----BEGIN CERTIFICATE-----\n";
            String certEnd = "\n-----END CERTIFICATE-----";

            int i = 0;
            byte[] der = null;
            String certInPemFormat = null;
            String tempData = null;
            StringBuilder sb = new StringBuilder();

            try {
                der = certificate.getEncoded();
            } catch (CertificateEncodingException e) {
                e.printStackTrace();
                return null;
            }

            tempData = new String(Base64.encode(der));

            certInPemFormat = certBegin;
            while (i <= tempData.length()) {
                if (i > 0 && i % 64 == 0) {
                    sb.append("\n");
                    certInPemFormat += sb.toString();
                    sb = new StringBuilder();
                }

                sb.append(tempData.charAt(i));
                i++;

                if (i == tempData.length()) {
                    certInPemFormat += sb.toString();
                    break;
                }
            }

            certInPemFormat += certEnd;

            return certInPemFormat;
        }
    }

要验证,我已经尝试了两种方法。

首先,我参考“http://www.java-tips.org/java-ee-tips-100042/158-xml-digital-signature-api/1473-using-the-java-xml-digital-signature-api.html

中的方法

守则:

import java.io.File;
import java.io.FileInputStream;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.sun.org.apache.xml.internal.security.Init;
import com.sun.org.apache.xml.internal.security.keys.KeyInfo;
import com.sun.org.apache.xml.internal.security.signature.XMLSignature;
import com.sun.org.apache.xml.internal.security.utils.Constants;
import com.sun.org.apache.xml.internal.security.utils.IgnoreAllErrorHandler;

import digitalcertificate.DigitalSignature;



public class CC {

    public static void main(String[] args) throws Exception {
        Init.init();
        String signatureFileName = "F:\\2016\\zzz.xml";
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE);

        File f = new File(signatureFileName);
        System.out.println("Verifying: " + signatureFileName);
        DocumentBuilder db = dbf.newDocumentBuilder();
        db.setErrorHandler(new IgnoreAllErrorHandler());
        Document doc = db.parse(new FileInputStream(f));
        Element sigElement = null;
        NodeList nl = doc.getElementsByTagNameNS(Constants.SignatureSpecNS, "Signature");
        if (nl.getLength() != 0) {
            System.out.println("Found " + nl.getLength() + " Signature elements");
            sigElement = (Element) nl.item(0);
            XMLSignature signature = new XMLSignature(sigElement, "");
            System.out.println("signature: " + DigitalSignature.encodeToBase64(signature.getSignatureValue()));
            KeyInfo keyInfo = signature.getKeyInfo();
            if (keyInfo != null) {
                if (keyInfo.containsX509Data()) {
                    System.out.println("Could find a X509Data element in the KeyInfo");
                    X509Certificate cert = signature.getKeyInfo().getX509Certificate();
                    System.out.println("Certificate: \n" + DigitalSignature.convertCertToPemFormat(cert));
                    if (cert != null) {
                        System.out.println("The XML signature is " + (signature.checkSignatureValue(cert) ? "valid (good" : "Invalid!!!! (bad)"));
                    } else {
                        System.out.println("Did not find a certificate");
                        PublicKey pk = signature.getKeyInfo().getPublicKey();
                        if (pk != null) {
                            System.out.println("The XML signature is " + (signature.checkSignatureValue(pk) ? "valid (good" : "Invalid!!!! (bad)"));
                        } else {
                            System.out.println("Did not find a public key, so i can't check the signature");
                        }
                    }
                }
            }
        }
    }
}

以下是我使用此方法时的结果:

> The XML signature is Invalid!!!! (bad)Feb 02, 2016 2:18:05 PM com.sun.org.apache.xml.internal.security.signature.XMLSignature checkSignatureValue
WARNING: Signature verification failed.

第二种方法是指这里的帖子(stackoverflow),但我忘了链接。 呵呵.. 这是第二种方法:

package digitalcertificate;

import java.io.File;
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyException;
import java.security.PublicKey;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509CRL;
import java.util.Iterator;
import java.util.List;

import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import sun.security.x509.X509CertImpl;

public class ValidateXMLFile {

    public static void main(String[] args) throws Exception {
        String signedDocument = "F:\\2016\\zzz.xml";

        int rc = validateDoc(signedDocument);

    }

    private static int validateDoc(String signedDocument) throws Exception {
        // Instantiate the document to be validated.
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(new File(signedDocument)));

        // Find signature element.
        NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
        if (nl.getLength() == 0) {
            throw new Exception("Cannot find signature element");
        }

        // Create a DOM XMLSignatureFactory that will be used to unmarshal
        // document containing the XMLSignature.
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");

        // Create a DOMValidateContext and specify a KeyValue KeySelector
        // and document context.
        DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0));

        // Unmarshal the signature.
        XMLSignature signature = fac.unmarshalXMLSignature(valContext);
        byte[] data = signature.getSignatureValue().getValue();
        System.out.println("Signature: " + DigitalSignature.convertCertificateToPemFormat(data));

        // Validate the XMLSignature.
        boolean coreValidity = signature.validate(valContext);

        // Check the core validation status.
        if (!coreValidity) {
            System.err.println("Signature failed core validation");
            boolean sv = signature.getSignatureValue().validate(valContext);
            System.out.println("Signature validation status: " + sv);

            // Check the validation status of each reference.
            Iterator<?> i = signature.getSignedInfo().getReferences().iterator();
            for (int j = 0; i.hasNext(); j++) {
                boolean refValid = ((Reference)i.next()).validate(valContext);
                System.out.println("Ref[" + j +"] validity status: " + refValid);
            }
        } else {
            System.out.println("Signature passed core validation");
        }

        return 0;
    }

    /**
     * @author Akbar
     * KeySelector which retrieves the public key out of the KeyValue
     * element and returns it.
     * NOTE: If the key algorithm doesn't match signature algorithm,
     * then the public key will be ignored.
     */
    private static class KeyValueKeySelector extends KeySelector {

        @Override
        public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose,
                AlgorithmMethod method, XMLCryptoContext context)
                throws KeySelectorException {

            if (keyInfo == null) {
                throw new KeySelectorException("Null Key Info Object");
            }

            SignatureMethod sm = (SignatureMethod)method;
            System.out.println("Signature method: " + sm.getAlgorithm());
            X509CertImpl certImpl = null;
            List<?> list = keyInfo.getContent();

            for (int i = 0; i < list.size(); i++) {
                XMLStructure xmlStructure = (XMLStructure)list.get(i);
                if (xmlStructure instanceof X509Data) {
                    X509Data xd = (X509Data)xmlStructure;
                    Object[] entries = xd.getContent().toArray();
                    X509CRL crl = null;
                    for (int j = 0; j < entries.length; j++) {
                        if (entries[i] instanceof X509CRL) {
                            crl = (X509CRL)entries[i];
                        }
                        if (entries[i] instanceof X509CertImpl) {
                            certImpl = (X509CertImpl)entries[i];
                            try {
                                certImpl.checkValidity();
                            } catch (CertificateExpiredException e) {
                                e.printStackTrace();
                            } catch (CertificateNotYetValidException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }

                if (xmlStructure instanceof KeyValue) {
                    PublicKey pk = null;

                    try {
                        pk = ((KeyValue)xmlStructure).getPublicKey();

                    } catch (KeyException e) {
                        throw new KeySelectorException(e); 
                    }

                    // Make sure algorithm is compatible with method.
                    if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
                        return new SimpleKeySelectorResult(pk);
                    }
                }
            }
            throw new KeySelectorException("No KeyValue element found!");
        }

        private boolean algEquals(String algURI, String algName) {
            if (algName.equalsIgnoreCase("DSA") && 
                algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
                return true;
            } else if (algName.equalsIgnoreCase("RSA") && 
                       algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
                return true;
            } else {
                return false;
            }
        }
    }

    private static class SimpleKeySelectorResult implements KeySelectorResult {
        private PublicKey pk;

        SimpleKeySelectorResult(PublicKey pk) {
            this.pk = pk;
        }

        @Override
        public Key getKey() {
            return pk;
        }
    }
}

结果是:

Signature validation status: false
Signature failed core validation
Ref[0] validity status: true

https://drive.google.com/open?id=0B87pqq4ZNBHkNWwzN1dBRHhfWlE 这是用于测试的密钥库和xml文件的链接。 密钥库凭据: 别名= Akbar pass = dragon270389

我最诚挚的问候, 阿克

0 个答案:

没有答案