All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.digidoc4j.ddoc.factory.DigiDocVerifyFactory Maven / Gradle / Ivy

Go to download

DDoc4J is Java Library for validating DDOC documents. It's not recommended to use it directly but rather through DigiDoc4J's API.

The newest version!
package org.digidoc4j.ddoc.factory;

import org.digidoc4j.ddoc.*;
import org.digidoc4j.ddoc.utils.ConfigManager;
import org.digidoc4j.ddoc.utils.ConvertUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.File;
import java.math.BigInteger;
import java.security.Provider;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Factory class to handle verification of signatures and container
 * @author Veiko Sinivee
 */
public class DigiDocVerifyFactory {
    private static final Logger m_logger = LoggerFactory.getLogger(DigiDocVerifyFactory.class);
    private static boolean m_prvInited = false;

    private static final String DIGIDOC_VERIFY_ALGORITHM = "RSA/NONE/PKCS1Padding";

    public static void initProvider() {
        try {
            if(!m_prvInited) {
                // only need this if we must sign the requests
                Provider prv = (Provider)Class.forName(ConfigManager.
                        instance().getProperty("DIGIDOC_SECURITY_PROVIDER")).newInstance();
                Security.addProvider(prv);
                m_prvInited = true;
            }
        } catch(Exception ex) {
            m_logger.error("Error initting provider: " + ex);
        }
    }

    /**
     * Helper method for comparing
     * digest values
     * @param dig1 first digest value
     * @param dig2 second digest value
     * @return true if they are equal
     */
    public static boolean compareDigests(byte[] dig1, byte[] dig2)
    {
        boolean ok = (dig1 != null) && (dig2 != null) &&
                (dig1.length == dig2.length);
        for(int i = 0; ok && (i < dig1.length); i++)
            if(dig1[i] != dig2[i])
                ok = false;
        return ok;
    }

    /**
     * Verifies the hash of one data-file
     * @param df DataFile object
     * @param ref Reference object
     * @param lerrs list of errors
     * @return true if ok
     */
    private static boolean verifyDataFileHash(DataFile df, Reference ref, List lerrs)
    {
        boolean bOk = true;
        if(df != null) {
            if(m_logger.isDebugEnabled())
                m_logger.debug("Check digest for DF: " + df.getId() + " ref: " + ((ref != null) ? ref.getUri() : "NULL"));
            String sDigType = null;
            if(ref != null)
                sDigType = ConfigManager.digAlg2Type(ref.getDigestAlgorithm());
            if(m_logger.isDebugEnabled())
                m_logger.debug("Check digest for DF: " + df.getId() + " type: " + sDigType);
            byte[] dfDig = null;
            try {
                if(sDigType != null)
                    dfDig = df.getDigestValueOfType(sDigType);
            } catch(DigiDocException ex) {
                lerrs.add(ex);
                bOk = false;
                m_logger.error("Error calculating hash for df: " + df.getId() + " - " + ex);
                ex.printStackTrace();
                if(ex.getNestedException() != null)
                    ex.getNestedException().printStackTrace();
            }
            if(ref != null) {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Compare digest: " + ((dfDig != null) ? Base64Util.encode(dfDig, 0) : "NONE") +
                            " hex: " + ((dfDig != null) ? ConvertUtils.bin2hex(dfDig) : "NONE") +
                            " alt digest: " + ((df.getAltDigest() != null) ? Base64Util.encode(df.getAltDigest(), 0) : "NONE") +
                            " to: " + ((ref.getDigestValue() != null) ? Base64Util.encode(ref.getDigestValue()) : "NONE") +
                            " hex: " + ((ref.getDigestValue() != null) ? ConvertUtils.bin2hex(ref.getDigestValue()) : "NONE"));
                DigiDocException exd = null;
                if(!SignedDoc.compareDigests(ref.getDigestValue(), dfDig)) {
                    lerrs.add((exd = new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE,
                            "Bad digest for DataFile: " + df.getId(), null)));
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("BAD DIGEST for DF: " + df.getId());
                    bOk = false;
                }
                if(!bOk && df.getAltDigest() != null) {
                    if(SignedDoc.compareDigests(ref.getDigestValue(), df.getAltDigest())) {
                        if(m_logger.isDebugEnabled()) {
                            m_logger.debug("DF: " + df.getId() + " alternate digest matches!");
                            m_logger.debug("GOOD ALT DIGEST for DF: " + df.getId());
                        }
                        if(exd != null)
                            lerrs.remove(exd);
                        ref.getSignedInfo().getSignature().setAltDigestMatch(true);
                        if(!ref.getSignedInfo().getSignature().getSignedDoc().getFormat().equals(SignedDoc.FORMAT_SK_XML))
                            lerrs.add((new DigiDocException(DigiDocException.ERR_DF_INV_HASH_GOOD_ALT_HASH,
                                    "Bad digest for DataFile: " + df.getId() + " alternate digest matches!", null)));
                        bOk = false;
                    }
                }
                else if(m_logger.isDebugEnabled())
                    m_logger.debug("GOOD DIGEST");
            } else {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("No Reference");
                lerrs.add(new DigiDocException(
                        DigiDocException.ERR_DATA_FILE_NOT_SIGNED,
                        "No Reference element for DataFile: " + df.getId(), null));
                bOk = false;
            }
        } else {
            if(m_logger.isDebugEnabled())
                m_logger.debug("Invalid data-file");
            bOk = false;
        }
        return bOk;
    }

    /**
     * Verifies SignedProperties digest
     * @param sig Signature object
     * @param lerrs list of errors
     * @return true if ok
     */
    private static boolean verifySignedPropretiesHash(Signature sig, List lerrs)
    {
        boolean bOk = true;
        if(m_logger.isDebugEnabled())
            m_logger.debug("Verifying signed-props of: " + sig.getId());
        SignedProperties sp = sig.getSignedProperties();
        if(sp != null) {
            Reference ref2 = sig.getSignedInfo().getReferenceForSignedProperties(sp);
            if(ref2 != null) {
                byte[] spDig = null;
                try {
                    spDig = sp.calculateDigest();
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("SignedProp real digest: " + Base64Util.encode(spDig, 0));
                } catch(DigiDocException ex) {
                    lerrs.add(ex);
                    bOk = false;
                }
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Compare it to: " + Base64Util.encode(ref2.getDigestValue(), 0));
                if(!SignedDoc.compareDigests(ref2.getDigestValue(), spDig)) {
                    lerrs.add(new DigiDocException(DigiDocException.ERR_DIGEST_COMPARE,
                            "Bad digest for SignedProperties: " + sp.getId(), null));
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("BAD DIGEST for sig-prop");
                    bOk = false;
                }
                else if(m_logger.isDebugEnabled())
                    m_logger.debug("GOOD DIGEST");
            } else {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("No Reference element for SignedProperties: " + sp.getId());
                lerrs.add(new DigiDocException(DigiDocException.ERR_SIG_PROP_NOT_SIGNED,
                        "No Reference element for SignedProperties: " + sp.getId(), null));
                bOk = false;
            }

        } else {
            if(m_logger.isDebugEnabled())
                m_logger.debug("No Reference element for SignedProperties of sig: " + sig.getId());
            lerrs.add(new DigiDocException(DigiDocException.ERR_SIG_PROP_NOT_SIGNED,
                    "No Reference element for SignedProperties sig: " + sig.getId(), null));
            bOk = false;
        }
        return bOk;
    }



    /**
     * Verifies the siganture
     * @param digest input data digest
     * @param signature signature value
     * @param cert certificate to be used on verify
     * @param bSoftCert use Sun verificateion api instead
     * @return true if signature verifies
     */
    public static boolean verify(byte[] digest, byte[] signature, X509Certificate cert, boolean bSoftCert, String sigMethod)
            throws DigiDocException
    {
        boolean rc = false;
        try {
            if(cert == null)
                throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid or missing signers cert!", null);
            if(bSoftCert) {
                String sigType = ConfigManager.instance().sigMeth2SigType(sigMethod, true);
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Verify xml:\n---\n" + new String(digest) + "\n---\n len: " +
                            digest.length + " method: " + sigMethod + " sig-type: " + sigType + "\n---\n" +
                            ConvertUtils.bin2hex(signature) + " sig-len: " + signature.length);
                if(sigType == null)
                    throw new DigiDocException(DigiDocException.ERR_VERIFY, "Signature method: " + sigMethod + " not provided!", null);
                java.security.Signature sig = java.security.Signature.getInstance(sigType, ConfigManager.addProvider());
                sig.initVerify(cert.getPublicKey());
                sig.update(digest);
                rc = sig.verify(signature);
            } else {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Verify sig: " + signature.length + " bytes, alg: " + DIGIDOC_VERIFY_ALGORITHM + " sig-alg: " + sigMethod);
                Cipher cryptoEngine = Cipher.getInstance(DIGIDOC_VERIFY_ALGORITHM, "BC");
                cryptoEngine.init(Cipher.DECRYPT_MODE, cert);
                byte[] decdig;
                try {
                    decdig = cryptoEngine.doFinal(signature);
                } catch(java.lang.ArrayIndexOutOfBoundsException ex2) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Invalid signature value. Signers cert and signature value don't match! - " + ex2);
                    throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid signature value! Signers cert and signature value don't match!", ex2);
                }
                String digType2 = ConfigManager.sigMeth2Type(sigMethod);
                String digType1 = ConvertUtils.findDigType(decdig);
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Decrypted digest: \'" + SignedDoc.bin2hex(decdig) + "\' len: " + decdig.length + " has-pref: " + digType1 +
                            " must-have: " + digType2 + " alg: " + sigMethod);
                if(digType1 != null && digType1.equals(SignedDoc.SHA1_DIGEST_TYPE_BAD)) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Invalid signature asn.1 prefix with 0x00 byte");
                    throw new DigiDocException(DigiDocException.ERR_SIGVAL_00, "Invalid signature asn.1 prefix with 0x00 byte", null);
                }
                if((digType1 == null) ||
                        (digType2 != null && digType1 != null && !digType2.equals(digType1))) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Signature asn.1 prefix: " + digType1 + " does not match: " + digType2);
                    throw new DigiDocException(DigiDocException.ERR_SIGVAL_ASN1, "Signature asn.1 prefix: " + digType1 + " does not match: " + digType2, null);
                }
                byte[] cdigest = ConvertUtils.removePrefix(decdig);
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Signed digest: \'" + ((cdigest != null) ? SignedDoc.bin2hex(cdigest) : "NULL") + "\' calc-digest: \'" + SignedDoc.bin2hex(digest) + "\'");
                if(decdig != null && cdigest != null &&
                        decdig.length == cdigest.length) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Signature value decrypted len: " + decdig.length + " missing ASN.1 structure prefix");
                    throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid signature value! Signature value decrypted len: " + decdig.length + " missing ASN.1 structure prefix", null);
                }
                rc = compareDigests(digest, cdigest);
            }
            if(m_logger.isDebugEnabled())
                m_logger.debug("Result: " + rc);
            if(!rc)
                throw new DigiDocException(DigiDocException.ERR_VERIFY, "Invalid signature value!", null);
        } catch(DigiDocException ex) {
            throw ex;
        } catch(Exception ex) {
            DigiDocException.handleException(ex, DigiDocException.ERR_VERIFY);
        }
        return rc;
    }

    public static boolean verifySignatureValue(SignedDoc sdoc, Signature sig, List lerrs)
    {
        boolean bOk = true;
        if(sdoc == null) {
            m_logger.error("SignedDoc is null");
            return false;
        }
        if(sig == null) {
            m_logger.error("Signature is null");
            return false;
        }
        if(m_logger.isDebugEnabled())
            m_logger.debug("Verifying signature value of: " + sig.getId());
        // verify signature value
        try {
            byte[] dig = sig.getSignedInfo().calculateDigest();
            if(m_logger.isDebugEnabled())
                m_logger.debug("SignedInfo real digest: " + Base64Util.encode(dig, 0) + " hex: " + SignedDoc.bin2hex(dig) +
                        " sig: " + ConvertUtils.bin2hex(sig.getSignatureValue().getValue()) +
                        " len: " + sig.getSignatureValue().getValue().length);
            if(sig.getSignatureValue() != null && sig.getSignatureValue().getValue() != null && dig != null) {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Verify sig: " + ConvertUtils.bin2hex(sig.getSignatureValue().getValue()) +
                            " len: " + sig.getSignatureValue().getValue().length + " hlen: " + ConvertUtils.bin2hex(sig.getSignatureValue().getValue()).length());
                bOk = verify(dig, sig.getSignatureValue().getValue(), sig.getKeyInfo().getSignersCertificate(), false, sig.getSignedInfo().getSignatureMethod());
                if(m_logger.isDebugEnabled())
                    m_logger.debug("GOOD DIGEST");
            } else {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Missing signature value!");
                lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNATURE_VALUE_VALUE, "Missing signature value!", null));
                bOk = false;
            }
        } catch(DigiDocException ex) {
            lerrs.add(ex);
            if(m_logger.isDebugEnabled()) {
                m_logger.debug("BAD DIGEST for sig-inf: " + sig.getId() + " - " + ex.toString());
                m_logger.debug("TRACE: " + ConvertUtils.getTrace(ex));
                //ex.printStackTrace();
                m_logger.debug("sig-val-len: " + ((sig.getSignatureValue() != null && sig.getSignatureValue().getValue() != null) ? sig.getSignatureValue().getValue().length : 0));
                m_logger.debug("signer: " + ((sig.getKeyInfo() != null && sig.getKeyInfo().getSignersCertificate() != null) ? sig.getKeyInfo().getSignersCertificate().getSubjectDN().getName() : "NULL"));
            }
            bOk = false;
        }
        return bOk;
    }

    /**
     * Verifies that the signers cert
     * has been signed by at least one
     * of the known root certs
     * @param cert certificate to check
     */
    public static boolean verifyCertificate(X509Certificate cert, X509Certificate caCert)
            throws DigiDocException {
        boolean rc = false;
        try {
            if (caCert != null) {
                cert.verify(caCert.getPublicKey());
                rc = true;
            }
        } catch (Exception ex) {
            DigiDocException.handleException(ex, DigiDocException.ERR_UNKNOWN_CA_CERT);
        }
        return rc;
    }

    /**
     * Verifies signers cerificate by a trusted CA cert
     * @param sig Signature object
     * @param lerrs list for errors
     * @return
     */
    public static boolean verifySignersCerificate(Signature sig, List lerrs)
    {
        boolean bOk = true;
        try {
            if(m_logger.isDebugEnabled())
                m_logger.debug("Verifying CA of signature: " + sig.getId() + " signed-at: " + ConvertUtils.date2string(sig.getSignedProperties().getSigningTime(), sig.getSignedDoc()) + " produced: " + ConvertUtils.date2string(sig.getSignatureProducedAtTime(), sig.getSignedDoc()));
            TrustServiceFactory tslFac = ConfigManager.instance().getTslFactory();
            if(sig.getKeyInfo().getSignersCertificate() == null) {
                lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNERS_CERT, "Signers cert missing!", null));
                return false;
            }
            X509Certificate caCert = tslFac.findCaForCert(sig.getKeyInfo().getSignersCertificate(), true, sig.getSignatureProducedAtTime());
            X509Certificate cert = sig.getKeyInfo().getSignersCertificate();
            if(m_logger.isDebugEnabled())
                m_logger.debug("Check signer: " + cert.getSubjectDN().getName() +
                        " issued by: " + cert.getIssuerDN().getName() +
                        " SUB from: " + ConvertUtils.date2string(cert.getNotBefore(), sig.getSignedDoc()) +
                        " to: " + ConvertUtils.date2string(cert.getNotAfter(), sig.getSignedDoc()) +
                        " by CA: " + ((caCert != null) ? caCert.getSubjectDN().getName() : "NOT FOUND") +
                        " CA from: " + ((caCert != null) ? ConvertUtils.date2string(caCert.getNotBefore(), sig.getSignedDoc()) : "?") +
                        " to: " + ((caCert != null) ? ConvertUtils.date2string(caCert.getNotAfter(), sig.getSignedDoc()) : "?") +
                        " ca-ahel: " + (DigiDocGenFactory.isTestCard(cert) ? "TEST" : "LIVE"));
            if(caCert != null) {
                bOk = verifyCertificate(cert, caCert);
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Signer: " + ConvertUtils.getCommonName(sig.getKeyInfo().getSignersCertificate().getSubjectDN().getName()) +
                            " is issued by trusted CA: " + ConvertUtils.getCommonName(caCert.getSubjectDN().getName()));
            } else {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("CA not found for: " + ConvertUtils.getCommonName(cert.getSubjectDN().getName()));
                lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNERS_CERT, "Signers cert not trusted, missing CA cert!", null));
                bOk = false;
            }
            if(!ConfigManager.isSignatureKey(cert)) {
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Signers cert does not have non-repudiation bit set!");
                lerrs.add(new DigiDocException(DigiDocException.ERR_SIGNERS_CERT_NONREPUD, "Signers cert does not have non-repudiation bit set!", null));
                bOk = false;
            }

            CertID cid = sig.getCertIdOfType(CertID.CERTID_TYPE_SIGNER);
            if(cert != null && cid != null) {
                // verify DN using RDN
                boolean bMatch = true;
                List aCertRdns = parseDN(ConvertUtils.convX509Name(cert.getIssuerX500Principal()));
                List aCertIdRdns = parseDN(cid.getIssuer());
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Signed: " + cid.getIssuer() + " cert: " + ConvertUtils.convX509Name(cert.getIssuerX500Principal()) +
                            " cert rdn-s: " + aCertRdns.size() + " signed rdn-s: " + aCertIdRdns.size());
                // don't have to match all RDN entries in cert. Just the signed ones agains the cert. Not opposite
                for(int i = 0; i < aCertIdRdns.size(); i++) {
                    Rdn r1 = (Rdn)aCertIdRdns.get(i);
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Signed RDN: " + r1.getId() + "/" +  r1.getValue());
                    boolean bF = false;
                    for(int j = 0; j < aCertRdns.size(); j++) {
                        Rdn r2 = (Rdn)aCertRdns.get(j);
                        if(r1.getId() != null && r2.getId() != null && r1.getId().equalsIgnoreCase(r2.getId()) &&
                                r1.getValue() != null && r2.getValue() != null && r1.getValue().equalsIgnoreCase(r2.getValue())) {
                            bF = true;
                        }
                    }
                    if(!bF) {
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("Different for signed: " + r1.getId() + "/" + r1.getValue());
                    }
                    if(!bF && r1.getId() != null &&
                            (r1.getId().equals("CN") ||
                                    r1.getId().equals("LT") ||
                                    r1.getId().equals("ST") ||
                                    r1.getId().equals("O") ||
                                    r1.getId().equals("OU") ||
                                    r1.getId().equals("C") ||
                                    r1.getId().equals("STREET") ||
                                    r1.getId().equals("DC") ||
                                    r1.getId().equals("UID")) ) {
                        m_logger.error("No match for signed: " + r1.getId() + "/" + r1.getValue());
                        bMatch = false;
                    }
                }
                if(!bMatch) {
                    m_logger.error("Signers cert issuer DN: " + ConvertUtils.convX509Name(cert.getIssuerX500Principal()) +
                            " and signed Issuername: " + cid.getIssuer() + " don't match");
                    lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Signing certificate issuer information does not match", null));
                }
                // verify cer issuer serial
                if(cid.getSerial() != null) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Signed IssuerSerial: " + cid.getSerial().toString() + " cert serial: " + cert.getSerialNumber().toString());
                    if(!cid.getSerial().equals(cert.getSerialNumber())) {
                        m_logger.error("Signers cert issuer serial: " + cert.getSerialNumber().toString() +
                                " and signed IssuerSerial: " + cid.getSerial().toString() + " don't match");
                        lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Signing certificate issuer information does not match", null));
                    }
                }
            }
        } catch(DigiDocException ex) {
            if(m_logger.isDebugEnabled())
                m_logger.debug("Signers certificate not trusted for: " + sig.getId());
            lerrs.add(ex);
            bOk = false;
        }
        return bOk;
    }

    /**
     * Verifies signing time of signature (as stored in signed properties)
     * @param sig Signature object
     * @param lerrs list of errors
     * @return true if ok
     */
    public static boolean verifySigningTime(Signature sig, List lerrs)
    {
        boolean bOk = true;
        if(m_logger.isDebugEnabled())
            m_logger.debug("Verifying signing time signature: " + sig.getId());
        try {
            Date dProdAt = null;
            if(sig != null && sig.getUnsignedProperties() != null && sig.getUnsignedProperties().getNotary() != null)
                dProdAt = sig.getUnsignedProperties().getNotary().getProducedAt();
            if(dProdAt != null)
                sig.getKeyInfo().getSignersCertificate().checkValidity(dProdAt);
            if(m_logger.isDebugEnabled())
                m_logger.debug("Signers cert: " + ConvertUtils.getCommonName(sig.getKeyInfo().getSignersCertificate().getSubjectDN().getName()) +
                        " was valid on: " + ConvertUtils.date2string(dProdAt, sig.getSignedDoc()));
        } catch(Exception ex) {
            m_logger.error("Signers certificate has expired for: " + sig.getId());
            lerrs.add(new DigiDocException(DigiDocException.ERR_CERT_EXPIRED,
                    "Signers certificate has expired!", null));
            bOk = false;
        }
        return bOk;
    }

    /**
     * Verifies OCSP confirmation for signature
     * @param sig Signature object
     * @param lerrs list of errors
     * @return true if ok
     */
    public static boolean verifySignatureOCSP(Signature sig, List lerrs)
    {
        boolean bOk = true;
        if(m_logger.isDebugEnabled())
            m_logger.debug("Verifying OCSP for signature: " + sig.getId());
        try {
            if(sig.getUnsignedProperties() != null && sig.getUnsignedProperties().countNotaries() > 0) {
                CertValue cvOcsp = sig.getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER);
                CertID cidOcsp = sig.getCertIdOfType(CertID.CERTID_TYPE_RESPONDER);
                X509Certificate rCert = null;
                String sIssuer = null;
                BigInteger sSerial = null;
                if(cvOcsp != null)
                    rCert = cvOcsp.getCert();
                if(cidOcsp != null) {
                    sIssuer = cidOcsp.getIssuer();
                    sSerial = cidOcsp.getSerial();
                }
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Responders cert: " + ((rCert != null) ? rCert.getSerialNumber().toString() : "NULL") + " - " +
                            ((rCert != null) ? rCert.getSubjectDN().getName() : "NULL") +
                            " complete cert refs nr: " + sSerial + " - " + sIssuer +
                            " ca-ahel: " + ((rCert != null) ? (DigiDocGenFactory.isTestCard(rCert) ? "TEST" : "LIVE") : "?"));
                // signer/ocsp live/test verification moved to utility
                if(rCert != null && !rCert.getSerialNumber().equals(sSerial)) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Wrong notarys certificate: " + rCert.getSerialNumber() + " ref: " + sSerial);
                    lerrs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT,
                            "Wrong notarys certificate: " + rCert.getSerialNumber() + " ref: " + sSerial, null));
                    bOk = false;
                }
                // verify notary certs digest using CompleteCertificateRefs
                try {
                    byte[] digest = SignedDoc.digestOfType(rCert.getEncoded(), SignedDoc.SHA1_DIGEST_TYPE);
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Not cert calc hash: " + Base64Util.encode(digest, 0) +
                                " cert-ref hash: " + Base64Util.encode(sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestValue(), 0));
                    if(!compareDigests(digest, sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestValue())) {
                        lerrs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT,
                                "Notary certificates digest doesn't match!", null));
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("Notary certificates digest doesn't match!");
                        bOk = false;
                    }
                } catch(DigiDocException ex) {
                    lerrs.add(ex);
                    bOk = false;
                } catch(Exception ex) {
                    bOk = false;
                    lerrs.add(new DigiDocException(DigiDocException.ERR_RESPONDERS_CERT,
                            "Error calculating notary certificate digest!", null));
                }
                // we support only 1 ocsp per signature
                if(sig.getUnsignedProperties().countNotaries() > 1) {
                    if(m_logger.isDebugEnabled())
                        m_logger.debug("Currently supports only one OCSP");
                    lerrs.add(new DigiDocException(DigiDocException.ERR_OCSP_VERIFY,
                            "Currently supports only one OCSP", null));
                    bOk = false;
                }

                // verify notarys digest using CompleteRevocationRefs
                try {
                    for(int i = 0; i < sig.getUnsignedProperties().countNotaries(); i++) {
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("Signature: " + sig.getId() + " not: " + i + " notaries: " + sig.getUnsignedProperties().countNotaries());
                        Notary not = sig.getUnsignedProperties().getNotaryById(i);

                        byte[] ocspData = not.getOcspResponseData();
                        if(m_logger.isDebugEnabled())
                               m_logger.debug("OCSP value: " + not.getId() + " data: " + ((ocspData != null) ? ocspData.length : 0) + " bytes");
                        if(ocspData == null || ocspData.length == 0) {
                            lerrs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "OCSP value is empty!", null));
                            bOk = false;
                            continue;
                        }
                        OcspRef orf = sig.getUnsignedProperties().getCompleteRevocationRefs().getOcspRefByUri("#" + not.getId());
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("OCSP ref: " + ((orf != null) ? orf.getUri() : "NULL"));
                        if(orf == null) {
                            lerrs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST, "No OCSP ref for uri: #" + not.getId(), null));
                            bOk = false;
                            continue;
                        }
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("OCSP data len: " + ocspData.length);
                        byte[] digest1 = SignedDoc.digestOfType(ocspData, SignedDoc.SHA1_DIGEST_TYPE);
                        byte[] digest2 = orf.getDigestValue();
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("Check ocsp: " + not.getId() +
                                    " calc hash: " + Base64Util.encode(digest1, 0) +
                                    " refs-hash: " + Base64Util.encode(digest2, 0));
                        if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_SK_XML) &&
                                !compareDigests(digest1, digest2)) {
                            lerrs.add(new DigiDocException(DigiDocException.ERR_NOTARY_DIGEST,
                                    "Notarys digest doesn't match!", null));
                            if(m_logger.isDebugEnabled())
                                m_logger.debug("Notarys digest doesn't match!");
                            bOk = false;
                        }
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("Check ocsp: " + not.getId() + " prodAt: " +
                                    ((not.getProducedAt() != null) ? ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()) : "NULL") +
                                    " orf prodAt: " + ((orf.getProducedAt() != null) ? ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()) : "NULL"));
                        if(not.getProducedAt() != null && orf.getProducedAt() != null &&
                                !ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()).
                                        equals(ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()))) {
                            if(m_logger.isDebugEnabled())
                                m_logger.debug("Notary: " + not.getId() + " producedAt: " +
                                        ((not.getProducedAt() != null) ? ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()) : "NULL") +
                                        " does not match OcpsRef-s producedAt: " + ((orf.getProducedAt() != null) ? ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()) : "NULL"));
                            lerrs.add(new DigiDocException(DigiDocException.ERR_OCSP_VERIFY, "Notary: " + not.getId() + " producedAt: " +
                                    ((not.getProducedAt() != null) ? ConvertUtils.date2string(not.getProducedAt(), sig.getSignedDoc()) : "NULL") +
                                    " does not match OcpsRef-s producedAt: " + ((orf.getProducedAt() != null) ? ConvertUtils.date2string(orf.getProducedAt(), sig.getSignedDoc()) : "NULL"), null));
                        }
                    }
                    } catch(DigiDocException ex) {
                        lerrs.add(ex);
                        bOk = false;
                    }
                try {
                    NotaryFactory notFac = ConfigManager.instance().getNotaryFactory();
                    for(int i = 0; i < sig.getUnsignedProperties().countNotaries(); i++) {
                        Notary not = sig.getUnsignedProperties().getNotaryById(i);
                        if(m_logger.isDebugEnabled())
                            m_logger.debug("Verify notary: " + not.getId() + " ocsp: " +
                                    ((not.getOcspResponseData() != null) ? not.getOcspResponseData().length : 0) +
                                    " responder: " + not.getResponderId());
                        notFac.parseAndVerifyResponse(sig, not);
                    }
                } catch(DigiDocException ex) {
                    lerrs.add(ex);
                    bOk = false;
                }

            } else {
                bOk = false;
                if(m_logger.isDebugEnabled())
                    m_logger.debug("Signature has no OCSP confirmation!");
                lerrs.add(new DigiDocException(DigiDocException.ERR_NO_CONFIRMATION,
                        "Signature has no OCSP confirmation!", null));
            }
        } catch(Exception ex) {
            if(m_logger.isDebugEnabled())
                m_logger.debug("Failed to verify OCSP for: " + sig.getId());
            lerrs.add(new DigiDocException(DigiDocException.ERR_CERT_EXPIRED,
                    "Failed to verify OCSP for: " + sig.getId(), null));
            bOk = false;
        }
        return bOk;
    }

    /**
     * Verifies signature
     * @param sdoc SignedDoc object
     * @param sig Signature object
     * @param lerrs list of errors
     * @return true if ok
     */
    public static boolean verifySignature(SignedDoc sdoc, Signature sig, List lerrs)
    {
        boolean bOk = true, b = false;
        initProvider();
        if(m_logger.isDebugEnabled())
            m_logger.debug("Verifying signature: " + sig.getId() + " profile: " + sig.getProfile());
        // verify DataFile hashes
        for(int i = 0; i < sdoc.countDataFiles(); i++) {
            DataFile df = sdoc.getDataFile(i);
            if(m_logger.isDebugEnabled())
                m_logger.debug("Verifying DF: " + df.getId() + " file: " + df.getFileName());
            Reference ref = sig.getSignedInfo().getReferenceForDataFile(df);
            if(ref != null) {
                b = verifyDataFileHash(df, ref, lerrs);
            } else {
                b = false;
                lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Missing Reference for file: " + df.getFileName(), null));
            }
            if(!b) bOk = false;
        }
        // check DF/Ref
        for(int i = 0; i < sdoc.countSignatures(); i++) {
            Signature sig1 = sdoc.getSignature(i);
            for(int j = 0; j < sig.getSignedInfo().countReferences(); j++) {
                Reference ref1 = sig.getSignedInfo().getReference(j);
                if(ref1.getType() != null)
                    continue;
                if((sdoc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) ||
                        sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) && // ddoc 1.0 formaadis erijuhtumid
                        (ref1.getUri().indexOf("-MIME") != -1 || ref1.getUri().indexOf("-SignedProperties") != -1)) continue;
                boolean bFound = false;
                for(int l = 0; l < sdoc.countDataFiles(); l++) {
                    DataFile df = sdoc.getDataFile(l);
                    String sFile = df.getFileName();
                    if(sFile != null && sFile.indexOf('/') != -1 || sFile.indexOf('\\') != -1) {
                        File fT = new File(sFile);
                        sFile = fT.getName();
                    }
                    if(ref1.getUri() != null) {
                        if((sdoc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) ||
                                sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML)) &&
                                ref1.getUri().startsWith("#") &&
                                df.getId().equals(ref1.getUri().substring(1)))
                            bFound = true;
                    }
                }
                if(!bFound) {
                    if(m_logger.isInfoEnabled())
                        m_logger.info("Missing DataFile for signature: " + sig.getId() + " reference " +ref1.getUri());
                    lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "Missing DataFile for signature: " + sig.getId() + " reference " +ref1.getUri(), null));
                }
            }
        }
        // verify 
        if(!sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML))
            b = verifySignedPropretiesHash(sig, lerrs);
        if(!b) bOk = false;
        // verify signature value
        b = verifySignatureValue(sdoc, sig, lerrs);
        if(!b) bOk = false;
        // verify signers cert...
        // check the certs validity dates
        b = verifySigningTime(sig, lerrs);
        if(!b) bOk = false;
        // check certificates CA
        b = verifySignersCerificate(sig, lerrs);
        if(!b) bOk = false;
        // verify OCSP
        if(sdoc.getFormat().equals(SignedDoc.FORMAT_SK_XML) ||
                sdoc.getFormat().equals(SignedDoc.FORMAT_DIGIDOC_XML) ||
                (sig.getProfile() != null &&
                        (sig.getProfile().equals(SignedDoc.PROFILE_TM)))) {
            b = verifySignatureOCSP(sig, lerrs);
            if(!b) bOk = false;
        }
        return bOk;
    }

    /**
     * Helper method to parse DN
     * @param dn certificate DN
     * @param chSep separator used
     * @return list of RDN entries
     */
    private static List findRdns(String dn, char chSep)
    {
        List lrdn = new ArrayList();
        StringBuffer sbId = new StringBuffer();
        StringBuffer sbVal = new StringBuffer();
        boolean bId = true; // parsing stage - id or value
        for(int i = 0; (dn != null) && (i < dn.length()); i++) {
            char ch = dn.charAt(i);
            // RDN end found
            if(( (ch == chSep) &&
                    (i == 0 || dn.charAt(i-1) != '\\')) || (i == dn.length()-1)) {
                if(i == dn.length()-1 && !bId)
                    sbVal.append(ch);
                if(sbId.length() > 0 && sbVal.length() > 0)
                    lrdn.add(new Rdn(sbId.toString().trim(), null, sbVal.toString().trim()));
                sbId = new StringBuffer();
                sbVal = new StringBuffer();
                bId = true;
            } else if(ch == '=' && (i == 0 || dn.charAt(i-1) != '\\')) {
                bId = false;
            } else { // handle content
                if(bId)
                    sbId.append(ch);
                else
                    sbVal.append(ch);
            }
        }
        return lrdn;
    }

    /**
     * Parses a DN normalized by rules of RFC2253 and returns a set of
     * Rdn objects containing RDN-s and values found in this DN
     * @param dn normalized DN string
     * @return array of Rdn objects
     */
    public static List parseDN(String dn)
    {
        // try first according to RFC2253
        List al = findRdns( dn, ',');
        if(al.size() < 3) // if not successfull try RFC1770
            al = findRdns( dn, '/');
        return al;
    }



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy