BYOKManager.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.utils.certificate.byok;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsException;
import org.slf4j.Logger;
/**
* BYOKManager
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class BYOKManager {
private static BYOKManager staticInstance;
public static synchronized void init(File f, boolean throwNotExists, Logger log) throws UtilsException {
if(staticInstance==null) {
staticInstance = new BYOKManager(f, throwNotExists, log);
}
}
public static BYOKManager getInstance() {
return staticInstance;
}
/*
* Consente di inizializzare una serie di keystore hardware
*
* La configurazione di ogni keystore deve essere definita nel file ksm.properties fornito come argomento dove la sintassi utilizzabile è la seguente
*
* ksm.<idKSM>.type: [required] identificativo univoco del ksm utilizzato nelle configurazioni di govway
* ksm.<idKSM>.label: [required] etichetta associata al ksm e visualizzata nelle maschere di configurazione
* ksm.<idKSM>.mode: [required] indica il tipo di operazione wrap e unwrap
* ksm.<idKSM>.input.<idParam>.name: [optional] se fornito definisce un parametro richiesto alla configurazione della credenziale a cui il ksm viene associato
* ksm.<idKSM>.input.<idParam>.label: [optional] definisce l'etichetta associata al parametro di configurazione
*
* Le restanti proprietà definiscono come accedere al ksm possono essere valorizzate utilizzando anche le seguenti variabili:
* ${ksm-key}: byte[] dell'archivio indicato
* ${ksm-base64-key}: byte[] dell'archivio indicato codificato in base64
* ${ksm-hex-key}: byte[] dell'archivio indicato codificato in base64
* ${ksm:<nomeparametro>}: parametro indicato nella configurazione del ksm
*
* L'operazione di wrap/unwrap può essere realizzata invocando un ksm remoto (via http) o cifrando/decifrando tramite keystore locali.
*
* ksm.<idKSM>.encryptionMode: [optional; default:remote] indica il tipo di encryption via ksm remoto (remote) o locale (local)
*
* Configurazioni per ksm remoto:
*
* ksm.<idKSM>.http.endpoint: [required] definisce l'endpoint del ksm
* ksm.<idKSM>.http.method: [required] definisce il metodo HTTP utilzzato per connettersi al ksm
* ksm.<idKSM>.http.header.<nome>: definisce un header HTTP che possiede il nome indicato nella proprietà stessa
* ksm.<idKSM>.http.payload.inline [optional] defnisce il payload da utilizzare nella richiesta http
* ksm.<idKSM>.http.payload.path [optional] alternativa alla precedente proprietà defnisce il path ad un file contenente il payload da utilizzare nella richiesta http
* ksm.<idKSM>.http.username [optional] definisce la credenziale http-basic (username)
* ksm.<idKSM>.http.password [optional] definisce la credenziale http-basic (password)
* ksm.<idKSM>.http.connectionTimeout [optional; int] tempo massimo in millisecondi di attesa per stabilire una connessione con il server ksm
* ksm.<idKSM>.http.readTimeout [optional; int] tempo massimo in millisecondi di attesa per la ricezione di una risposta dal server
*
* Le seguenti proprietà opzionali consentono invece di utilizzare e configurare un connettore di tipo https
* ksm.<idKSM>.https [optional; boolean] indica se utilizzare o meno un connettore di tipo https
* ksm.<idKSM>.https.hostnameVerifier [optional; boolean] indica se deve essere verificato l'hostname rispetto al certificato server
*
* ksm.<idKSM>.https.serverAuth [optional; boolean] indica se deve essere effettuata l'autenticazione del certificato server
* ksm.<idKSM>.https.serverAuth.trustStore.path: truststore per effettuare l'autenticazione
* ksm.<idKSM>.https.serverAuth.trustStore.type: tipo di truststore
* ksm.<idKSM>.https.serverAuth.trustStore.password password del truststore
* ksm.<idKSM>.https.serverAuth.trustStore.crls: crl
* ksm.<idKSM>.https.serverAuth.trustStore.ocspPolicy: OCSP Policy
*
* ksm.<idKSM>.https.clientAuth [optional; boolean] indica se deve essere inviato un certificato client
* ksm.<idKSM>.https.clientAuth.keyStore.path: keystore per effettuare l'autenticazione client
* ksm.<idKSM>.https.clientAuth.keyStore.type: tipo di keystore
* ksm.<idKSM>.https.clientAuth.keyStore.password password del keystore
* ksm.<idKSM>.https.clientAuth.key.alias: identifica la chiave privata
* ksm.<idKSM>.https.clientAuth.key.password: password della chiave privata
*
* Configurazioni per ksm locale:
*
* ksm.<idKSM>.TODO
*
**/
private HashMap<String, BYOKConfig> ksmKeystoreMapIDtoConfig = new HashMap<>();
private HashMap<String, String> ksmKeystoreMapLabelToID = new HashMap<>();
private HashMap<String, String> ksmKeystoreMapTypeToID = new HashMap<>();
private List<String> unwrapTypes = new ArrayList<>();
private List<String> unwrapLabels = new ArrayList<>();
private List<String> wrapTypes = new ArrayList<>();
private List<String> wrapLabels = new ArrayList<>();
private BYOKManager(File f, boolean throwNotExists, Logger log) throws UtilsException {
String prefixFile = "File '"+f.getAbsolutePath()+"'";
if(!f.exists()) {
if(throwNotExists) {
throw new UtilsException(prefixFile+" not exists");
}
}
else {
if(!f.canRead()) {
throw new UtilsException(prefixFile+" cannot read");
}
Properties p = new Properties();
try {
try(FileInputStream fin = new FileInputStream(f)){
p.load(fin);
}
}catch(Exception t) {
throw new UtilsException(prefixFile+"; initialize error: "+t.getMessage(),t);
}
init(p, log);
}
}
/**private KSMManager(Properties p, Logger log, boolean accessKeystore) throws UtilsException {
init(p, log, accessKeystore);
}*/
private void init(Properties p, Logger log) throws UtilsException {
List<String> idKeystore = new ArrayList<>();
if(p!=null && !p.isEmpty()) {
init(p, idKeystore);
}
if(!idKeystore.isEmpty()) {
for (String idK : idKeystore) {
init(p, log, idK);
}
}
else {
log.warn("La configurazione fornita per KSM non contiene alcun keystore");
}
}
private void init(Properties p, List<String> idKeystore) {
Enumeration<?> enKeys = p.keys();
while (enKeys.hasMoreElements()) {
Object object = enKeys.nextElement();
if(object instanceof String) {
String key = (String) object;
init(key, idKeystore);
}
}
}
private void init(String key, List<String> idKeystore) {
if(key.startsWith(BYOKCostanti.PROPERTY_PREFIX) && key.length()>(BYOKCostanti.PROPERTY_PREFIX.length())) {
String tmp = key.substring(BYOKCostanti.PROPERTY_PREFIX.length());
if(tmp!=null && tmp.contains(".")) {
int indeoOf = tmp.indexOf(".");
if(indeoOf>0) {
String idK = tmp.substring(0,indeoOf);
if(!idKeystore.contains(idK)) {
idKeystore.add(idK);
}
}
}
}
}
private void init(Properties p, Logger log, String idK) throws UtilsException {
String prefix = BYOKCostanti.PROPERTY_PREFIX + idK + ".";
Properties pKeystore = Utilities.readProperties(prefix, p);
BYOKConfig ksmKeystore = new BYOKConfig(idK, pKeystore, log);
// check label
boolean alreadyExists = false;
for (String l : this.ksmKeystoreMapLabelToID.keySet()) {
if(ksmKeystore.getLabel().equalsIgnoreCase(l)) {
alreadyExists = true;
break;
}
}
if(alreadyExists) {
throw new UtilsException("Same label found for ksm '"+this.ksmKeystoreMapLabelToID.get(ksmKeystore.getLabel())+"' e '"+idK+"'");
}
this.ksmKeystoreMapLabelToID.put(ksmKeystore.getLabel(), idK);
// check type
alreadyExists = false;
for (String type : this.ksmKeystoreMapTypeToID.keySet()) {
if(ksmKeystore.getType().equalsIgnoreCase(type)) {
alreadyExists = true;
break;
}
}
if(alreadyExists) {
throw new UtilsException("Same type found for ksm '"+this.ksmKeystoreMapTypeToID.get(ksmKeystore.getLabel())+"' e '"+idK+"'");
}
this.ksmKeystoreMapTypeToID.put(ksmKeystore.getType(), idK);
// registro nelle liste
if(BYOKMode.UNWRAP.equals(ksmKeystore.getMode())) {
this.unwrapTypes.add(ksmKeystore.getType());
this.unwrapLabels.add(ksmKeystore.getLabel());
}
else {
this.wrapTypes.add(ksmKeystore.getType());
this.wrapLabels.add(ksmKeystore.getLabel());
}
this.ksmKeystoreMapIDtoConfig.put(idK, ksmKeystore);
String d = "KSM "+idK+" registrato (type:"+ksmKeystore.getType()+") label:"+ksmKeystore.getLabel()+"";
log.info(d);
}
public BYOKConfig getKSMConfigByType(String type) throws UtilsException {
if(!this.ksmKeystoreMapTypeToID.containsKey(type)) {
throw new UtilsException("KSM type '"+type+"' unknown");
}
String idK = this.ksmKeystoreMapTypeToID.get(type);
if(!this.ksmKeystoreMapIDtoConfig.containsKey(idK)) {
throw new UtilsException("KSM config for type '"+type+"' unknown ? (id:"+idK+")");
}
return this.ksmKeystoreMapIDtoConfig.get(idK);
}
public BYOKConfig getKSMConfigByLabel(String label) throws UtilsException {
if(!this.ksmKeystoreMapLabelToID.containsKey(label)) {
throw new UtilsException("KSM label '"+label+"' unknown");
}
String idK = this.ksmKeystoreMapLabelToID.get(label);
if(!this.ksmKeystoreMapIDtoConfig.containsKey(idK)) {
throw new UtilsException("KSM config for label '"+label+"' unknown ? (id:"+idK+")");
}
return this.ksmKeystoreMapIDtoConfig.get(idK);
}
public List<String> getKeystoreTypes() {
List<String> l = new ArrayList<>();
if(!this.ksmKeystoreMapLabelToID.isEmpty()) {
for (String type : this.ksmKeystoreMapLabelToID.keySet()) {
l.add(type);
}
}
return l;
}
public boolean existsKSMConfigByType(String type) {
if(type==null) {
return false;
}
for (String i : this.ksmKeystoreMapTypeToID.keySet()) {
if(type.equalsIgnoreCase(i)) {
return true;
}
}
return false;
}
public boolean existsKSMConfigByLabel(String label) {
if(label==null) {
return false;
}
for (String i : this.ksmKeystoreMapLabelToID.keySet()) {
if(label.equalsIgnoreCase(i)) {
return true;
}
}
return false;
}
public List<String> getUnwrapTypes() {
return this.unwrapTypes;
}
public List<String> getUnwrapLabels() {
return this.unwrapLabels;
}
public List<String> getWrapTypes() {
return this.wrapTypes;
}
public List<String> getWrapLabels() {
return this.wrapLabels;
}
}