Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
ee.sk.digidoc.factory.DigiDocVerifyFactory Maven / Gradle / Ivy
package ee.sk.digidoc.factory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigInteger;
import java.security.Provider;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import javax.crypto.Cipher;
import org.apache.commons.compress.archivers.*;
import org.apache.log4j.Logger;
import org.bouncycastle.tsp.TSPAlgorithms;
import org.bouncycastle.tsp.TimeStampResponse;
import ee.sk.digidoc.*;
import ee.sk.utils.ConfigManager;
import ee.sk.utils.ConvertUtils;
/**
* Factory class to handle verification of signatures and container
* @author Veiko Sinivee
*/
public class DigiDocVerifyFactory {
//private SignedDoc m_sdoc;
private static Logger m_logger = Logger.getLogger(DigiDocVerifyFactory.class);
private static boolean m_prvInited = false;
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();
//prv.list(System.out);
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;
}
public static boolean verifyManifestEntries(SignedDoc sdoc, List lerrs)
throws DigiDocException
{
boolean bOk = true;
if(m_logger.isDebugEnabled())
m_logger.debug("Verifying manifest entries");
if(sdoc != null && sdoc.getFormat() != null && sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
// compare ManifestFileEntry-s and DataFile-s match
for(int i = 0; i < sdoc.countDataFiles(); i++) {
DataFile df = sdoc.getDataFile(i);
boolean bF = false;
String sFileName = df.getFileName();
File ft1 = new File(df.getFileName());
sFileName = ft1.getName();
if(sdoc.getManifest() != null) {
for(int j = 0; j < sdoc.getManifest().getNumFileEntries(); j++) {
ManifestFileEntry mfe = sdoc.getManifest().getFileEntry(j);
if(mfe != null) {
if(m_logger.isDebugEnabled())
m_logger.debug("Manifest entry: " + mfe.getFullPath() + " mime: " + mfe.getMediaType() + " df: " + df.getId() + " df-mime: " + df.getMimeType());
if(mfe.getFullPath() != null && mfe.getFullPath().equals(sFileName)) {
if(bF) {
lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
"Duplicate ManifestFileEntry for: " + df.getFileName(), null));
if(m_logger.isDebugEnabled())
m_logger.error("Duplicate ManifestFileEntry for: " + df.getFileName());
bOk = false;
} else {
bF = true;
}
if(mfe.getMediaType() == null || df.getMimeType() == null ||
!mfe.getMediaType().equals(df.getMimeType())) {
lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_MIME_TYPE,
"DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() +
" does not match manifest mime type: " + mfe.getMediaType(), null));
if(m_logger.isDebugEnabled())
m_logger.error("DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() +
" does not match manifest mime type: " + mfe.getMediaType());
bOk = false;
}
}
}
} // for j
for(int s = 0; s < sdoc.countSignatures(); s++) {
Signature sig = sdoc.getSignature(s);
Reference dRef = sig.getSignedInfo().getReferenceForDataFile(df);
if(dRef != null) {
DataObjectFormat dof = sig.getSignedInfo().getDataObjectFormatForReference(dRef);
if(dof != null) {
if(df.getMimeType() != null && dof.getMimeType() != null && !dof.getMimeType().equals(df.getMimeType())) {
lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_MIME_TYPE,
"DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() +
" does not match signature: " + sig.getId() + " mime type: " + dof.getMimeType(), null));
if(m_logger.isDebugEnabled())
m_logger.error("DataFile " + df.getFileName() + " mime-type: " + df.getMimeType() +
" does not match signature: " + sig.getId() + " mime type: " + dof.getMimeType());
bOk = false;
}
}
}
}
} // for s
if(!bF) {
lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
"Missing ManifestFileEntry for: " + df.getFileName(), null));
if(m_logger.isDebugEnabled())
m_logger.error("Missing ManifestFileEntry1 for: " + sFileName);
}
}
for(int j = 0; j < sdoc.getManifest().getNumFileEntries(); j++) {
ManifestFileEntry mfe = sdoc.getManifest().getFileEntry(j);
if(mfe == null) {
m_logger.error("Invalid manifest entry");
continue;
}
if(m_logger.isDebugEnabled())
m_logger.debug("Check manifest entry: " + mfe.getFullPath() + " mime: " + mfe.getMediaType());
if(mfe.getFullPath() != null && mfe.getFullPath().equals("/")) continue; // container root element
boolean bF = false;
for(int i = 0; i < sdoc.countDataFiles(); i++) {
DataFile df = sdoc.getDataFile(i);
String sFileName = df.getFileName();
File ft1 = new File(df.getFileName());
sFileName = ft1.getName();
if(m_logger.isDebugEnabled())
m_logger.debug("Manifest entry: " + mfe.getFullPath() + " mime: " + mfe.getMediaType() + " found df: " + df.getId() + " df-mime: " + df.getMimeType());
if(mfe.getFullPath() != null && mfe.getFullPath().equals(sFileName)) {
if(bF) {
lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
"Duplicate DataFile: " + df.getId() + " with name: " + df.getFileName(), null));
if(m_logger.isDebugEnabled())
m_logger.error("Duplicate DataFile: " + df.getId() + " with name: " + df.getFileName());
bOk = false;
} else {
bF = true;
}
}
}
if(!bF) {
lerrs.add(new DigiDocException(DigiDocException.ERR_MANIFEST_ENTRY,
"Missing DataFile for ManifestFileEntry: " + mfe.getFullPath(), null));
if(m_logger.isDebugEnabled())
m_logger.error("Missing DataFile for ManifestFileEntry: " + mfe.getFullPath());
}
}
}
return bOk;
}
private static final String DIG_TYPE_WARNING = "The current BDoc container uses weaker encryption method than officialy accepted in Estonia. "+
"We do not recommend you to add signature to this document. There is an option to re-sign this document in a new container.";
private static final String DIGIDOC_VERIFY_ALGORITHM = "RSA/NONE/PKCS1Padding";
/**
* 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(SignedDoc sdoc, 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((exd = 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;
}
if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
String sFile = df.getFileName();
if(sFile != null && (sFile.indexOf('/') != -1 || sFile.indexOf('\\') != -1)) {
File fT = new File(sFile);
sFile = fT.getName();
}
ManifestFileEntry mfe = sdoc.getManifest().findFileEntryByPath(sFile);
if(m_logger.isDebugEnabled()) {
m_logger.debug("DF: " + df.getId() + " file: " + sFile + " manifest-entry: "+ ((mfe != null) ? "OK" : "NULL"));
if(mfe == null) {
if(m_logger.isDebugEnabled())
m_logger.debug("No manifest.xml entry for: " + df.getFileName());
lerrs.add(new DigiDocException(
DigiDocException.ERR_DATA_FILE_FILE_NAME,
"No manifest.xml entry for: " + df.getFileName(), 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();
boolean bSha1Check = ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true);
if(sp != null) {
Reference ref2 = sig.getSignedInfo().getReferenceForSignedProperties(sp);
if(ref2 != null && sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
ref2.getDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
if(m_logger.isInfoEnabled())
m_logger.info("SignedProperties for signature: " + sig.getId() + " has weak digest type: " + ref2.getDigestAlgorithm());
}
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 = null;
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; // pass it on, but check other exceptions
} 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(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
(sig.getSignedInfo().getSignatureMethod().equals(SignedDoc.RSA_SHA1_SIGNATURE_METHOD) ||
sig.getSignedInfo().getSignatureMethod().equals(SignedDoc.ECDSA_SHA1_SIGNATURE_METHOD)) &&
ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
if(m_logger.isInfoEnabled())
m_logger.info("Signature: " + sig.getId() + " has weak signature method: " + sig.getSignedInfo().getSignatureMethod());
}
if(sig.getSignatureValue() != null && sig.getSignatureValue().getValue() != null && dig != null) {
if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && sig.isEllipticCurveSiganture()) {
if(m_logger.isDebugEnabled())
m_logger.debug("Verify sdoc: " + sdoc.getFormat() + "/" + sdoc.getVersion() + " prefs: " + sdoc.getXmlDsigNs() + "/" + sdoc.getAsicNs() + "/" + sdoc.getXadesNs());
//DigiDocXmlGenFactory genFac = new DigiDocXmlGenFactory(sdoc);
byte[] xml = sig.getSignedInfo().getOrigXml(); //genFac.signedInfoToXML(sig, sig.getSignedInfo());
if(m_logger.isDebugEnabled())
m_logger.debug("Verify xml:\n---\n" + new String(xml) + "\n---\n");
bOk = verify(xml, sig.getSignatureValue().getValue(), sig.getKeyInfo().getSignersCertificate(), true, sig.getSignedInfo().getSignatureMethod());
} else {
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;
}
public static boolean verifySignatureFromLiveAndOcspFromTest(Signature sig, List lerrs)
{
boolean bOk = true;
if(m_logger.isDebugEnabled())
m_logger.debug("Verifying live/test for signature: " + sig.getId());
X509Certificate cert = null, rCert = null;
if(sig != null) {
CertValue cvOcsp = sig.getCertValueOfType(CertValue.CERTVAL_TYPE_RESPONDER);
if(sig.getKeyInfo() != null && cvOcsp != null) {
cert = sig.getKeyInfo().getSignersCertificate();
rCert = cvOcsp.getCert();
if(cert != null && rCert != null &&
DigiDocGenFactory.isTestCard(rCert) &&
!DigiDocGenFactory.isTestCard(cert)) {
if(m_logger.isDebugEnabled())
m_logger.debug("Signer from LIVE CA-chain but OCSP from TEST CA-chain!");
lerrs.add(new DigiDocException(DigiDocException.ERR_TEST_SIGNATURE,
"Signer from LIVE CA-chain but OCSP from TEST CA-chain!", 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;
byte [] cHash = null;
if(cvOcsp != null)
rCert = cvOcsp.getCert();
//if(cidOcsp == null)
// cidOcsp = sig.getCertIdOfType(CertID.CERTID_TYPE_SIGNER);
if(cidOcsp != null) {
sIssuer = cidOcsp.getIssuer();
sSerial = cidOcsp.getSerial();
cHash = cidOcsp.getDigestValue();
}
X509Certificate cert = sig.getKeyInfo().getSignersCertificate();
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) &&
!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) {
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 {
if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) {
byte[] digest = SignedDoc.digestOfType(rCert.getEncoded(), (sig.getSignedDoc().getFormat().
equals(SignedDoc.FORMAT_BDOC) ? SignedDoc.SHA256_DIGEST_TYPE : 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;
}
if(sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
if(m_logger.isInfoEnabled())
m_logger.info("CompleteCertificateRefs for signature: " + sig.getId() + " has weak digest type: " +
sig.getUnsignedProperties().getCompleteCertificateRefs().getCertDigestAlgorithm());
}
}
// TODO: in bdoc verify responders ca hash - verify all hashes in certrefs
} 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
if(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC)) {
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, ((sig.getSignedDoc().getFormat().
equals(SignedDoc.FORMAT_BDOC) && (orf.getDigestAlgorithm().equals(SignedDoc.SHA256_DIGEST_ALGORITHM_1) ||
orf.getDigestAlgorithm().equals(SignedDoc.SHA256_DIGEST_ALGORITHM_2))) ?
SignedDoc.SHA256_DIGEST_TYPE : SignedDoc.SHA1_DIGEST_TYPE));
//if(m_logger.isDebugEnabled())
// m_logger.debug("Calculated digest: " + Base64Util.encode(digest1, 0));
byte[] digest2 = orf.getDigestValue();
//if(m_logger.isDebugEnabled())
// m_logger.debug("Real digest: " + Base64Util.encode(digest2, 0));
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(sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
orf.getDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
if(m_logger.isInfoEnabled())
m_logger.info("CompleteRevocationRefs for signature: " + sig.getId() + " has weak digest type: " +
orf.getDigestAlgorithm());
}
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(!sig.getSignedDoc().getFormat().equals(SignedDoc.FORMAT_SK_XML)) {
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;
}
} // don't verify complete revocation refs in bdoc
// verify notary status
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());
if(sig.getProfile() != null &&
(sig.getProfile().equals(SignedDoc.BDOC_PROFILE_T) ||
sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TS) ||
sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TSA))) {
if(m_logger.isDebugEnabled())
m_logger.debug("T, TS and TSA profiles are currently not supported!");
lerrs.add(new DigiDocException(DigiDocException.ERR_VERIFY, "T, TS and TSA profiles are currently not supported!", null));
}
// 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 && ref.getDigestAlgorithm() != null &&
sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && // check digest type
ref.getDigestAlgorithm().equals(SignedDoc.SHA1_DIGEST_ALGORITHM) &&
ConfigManager.instance().getBooleanProperty("BDOC_SHA1_CHECK", true)) {
lerrs.add(new DigiDocException(DigiDocException.WARN_WEAK_DIGEST, DIG_TYPE_WARNING, null));
if(m_logger.isInfoEnabled())
m_logger.info("DataFile: " + df.getId() + " has weak digest type: " + ref.getDigestAlgorithm());
} // kontrolli kas on Referencet millele ei ole andmefaili
if(ref != null) {
b = verifyDataFileHash(sdoc, 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 ||
(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) && // bdoc 2.0-s ei ole manifest.xml-i r��si!
ref1.getUri().indexOf("META-INF/manifest.xml") != -1)) 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(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC) &&
ref1.getUri().indexOf(sFile) != -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 mime-type hashes
if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
for(int i = 0; i < sig.getSignedInfo().countReferences(); i++) {
Reference ref = sig.getSignedInfo().getReference(i);
if(!ref.getUri().startsWith("#")) {
DataObjectFormat dof = sig.getSignedInfo().getDataObjectFormatForReference(ref);
if(dof == null) {
if(m_logger.isDebugEnabled())
m_logger.debug("No DataObjectFormat element for Reference: " + ref.getId());
lerrs.add(new DigiDocException(DigiDocException.ERR_DATA_FILE_MIME_TYPE,
"No DataObjectFormat element for Reference: " + ref.getId(), 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;
// TODO: Profile T & CL verify Timestamp T0
if(sdoc.getFormat().equals(SignedDoc.FORMAT_BDOC)) {
b = verifySignaturePolicies(sdoc, 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.BDOC_PROFILE_TM) ||
sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TMA) ||
sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TS) ||
sig.getProfile().equals(SignedDoc.BDOC_PROFILE_TSA)))) {
b = verifySignatureOCSP(sig, lerrs);
if(!b) bOk = false;
}
// verify timestamps
/*ArrayList tsaCerts = findTSACerts();
if(m_timestamps != null && m_timestamps.size() > 0) {
TimestampFactory tsFac = null;
try {
tsFac = ConfigManager.instance().getTimestampFactory();
} catch(DigiDocException ex) {
//m_logger.error("Failed to get TimestampFactory: " + ex);
errs.add(ex);
}
ArrayList e = tsFac.verifySignaturesTimestamps(this);
if(!e.isEmpty())
errs.addAll(e);
for(int i = 0; i < m_timestamps.size(); i++) {
TimestampInfo ts = (TimestampInfo)m_timestamps.get(i);
if(m_logger.isDebugEnabled())
m_logger.debug("TS: " + ts.getId() + " type: " + ts.getType() + " time: " + ts.getTime());
if(ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIGNATURE)
dt1 = ts.getTime();
if(ts.getType() == TimestampInfo.TIMESTAMP_TYPE_SIG_AND_REFS)
dt2 = ts.getTime();
}
int nMaxTsTimeErrSecs = ConfigManager.instance().getIntProperty("MAX_TSA_TIME_ERR_SECS", 0);
if(dt1 != null && dt2 != null) {
dt1 = new Date(dt1.getTime() - (nMaxTsTimeErrSecs * 1000));
dt2 = new Date(dt2.getTime() + (nMaxTsTimeErrSecs * 1000));
if(dt2.before(dt1))
errs.add(new DigiDocException(DigiDocException.ERR_TIMESTAMP_VERIFY, "SignAndRefsTimeStamp is before SignatureTimeStamp", null));
if(do1.before(dt1) || do1.after(dt2))
errs.add(new DigiDocException(DigiDocException.ERR_TIMESTAMP_VERIFY, "OCSP time is not between SignAndRefsTimeStamp and SignatureTimeStamp", null));
}
}
} // profiles T/C/TM/TS
*/
return bOk;
}
/**
* Verifies signature policies
* @param sdoc SignedDoc object
* @param sig Signature object
* @param lerrs list of errors
* @return true if signature declares valid bdoc 2.0 nonce policy
*/
public static boolean verifySignaturePolicies(SignedDoc sdoc, Signature sig, List lerrs)
{
boolean bOk = false;
if(m_logger.isInfoEnabled())
m_logger.debug("Check signature: " + sig.getId() + " profile: " + sig.getProfile() + " format: " + sdoc.getFormat() + " policies");
try {
if(sig.getSignedProperties() != null &&
sig.getSignedProperties().getSignaturePolicyIdentifier() != null &&
sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId() != null &&
sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyId() != null &&
sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyId().getIdentifier() != null) {
Identifier id = sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyId().getIdentifier();
if(m_logger.isInfoEnabled())
m_logger.debug("Signature: " + sig.getId() + " has policy: " + id.getQualifier() +
" uri: " + id.getUri() + " hash: " +
Base64Util.encode(sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getDigestValue()));
if(id.getQualifier().equals(Identifier.OIDAsURN) &&
id.getUri().equals(DigiDocGenFactory.BDOC_210_OID)) { // has bdoc 2.0 nonce policy
bOk = true;
// check policy hash
if(sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getDigestValue() == null ||
sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getDigestValue().length == 0) {
if(m_logger.isDebugEnabled())
m_logger.debug("Signature: " + sig.getId() + " has no signature policy hash");
lerrs.add(new DigiDocException(DigiDocException.ERR_NONCE_POLICY_HASH,
"Signature: " + sig.getId() + " has invalid signature policy hash", null));
}
// check policy uri
boolean bUriOk = false;
for(int i = 0; i < sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().countSigPolicyQualifiers(); i++) {
SigPolicyQualifier spq = sig.getSignedProperties().getSignaturePolicyIdentifier().getSignaturePolicyId().getSigPolicyQualifier(i);
if(spq instanceof SpUri) {
SpUri sna = (SpUri)spq;
if(sna.getUri() != null && sna.getUri().trim().length() > 0) {
bUriOk = true;
}
}
}
if(!bUriOk) { // invalid uri
bOk = false;
if(m_logger.isDebugEnabled())
m_logger.debug("Signature: " + sig.getId() + " has no signature policy uri!");
lerrs.add(new DigiDocException(DigiDocException.ERR_NONCE_POLICY_URL,
"Signature: " + sig.getId() + " has no nonce policy uri!", null));
}
} else { // unknown policy
if(m_logger.isDebugEnabled())
m_logger.debug("Signature: " + sig.getId() + " has unknown policy: " + id.getQualifier() + " uri: " + id.getUri());
lerrs.add(new DigiDocException(DigiDocException.ERR_NONCE_POLICY_OID,
"Signature: " + sig.getId() + " has unknown policy: " + id.getQualifier() + " uri: " + id.getUri(), null));
}
} else { // no policy
if(m_logger.isDebugEnabled())
m_logger.debug("No signature policy for sig: " + sig.getId());
lerrs.add(new DigiDocException(DigiDocException.ERR_POLICY_NONE,
"Signature: " + sig.getId() + " has no policy!", null));
}
} catch(Exception ex) {
if(m_logger.isDebugEnabled())
m_logger.debug("Failed to verify sig policies: " + sig.getId() + " - " + ex);
lerrs.add(new DigiDocException(DigiDocException.ERR_POLICY_NONE,
"Failed to verify sig policies: " + sig.getId() + " - " + ex, null));
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;
}
}