javaxml-signaturexades4j

xades4j verification issue with embeded x509 certificate


I am using smart card for signing and SunMSCAPI provider and windows key store. Using xades4j 1.3.2 version I am singing an xml file as follows and it is working perfectly and the certificate is also embeded in xml file in KeyInfo,

private Document signXMLData(Document doc) {
        try {
            XadesSigningProfile p;
            p = new XadesBesSigningProfile(keyingDataProvider).withSignaturePropertiesProvider(new SignaturePropertiesProvider() {
                @Override
                public void provideProperties(SignaturePropertiesCollector signedPropsCol) {
                    signedPropsCol.setSignerRole(new SignerRoleProperty(SignerRole));
                    signedPropsCol.setSignatureProductionPlace(new SignatureProductionPlaceProperty(City, State, PostalCode, Country));
                    signedPropsCol.setSigningTime(new SigningTimeProperty());
                }
            });
            XadesSigner signer = p.newSigner();
            Element elemToSign = doc.getDocumentElement();
            new Enveloped(signer).sign(elemToSign);
            Log.LogOperation("XML signing completed successfully.");
        } catch (Exception ex) {
            Log.LogException(ex);
            doc = null;
        }
        return doc;
    }

But my problem is, while verifying using following code, I am getting following error,

Code

public boolean VerifyXMLSign(String data) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException, Exception {
        InputSource isIn = new InputSource();
        isIn.setCharacterStream(new StringReader(data));

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().parse(isIn);
        //Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("c:/xml.xml"));
        DOMHelper.useIdAsXmlId(doc.getDocumentElement());

        KeyStore ks = KeyStore.getInstance("Windows-ROOT", "SunMSCAPI");
        ks.load(null, null);

        X509Certificate x509Certificate = null;
        PublicKey key = null;
        DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(ks), getSigElement(doc));
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
        fac.newDigestMethod(DigestMethod.SHA256, null);
        XMLSignature signature = fac.unmarshalXMLSignature(valContext);
        KeyInfo keyInfo = signature.getKeyInfo();
        Iterator hKeyInfo = keyInfo.getContent().iterator();
        while (hKeyInfo.hasNext()) {
            XMLStructure hX509Data = (XMLStructure) hKeyInfo.next();
            if (!(hX509Data instanceof X509Data)) {
                continue;
            }
            X509Data x509Data = (X509Data) hX509Data;
            Iterator hX509Certificate = x509Data.getContent().iterator();
            while (hX509Certificate.hasNext()) {
                Object oX509Certificate = hX509Certificate.next();
                if (!(oX509Certificate instanceof X509Certificate)) {
                    continue;
                }
                x509Certificate = ((X509Certificate) oX509Certificate);
                key = x509Certificate.getPublicKey();
            }
        }

        //FileSystemDirectoryCertStore certStore = createDirectoryCertStore("my");
        CertificateFactory cf = CertificateFactory.getInstance("X509");
        ks.setCertificateEntry("df", x509Certificate);

        CertificateValidationProvider validationProviderMySigs = new PKIXCertificateValidationProvider(ks, false, null);//certStore.getStore());
        XadesVerificationProfile instance = new XadesVerificationProfile(validationProviderMySigs);
        XadesVerifier verifier = instance.newVerifier();
        XAdESVerificationResult r = verifier.verify(getSigElement(doc), null);

        System.out.println(r.getSignatureForm());
        System.out.println(r.getSignatureAlgorithmUri());
        System.out.println(r.getSignedDataObjects().size());
        System.out.println(r.getQualifyingProperties().all().size());
        return false;
    }

Error

Log Date Time:- 2017/06/08 14:38:01
xades4j.utils.XadesProfileResolutionException: com.google.inject.internal.ComputationException: java.lang.SecurityException: class "org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter"'s signer information does not match signer information of other classes in the same package
    at xades4j.utils.XadesProfileCore.getInstance(XadesProfileCore.java:223)
    at xades4j.verification.XadesVerificationProfile.newVerifier(XadesVerificationProfile.java:147)
    at slr.DigitalVerification.VerifyXMLSign(DigitalVerification.java:163)
    at slr.Handlers$OpenEVerifierHandler.handle(Handlers.java:186)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
    at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
    at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
    at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647)
    at sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:158)
    at sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:433)
    at sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:398)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.google.inject.internal.ComputationException: ..........

I tried the method suggested in https://github.com/luisgoncalves/xades4j/issues/37 but that also giving the same error.

Please suggest what am I doing wrong.


Solution

  • From the error it seems something related to mixing up security providers, probably because you create the KeyStore from "SunMSCAPI" and then the X509Certificate is created by the provider used on the XML signature, which seems to be bouncy castle.

    Anyway, since the signing certificate is included on the signature you don't need to be parsing the XML and looking for it. The purpose of xades4j is to abstract you away from that stuff. xades4j will collect all the certificates in KeyInfo and use them to build a chain when validating the signing certificate (in this case there's likely only one).

    If all the intermediate certificates and root certificates needed to validate your singing certificate are either on Windows-ROOT or within KeyInfo, you just need to create the trusted roots KeyStore and pass it to PKIXCertificateValidationProvider as-is. If you need to include additional certificates, you can use the CertStore parameter of PKIXCertificateValidationProvider constructor.

    So, to sum up, all the code you should need is the first and last parts. Something like:

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    Document doc = dbf.newDocumentBuilder().parse(isIn);
    DOMHelper.useIdAsXmlId(doc.getDocumentElement());
    
    KeyStore ks = KeyStore.getInstance("Windows-ROOT", "SunMSCAPI");
    ks.load(null, null);
    CertificateValidationProvider validationProviderMySigs = new PKIXCertificateValidationProvider(ks, false, null);
    XadesVerificationProfile instance = new XadesVerificationProfile(validationProviderMySigs);
    XadesVerifier verifier = instance.newVerifier();
    XAdESVerificationResult r = verifier.verify(getSigElement(doc), null);