DriverBYOK.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.pdd.core.byok;

  21. import java.util.HashMap;
  22. import java.util.Map;

  23. import org.apache.commons.lang.StringUtils;
  24. import org.openspcoop2.core.byok.BYOKUtilities;
  25. import org.openspcoop2.core.byok.BYOKWrappedValue;
  26. import org.openspcoop2.core.byok.IDriverBYOK;
  27. import org.openspcoop2.pdd.core.dynamic.DynamicInfo;
  28. import org.openspcoop2.pdd.core.dynamic.DynamicMapBuilderUtils;
  29. import org.openspcoop2.pdd.core.dynamic.DynamicUtils;
  30. import org.openspcoop2.pdd.core.jmx.JMXUtils;
  31. import org.openspcoop2.protocol.sdk.Busta;
  32. import org.openspcoop2.protocol.sdk.Context;
  33. import org.openspcoop2.protocol.sdk.state.RequestInfo;
  34. import org.openspcoop2.security.SecurityException;
  35. import org.openspcoop2.security.keystore.BYOKLocalEncrypt;
  36. import org.openspcoop2.utils.LoggerWrapperFactory;
  37. import org.openspcoop2.utils.Semaphore;
  38. import org.openspcoop2.utils.SemaphoreLock;
  39. import org.openspcoop2.utils.UtilsException;
  40. import org.openspcoop2.utils.certificate.byok.BYOKCostanti;
  41. import org.openspcoop2.utils.certificate.byok.BYOKInstance;
  42. import org.openspcoop2.utils.certificate.byok.BYOKManager;
  43. import org.openspcoop2.utils.certificate.byok.BYOKMode;
  44. import org.openspcoop2.utils.certificate.byok.BYOKProvider;
  45. import org.openspcoop2.utils.certificate.byok.BYOKRemoteUtils;
  46. import org.openspcoop2.utils.certificate.byok.BYOKRequestParams;
  47. import org.openspcoop2.utils.certificate.byok.BYOKSecurityConfig;
  48. import org.openspcoop2.utils.certificate.byok.BYOKSecurityConfigParameter;
  49. import org.openspcoop2.utils.transport.http.HttpResponse;
  50. import org.openspcoop2.utils.transport.http.HttpUtilities;
  51. import org.slf4j.Logger;

  52. /**
  53.  * DriverBYOK
  54.  *
  55.  * @author Andrea Poli (apoli@link.it)
  56.  * @author $Author$
  57.  * @version $Rev$, $Date$
  58.  */
  59. public class DriverBYOK implements IDriverBYOK {

  60.     private Logger log;
  61.     private String securityPolicy;
  62.     private String securityRemotePolicy;
  63.     private Map<String, Map<String, Object>> dynamicMapForSecurityPolicy; // per supportare l'unwrap di secrets codificati con security policy differenti da quelle di default
  64.     private boolean checkJmxPrefixOperazioneNonRiuscita;
  65.    
  66.     public DriverBYOK(Logger log, String securityPolicy, String securityRemotePolicy) {
  67.         this(log, securityPolicy, securityRemotePolicy, buildDynamicMap(log), false);
  68.     }
  69.     public static Map<String, Object> buildDynamicMap(Logger log){
  70.         Map<String, Object> dynamicMap = new HashMap<>();
  71.         DynamicInfo dynamicInfo = new  DynamicInfo();
  72.         DynamicUtils.fillDynamicMap(log, dynamicMap, dynamicInfo);
  73.         return dynamicMap;
  74.     }
  75.     DriverBYOK(Logger log, String securityPolicy, String securityRemotePolicy, Map<String, Object> dynamicMapParam, boolean checkJmxPrefixOperazioneNonRiuscita) {
  76.         this.log = log;
  77.         if(securityPolicy!=null && StringUtils.isNotEmpty(securityPolicy)) {
  78.             this.securityPolicy = securityPolicy;
  79.         }
  80.         if(securityRemotePolicy!=null && StringUtils.isNotEmpty(securityRemotePolicy)) {
  81.             this.securityRemotePolicy = securityRemotePolicy;
  82.         }
  83.        
  84.         this.dynamicMapForSecurityPolicy = new HashMap<>();
  85.         Map<String, Object> defaultPolicy = dynamicMapParam==null ? new HashMap<>() : dynamicMapParam;
  86.         this.dynamicMapForSecurityPolicy.put(this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy, defaultPolicy);
  87.         this.checkJmxPrefixOperazioneNonRiuscita = checkJmxPrefixOperazioneNonRiuscita;
  88.     }
  89.    
  90.     private Semaphore semaphoreDynamicMap = new Semaphore("dynamicMap");
  91.     private Map<String, Object> getDynamicMap(String securityPolicy) throws UtilsException{
  92.         if(!this.dynamicMapForSecurityPolicy.containsKey(securityPolicy)) {
  93.             this.initDynamicMap(securityPolicy);
  94.         }
  95.         return this.dynamicMapForSecurityPolicy.get(securityPolicy);
  96.     }
  97.     private void initDynamicMap(String securityPolicy) throws UtilsException {
  98.         SemaphoreLock lock = this.semaphoreDynamicMap.acquire("initDynamicMap");
  99.         try {
  100.             if(!this.dynamicMapForSecurityPolicy.containsKey(securityPolicy)) {
  101.                 Map<String, Object> mDefault = this.dynamicMapForSecurityPolicy.get(this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy);
  102.                 Map<String, Object> mNew = new HashMap<>();
  103.                 for (Map.Entry<String,Object> entry : mDefault.entrySet()) {
  104.                     if(!BYOKCostanti.VARIABILE_KMS.equals(entry.getKey()) && !BYOKCostanti.VARIABILE_KSM_DEPRECATED.equals(entry.getKey())){ // devo escludere kms object contenente le variabili di un'altra security policy
  105.                         mNew.put(entry.getKey(), entry.getValue());
  106.                     }
  107.                 }
  108.                 this.dynamicMapForSecurityPolicy.put(securityPolicy, mNew);
  109.             }
  110.         }finally {
  111.             this.semaphoreDynamicMap.release(lock, "initDynamicMap");
  112.         }
  113.     }
  114.    
  115.     @Override
  116.     public BYOKWrappedValue wrap(String value) throws UtilsException {
  117.         if(value==null) {
  118.             throw new UtilsException("Value undefined");
  119.         }
  120.         if(BYOKUtilities.isWrappedValue(value)) {
  121.             return new BYOKWrappedValue(value, BYOKUtilities.extractPrefixWrappedValue(value));
  122.         }
  123.        
  124.         if(this.securityPolicy==null || StringUtils.isEmpty(value)) {
  125.             return null;
  126.         }
  127.        
  128.         BYOKRequestParams p = getBYOKRequestParams(true, this.securityPolicy);
  129.         String prefix = BYOKUtilities.newPrefixWrappedValue((this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy));
  130.         byte[]wrapped = process(getBYOKInstance(this.log,value.getBytes(),p));
  131.        
  132.         String wrappedValue = new String(wrapped);
  133.         if(!wrappedValue.startsWith(prefix)){
  134.             wrappedValue = prefix + wrappedValue;
  135.         }
  136.        
  137.         return new BYOKWrappedValue(wrappedValue,
  138.                 prefix.substring(0, prefix.length()-1) // elimino il punto finale
  139.                 );
  140.     }
  141.     @Override
  142.     public BYOKWrappedValue wrap(byte[] value) throws UtilsException {
  143.         if(value==null) {
  144.             throw new UtilsException("Value undefined");
  145.         }
  146.         if(BYOKUtilities.isWrappedValue(value)) {
  147.             return new BYOKWrappedValue(value, BYOKUtilities.extractPrefixWrappedValue(value));
  148.         }
  149.        
  150.         if(this.securityPolicy==null) {
  151.             return null;
  152.         }
  153.        
  154.         BYOKRequestParams p = getBYOKRequestParams(true, this.securityPolicy);
  155.         String prefix = BYOKUtilities.newPrefixWrappedValue((this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy));
  156.         byte[]wrapped = process(getBYOKInstance(this.log,value,p));
  157.        
  158.         String wrappedValue = new String(wrapped);
  159.         if(!wrappedValue.startsWith(prefix)){
  160.             wrappedValue = prefix + wrappedValue;
  161.         }
  162.        
  163.         return new BYOKWrappedValue(wrappedValue,
  164.                 prefix.substring(0, prefix.length()-1) // elimino il punto finale
  165.                 );
  166.     }

  167.     public boolean isAlreadyWrappedBySecPolicy(String check) throws UtilsException {
  168.         // Serve per evitare di effettuare un ulteriore livello di wrap ad una informazione già cifrata
  169.         // nei metodi wrap non viene usato poichè viene già verificato più in generale con 'isWrappedValue'
  170.         // si lascia come utility generica il metodo
  171.         try {
  172.             return getSecPolicyIdForUnwrap(check)!=null;
  173.         }catch(Exception e) {
  174.             return false;
  175.         }
  176.     }
  177.     private String getSecPolicyIdForUnwrap(String check) throws UtilsException {
  178.         // Serve per decodificare valori cifrati con security policy differente da quella impostata per la gestione della cifratura
  179.         // Il comportamento serve a supportare eventuali valori cifrati dopo un cambio della policy senza un aggiornamento delle informazioni sensibili
  180.         String secPolicy = this.securityPolicy;
  181.         String wrapPolicy = BYOKUtilities.getPolicy(check);
  182.         if(!secPolicy.equals(wrapPolicy)) {
  183.             // dato cifrato con altra policy
  184.             // verifico che comunque esista
  185.             if(BYOKManager.getInstance().existsSecurityEngineByType(wrapPolicy)){
  186.                 secPolicy = wrapPolicy;
  187.             }
  188.             else {
  189.                 throw new UtilsException("Security policy '"+wrapPolicy+"' unknown");
  190.             }
  191.         }
  192.         return secPolicy;
  193.     }
  194.    
  195.     @Override
  196.     public byte[] unwrap(byte[] value) throws UtilsException {
  197.         if(value==null || value.length<=0) {
  198.             return value;
  199.         }
  200.         String check = new String(value);
  201.         if(BYOKUtilities.isWrappedValue(check)) {
  202.             if(this.securityPolicy==null) {
  203.                 return value;
  204.             }
  205.            
  206.             String rawWrappedValue =  BYOKUtilities.getRawWrappedValue(check);
  207.            
  208.             BYOKRequestParams p = getBYOKRequestParams(false, getSecPolicyIdForUnwrap(check));
  209.             return process(getBYOKInstance(this.log,rawWrappedValue.getBytes(),p));
  210.         }
  211.         return value;
  212.     }
  213.     @Override
  214.     public byte[] unwrap(String value) throws UtilsException {
  215.         return unwrap(value, null, false);
  216.     }
  217.     public byte[] unwrap(String value, boolean checkAppendPrefix) throws UtilsException{
  218.         return unwrap(value,
  219.                 this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy,
  220.                 checkAppendPrefix);
  221.     }
  222.     public byte[] unwrap(String value, String securityPolicy, boolean checkAppendPrefix) throws UtilsException {
  223.         if(BYOKUtilities.isWrappedValue(value)) {
  224.             if(this.securityPolicy==null) {
  225.                 return value.getBytes();
  226.             }
  227.            
  228.             String rawWrappedValue =  BYOKUtilities.getRawWrappedValue(value);
  229.             BYOKRequestParams p = getBYOKRequestParams(false, getSecPolicyIdForUnwrap(value));
  230.             return process(getBYOKInstance(this.log,rawWrappedValue.getBytes(),p));
  231.         }
  232.         else if(checkAppendPrefix){
  233.             String newWrappedValue = BYOKUtilities.newPrefixWrappedValue(securityPolicy)+value;
  234.             return this.unwrap(newWrappedValue);
  235.         }
  236.         else {
  237.             return value.getBytes();
  238.         }
  239.     }
  240.    
  241.     public String unwrapAsString(String value, boolean checkAppendPrefix) throws UtilsException{
  242.         return unwrapAsString(value,
  243.                 this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy,
  244.                 checkAppendPrefix);
  245.     }
  246.     public String unwrapAsString(String value, String securityPolicy, boolean checkAppendPrefix) throws UtilsException{
  247.         if(BYOKUtilities.isWrappedValue(value)) {
  248.             return this.unwrapAsString(value);
  249.         }
  250.         else if(checkAppendPrefix){
  251.             String newWrappedValue = BYOKUtilities.newPrefixWrappedValue(securityPolicy)+value;
  252.             String unwrappedValue = this.unwrapAsString(newWrappedValue);
  253.             if(newWrappedValue.equals(unwrappedValue)) {
  254.                 // decodifica non riuscita
  255.                 return value;
  256.             }
  257.             else {
  258.                 return unwrappedValue;
  259.             }
  260.         }
  261.         else {
  262.             return value;
  263.         }
  264.     }
  265.    
  266.     private BYOKInstance getBYOKInstance(Logger log,byte[] key, BYOKRequestParams p) throws UtilsException {
  267.         return BYOKInstance.newInstance(log, p, key);
  268.     }
  269.     private BYOKRequestParams getBYOKRequestParams(boolean wrap, String securityPolicy) throws UtilsException {
  270.         return getBYOKRequestParamsBySecurityPolicy(wrap, securityPolicy, this. getDynamicMap(securityPolicy));
  271.     }
  272.     public static BYOKRequestParams getBYOKRequestParamsBySecurityPolicy(boolean wrap, String securityPolicy, Map<String, Object> dynamicMap) throws UtilsException {
  273.        
  274.         if(securityPolicy==null) {
  275.             return null;
  276.         }
  277.        
  278.         BYOKManager manager = BYOKManager.getInstance();
  279.         if(manager==null) {
  280.             throw new UtilsException("BYOKManager not initialized");
  281.         }
  282.        
  283.         BYOKSecurityConfig secConfig = manager.getKMSSecurityConfig(securityPolicy);
  284.        
  285.         String kmsId = wrap ? secConfig.getWrapId() : secConfig.getUnwrapId();
  286.         if(kmsId==null) {
  287.             throw new UtilsException("BYOK security configuration '"+securityPolicy+"' without "+(wrap ?  "wrap" : "unwrap")+" kms id");
  288.         }
  289.                
  290.         Map<String, String> inputMap = new HashMap<>();
  291.         if(secConfig.getInputParameters()!=null && !secConfig.getInputParameters().isEmpty()) {
  292.             for (BYOKSecurityConfigParameter sec : secConfig.getInputParameters()) {
  293.                 inputMap.put(sec.getName(), sec.getValue());
  294.             }
  295.         }
  296.        
  297.         return getBYOKRequestParamsByKmsId(kmsId, manager,
  298.                 inputMap, dynamicMap);
  299.     }
  300.    
  301.     public static BYOKRequestParams getBYOKRequestParamsByUnwrapBYOKPolicy(String kmsId,
  302.             Busta busta, RequestInfo requestInfo, Context context, Logger log) throws UtilsException {
  303.         if(BYOKProvider.isPolicyDefined(kmsId)){
  304.             Map<String,Object> dynamicMap = DynamicMapBuilderUtils.buildDynamicMap(busta, requestInfo, context, log);
  305.             return BYOKProvider.getBYOKRequestParamsByUnwrapBYOKPolicy(kmsId, dynamicMap);
  306.         }
  307.         return null;
  308.     }
  309.    
  310.     public static BYOKRequestParams getBYOKRequestParamsByKmsId(String kmsId,
  311.             Map<String, String> inputMap, Map<String, Object> dynamicMap) throws UtilsException {
  312.         return BYOKRequestParams.getBYOKRequestParamsByKmsId(kmsId,
  313.                 inputMap,dynamicMap);
  314.     }
  315.     public static BYOKRequestParams getBYOKRequestParamsByKmsId(String kmsId, BYOKManager manager,
  316.             Map<String, String> inputMap, Map<String, Object> dynamicMap) throws UtilsException {
  317.         return BYOKRequestParams.getBYOKRequestParamsByKmsId(kmsId, manager,
  318.                 inputMap, dynamicMap);
  319.     }
  320.    
  321.     private byte[] process(BYOKInstance instance) throws UtilsException{
  322.         return processInstance(instance, this.checkJmxPrefixOperazioneNonRiuscita);
  323.     }
  324.     public static byte[] processInstance(BYOKInstance instance, boolean checkJmxPrefixOperazioneNonRiuscita) throws UtilsException{
  325.        
  326.         try{
  327.             if(instance==null){
  328.                 throw new SecurityException("Instance non fornita");
  329.             }
  330.            
  331.             if(instance.getHttpRequest()!=null) {
  332.                 return remoteProcess(instance, checkJmxPrefixOperazioneNonRiuscita);
  333.             }
  334.             else {
  335.                 BYOKLocalEncrypt localEncrypt = new BYOKLocalEncrypt();
  336.                 if(BYOKMode.WRAP.equals(instance.getConfig().getMode())) {
  337.                     return localEncrypt.wrap(instance.getLocalConfigResolved(), instance.getLocalKey()).getBytes();
  338.                 }
  339.                 else {
  340.                     return localEncrypt.unwrap(instance.getLocalConfigResolved(), instance.getLocalKey());
  341.                 }
  342.             }

  343.         }catch(Exception e){
  344.             throw new UtilsException(e.getMessage(),e);
  345.         }
  346.        
  347.     }
  348.     private static byte[] remoteProcess(BYOKInstance instance, boolean checkJmxPrefixOperazioneNonRiuscita) throws UtilsException{
  349.         String debugUrl = "'"+instance.getConfig().getLabel()+"' (endpoint:"+instance.getHttpRequest().getUrl()+")";
  350.        
  351.         HttpResponse httpResponse = HttpUtilities.httpInvoke(instance.getHttpRequest());
  352.         if(httpResponse==null || httpResponse.getContent()==null) {
  353.             throw new UtilsException("Store "+debugUrl+" unavailable");
  354.         }
  355.         if(httpResponse.getResultHTTPOperation()!=200) {
  356.             throw new UtilsException("Retrieve store "+debugUrl+" failed (returnCode:"+httpResponse.getResultHTTPOperation()+")");
  357.         }
  358.         byte [] content = null;
  359.         if(checkJmxPrefixOperazioneNonRiuscita) {
  360.             byte[] b = httpResponse.getContent();
  361.             if(b==null || b.length<=0) {
  362.                 throw new UtilsException("Store "+debugUrl+" empty response");
  363.             }
  364.             String check = new String(b);
  365.             if(check.startsWith(JMXUtils.MSG_OPERAZIONE_NON_EFFETTUATA)) {
  366.                 throw new UtilsException("Retrieve store "+debugUrl+" failed (returnCode:"+httpResponse.getResultHTTPOperation()+"): "+check);
  367.             }
  368.             content = b;
  369.         }
  370.         else {
  371.             content = httpResponse.getContent();
  372.         }
  373.         if(content!=null && content.length>0) {
  374.             content = BYOKRemoteUtils.normalizeResponse(instance, content, LoggerWrapperFactory.getLogger(DriverBYOK.class));
  375.         }
  376.         return content;
  377.     }
  378.    
  379.     public boolean isWrappedWithInternalPolicy(byte[] value) {
  380.         if(value==null || value.length<=0) {
  381.             return false;
  382.         }
  383.         return isWrappedWithInternalPolicy(new String(value));
  384.     }
  385.     public boolean isWrappedWithInternalPolicy(String value) {
  386.         String policy = this.securityRemotePolicy!=null ? this.securityRemotePolicy : this.securityPolicy;
  387.         return DriverBYOKUtilities.isWrappedWithPolicy(this.log, value, policy);
  388.     }
  389.    
  390. }