/* DigiDoc4J library * * This software is released under either the GNU Library General Public * License (see LICENSE.LGPL). * * Note that the only valid version of the LGPL license as far as this * project is concerned is the original GNU Library General Public License * Version 2.1, February 1999 */ package org.digidoc4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.digidoc4j.exceptions.ConfigurationException; import org.digidoc4j.exceptions.DigiDoc4JException; import org.digidoc4j.impl.ConfigurationSingeltonHolder; import org.digidoc4j.impl.bdoc.tsl.TslManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.concurrent.ExecutorService; import static java.util.Arrays.asList; import static org.apache.commons.lang.StringUtils.isNotBlank; import static org.apache.commons.lang.StringUtils.isNotEmpty; import eu.europa.esig.dss.client.http.Protocol; /** * Possibility to create custom configurations for {@link Container} implementations. * <p> * It is possible to get the default Configuration object used in all containers by using * {@link Configuration#getInstance()}. This will return a singelton Configuration object used by default * if no configuration is provided. * </p> * <p/> * You can specify the configuration mode, either {@link Configuration.Mode#TEST} or {@link Configuration.Mode#PROD} * configuration. Default is {@link Configuration.Mode#PROD}. * <p/> * <p> * It is a good idea to use only a single configuration object for all the containers so the operation times would be faster. * </p> * It is also possible to set the mode using the System property. Setting the property "digidoc4j.mode" to "TEST" forces * the default mode to {@link Configuration.Mode#TEST} mode * <p/> * Configurations will be loaded from a file. The file must be in yaml format.<p/> * <p/> * <H3>Required entries of the configuration file:</H3> * The configuration file must contain one or more Certificate Authorities under the heading DIGIDOC_CAS * similar to following format (values are examples only):<br> * <p> * <pre> * DIGIDOC_CAS: * - DIGIDOC_CA: * NAME: CA name * TRADENAME: Tradename * CERTS: * - jar://certs/cert1.crt * - jar://certs/cert2.crt * OCSPS: * </pre> * <p/> * Each DIGIDOC_CA entry must contain one or more OCSP certificates under the heading "OCSPS" * similar to following format (values are examples only):<br> * <p> * <pre> * - OCSP: * CA_CN: your certificate authority common name * CA_CERT: jar://your ca_cn.crt * CN: your common name * CERTS: * - jar://certs/Your first OCSP Certifications file.crt * - jar://certs/Your second OCSP Certifications file.crt * URL: http://ocsp.test.test * </pre> * <p>All entries must exist and be valid. Under CERTS must be at least one entry.</p> * <p/> * <p/> * <H3>Optional entries of the configuration file:</H3> * <ul> * <li>CANONICALIZATION_FACTORY_IMPL: Canonicalization factory implementation.<br> * Default value: {@value #DEFAULT_FACTORY_IMPLEMENTATION}</li> * <li>CONNECTION_TIMEOUT: TSL HTTP Connection timeout (milliseconds).<br> * Default value: 1000 </li> * <li>DIGIDOC_FACTORY_IMPL: Factory implementation.<br> * Default value: {@value #DEFAULT_FACTORY_IMPLEMENTATION}</li> * <li>DATAFILE_HASHCODE_MODE: Is the datafile containing only a hash (not the actual file)? * Allowed values: true, false.<br> * Default value: {@value #DEFAULT_DATAFILE_HASHCODE_MODE}</li> * <li>DIGIDOC_DF_CACHE_DIR: Temporary directory to use. Default: uses system's default temporary directory</li> * <li>DIGIDOC_MAX_DATAFILE_CACHED: Maximum datafile size that will be cached in MB. * Must be numeric. Set to -1 to cache all files. Set to 0 to prevent caching for all files<br> * Default value: {@value #DEFAULT_MAX_DATAFILE_CACHED}</li> * <li>DIGIDOC_NOTARY_IMPL: Notary implementation.<br> * Default value: {@value #DEFAULT_NOTARY_IMPLEMENTATION}</li> * <li>DIGIDOC_OCSP_SIGN_CERT_SERIAL: OCSP Signing certificate serial number</li> * <li>DIGIDOC_SECURITY_PROVIDER: Security provider.<br> * Default value: {@value #DEFAULT_SECURITY_PROVIDER}</li> * <li>DIGIDOC_SECURITY_PROVIDER_NAME: Name of the security provider.<br> * Default value: {@value #DEFAULT_SECURITY_PROVIDER_NAME}</li> * <li>DIGIDOC_TSLFAC_IMPL: TSL Factory implementation.<br> * Default value: {@value #DEFAULT_TSL_FACTORY_IMPLEMENTATION}</li> * <li>DIGIDOC_USE_LOCAL_TSL: Use local TSL? Allowed values: true, false<br> * Default value: {@value #DEFAULT_USE_LOCAL_TSL}</li> * <li>KEY_USAGE_CHECK: Should key usage be checked? Allowed values: true, false.<br> * Default value: {@value #DEFAULT_KEY_USAGE_CHECK}</li> * <li>DIGIDOC_PKCS12_CONTAINER: OCSP access certificate file</li> * <li>DIGIDOC_PKCS12_PASSWD: OCSP access certificate password</li> * <li>OCSP_SOURCE: Online Certificate Service Protocol source</li> * <li>SIGN_OCSP_REQUESTS: Should OCSP requests be signed? Allowed values: true, false</li> * <li>TSL_LOCATION: TSL Location</li> * <li>TSP_SOURCE: Time Stamp Protocol source address</li> * <li>VALIDATION_POLICY: Validation policy source file</li> * <li>TSL_KEYSTORE_LOCATION: keystore location for tsl signing certificates</li> * <li>TSL_KEYSTORE_PASSWORD: keystore password for the keystore in TSL_KEYSTORE_LOCATION</li> * <li>TSL_CACHE_EXPIRATION_TIME: TSL cache expiration time in milliseconds</li> * <li>TRUSTED_TERRITORIES: list of countries and territories to trust and load TSL certificates (for example, EE, LV, FR)</li> * <li>HTTP_PROXY_HOST: network proxy host name</li> * <li>HTTP_PROXY_PORT: network proxy port</li> * <li>HTTP_PROXY_USER: network proxy user (for basic auth proxy)</li> * <li>HTTP_PROXY_PASSWORD: network proxy password (for basic auth proxy)</li> * <li>SSL_KEYSTORE_PATH: SSL KeyStore path</li> * <li>SSL_KEYSTORE_TYPE: SSL KeyStore type (default is "jks")</li> * <li>SSL_KEYSTORE_PASSWORD: SSL KeyStore password (default is an empty string)</li> * <li>SSL_TRUSTSTORE_PATH: SSL TrustStore path</li> * <li>SSL_TRUSTSTORE_TYPE: SSL TrustStore type (default is "jks")</li> * <li>SSL_TRUSTSTORE_PASSWORD: SSL TrustStore password (default is an empty string)</li> * </ul> */ public class Configuration implements Serializable { private static final Logger logger = LoggerFactory.getLogger(Configuration.class); private static final int ONE_SECOND = 1000; private static final long ONE_DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; private static final int ONE_DAY_IN_MINUTES = 24 * 60; public static final long ONE_MB_IN_BYTES = 1048576; public static final String DEFAULT_CANONICALIZATION_FACTORY_IMPLEMENTATION = "ee.sk.digidoc.c14n.TinyXMLCanonicalizer"; public static final String DEFAULT_SECURITY_PROVIDER = "org.bouncycastle.jce.provider.BouncyCastleProvider"; public static final String DEFAULT_SECURITY_PROVIDER_NAME = "BC"; public static final String DEFAULT_NOTARY_IMPLEMENTATION = "ee.sk.digidoc.factory.BouncyCastleNotaryFactory"; public static final String DEFAULT_TSL_FACTORY_IMPLEMENTATION = "ee.sk.digidoc.tsl.DigiDocTrustServiceFactory"; public static final String DEFAULT_FACTORY_IMPLEMENTATION = "ee.sk.digidoc.factory.SAXDigiDocFactory"; public static final String DEFAULT_KEY_USAGE_CHECK = "false"; public static final String DEFAULT_DATAFILE_HASHCODE_MODE = "false"; public static final String DEFAULT_USE_LOCAL_TSL = "true"; public static final String DEFAULT_MAX_DATAFILE_CACHED = "-1"; public static final String DEFAULT_TSL_KEYSTORE_LOCATION = "keystore/keystore.jks"; public static final List<String> DEFAULT_TRUESTED_TERRITORIES = Arrays.asList("AT", "BE", "BG", "CY", "CZ",/*"DE",*/"DK", "EE", "ES", "FI", "FR", "GR", "HU",/*"HR",*/"IE", "IS", "IT", "LT", "LU", "LV", "LI", "MT","NO","NL", "PL", "PT", "RO", "SE", "SI", "SK", "UK"); public static final long CACHE_ALL_DATA_FILES = -1; public static final long CACHE_NO_DATA_FILES = 0; public static final String TEST_OCSP_URL = "http://demo.sk.ee/ocsp"; public static final String PROD_OCSP_URL = "http://ocsp.sk.ee/"; private static final String SIGN_OCSP_REQUESTS = "SIGN_OCSP_REQUESTS"; private static final String OCSP_PKCS_12_CONTAINER = "DIGIDOC_PKCS12_CONTAINER"; private static final String OCSP_PKCS_12_PASSWD = "DIGIDOC_PKCS12_PASSWD"; private final Mode mode; private LinkedHashMap configurationFromFile; private String configurationInputSourceName; private Hashtable<String, String> jDigiDocConfiguration = new Hashtable<>(); private ArrayList<String> inputSourceParseErrors = new ArrayList<>(); private TslManager tslManager; Map<String, String> configuration = new HashMap<>(); private String httpProxyHost; private Integer httpProxyPort; private String httpProxyUser; private String httpProxyPassword; private List<String> trustedTerritories; private String sslKeystorePath; private String sslKeystoreType; private String sslKeystorePassword; private String sslTruststorePath; private String sslTruststoreType; private String sslTruststorePassword; private transient ExecutorService threadExecutor; /** * Application mode */ public enum Mode { TEST, PROD } /** * Getting the default Configuration object. <br/> * * The default configuration object is a singelton, meaning that all the containers will use the same configuration object. * It is a good idea to use only a single configuration object for all the containers so the operation times would be faster. * * @return default configuration. */ public static Configuration getInstance() { return ConfigurationSingeltonHolder.getInstance(); } private void initDefaultValues() { logger.debug(""); tslManager = new TslManager(this); configuration.put("connectionTimeout", String.valueOf(ONE_SECOND)); configuration.put("socketTimeout", String.valueOf(ONE_SECOND)); configuration.put("tslKeyStorePassword", "digidoc4j-password"); configuration.put("revocationAndTimestampDeltaInMinutes", String.valueOf(ONE_DAY_IN_MINUTES)); configuration.put("tslCacheExpirationTime", String.valueOf(ONE_DAY_IN_MILLISECONDS)); if (mode == Mode.TEST) { configuration.put("tspSource", "http://demo.sk.ee/tsa"); configuration.put("tslLocation", "https://demo.sk.ee/TSL/tl-mp-test-EE.xml"); configuration.put("tslKeyStoreLocation", "keystore/test-keystore.jks"); configuration.put("validationPolicy", "conf/test_constraint.xml"); configuration.put("ocspSource", TEST_OCSP_URL); configuration.put(SIGN_OCSP_REQUESTS, "false"); jDigiDocConfiguration.put(SIGN_OCSP_REQUESTS, "false"); } else { configuration.put("tspSource", "http://tsa.sk.ee"); configuration.put("tslLocation", "https://ec.europa.eu/information_society/policy/esignature/trusted-list/tl-mp.xml"); configuration.put("tslKeyStoreLocation", DEFAULT_TSL_KEYSTORE_LOCATION); configuration.put("validationPolicy", "conf/constraint.xml"); configuration.put("ocspSource", PROD_OCSP_URL); configuration.put(SIGN_OCSP_REQUESTS, "false"); jDigiDocConfiguration.put(SIGN_OCSP_REQUESTS, "false"); trustedTerritories = DEFAULT_TRUESTED_TERRITORIES; } logger.debug(mode + "configuration:\n" + configuration); loadInitialConfigurationValues(); } /** * Are requirements met for signing OCSP certificate? * * @return value indicating if requirements are met */ public boolean isOCSPSigningConfigurationAvailable() { boolean available = isNotEmpty(getOCSPAccessCertificateFileName()) && getOCSPAccessCertificatePassword().length != 0; logger.debug("Is OCSP signing configuration available: " + available); return available; } /** * Get OCSP access certificate filename * * @return filename for the OCSP access certificate */ public String getOCSPAccessCertificateFileName() { logger.debug("Loading OCSPAccessCertificateFile"); String ocspAccessCertificateFile = getConfigurationParameter("OCSPAccessCertificateFile"); logger.debug("OCSPAccessCertificateFile " + ocspAccessCertificateFile + " loaded"); return ocspAccessCertificateFile; } /** * Get OSCP access certificate password * * @return password */ public char[] getOCSPAccessCertificatePassword() { logger.debug("Loading OCSPAccessCertificatePassword"); char[] result = {}; String password = getConfigurationParameter("OCSPAccessCertificatePassword"); if (isNotEmpty(password)) { result = password.toCharArray(); } logger.debug("OCSPAccessCertificatePassword loaded"); return result; } /** * Set OCSP access certificate filename * * @param fileName filename for the OCSP access certficate */ public void setOCSPAccessCertificateFileName(String fileName) { logger.debug("Setting OCSPAccessCertificateFileName: " + fileName); setConfigurationParameter("OCSPAccessCertificateFile", fileName); jDigiDocConfiguration.put(OCSP_PKCS_12_CONTAINER, fileName); logger.debug("OCSPAccessCertificateFile is set"); } /** * Set OCSP access certificate password * * @param password password to set */ public void setOCSPAccessCertificatePassword(char[] password) { logger.debug("Setting OCSPAccessCertificatePassword: "); String value = String.valueOf(password); setConfigurationParameter("OCSPAccessCertificatePassword", value); jDigiDocConfiguration.put(OCSP_PKCS_12_PASSWD, value); logger.debug("OCSPAccessCertificatePassword is set"); } public void setSignOCSPRequests(boolean shouldSignOcspRequests) { logger.debug("Should sign OCSP requests: " + shouldSignOcspRequests); String valueToSet = String.valueOf(shouldSignOcspRequests); setConfigurationParameter(SIGN_OCSP_REQUESTS, valueToSet); jDigiDocConfiguration.put(SIGN_OCSP_REQUESTS, valueToSet); } /** * Create new configuration */ public Configuration() { mode = ("TEST".equalsIgnoreCase(System.getProperty("digidoc4j.mode")) ? Mode.TEST : Mode.PROD); loadConfiguration("digidoc4j.yaml"); initDefaultValues(); logger.info("Configuration loaded for " + mode + " mode"); } /** * Create new configuration for application mode specified * * @param mode Application mode */ public Configuration(Mode mode) { logger.debug("Mode: " + mode); this.mode = mode; loadConfiguration("digidoc4j.yaml"); initDefaultValues(); logger.info("Configuration loaded for " + mode + " mode"); } /** * Add configuration settings from a stream. After loading closes stream. * * @param stream Input stream * @return configuration hashtable */ public Hashtable<String, String> loadConfiguration(InputStream stream) { configurationInputSourceName = "stream"; return loadConfigurationSettings(stream); } /** * Add configuration settings from a file * * @param file File name * @return configuration hashtable */ public Hashtable<String, String> loadConfiguration(String file) { logger.info("Loading configuration from file " + file); configurationInputSourceName = file; InputStream resourceAsStream = null; try { resourceAsStream = new FileInputStream(file); } catch (FileNotFoundException e) { logger.info("Configuration file " + file + " not found. Trying to search from jar file."); } if (resourceAsStream == null) { resourceAsStream = getResourceAsStream(file); } return loadConfigurationSettings(resourceAsStream); } private Hashtable<String, String> loadConfigurationSettings(InputStream stream) { configurationFromFile = new LinkedHashMap(); Yaml yaml = new Yaml(); try { configurationFromFile = (LinkedHashMap) yaml.load(stream); } catch (Exception e) { ConfigurationException exception = new ConfigurationException("Configuration from " + configurationInputSourceName + " is not correctly formatted"); logger.error(exception.getMessage()); throw exception; } IOUtils.closeQuietly(stream); return mapToJDigiDocConfiguration(); } private InputStream getResourceAsStream(String certFile) { InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(certFile); if (resourceAsStream == null) { String message = "File " + certFile + " not found in classpath."; logger.error(message); throw new ConfigurationException(message); } return resourceAsStream; } /** * Returns configuration needed for JDigiDoc library * * @return configuration values */ public Hashtable<String, String> getJDigiDocConfiguration() { loadCertificateAuthoritiesAndCertificates(); reportFileParseErrors(); return jDigiDocConfiguration; } /** * Gives back all configuration parameters needed for jDigiDoc * * @return Hashtable containing jDigiDoc configuration parameters */ private Hashtable<String, String> mapToJDigiDocConfiguration() { logger.debug("loading JDigiDoc configuration"); inputSourceParseErrors = new ArrayList<>(); loadInitialConfigurationValues(); reportFileParseErrors(); return jDigiDocConfiguration; } private void loadCertificateAuthoritiesAndCertificates() { logger.debug(""); @SuppressWarnings("unchecked") ArrayList<LinkedHashMap> digiDocCAs = (ArrayList<LinkedHashMap>) configurationFromFile.get("DIGIDOC_CAS"); if (digiDocCAs == null) { String errorMessage = "Empty or no DIGIDOC_CAS entry"; logError(errorMessage); return; } int numberOfDigiDocCAs = digiDocCAs.size(); jDigiDocConfiguration.put("DIGIDOC_CAS", String.valueOf(numberOfDigiDocCAs)); for (int i = 0; i < numberOfDigiDocCAs; i++) { String caPrefix = "DIGIDOC_CA_" + (i + 1); LinkedHashMap digiDocCA = (LinkedHashMap) digiDocCAs.get(i).get("DIGIDOC_CA"); if (digiDocCA == null) { String errorMessage = "Empty or no DIGIDOC_CA for entry " + (i + 1); logError(errorMessage); } else { loadCertificateAuthorityCerts(digiDocCA, caPrefix); loadOCSPCertificates(digiDocCA, caPrefix); } } } private void logError(String errorMessage) { logger.error(errorMessage); inputSourceParseErrors.add(errorMessage); } private void reportFileParseErrors() { logger.debug(""); if (inputSourceParseErrors.size() > 0) { StringBuilder errorMessage = new StringBuilder(); errorMessage.append("Configuration from "); errorMessage.append(configurationInputSourceName); errorMessage.append(" contains error(s):\n"); for (String message : inputSourceParseErrors) { errorMessage.append(message); } throw new ConfigurationException(errorMessage.toString()); } } private void loadInitialConfigurationValues() { logger.debug(""); setJDigiDocConfigurationValue("DIGIDOC_SECURITY_PROVIDER", DEFAULT_SECURITY_PROVIDER); setJDigiDocConfigurationValue("DIGIDOC_SECURITY_PROVIDER_NAME", DEFAULT_SECURITY_PROVIDER_NAME); setJDigiDocConfigurationValue("KEY_USAGE_CHECK", DEFAULT_KEY_USAGE_CHECK); setJDigiDocConfigurationValue("DIGIDOC_OCSP_SIGN_CERT_SERIAL", ""); setJDigiDocConfigurationValue("DATAFILE_HASHCODE_MODE", DEFAULT_DATAFILE_HASHCODE_MODE); setJDigiDocConfigurationValue("CANONICALIZATION_FACTORY_IMPL", DEFAULT_CANONICALIZATION_FACTORY_IMPLEMENTATION); setJDigiDocConfigurationValue("DIGIDOC_MAX_DATAFILE_CACHED", DEFAULT_MAX_DATAFILE_CACHED); setJDigiDocConfigurationValue("DIGIDOC_USE_LOCAL_TSL", DEFAULT_USE_LOCAL_TSL); setJDigiDocConfigurationValue("DIGIDOC_NOTARY_IMPL", DEFAULT_NOTARY_IMPLEMENTATION); setJDigiDocConfigurationValue("DIGIDOC_TSLFAC_IMPL", DEFAULT_TSL_FACTORY_IMPLEMENTATION); setJDigiDocConfigurationValue("DIGIDOC_OCSP_RESPONDER_URL", getOcspSource()); setJDigiDocConfigurationValue("DIGIDOC_FACTORY_IMPL", DEFAULT_FACTORY_IMPLEMENTATION); setJDigiDocConfigurationValue("DIGIDOC_DF_CACHE_DIR", null); setConfigurationValue("TSL_LOCATION", "tslLocation"); setConfigurationValue("TSP_SOURCE", "tspSource"); setConfigurationValue("VALIDATION_POLICY", "validationPolicy"); setConfigurationValue("OCSP_SOURCE", "ocspSource"); setConfigurationValue(OCSP_PKCS_12_CONTAINER, "OCSPAccessCertificateFile"); setConfigurationValue(OCSP_PKCS_12_PASSWD, "OCSPAccessCertificatePassword"); setConfigurationValue("CONNECTION_TIMEOUT", "connectionTimeout"); setConfigurationValue("SOCKET_TIMEOUT", "socketTimeout"); setConfigurationValue(SIGN_OCSP_REQUESTS, SIGN_OCSP_REQUESTS); setConfigurationValue("TSL_KEYSTORE_LOCATION", "tslKeyStoreLocation"); setConfigurationValue("TSL_KEYSTORE_PASSWORD", "tslKeyStorePassword"); setConfigurationValue("TSL_CACHE_EXPIRATION_TIME", "tslCacheExpirationTime"); setConfigurationValue("REVOCATION_AND_TIMESTAMP_DELTA_IN_MINUTES", "revocationAndTimestampDeltaInMinutes"); setJDigiDocConfigurationValue(SIGN_OCSP_REQUESTS, Boolean.toString(hasToBeOCSPRequestSigned())); setJDigiDocConfigurationValue(OCSP_PKCS_12_CONTAINER, getOCSPAccessCertificateFileName()); initOcspAccessCertPasswordForJDigidoc(); httpProxyHost = getParameterFromFile("HTTP_PROXY_HOST"); httpProxyPort = getIntParameterFromFile("HTTP_PROXY_PORT"); httpProxyUser = getParameterFromFile("HTTP_PROXY_USER"); httpProxyPassword = getParameterFromFile("HTTP_PROXY_PASSWORD"); sslKeystorePath = getParameterFromFile("SSL_KEYSTORE_PATH"); sslKeystoreType = getParameterFromFile("SSL_KEYSTORE_TYPE"); sslKeystorePassword = getParameterFromFile("SSL_KEYSTORE_PASSWORD"); sslTruststorePath = getParameterFromFile("SSL_TRUSTSTORE_PATH"); sslTruststoreType = getParameterFromFile("SSL_TRUSTSTORE_TYPE"); sslTruststorePassword = getParameterFromFile("SSL_TRUSTSTORE_PASSWORD"); updateTrustedTerritories(); } private void updateTrustedTerritories() { List<String> territories = getStringListParameterFromFile("TRUSTED_TERRITORIES"); if(territories != null) { trustedTerritories = territories; } } private String getParameterFromFile(String key) { if (configurationFromFile == null) { return null; } Object fileValue = configurationFromFile.get(key); if (fileValue == null) { return null; } String value = fileValue.toString(); if(valueIsAllowed(key, value)) { return value; } return null; } private Integer getIntParameterFromFile(String key) { String value = getParameterFromFile(key); if(value == null) { return null; } return new Integer(value); } private List<String> getStringListParameterFromFile(String key) { String value = getParameterFromFile(key); if(value == null) { return null; } return Arrays.asList(value.split("\\s*,\\s*")); //Split by comma and trim whitespace } private void setConfigurationValue(String fileKey, String configurationKey) { if (configurationFromFile == null) return; Object fileValue = configurationFromFile.get(fileKey); if (fileValue != null) { configuration.put(configurationKey, fileValue.toString()); } } private void setJDigiDocConfigurationValue(String key, String defaultValue) { String value = defaultIfNull(key, defaultValue); if (value != null) { jDigiDocConfiguration.put(key, value); } } /** * Enables big files support. Sets limit in MB when handling files are creating temporary file for streaming in * container creation and adding data files. * <p/> * Used by DigiDoc4J and by JDigiDoc. * * @param maxFileSizeCachedInMB Maximum size in MB. * @deprecated obnoxious naming. Use {@link Configuration#setMaxFileSizeCachedInMemoryInMB(long)} instead. */ @Deprecated public void enableBigFilesSupport(long maxFileSizeCachedInMB) { logger.debug("Set maximum datafile cached to: " + maxFileSizeCachedInMB); String value = Long.toString(maxFileSizeCachedInMB); if (isValidIntegerParameter("DIGIDOC_MAX_DATAFILE_CACHED", value)) { jDigiDocConfiguration.put("DIGIDOC_MAX_DATAFILE_CACHED", value); } } /** * Sets limit in MB when handling files are creating temporary file for streaming in * container creation and adding data files. * <p/> * Used by DigiDoc4J and by JDigiDoc. * * @param maxFileSizeCachedInMB maximum data file size in MB stored in memory. */ public void setMaxFileSizeCachedInMemoryInMB(long maxFileSizeCachedInMB) { enableBigFilesSupport(maxFileSizeCachedInMB); } /** * @return is big file support enabled * @deprecated obnoxious naming. Use {@link Configuration#storeDataFilesOnlyInMemory()} instead. */ @Deprecated public boolean isBigFilesSupportEnabled() { return getMaxDataFileCachedInMB() >= 0; } /** * If all the data files should be stored in memory. Default is true (data files are temporarily stored only in memory). * @return true if everything is stored in memory, and false if data is temporarily stored on disk. */ public boolean storeDataFilesOnlyInMemory() { long maxDataFileCachedInMB = getMaxDataFileCachedInMB(); return maxDataFileCachedInMB == -1 || maxDataFileCachedInMB == Long.MAX_VALUE; } /** * Returns configuration item must be OCSP request signed. Reads it from configuration parameter SIGN_OCSP_REQUESTS. * Default value is false for {@link Configuration.Mode#PROD} and false for {@link Configuration.Mode#TEST} * * @return must be OCSP request signed */ public boolean hasToBeOCSPRequestSigned() { String signOcspRequests = getConfigurationParameter(SIGN_OCSP_REQUESTS); return StringUtils.equalsIgnoreCase("true", signOcspRequests); } /** * Get the maximum size of data files to be cached. Used by DigiDoc4J and by JDigiDoc. * * @return Size in MB. if size < 0 no caching is used */ public long getMaxDataFileCachedInMB() { String maxDataFileCached = jDigiDocConfiguration.get("DIGIDOC_MAX_DATAFILE_CACHED"); logger.debug("Maximum datafile cached in MB: " + maxDataFileCached); if (maxDataFileCached == null) return CACHE_ALL_DATA_FILES; return Long.parseLong(maxDataFileCached); } /** * Get the maximum size of data files to be cached. Used by DigiDoc4J and by JDigiDoc. * * @return Size in MB. if size < 0 no caching is used */ public long getMaxDataFileCachedInBytes() { long maxDataFileCachedInMB = getMaxDataFileCachedInMB(); if (maxDataFileCachedInMB == CACHE_ALL_DATA_FILES) { return CACHE_ALL_DATA_FILES; } else { return (maxDataFileCachedInMB * ONE_MB_IN_BYTES); } } private String defaultIfNull(String configParameter, String defaultValue) { logger.debug("Parameter: " + configParameter); if (configurationFromFile == null) return defaultValue; Object value = configurationFromFile.get(configParameter); if (value != null) { return valueIsAllowed(configParameter, value.toString()) ? value.toString() : ""; } String configuredValue = jDigiDocConfiguration.get(configParameter); return configuredValue != null ? configuredValue : defaultValue; } private boolean valueIsAllowed(String configParameter, String value) { logger.debug("Parameter: " + configParameter + ", value: " + value); List<String> mustBeBooleans = asList(SIGN_OCSP_REQUESTS, "KEY_USAGE_CHECK", "DATAFILE_HASHCODE_MODE", "DIGIDOC_USE_LOCAL_TSL"); List<String> mustBeIntegers = asList("DIGIDOC_MAX_DATAFILE_CACHED", "HTTP_PROXY_PORT"); boolean errorFound = false; if (mustBeBooleans.contains(configParameter)) { errorFound = !(isValidBooleanParameter(configParameter, value)); } if (mustBeIntegers.contains(configParameter)) { errorFound = !(isValidIntegerParameter(configParameter, value)) || errorFound; } return (!errorFound); } private boolean isValidBooleanParameter(String configParameter, String value) { if (!("true".equals(value.toLowerCase()) || "false".equals(value.toLowerCase()))) { String errorMessage = "Configuration parameter " + configParameter + " should be set to true or false" + " but the actual value is: " + value + "."; logError(errorMessage); return false; } return true; } private boolean isValidIntegerParameter(String configParameter, String value) { Integer parameterValue; try { parameterValue = Integer.parseInt(value); } catch (Exception e) { String errorMessage = "Configuration parameter " + configParameter + " should have an integer value" + " but the actual value is: " + value + "."; logError(errorMessage); return false; } if (configParameter.equals("DIGIDOC_MAX_DATAFILE_CACHED") && parameterValue < -1) { String errorMessage = "Configuration parameter " + configParameter + " should be greater or equal -1" + " but the actual value is: " + value + "."; logError(errorMessage); return false; } return true; } private void loadOCSPCertificates(LinkedHashMap digiDocCA, String caPrefix) { logger.debug(""); String errorMessage; @SuppressWarnings("unchecked") ArrayList<LinkedHashMap> ocsps = (ArrayList<LinkedHashMap>) digiDocCA.get("OCSPS"); if (ocsps == null) { errorMessage = "No OCSPS entry found or OCSPS entry is empty. Configuration from: " + configurationInputSourceName; logError(errorMessage); return; } int numberOfOCSPCertificates = ocsps.size(); jDigiDocConfiguration.put(caPrefix + "_OCSPS", String.valueOf(numberOfOCSPCertificates)); for (int i = 1; i <= numberOfOCSPCertificates; i++) { String prefix = caPrefix + "_OCSP" + i; LinkedHashMap ocsp = ocsps.get(i - 1); List<String> entries = asList("CA_CN", "CA_CERT", "CN", "URL"); for (String entry : entries) { if (!loadOCSPCertificateEntry(entry, ocsp, prefix)) { errorMessage = "OCSPS list entry " + i + " does not have an entry for " + entry + " or the entry is empty\n"; logError(errorMessage); } } if (!getOCSPCertificates(prefix, ocsp)) { errorMessage = "OCSPS list entry " + i + " does not have an entry for CERTS or the entry is empty\n"; logError(errorMessage); } } } private boolean loadOCSPCertificateEntry(String ocspsEntryName, LinkedHashMap ocsp, String prefix) { Object ocspEntry = ocsp.get(ocspsEntryName); if (ocspEntry == null) return false; jDigiDocConfiguration.put(prefix + "_" + ocspsEntryName, ocspEntry.toString()); return true; } @SuppressWarnings("unchecked") private boolean getOCSPCertificates(String prefix, LinkedHashMap ocsp) { ArrayList<String> certificates = (ArrayList<String>) ocsp.get("CERTS"); if (certificates == null) return false; for (int j = 0; j < certificates.size(); j++) { if (j == 0) { jDigiDocConfiguration.put(prefix + "_CERT", certificates.get(0)); } else { jDigiDocConfiguration.put(prefix + "_CERT_" + j, certificates.get(j)); } } return true; } private void loadCertificateAuthorityCerts(LinkedHashMap digiDocCA, String caPrefix) { logger.debug(""); ArrayList<String> certificateAuthorityCerts = getCACertsAsArray(digiDocCA); jDigiDocConfiguration.put(caPrefix + "_NAME", digiDocCA.get("NAME").toString()); jDigiDocConfiguration.put(caPrefix + "_TRADENAME", digiDocCA.get("TRADENAME").toString()); int numberOfCACertificates = certificateAuthorityCerts.size(); jDigiDocConfiguration.put(caPrefix + "_CERTS", String.valueOf(numberOfCACertificates)); for (int i = 0; i < numberOfCACertificates; i++) { String certFile = certificateAuthorityCerts.get(i); jDigiDocConfiguration.put(caPrefix + "_CERT" + (i + 1), certFile); } } @SuppressWarnings("unchecked") private ArrayList<String> getCACertsAsArray(LinkedHashMap digiDocCa) { return (ArrayList<String>) digiDocCa.get("CERTS"); } public String getTslLocation() { String urlString = getConfigurationParameter("tslLocation"); if (!Protocol.isFileUrl(urlString)) return urlString; try { String filePath = new URL(urlString).getPath(); if (!new File(filePath).exists()) { URL resource = getClass().getClassLoader().getResource(filePath); if (resource != null) urlString = resource.toString(); } } catch (MalformedURLException e) { logger.warn(e.getMessage()); } return urlString; } /** * Set the TSL certificate source. * * @param certificateSource TSL certificate source * When certificateSource equals null then getTSL() will load the TSL according to the TSL * location specified . */ public void setTSL(TSLCertificateSource certificateSource) { tslManager.setTsl(certificateSource); } /** * Loads TSL certificates * If configuration mode is TEST then TSL signature is not checked. * * @return TSL source */ public TSLCertificateSource getTSL() { return tslManager.getTsl(); } public boolean shouldValidateTslSignature() { return mode != Mode.TEST; } /** * Set the TSL location. * TSL can be loaded from file (file://) or from web (http://). If file protocol is used then * first try is to locate file from this location if file does not exist then it tries to load * relatively from classpath. * <p/> * Setting new location clears old values * <p/> * Windows wants it in file:DRIVE:/directories/tsl-file.xml format * * @param tslLocation TSL Location to be used */ public void setTslLocation(String tslLocation) { logger.debug("Set TSL location: " + tslLocation); setConfigurationParameter("tslLocation", tslLocation); tslManager.setTsl(null); } /** * Get the TSP Source * * @return TSP Source */ public String getTspSource() { String tspSource = getConfigurationParameter("tspSource"); logger.debug("TSP Source: " + tspSource); return tspSource; } /** * Set HTTP connection timeout * @param connectionTimeout connection timeout in milliseconds */ public void setConnectionTimeout(int connectionTimeout) { logger.debug("Set connection timeout to " + connectionTimeout + " ms"); setConfigurationParameter("connectionTimeout", String.valueOf(connectionTimeout)); } /** * Set HTTP socket timeout * @param socketTimeoutMilliseconds socket timeout in milliseconds */ public void setSocketTimeout(int socketTimeoutMilliseconds) { logger.debug("Set socket timeout to " + socketTimeoutMilliseconds + " ms"); setConfigurationParameter("socketTimeout", String.valueOf(socketTimeoutMilliseconds)); } /** * Get HTTP connection timeout * * @return connection timeout in milliseconds */ public int getConnectionTimeout() { return Integer.parseInt(getConfigurationParameter("connectionTimeout")); } /** * Get HTTP socket timeout * * @return socket timeout in milliseconds */ public int getSocketTimeout() { return Integer.parseInt(getConfigurationParameter("socketTimeout")); } /** * Set the TSP Source * * @param tspSource TSPSource to be used */ public void setTspSource(String tspSource) { logger.debug("Set TSP source: " + tspSource); setConfigurationParameter("tspSource", tspSource); } /** * Get the OCSP Source * * @return OCSP Source */ public String getOcspSource() { String ocspSource = getConfigurationParameter("ocspSource"); logger.debug("OCSP source: " + ocspSource); return ocspSource; } /** * Set the KeyStore Location that holds potential TSL Signing certificates * @param tslKeyStoreLocation KeyStore location to use */ public void setTslKeyStoreLocation(String tslKeyStoreLocation) { logger.debug("Set tsl KeyStore Location: " + tslKeyStoreLocation); setConfigurationParameter("tslKeyStoreLocation", tslKeyStoreLocation); } /** * Get the Location to Keystore that holds potential TSL Signing certificates * @return KeyStore Location */ public String getTslKeyStoreLocation() { String keystoreLocation = getConfigurationParameter("tslKeyStoreLocation"); logger.debug("tsl KeyStore Location: " + keystoreLocation); return keystoreLocation; } /** * Set the password for Keystore that holds potential TSL Signing certificates * @param tslKeyStorePassword Keystore password */ public void setTslKeyStorePassword(String tslKeyStorePassword) { logger.debug("Set tsl KeyStore Password: " + tslKeyStorePassword); setConfigurationParameter("tslKeyStorePassword", tslKeyStorePassword); } /** * Get the password for Keystore that holds potential TSL Signing certificates * @return Tsl Keystore password */ public String getTslKeyStorePassword() { String keystorePassword = getConfigurationParameter("tslKeyStorePassword"); logger.debug("tsl KeyStore Password: " + keystorePassword); return keystorePassword; } /** * Sets the expiration time for TSL cache in milliseconds. * If more time has passed from the cache's creation time time, then a fresh TSL is downloaded and cached, * otherwise a cached copy is used. * * @param cacheExpirationTimeInMilliseconds cache expiration time in milliseconds */ public void setTslCacheExpirationTime(long cacheExpirationTimeInMilliseconds) { logger.debug("Setting TSL cache expiration time in milliseconds: " + cacheExpirationTimeInMilliseconds); setConfigurationParameter("tslCacheExpirationTime", String.valueOf(cacheExpirationTimeInMilliseconds)); } /** * Returns TSL cache expiration time in milliseconds. * * @return TSL cache expiration time in milliseconds. */ public long getTslCacheExpirationTime() { String tslCacheExpirationTime = getConfigurationParameter("tslCacheExpirationTime"); logger.debug("TSL cache expiration time in milliseconds: " + tslCacheExpirationTime); return Long.parseLong(tslCacheExpirationTime); } /** * Set the OCSP source * * @param ocspSource OCSP Source to be used */ public void setOcspSource(String ocspSource) { logger.debug("Set OCSP source: " + ocspSource); setConfigurationParameter("ocspSource", ocspSource); } /** * Get the validation policy * * @return Validation policy */ public String getValidationPolicy() { String validationPolicy = getConfigurationParameter("validationPolicy"); logger.debug("Validation policy: " + validationPolicy); return validationPolicy; } /** * Set the validation policy * * @param validationPolicy Policy to be used */ public void setValidationPolicy(String validationPolicy) { logger.debug("Set validation policy: " + validationPolicy); setConfigurationParameter("validationPolicy", validationPolicy); } public int getRevocationAndTimestampDeltaInMinutes() { String timeDelta = getConfigurationParameter("revocationAndTimestampDeltaInMinutes"); logger.debug("Revocation and timestamp delta in minutes: " + timeDelta); return Integer.parseInt(timeDelta); } public void setRevocationAndTimestampDeltaInMinutes(int timeInMinutes) { logger.debug("Set revocation and timestamp delta in minutes: " + timeInMinutes); setConfigurationParameter("revocationAndTimestampDeltaInMinutes", String.valueOf(timeInMinutes)); } public String getHttpProxyHost() { return httpProxyHost; } /** * Set HTTP network proxy host. * @param httpProxyHost http proxy host. */ public void setHttpProxyHost(String httpProxyHost) { this.httpProxyHost = httpProxyHost; } public Integer getHttpProxyPort() { return httpProxyPort; } /** * Set HTTP network proxy port. */ public void setHttpProxyPort(int httpProxyPort) { this.httpProxyPort = httpProxyPort; } /** * Set HTTP network proxy user name. * @param httpProxyUser username. */ public void setHttpProxyUser(String httpProxyUser) { this.httpProxyUser = httpProxyUser; } public String getHttpProxyUser() { return httpProxyUser; } /** * Set HTTP network proxy password. * @param httpProxyPassword password. */ public void setHttpProxyPassword(String httpProxyPassword) { this.httpProxyPassword = httpProxyPassword; } public String getHttpProxyPassword() { return httpProxyPassword; } public boolean isNetworkProxyEnabled() { return httpProxyPort != null && isNotBlank(httpProxyHost); } public boolean isSslConfigurationEnabled() { return sslKeystorePath != null && isNotBlank(sslKeystorePath); } /** * Set SSL KeyStore path. * @param sslKeystorePath path to a file */ public void setSslKeystorePath(String sslKeystorePath) { this.sslKeystorePath = sslKeystorePath; } /** * Get SSL KeyStore path. * @return path to a file */ public String getSslKeystorePath() { return sslKeystorePath; } /** * Set SSL KeyStore type. Default is "jks". * @param sslKeystoreType type. */ public void setSslKeystoreType(String sslKeystoreType) { this.sslKeystoreType = sslKeystoreType; } /** * Get SSL KeyStore type. * @return type. */ public String getSslKeystoreType() { return sslKeystoreType; } /** * Set SSL KeyStore password. Default is an empty string. * @param sslKeystorePassword password. */ public void setSslKeystorePassword(String sslKeystorePassword) { this.sslKeystorePassword = sslKeystorePassword; } public String getSslKeystorePassword() { return sslKeystorePassword; } /** * Set SSL TrustStore path. * @param sslTruststorePath path to a file. */ public void setSslTruststorePath(String sslTruststorePath) { this.sslTruststorePath = sslTruststorePath; } /** * Get SSL TrustStore path. * @return path to a file. */ public String getSslTruststorePath() { return sslTruststorePath; } /** * Set SSL TrustStore type. Default is "jks". * @param sslTruststoreType type. */ public void setSslTruststoreType(String sslTruststoreType) { this.sslTruststoreType = sslTruststoreType; } /** * Get SSL TrustStore type. * @return type. */ public String getSslTruststoreType() { return sslTruststoreType; } /** * Set SSL TrustStore password. Default is an empty string. * @param sslTruststorePassword password. */ public void setSslTruststorePassword(String sslTruststorePassword) { this.sslTruststorePassword = sslTruststorePassword; } public String getSslTruststorePassword() { return sslTruststorePassword; } public void setThreadExecutor(ExecutorService threadExecutor) { this.threadExecutor = threadExecutor; } public ExecutorService getThreadExecutor() { return threadExecutor; } /** * Set countries and territories (2 letter country codes) whom to trust and accept certificates. * <p/> * It is possible accept signatures (and certificates) only from particular countries by filtering * trusted territories. Only the TSL (and certificates) from those countries are then downloaded and * others are skipped. * <p/> * For example, it is possible to trust signatures only from these three countries: Estonia, Latvia and France, * and skip all other countries: "EE", "LV", "FR". * * @param trustedTerritories list of 2 letter country codes. */ public void setTrustedTerritories(String... trustedTerritories) { this.trustedTerritories = Arrays.asList(trustedTerritories); } public List<String> getTrustedTerritories() { return trustedTerritories; } private void setConfigurationParameter(String key, String value) { logger.debug("Key: " + key + ", value: " + value); configuration.put(key, value); } private String getConfigurationParameter(String key) { logger.debug("Key: " + key); String value = configuration.get(key); logger.debug("Value: " + value); return value; } /** * @return true when configuration is Configuration.Mode.TEST * @see Configuration.Mode#TEST */ public boolean isTest() { boolean isTest = mode == Mode.TEST; logger.debug("Is test: " + isTest); return isTest; } /** * Clones configuration * * @return new configuration object */ public Configuration copy() { ObjectOutputStream oos = null; ObjectInputStream ois = null; Configuration copyConfiguration = null; // deep copy ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { oos = new ObjectOutputStream(bos); oos.writeObject(this); oos.flush(); ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bin); copyConfiguration = (Configuration) ois.readObject(); } catch (Exception e) { throw new DigiDoc4JException(e); } finally { IOUtils.closeQuietly(oos); IOUtils.closeQuietly(ois); IOUtils.closeQuietly(bos); } return copyConfiguration; } private void initOcspAccessCertPasswordForJDigidoc() { char[] ocspAccessCertificatePassword = getOCSPAccessCertificatePassword(); if(ocspAccessCertificatePassword != null && ocspAccessCertificatePassword.length > 0) { setJDigiDocConfigurationValue(OCSP_PKCS_12_PASSWD, String.valueOf(ocspAccessCertificatePassword)); } } }