FileTraceEncryptConfig.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it).
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package org.openspcoop2.pdd.logger.filetrace;

import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.certificate.KeyStore;
import org.openspcoop2.utils.certificate.KeystoreType;
import org.openspcoop2.utils.certificate.hsm.HSMManager;
import org.openspcoop2.utils.certificate.hsm.HSMUtils;
import org.openspcoop2.utils.properties.PropertiesReader;
import org.openspcoop2.utils.security.JwtHeaders;

/**     
 * FileTraceEncryptConfig
 *
 * @author Poli Andrea (poli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class FileTraceEncryptConfig {

	private String name;
	private String encryptionEngine;
	
	private KeystoreType  keystoreType;
	private String keystoreHsmType;
	private String keystorePath;
	private String keystorePassword;
	
	private String keyPath;
	private String keyAlgorithm;
	private String keyAlias;
	private String keyPassword;
	private String keyId;
	private boolean keyWrap = false;
	
	private String contentAlgorithm;
	
	private String javaEncoding;

	private boolean joseIncludeCert;
	private boolean joseIncludePublicKey;
	private boolean joseIncludeKeyId;
	private boolean joseIncludeCertSha1;
	private boolean joseIncludeCertSha256;
	
	private String ksmId;
	private Map<String, String> ksmInput;
	
	private static final String ENCRYPTIONT_ENGINE_JAVA = "java";
	private static final String ENCRYPTIONT_ENGINE_JOSE = "jose";
	
	private static final String JAVA_ENCODING_BASE64 = "base64";
	private static final String JAVA_ENCODING_HEX = "hex";
	
	private static final String PREFIX_ENCRYPT = "encrypt.";
	private static final String DEBUG_PREFIX = "Property '"+PREFIX_ENCRYPT;
	private static final String UNDEFINED = " undefined";
	
	private static final String MODE = ".mode";
	private static final String KEYSTORE_TYPE = ".keystore.type";
	private static final String KEYSTORE_PATH = ".keystore.path";
	private static final String KEYSTORE_PASSWORD = ".keystore.password";
	private static final String KEY_PATH = ".key.path";
	private static final String KEY_ALGORITHM = ".key.algorithm";
	private static final String KEY_ALIAS = ".key.alias";
	private static final String KEY_PASSWORD = ".key.password";
	private static final String KEY_ID = ".key.id";
	private static final String KEY_WRAP = ".key.wrap";
	
	private static final String CONTENT_ALGORITHM = ".algorithm";
	
	private static final String JAVA_ENCODING = ".encoding";

	private static final String JOSE_INCLUDE_CERT = ".include.cert";
	private static final String JOSE_INCLUDE_PUBLIC_KEY = ".include.public.key";
	private static final String JOSE_INCLUDE_KEY_ID = ".include.key.id";
	private static final String JOSE_INCLUDE_CERT_SHA1 = ".include.cert.sha1";
	private static final String JOSE_INCLUDE_CERT_SHA256 = ".include.cert.sha256";
	
	private static final String JOSE_KSM = ".ksm";
	private static final String JOSE_KSM_PARAM = ".ksm.param.";

	
	public static Map<String, FileTraceEncryptConfig> parse(PropertiesReader reader) throws UtilsException{
		
		
		Properties propertiesMap = reader.readProperties(PREFIX_ENCRYPT);
		
		Map<String, FileTraceEncryptConfig> result = new HashMap<>();
		
		List<String> keys = new ArrayList<>();
		if(propertiesMap!=null && !propertiesMap.isEmpty()) {
			for (Object s : propertiesMap.keySet()) {
				String key = (String) s;
				if(key.endsWith(MODE) && key.length()>MODE.length()) {
					String keyMode = key.substring(0,key.length()-MODE.length());
					if(keys.contains(keyMode)) {
						throw new UtilsException(DEBUG_PREFIX+key+"' already defined");
					}
					keys.add(keyMode);
				}
			}
		}
		
		parse(result, propertiesMap, keys);
		
		return result;
	}
	private static void parse(Map<String, FileTraceEncryptConfig> result, Properties propertiesMap, List<String> keys) throws UtilsException {
		if(propertiesMap!=null && !keys.isEmpty()) {
			for (String encMode : keys) {
				FileTraceEncryptConfig c = new FileTraceEncryptConfig();
				c.name = encMode;
				
				parseEngine(encMode, propertiesMap, c);
				
				parseKestore(encMode, propertiesMap, c);
				
				parseKsm(encMode, propertiesMap, c);
				
				result.put(encMode, c);
			}
		}
	}
	private static void parseEngine(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c) throws UtilsException {
		String modePName = encMode+MODE;
		c.encryptionEngine = propertiesMap.getProperty(modePName);
		if(!ENCRYPTIONT_ENGINE_JAVA.equals(c.encryptionEngine) &&
				!ENCRYPTIONT_ENGINE_JOSE.equals(c.encryptionEngine)) {
			throw new UtilsException(DEBUG_PREFIX+modePName+"' with unsupported engine mode '"+c.encryptionEngine+"'");
		}
		
		String algoPName = encMode+CONTENT_ALGORITHM;
		String algo = propertiesMap.getProperty(algoPName);
		if(algo==null || StringUtils.isEmpty(algo.trim())) {
			throw new UtilsException(DEBUG_PREFIX+algoPName+"'"+UNDEFINED);
		}
		c.contentAlgorithm = algo.trim();
		
		if(ENCRYPTIONT_ENGINE_JAVA.equals(c.encryptionEngine)) {	
			String encodingPName = encMode+JAVA_ENCODING;
			String encoding = propertiesMap.getProperty(encodingPName);
			if(encoding==null || StringUtils.isEmpty(encoding.trim())) {
				throw new UtilsException(DEBUG_PREFIX+encodingPName+"'"+UNDEFINED);
			}
			c.javaEncoding = encoding.trim();
			if(!JAVA_ENCODING_BASE64.equals(c.javaEncoding) &&
					!JAVA_ENCODING_HEX.equals(c.javaEncoding)) {
				throw new UtilsException(DEBUG_PREFIX+encodingPName+"' with unsupported encryption mode '"+c.javaEncoding+"'");
			}
			
			String keyWrapPName = encMode+KEY_WRAP;
			String keyWrap = propertiesMap.getProperty(keyWrapPName);
			if(keyWrap!=null && StringUtils.isNotEmpty(keyWrap.trim())) {
				c.keyWrap = "true".equalsIgnoreCase(keyWrap);
			}
		}
		else if(ENCRYPTIONT_ENGINE_JOSE.equals(c.encryptionEngine)) {
			parseEngineJose(encMode, propertiesMap, c);
		}
	}
	private static void parseEngineJose(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c)  {
		c.joseIncludeCert = parseEngineJoseProperty(encMode+JOSE_INCLUDE_CERT, propertiesMap);
		c.joseIncludePublicKey = parseEngineJoseProperty(encMode+JOSE_INCLUDE_PUBLIC_KEY, propertiesMap);
		c.joseIncludeKeyId = parseEngineJoseProperty(encMode+JOSE_INCLUDE_KEY_ID, propertiesMap);
		c.joseIncludeCertSha1 = parseEngineJoseProperty(encMode+JOSE_INCLUDE_CERT_SHA1, propertiesMap);
		c.joseIncludeCertSha256 = parseEngineJoseProperty(encMode+JOSE_INCLUDE_CERT_SHA256, propertiesMap);
		
		String keyIdPName = encMode+KEY_ID;
		String keyId = propertiesMap.getProperty(keyIdPName);
		if(keyId!=null) {
			c.keyId = keyId.trim();
		}
	}
	private static boolean parseEngineJoseProperty(String pName, Properties propertiesMap) {
		String v = propertiesMap.getProperty(pName);
		return "true".equalsIgnoreCase(v);
	}
	private static void parseKestore(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c) throws UtilsException {
		String keystoreTypePName = encMode+KEYSTORE_TYPE;
		String keystoreType = propertiesMap.getProperty(keystoreTypePName);
		if(keystoreType==null || StringUtils.isEmpty(keystoreType.trim())) {
			throw new UtilsException(DEBUG_PREFIX+keystoreTypePName+"'"+UNDEFINED);
		}
		keystoreType = keystoreType.trim();
		c.keystoreType = KeystoreType.toEnumFromName(keystoreType);
		if(c.keystoreType==null) {
			if(keystoreType!=null) {
				HSMManager hsmManager = HSMManager.getInstance();
				if(hsmManager.existsKeystoreType(keystoreType)) {
					c.keystoreType = KeystoreType.PKCS11;
					c.keystoreHsmType = keystoreType;
				}
				else {
					throw new UtilsException(DEBUG_PREFIX+keystoreTypePName+"' with unsupported value '"+keystoreType+"'");
				}
			}
			else {
				throw new UtilsException(DEBUG_PREFIX+keystoreTypePName+"'"+UNDEFINED);
			}
		}
		switch (c.keystoreType) {
		case JKS:
		case PKCS12:
		case PKCS11:
		case JWK_SET:
		case JCEKS:
			
			parseKeystore(encMode, propertiesMap, c);
			
			break;
		case PUBLIC_KEY:
		case SYMMETRIC_KEY:
			
			parseKey(encMode, propertiesMap, c);
			
			break;
		
		default:
			throw new UtilsException(DEBUG_PREFIX+encMode+KEYSTORE_TYPE+"' with unsupported value '"+keystoreType+"'");
		}
	}
	private static void parseKeystore(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c) throws UtilsException {
		
		if(KeystoreType.PKCS11.equals(c.getKeystoreType())) {
			c.keystorePath = HSMUtils.KEYSTORE_HSM_STORE_PASSWORD_UNDEFINED;
		}
		else {
			String keystorePathPName = encMode+KEYSTORE_PATH;
			String keystorePath = propertiesMap.getProperty(keystorePathPName);
			if(keystorePath==null || StringUtils.isEmpty(keystorePath.trim())) {
				throw new UtilsException(DEBUG_PREFIX+keystorePathPName+"'"+UNDEFINED);
			}
			c.keystorePath = keystorePath.trim();
		}
		
		if(KeystoreType.PKCS11.equals(c.getKeystoreType())) {
			c.keystorePassword = HSMUtils.KEYSTORE_HSM_STORE_PASSWORD_UNDEFINED;
		}
		else if(!KeystoreType.JWK_SET.equals(c.getKeystoreType())) {
			String keystorePasswordPName = encMode+KEYSTORE_PASSWORD;
			String keystorePassword = propertiesMap.getProperty(keystorePasswordPName);
			if(keystorePassword==null) {
				throw new UtilsException(DEBUG_PREFIX+keystorePasswordPName+"'"+UNDEFINED);
			}
			c.keystorePassword = keystorePassword.trim();
		}
		
		parseKeystoreKey(encMode, propertiesMap, c);
	}
	private static void parseKeystoreKey(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c) throws UtilsException {
		String keyAliasPName = encMode+KEY_ALIAS;
		String keyAlias = propertiesMap.getProperty(keyAliasPName);
		if(keyAlias==null || StringUtils.isEmpty(keyAlias.trim())) {
			throw new UtilsException(DEBUG_PREFIX+keyAliasPName+"'"+UNDEFINED);
		}
		c.keyAlias = keyAlias.trim();
		
		if(KeystoreType.JCEKS.equals(c.getKeystoreType())) {
			String keyPasswordPName = encMode+KEY_PASSWORD;
			String keyPassword = propertiesMap.getProperty(keyPasswordPName);
			if(keyPassword!=null) {
				c.keyPassword = keyPassword.trim();
			}
		}
		
		String keyAlgoPName = encMode+KEY_ALGORITHM;
		String keyAlgo = propertiesMap.getProperty(keyAlgoPName);
		if(keyAlgo==null || StringUtils.isEmpty(keyAlgo.trim())) {
			throw new UtilsException(DEBUG_PREFIX+keyAlgoPName+"'"+UNDEFINED);
		}
		c.keyAlgorithm = keyAlgo.trim();
		
	}
	private static void parseKey(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c) throws UtilsException {
		String keyPathPName = encMode+KEY_PATH;
		String keyPath = propertiesMap.getProperty(keyPathPName);
		if(keyPath==null || StringUtils.isEmpty(keyPath.trim())) {
			throw new UtilsException(DEBUG_PREFIX+keyPathPName+"'"+UNDEFINED);
		}
		c.keyPath = keyPath.trim();
						
		
		String keyAlgoPName = encMode+KEY_ALGORITHM;
		String keyAlgo = propertiesMap.getProperty(keyAlgoPName);
		if(keyAlgo==null || StringUtils.isEmpty(keyAlgo.trim())) {
			throw new UtilsException(DEBUG_PREFIX+keyAlgoPName+"'"+UNDEFINED);
		}
		c.keyAlgorithm = keyAlgo.trim();
	}
	
	private static void parseKsm(String encMode, Properties propertiesMap, 
			FileTraceEncryptConfig c) {
		String ksmPName = encMode+JOSE_KSM;
		String ksm = propertiesMap.getProperty(ksmPName);
		if(ksm!=null && StringUtils.isNotEmpty(ksm.trim())) {
			c.ksmId = ksm.trim();
		}
		
		List<String> inputParams = new ArrayList<>();
		initKsmParamsInput(encMode, propertiesMap, inputParams);
		if(!inputParams.isEmpty()) {
			c.ksmInput = new HashMap<>();
			for (String inputId : inputParams) {
				String value = propertiesMap.getProperty(encMode+JOSE_KSM_PARAM+inputId);
				if(value!=null) {
					c.ksmInput.put(inputId, value.trim());
				}
			}
		}
	}
	private static void initKsmParamsInput(String encMode, Properties propertiesMap, List<String> idKeystore) {
		String ksmParam = encMode+JOSE_KSM_PARAM;
		Enumeration<?> enKeys = propertiesMap.keys();
		while (enKeys.hasMoreElements()) {
			Object object = enKeys.nextElement();
			if(object instanceof String) {
				String key = (String) object;
				initKsmParamsInput(key, ksmParam, idKeystore);	
			}
		}
	}
	private static void initKsmParamsInput(String key, String prefix, List<String> idKeystore) {
		if(key.startsWith(prefix) && key.length()>(prefix.length())) {
			String tmp = key.substring(prefix.length());
			if(tmp!=null && StringUtils.isNotEmpty(tmp) &&
				!idKeystore.contains(tmp)) {
				idKeystore.add(tmp);
			}
		}
	}
	
	
	public String getName() {
		return this.name;
	}
	
	public boolean isJavaEngine() {
		return ENCRYPTIONT_ENGINE_JAVA.equals(this.encryptionEngine);
	}
	public boolean isJoseEngine() {
		return ENCRYPTIONT_ENGINE_JOSE.equals(this.encryptionEngine);
	}
	
	public KeystoreType getKeystoreType() {
		return this.keystoreType;
	}
	
	public String getKeystoreHsmType() {
		return this.keystoreHsmType;
	}

	public String getKeystorePath() {
		return this.keystorePath;
	}
	
	public String getKeystorePassword() {
		return this.keystorePassword;
	}

	public String getKeyPath() {
		return this.keyPath;
	}

	public String getKeyAlgorithm() {
		return this.keyAlgorithm;
	}

	public String getKeyAlias() {
		return this.keyAlias;
	}
	public void setKeyAlias(String keyAlias) {
		this.keyAlias = keyAlias;
	}
	public void generateKeyAlias() {
		this.keyAlias = UUID.randomUUID().toString();
	}
	
	public String getKeyPassword() {
		return this.keyPassword;
	}
	
	public String getKeyId() {
		return this.keyId;
	}
	
	public boolean isKeyWrap() {
		return this.keyWrap;
	}
	
	public String getContentAlgorithm() {
		return this.contentAlgorithm;
	}
	
	public boolean isJavaBase64Encoding() {
		return JAVA_ENCODING_BASE64.equals(this.javaEncoding);
	}
	public boolean isJavaHexEncoding() {
		return JAVA_ENCODING_HEX.equals(this.javaEncoding);
	}

	public JwtHeaders getJwtHeaders(KeyStore ks) throws UtilsException {
		return getJwtHeaders(ks, null);
	}
	public JwtHeaders getJwtHeaders(JsonWebKeys jsonWebKeys) throws UtilsException {
		return getJwtHeaders(null, jsonWebKeys);
	}
	private JwtHeaders getJwtHeaders(KeyStore ks, JsonWebKeys jsonWebKeys) throws UtilsException {
		JwtHeaders jwtHeaders = new JwtHeaders();
		if(this.joseIncludeKeyId) {
			jwtHeaders.setKid(this.keyAlias);
		}

		if(this.joseIncludeCert) {
			jwtHeaders.setAddX5C(true);
		}
		if(this.joseIncludeCertSha1) {
			jwtHeaders.setX509IncludeCertSha1(true);
		}
		if(this.joseIncludeCertSha256) {
			jwtHeaders.setX509IncludeCertSha256(true);
		}
		if(ks!=null && (this.joseIncludeCert || this.joseIncludeCertSha1 || this.joseIncludeCertSha256)) {
			Certificate cert = ks.getCertificate(this.keyAlias);
			if(cert instanceof X509Certificate) {
				jwtHeaders.addX509cert((X509Certificate)cert);
			}
		}
		
		if(jsonWebKeys!=null && this.joseIncludePublicKey) {
			jwtHeaders.setJwKey(jsonWebKeys, this.keyAlias);
		}
		
		return jwtHeaders;
	}

	public String getKsmId() {
		return this.ksmId;
	}
	public Map<String, String> getKsmInput() {
		return this.ksmInput;
	}

}