2

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.

Matt
  • 1,953
  • 1
  • 19
  • 42

1 Answers1

3

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);
lgoncalves
  • 2,040
  • 1
  • 14
  • 12
  • Thank you for the valuable comment. I tried all above possibilities. the X509Certificate is created by BC. I tried keystore values BKS, JKS and imported certificates using the code in my question. But in all cases same error is coming on line XadesVerifier verifier = instance.newVerifier();. I tried above code also, but that also throwing same error. Is there any other workaround? – Matt Jun 08 '17 at 11:15
  • What if you don't supply a provider on the `KeyStore.getInstance()` call? – lgoncalves Jun 08 '17 at 12:54
  • If you mean with KeyStore.DefaultProvider() it is giving same error. getinstance is expecting some value. – Matt Jun 08 '17 at 17:21
  • I mean the [overload without the provider parameter](https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.html#getInstance(java.lang.String)), which should return the first provider that supports the supplied type. If this doesn't work, I'm out of ideas... – lgoncalves Jun 08 '17 at 17:25
  • Oh sorry, this way also same result, KeyStore ks = KeyStore.getInstance("Windows-ROOT"); – Matt Jun 08 '17 at 17:30
  • Can you create a GIST with the full validation code you're using now? – lgoncalves Jun 08 '17 at 17:41
  • This probably has nothing to do with xades4j. Check out [this SO question](https://stackoverflow.com/questions/2877262/java-securityexception-signer-information-does-not-match). Are you including Bouncycastle on your application directly? because xades4j may be pulling a different version or something like that. – lgoncalves Jun 08 '17 at 17:45