luisgoncalves / xades4j

A Java library for XAdES signature services
GNU Lesser General Public License v3.0
109 stars 65 forks source link

problem to validate a XML Xades BES #224

Closed VitorMascarenhas closed 3 years ago

VitorMascarenhas commented 3 years ago

Good Morning

i want to validate a signed xml file in bes xades and i am not getting it, give me the following exception, my code:

public class TestXades {

public static void main(String[] args) throws Exception {

        // Instantiate the document to be validated
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        Document doc = dbf.newDocumentBuilder().parse(new FileInputStream("C:\\test\\ubl21-335507378.xml"));

        // Find Signature element
        Element sigElement = getSigElement(doc);
        if (null == sigElement) {
            throw new Exception("Cannot find Signature element");
        }

        SignatureSpecificVerificationOptions ssvo = new SignatureSpecificVerificationOptions();
        ssvo.useDataForAnonymousReference(new FileInputStream("C:\\test\\ubl21-335507378.xml"));

        Key validationKey = null;
        X509Certificate cert = null;
        MyKeySelectorResult ksResult;
        try {
            ksResult = X509KeySelectorXades.getX509FromXadesFile(doc, "RSA");
            validationKey = ksResult.getKey();
            cert = ksResult.getCertificate();
            if (validationKey == null) {
                throw new XMLSignatureException("the keyselector did " + "not find a validation key");
            }
        } catch (KeySelectorException kse) {
            throw new XMLSignatureException("cannot find validation " + "key", kse);
        }

        CertificateValidationProvider certValidator = new CertificateValidationProviderWithX509(cert, false, null);

        XadesVerificationProfile p = new XadesVerificationProfile(certValidator);
        XadesVerifier verifier = p.newVerifier();

        // XAdESVerificationResult r = verifier.verify(sigElement, ssvo);
        XAdESVerificationResult r = null;
        boolean isOK = false;
        try {
            r = verifier.verify(sigElement, ssvo);
            // r = verifier.verify(sigElement, null);
            isOK = true;
        } catch (xades4j.verification.ReferenceValueException e1) {
            e1.printStackTrace();
        }

        XAdESForm xe =  r.getSignatureForm();

        if (!isOK) {
            System.out.println("XADES KO -------------------->");
        } else {
            System.out.println("XADES OK-------------------->");
            System.out.println(r.getCanonicalizationAlgorithmUri());
            System.out.println(r.getXmlSignature());
            System.out.println(r.getSignatureForm());
            System.out.println(r.getSignatureAlgorithmUri());
            System.out.println(r.getSignedDataObjects().size());
            System.out.println(r.getQualifyingProperties().all().size());
        }

    }

    public static Element getSigElement(Document doc) throws Exception {
        return (Element) doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0);
    }

}

public class MyKeySelectorResult implements KeySelectorResult{ private Key key; private X509Certificate certificate;

public MyKeySelectorResult(Key key, X509Certificate certificate) {
    super();
    this.key = key;
    this.certificate = certificate;
}

public MyKeySelectorResult() {
    super();
}

public void setKey(Key key) {
    this.key = key;
}

public Key getKey() {
    return key;
}

public void setCertificate(X509Certificate certificate) {
    this.certificate = certificate;
}

public X509Certificate getCertificate() {
    return certificate;
}

}

public class CertificateValidationProviderWithX509 implements CertificateValidationProvider {

/* langlade*/
   private final X509Certificate certX509;
   /* langlade*/
    private final boolean revocationEnabled;
    private final int maxPathLength;
    private final CertStore[] intermCertsAndCrls;
    private final CertPathBuilder certPathBuilder;

    /**
     * @param certX509 the X509Certificate 
     * @param revocationEnabled whether revocation is enabled
     * @param intermCertsAndCrls a set of {@code CertStore}s that contain certificates to be
     *      used in the construction of the certification path. May contain CRLs to be used
     *      if revocation is enabled
     * @see xades4j.utils.FileSystemDirectoryCertStore
     * @throws NoSuchAlgorithmException if there is no provider for PKIX CertPathBuilder
     */
    public CertificateValidationProviderWithX509(/* langlade*/
            X509Certificate certX509,/* langlade*/
            boolean revocationEnabled,
            int maxPathLength,
            CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException
    {
        if (null == certX509)
            throw new NullPointerException();
        /* langlade*/
        this.certX509 = certX509;
        /* langlade*/
        this.revocationEnabled = revocationEnabled;
        this.maxPathLength = maxPathLength;
        this.intermCertsAndCrls = intermCertsAndCrls;
        this.certPathBuilder = CertPathBuilder.getInstance("PKIX");
    }

    public CertificateValidationProviderWithX509(/* langlade*/
            X509Certificate certX509,/* langlade*/
            boolean revocationEnabled,
            CertStore... intermCertsAndCrls) throws NoSuchAlgorithmException
    {
        this(certX509, revocationEnabled, 6, intermCertsAndCrls);
    }

    @Override
    public ValidationData validate(
            X509CertSelector certSelector,
            Date validationDate,
            Collection<X509Certificate> otherCerts) throws CertificateValidationException, UnexpectedJCAException
    {
        PKIXBuilderParameters builderParams = null;
        try
        {
            /* langlade*/
            Set<TrustAnchor> hashSet = new HashSet<TrustAnchor>(); 
             hashSet.add(new TrustAnchor(this.certX509, null));
            builderParams = new PKIXBuilderParameters(hashSet, certSelector);
            /* langlade*/
        } catch (InvalidAlgorithmParameterException ex)
        {
            ex.printStackTrace();
        }

        PKIXCertPathBuilderResult builderRes = null;
        try
        {
            // Certificates to be used to build the certification path.
            // - The other certificates from the signature (e.g. from KeyInfo).
            if (otherCerts != null)
            {
                CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(otherCerts);
                CertStore othersCertStore = CertStore.getInstance("Collection", ccsp);
                builderParams.addCertStore(othersCertStore);
            }
            // - The external certificates/CRLs.
            /* langlade*/
            if(null != this.intermCertsAndCrls){
                for (int i = 0; i < this.intermCertsAndCrls.length; i++)
                {
                    builderParams.addCertStore(this.intermCertsAndCrls[i]);
                }
            }
            /* langlade*/
            builderParams.setRevocationEnabled(revocationEnabled);
            builderParams.setMaxPathLength(maxPathLength);
            builderParams.setDate(validationDate);

            builderRes = (PKIXCertPathBuilderResult)certPathBuilder.build(builderParams);
        } catch (CertPathBuilderException ex)
        {
            ex.printStackTrace();
        } catch (InvalidAlgorithmParameterException ex)
        {
            // SHOULD NOT be thrown due to wrong type of parameters.
            // Seems to be thrown when the CertSelector (in builderParams) criteria
            // cannot be applied.
            ex.printStackTrace();
        } catch (NoSuchAlgorithmException ex)
        {
            // SHOULD NOT be thrown.
            throw new UnexpectedJCAException("No provider for Collection CertStore", ex);
        }

        // The cert path returned by the builder ends in a certificate issued by
        // the trust anchor. However, the complete path may be needed for property
        // verification.
        List<X509Certificate> certPath = (List<X509Certificate>)builderRes.getCertPath().getCertificates();
        // - Create a new list since the previous is immutable.
        certPath = new ArrayList<X509Certificate>(certPath);
        // - Add the trust anchor certificate.
        certPath.add(builderRes.getTrustAnchor().getTrustedCert());

        if (revocationEnabled)
            return new ValidationData(certPath, getCRLsForCertPath(certPath, validationDate));
        return new ValidationData(certPath);
    }

    private Collection<X509CRL> getCRLsForCertPath(
            List<X509Certificate> certPath,
            Date validationDate) throws CertificateValidationException
    {
        // Map the issuers certificates in the chain. This is used to know the issuers
        // and later to verify the signatures in the CRLs.
        Map<X500Principal, X509Certificate> issuersCerts = new HashMap<X500Principal, X509Certificate>(certPath.size() - 1);
        for (int i = 0; i < certPath.size() - 1; i++)
        {
            // The issuer of one certificate is the subject of the following one.
            issuersCerts.put(certPath.get(i).getIssuerX500Principal(), certPath.get(i + 1));
        }

        // Select all the CRLs from the issuers involved in the certification path
        // that are valid at the moment.
        X509CRLSelector crlSelector = new X509CRLSelector();
        for (X500Principal issuer : issuersCerts.keySet())
        {
            // - "The issuer distinguished name in the X509CRL must match at least
            //   one of the specified distinguished names."
            crlSelector.addIssuer(issuer);
        }
        // - "The specified date must be equal to or later than the value of the
        //   thisUpdate component of the X509CRL and earlier than the value of the
        //   nextUpdate component."
        crlSelector.setDateAndTime(validationDate);

        Set<X509CRL> crls = new HashSet<X509CRL>();
        try
        {
            // Get the CRLs on each CertStore.
            /* langlade*/
            if(null != this.intermCertsAndCrls){
                for (int i = 0; i < this.intermCertsAndCrls.length; i++)
                {
                    Collection storeCRLs = this.intermCertsAndCrls[i].getCRLs(crlSelector);
                    crls.addAll(Collections.checkedCollection(storeCRLs, X509CRL.class));

                }
            }
            /* langlade*/
        } catch (CertStoreException ex)
        {
            throw new CertificateValidationException(null, "Cannot get CRLs: " + ex.getMessage());
        }

        // Verify the CRLs' signatures. The issuers' certificates were validated
        // as part of the cert path creation.
        for (X509CRL crl : crls)
        {
            try
            {
                X509Certificate crlIssuerCert = issuersCerts.get(crl.getIssuerX500Principal());
                crl.verify(crlIssuerCert.getPublicKey());
            } catch (Exception ex)
            {
                throw new CertificateValidationException(null, "Invalid CRL signature: " + crl.getIssuerX500Principal().getName());
            }
        }
        return crls;
    }

}

public class X509KeySelector extends KeySelector {

static boolean algEquals(String algURI, String algName) {

    if ((algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1))
            || (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase("RSA"))) {
        return true;
    } else {
        return false;
    }
}

public KeySelectorResult select(KeyInfo keyInfo,
                                KeySelector.Purpose purpose,
                                AlgorithmMethod method,
                                XMLCryptoContext context) throws KeySelectorException {
    if (keyInfo == null) {
        throw new KeySelectorException("ERROR: KeyInfo object null!");
    }
    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;
            final X509Certificate x509Certificate = ((X509Certificate) oX509Certificate);
            final PublicKey key = x509Certificate.getPublicKey();
            // System.out.println(x509Certificate.getSubjectDN());
            if (algEquals(method.getAlgorithm(), key.getAlgorithm())) {
                return new MyKeySelectorResult() {
                    public Key getKey() {
                        return key;
                    }
                    public X509Certificate getCertificate() {
                        return x509Certificate;
                    }
                };
            }
        }
    }
    throw new KeySelectorException("ERROR: No X509Certificate found!");
}

}

public class X509KeySelectorXades {

public static MyKeySelectorResult getX509FromXadesFile(Document doc, String algorithmeMethod) throws Exception {
    // Find Signature element
    NodeList nl = doc.getElementsByTagName("ds:Signature");
    if (nl.getLength() == 0) {
        throw new Exception("Cannot find Signature element");
    }

    /*
     * ---------------
     */
    DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(), nl.item(0));
    XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
    fac.newDigestMethod(DigestMethod.SHA256, null);
    XMLSignature signature = fac.unmarshalXMLSignature(valContext);
    KeyInfo keyInfo = signature.getKeyInfo();

    MyKeySelectorResult ksr = select(keyInfo, algorithmeMethod);

    return ksr;
}

public static MyKeySelectorResult getX509FromXadesFile(InputStream fileInput, String algorithmeMethod) throws Exception {

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    Document doc = dbf.newDocumentBuilder().parse(fileInput);

    return getX509FromXadesFile(doc, algorithmeMethod);
}

public static MyKeySelectorResult select(KeyInfo keyInfo, String method) throws KeySelectorException {
    if (keyInfo == null) {
        throw new KeySelectorException("ERROR: KeyInfo object null!");
    }
    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;
            final X509Certificate x509Certificate = ((X509Certificate) oX509Certificate);
            final PublicKey key = x509Certificate.getPublicKey();
            // System.out.println(x509Certificate.getSubjectDN());
            if (algEquals(method, key.getAlgorithm())) {
                return new MyKeySelectorResult(key,x509Certificate) ;
            }
        }
    }
    throw new KeySelectorException("ERROR: No X509Certificate found!");
}

static boolean algEquals(String algURI, String algName) {

    if ((algName.equalsIgnoreCase("DSA") && algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1))
            || (algName.equalsIgnoreCase("RSA") && algURI.equalsIgnoreCase("RSA"))) {
        return true;
    } else {
        return false;
    }
}

}

xades4j.verification.SigningCertificateCertsNotInCertPathException: Exception in thread "main" xades4j.verification.SigningCertificateCertsNotInCertPathException: Verification failed for property 'SigningCertificate': SigningCertificate property contains one or more certificates that are not part of the certification path. at xades4j.verification.SigningCertificateVerifier.verify(SigningCertificateVerifier.java:113) at xades4j.verification.SigningCertificateVerifier.verify(SigningCertificateVerifier.java:35) at xades4j.verification.QualifyingPropertiesVerifierImpl.verifyProperties(QualifyingPropertiesVerifierImpl.java:59) at xades4j.verification.XadesVerifierImpl.verify(XadesVerifierImpl.java:212) at xxx.XXXXMLDigitalSignature.TestXades.main(TestXades.java:71) Process exited. Debugger disconnected from local process.

I am trying to validate a duly signed file, I also tested it with files found in the unit tests of the xades4j project and the exception is the same.

luisgoncalves commented 3 years ago

Hi

That's a lot of (poorly formatted) code... I haven't read it all... Anyway, the exception message should be helpful, no?

If the SigningCertificate property in the signed file is correct - i.e. it only contains certificates that are part of the expected certificate path - could your CertificateValidationProvider be returning less/other certificates than the ones that are part of the certificate path? This could cause the error.

luisgoncalves commented 3 years ago

It's also possible that the signature in the signed file is not correctly formed. Can you post the contents of the SigningCertificate property?

VitorMascarenhas commented 3 years ago

Hi

pY1yWia4eT3Y0o+u7m1AKLXTWnK2B2aHVF8Ebk2HAvM= CN=MULTICERT Trust Services Certification Authority 002, OU=Certification Authority, O=MULTICERT - Serviços de Certificação Electrónica S.A., C=PT 151509000615619905591926071863597790011 YE0y0DaJWu07/vrrcnwAnsDys836QqHHFzDmpyw76dQ= CN=MULTICERT Root Certification Authority 01, O=MULTICERT - Serviços de Certificação Electrónica S.A., C=PT 6074693700342339162 gs/a46cLbjdalu08/JEugaAgEEqLqIYnK1ljreyiRBE= CN=MULTICERT Root Certification Authority 01, O=MULTICERT - Serviços de Certificação Electrónica S.A., C=PT 7394183213738552410 234 / 5000 Resultados da tradução this problem also occurs when testing the signature validation of signed xml files, some of them are in the test folder of your project, this is what I base myself on so that the problem is in my code and not in the signature. auto_awesome Será que quis dizer: mais uma vez agradeço a sua disponibilidade para me ajudares neste problema 75 / 5000 Resultados da tradução once again thank you for your willingness to help me with this problem
VitorMascarenhas commented 3 years ago

Hi

That's a lot of (poorly formatted) code... I haven't read it all... Anyway, the exception message should be helpful, no?

If the SigningCertificate property in the signed file is correct - i.e. it only contains certificates that are part of the expected certificate path - could your CertificateValidationProvider be returning less/other certificates than the ones that are part of the certificate path? This could cause the error.

@luisgoncalves

Owner luisgoncalves commented 3 days ago It's also possible that the signature in the signed file is not correctly formed. Can you post the contents of the SigningCertificate property?

@VitorMascarenhas

Author VitorMascarenhas commented 5 minutes ago Hi

xades:SigningCertificate xades:Cert xades:CertDigest

ds:DigestValuepY1yWia4eT3Y0o+u7m1AKLXTWnK2B2aHVF8Ebk2HAvM=</ds:DigestValue> </xades:CertDigest> xades:IssuerSerial ds:X509IssuerNameCN=MULTICERT Trust Services Certification Authority 002, OU=Certification Authority, O=MULTICERT - Serviços de Certificação Electrónica S.A., C=PT</ds:X509IssuerName> ds:X509SerialNumber151509000615619905591926071863597790011</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> xades:Cert xades:CertDigest

ds:DigestValueYE0y0DaJWu07/vrrcnwAnsDys836QqHHFzDmpyw76dQ=</ds:DigestValue> </xades:CertDigest> xades:IssuerSerial ds:X509IssuerNameCN=MULTICERT Root Certification Authority 01, O=MULTICERT - Serviços de Certificação Electrónica S.A., C=PT</ds:X509IssuerName> ds:X509SerialNumber6074693700342339162</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> xades:Cert xades:CertDigest

ds:DigestValuegs/a46cLbjdalu08/JEugaAgEEqLqIYnK1ljreyiRBE=</ds:DigestValue> </xades:CertDigest> xades:IssuerSerial ds:X509IssuerNameCN=MULTICERT Root Certification Authority 01, O=MULTICERT - Serviços de Certificação Electrónica S.A., C=PT</ds:X509IssuerName> ds:X509SerialNumber7394183213738552410</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> </xades:SigningCertificate>

this problem also occurs when testing the signature validation of signed xml files, some of them are in the test folder of your project, this is what I base myself on so that the problem is in my code and not in the signature.

once again thank you for your willingness to help me with this problem

luisgoncalves commented 3 years ago

At first glance, it seems that the SigningCertificate property reference two certificates issued by "MULTICERT Root Certification Authority 01". Could it be that only one of them belongs to the certification path of the certificate/key used for the signature?

What's the expected certification path, from the top-level CA to the signing certificate?

luisgoncalves commented 3 years ago

Any progress?

VitorMascarenhas commented 3 years ago

Hi

No not yet. I will continue to try and if I get something I post the solution.

Thanks.

luisgoncalves commented 3 years ago

Reopen if you need more help.