GestoreTokenNegoziazioneUtilities.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.core.token;

import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
import org.openspcoop2.core.config.InvocazioneCredenziali;
import org.openspcoop2.core.config.PortaApplicativa;
import org.openspcoop2.core.config.PortaDelegata;
import org.openspcoop2.core.config.ResponseCachingConfigurazione;
import org.openspcoop2.core.config.constants.CostantiConfigurazione;
import org.openspcoop2.core.config.constants.StatoFunzionalita;
import org.openspcoop2.core.constants.CostantiConnettori;
import org.openspcoop2.core.constants.TipiConnettore;
import org.openspcoop2.core.constants.TipoPdD;
import org.openspcoop2.core.constants.TransferLengthModes;
import org.openspcoop2.core.id.IDGenericProperties;
import org.openspcoop2.core.id.IDServizio;
import org.openspcoop2.core.id.IDSoggetto;
import org.openspcoop2.core.mvc.properties.provider.ProviderException;
import org.openspcoop2.core.mvc.properties.provider.ProviderValidationException;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.OpenSPCoop2MessageFactory;
import org.openspcoop2.message.OpenSPCoop2MessageParseResult;
import org.openspcoop2.message.constants.MessageType;
import org.openspcoop2.pdd.config.ConfigurazionePdDManager;
import org.openspcoop2.pdd.config.ForwardProxy;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.core.PdDContext;
import org.openspcoop2.pdd.core.connettori.ConnettoreBaseHTTP;
import org.openspcoop2.pdd.core.connettori.ConnettoreHTTP;
import org.openspcoop2.pdd.core.connettori.ConnettoreHTTPS;
import org.openspcoop2.pdd.core.connettori.ConnettoreMsg;
import org.openspcoop2.pdd.core.controllo_traffico.PolicyTimeoutConfig;
import org.openspcoop2.pdd.core.dynamic.DynamicMapBuilderUtils;
import org.openspcoop2.pdd.core.token.parser.Claims;
import org.openspcoop2.pdd.core.token.parser.ClaimsNegoziazione;
import org.openspcoop2.pdd.core.token.parser.INegoziazioneTokenParser;
import org.openspcoop2.protocol.sdk.Busta;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.state.IState;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.security.SecurityException;
import org.openspcoop2.security.keystore.JWKSetStore;
import org.openspcoop2.security.keystore.KeyPairStore;
import org.openspcoop2.security.keystore.MerlinKeystore;
import org.openspcoop2.security.keystore.cache.GestoreKeystoreCache;
import org.openspcoop2.security.message.constants.SecurityConstants;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.certificate.KeyStore;
import org.openspcoop2.utils.certificate.KeystoreParams;
import org.openspcoop2.utils.certificate.byok.BYOKProvider;
import org.openspcoop2.utils.certificate.byok.BYOKRequestParams;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.id.UniqueIdentifierManager;
import org.openspcoop2.utils.json.JSONUtils;
import org.openspcoop2.utils.properties.PropertiesUtilities;
import org.openspcoop2.utils.security.JOSESerialization;
import org.openspcoop2.utils.security.JWSOptions;
import org.openspcoop2.utils.security.JsonSignature;
import org.openspcoop2.utils.security.JwtHeaders;
import org.openspcoop2.utils.transport.TransportRequestContext;
import org.openspcoop2.utils.transport.TransportUtils;
import org.openspcoop2.utils.transport.http.HttpConstants;
import org.openspcoop2.utils.transport.http.HttpRequestMethod;
import org.openspcoop2.utils.transport.http.HttpResponse;
import org.slf4j.Logger;

import com.fasterxml.jackson.databind.node.ObjectNode;

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

	static EsitoNegoziazioneToken endpointTokenEngine(boolean debug, Logger log, PolicyNegoziazioneToken policyNegoziazioneToken,
			Busta busta, RequestInfo requestInfo, TipoPdD tipoPdD,
			NegoziazioneTokenDynamicParameters dynamicParameters, IProtocolFactory<?> protocolFactory,
			IState state, boolean delegata, String idModulo, PortaApplicativa pa, PortaDelegata pd,
			IDSoggetto idDominio, IDServizio idServizio,
			InformazioniNegoziazioneToken previousToken,
			InformazioniNegoziazioneToken_DatiRichiesta datiRichiesta) {
		
		EsitoNegoziazioneToken esito = null;
		
		boolean refreshModeEnabled = false;
		try {
			if(policyNegoziazioneToken.isClientCredentialsGrant()) {
				refreshModeEnabled = OpenSPCoop2Properties.getInstance().isGestioneRetrieveToken_refreshToken_grantType_clientCredentials();	
				if(datiRichiesta!=null) {
					datiRichiesta.setGrantType(Costanti.ID_RETRIEVE_TOKEN_METHOD_CLIENT_CREDENTIAL);
				}
			}
			else if(policyNegoziazioneToken.isUsernamePasswordGrant()) {
				refreshModeEnabled = OpenSPCoop2Properties.getInstance().isGestioneRetrieveToken_refreshToken_grantType_usernamePassword();	
				if(datiRichiesta!=null) {
					datiRichiesta.setGrantType(Costanti.ID_RETRIEVE_TOKEN_METHOD_USERNAME_PASSWORD);
				}
			}
			else if(policyNegoziazioneToken.isRfc7523x509Grant()) {
				refreshModeEnabled = OpenSPCoop2Properties.getInstance().isGestioneRetrieveToken_refreshToken_grantType_rfc7523_x509();	
				if(datiRichiesta!=null) {
					datiRichiesta.setGrantType(Costanti.ID_RETRIEVE_TOKEN_METHOD_RFC_7523_X509);
				}
			}
			else if(policyNegoziazioneToken.isRfc7523ClientSecretGrant()) {
				refreshModeEnabled = OpenSPCoop2Properties.getInstance().isGestioneRetrieveToken_refreshToken_grantType_rfc7523_clientSecret();
				if(datiRichiesta!=null) {
					datiRichiesta.setGrantType(Costanti.ID_RETRIEVE_TOKEN_METHOD_RFC_7523_CLIENT_SECRET);
				}
			}
			else if(policyNegoziazioneToken.isCustomGrant()) {
				refreshModeEnabled = OpenSPCoop2Properties.getInstance().isGestioneRetrieveToken_refreshToken_grantType_custom();	
				if(datiRichiesta!=null) {
					datiRichiesta.setGrantType(Costanti.ID_RETRIEVE_TOKEN_METHOD_CUSTOM);
				}
			}
		}catch(Exception t) {
			log.error("Errore durante la comprensione della modalità di refresh: "+t.getMessage(),t);
		}
		
		
		if(refreshModeEnabled && previousToken!=null && previousToken.getRefreshToken()!=null) {
			try {
				// Verico scadenza refresh
				if(previousToken.getRefreshExpiresIn()!=null) {
					Date now = DateManager.getDate();
					int secondsPreExpire = -1;
					OpenSPCoop2Properties properties = OpenSPCoop2Properties.getInstance();
					if(properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_percent()!=null && properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_percent()>0) {
						int percent = properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_percent();
						long secondsExpire = previousToken.getRefreshExpiresIn().getTime() / 1000l;
						long secondsIat = previousToken.getRetrievedRefreshTokenIn().getTime() / 1000l;
						long secondsDiff = secondsExpire - secondsIat;
						if(secondsDiff>0) {
							float perc1 = Float.parseFloat(secondsDiff+"") / 100f;
							float perc2 = perc1 * Float.parseFloat(percent+"");
							int s = Math.round(perc2);
							if(s>0) {
								secondsPreExpire = s;
							}
						}
					}
					else if(properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_seconds()!=null && properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_seconds()>0) {
						secondsPreExpire = properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_seconds();
					}
					if(secondsPreExpire>0) {
						now = new Date(now.getTime() + (secondsPreExpire*1000));
						/**System.out.println("Controllo scadenza per refresh token now+tollerance("+secondsPreExpire+")["+org.openspcoop2.utils.date.DateUtils.getSimpleDateFormatMs().format(now)+"] exp["+
						//		org.openspcoop2.utils.date.DateUtils.getSimpleDateFormatMs().format(previousToken.getRefreshExpiresIn())+"]");*/
					}			
					if(!now.before(previousToken.getRefreshExpiresIn())){
						// scaduto refresh token
						log.debug("Refresh token scaduto");
						refreshModeEnabled = false;
					}
				}
				if(refreshModeEnabled) {
					esito = invokeEndpointToken(debug, log, policyNegoziazioneToken,
							busta, requestInfo, tipoPdD,
							dynamicParameters, protocolFactory, 
							state, delegata, idModulo, pa, pd,
							idDominio, idServizio,
							refreshModeEnabled, previousToken,
							datiRichiesta);
					if(esito!=null && esito.isValido()) {
						// ricontrollo tutte le date (l'ho appena preso, dovrebbero essere buone) 
						boolean checkPerRinegoziazione = false;
						validazioneInformazioniNegoziazioneToken(checkPerRinegoziazione, esito,  
								policyNegoziazioneToken.isSaveErrorInCache());
						if(datiRichiesta!=null) {
							datiRichiesta.setRefresh(true);
						}
						return esito;
					}
					if(esito==null || esito.getEccezioneProcessamento()==null) {
						throw new TokenException("token not refreshed");
					}
					else {
						throw new TokenException("token not refreshed: "+esito.getEccezioneProcessamento());
					}
				}
			}catch(Exception t) {
				String msgError = "Refresh token failed: "+t.getMessage();
				log.error(msgError);
			}
		}
		
		if(datiRichiesta!=null) {
			datiRichiesta.setRefresh(null);
		}
		return invokeEndpointToken(debug, log, policyNegoziazioneToken,
				busta, requestInfo, tipoPdD,
				dynamicParameters, protocolFactory, 
				state, delegata, idModulo, pa, pd,
				idDominio, idServizio,
				false, null,
				datiRichiesta);
		
	}
	
	
	private static EsitoNegoziazioneToken invokeEndpointToken(boolean debug, Logger log, PolicyNegoziazioneToken policyNegoziazioneToken,
			Busta busta, RequestInfo requestInfo, TipoPdD tipoPdD,
			NegoziazioneTokenDynamicParameters dynamicParameters, IProtocolFactory<?> protocolFactory,
			IState state, boolean delegata, String idModulo, PortaApplicativa pa, PortaDelegata pd,
			IDSoggetto idDominio, IDServizio idServizio,
			boolean refreshModeEnabled, InformazioniNegoziazioneToken previousToken,
			InformazioniNegoziazioneToken_DatiRichiesta datiRichiesta) {
	
		EsitoNegoziazioneToken esitoNegoziazioneToken = new EsitoNegoziazioneToken();
		
		esitoNegoziazioneToken.setTokenInternalError();
		
		try{
			String detailsError = null;
			InformazioniNegoziazioneToken informazioniToken = null;
			Exception eProcess = null;
			
			INegoziazioneTokenParser tokenParser = policyNegoziazioneToken.getNegoziazioneTokenParser();
			
			HttpResponse httpResponse = null;
			Integer httpResponseCode = null;
			byte[] risposta = null;
			try {
				httpResponse = http(debug, log, policyNegoziazioneToken,
						busta, requestInfo, tipoPdD,
						dynamicParameters, protocolFactory, 
						state, delegata, idModulo, pa, pd,
						idDominio, idServizio,
						refreshModeEnabled, (refreshModeEnabled && previousToken!=null) ? previousToken.getRefreshToken() : null,
						datiRichiesta);
				risposta = httpResponse.getContent();
				httpResponseCode = httpResponse.getResultHTTPOperation();
			}catch(Exception e) {
				detailsError = "(Errore di Connessione) "+ e.getMessage();
				eProcess = e;
			}
			
			if(detailsError==null) {
				try {
					if(datiRichiesta!=null && datiRichiesta.getPrepareRequest()!=null) {
						datiRichiesta.setParseResponse(DateManager.getDate());
					}
					informazioniToken = new InformazioniNegoziazioneToken(datiRichiesta, httpResponseCode, new String(risposta),tokenParser,previousToken);
				}catch(Exception e) {
					detailsError = "Risposta del servizio di negoziazione token non valida: "+e.getMessage();
					eProcess = e;
				}
			}
    		  		
    		if(informazioniToken!=null && informazioniToken.isValid()) {
    			esitoNegoziazioneToken.setTokenValido();
    			esitoNegoziazioneToken.setInformazioniNegoziazioneToken(informazioniToken);
    			esitoNegoziazioneToken.setToken(informazioniToken.getAccessToken());
    			esitoNegoziazioneToken.setNoCache(false);
			}
    		else {
    			esitoNegoziazioneToken.setTokenValidazioneFallita();
    			esitoNegoziazioneToken.setNoCache(!policyNegoziazioneToken.isSaveErrorInCache());
    			esitoNegoziazioneToken.setEccezioneProcessamento(eProcess);
    			if(detailsError!=null) {
    				esitoNegoziazioneToken.setDetails(detailsError);	
				}
				else {
					esitoNegoziazioneToken.setDetails("AccessToken non recuperabile");	
				} 
    			
    			// comunque lo aggiungo per essere consultabile nei casi di errore
    			if(OpenSPCoop2Properties.getInstance().isGestioneRetrieveToken_saveTokenInfo_retrieveFailed()) {
    				if(informazioniToken!=null) {
		    			if(httpResponseCode!=null) {
		    				informazioniToken.setHttpResponseCode(httpResponseCode+"");
		    			}
		    			informazioniToken.setErrorDetails(esitoNegoziazioneToken.getDetails());
    				}
    				else {
    					informazioniToken = new InformazioniNegoziazioneToken(datiRichiesta,
    							esitoNegoziazioneToken.getDetails(), httpResponseCode, risposta);
    				}
    				esitoNegoziazioneToken.setInformazioniNegoziazioneToken(informazioniToken); 
    			}
    		}
    		
		}catch(Exception e){
			esitoNegoziazioneToken.setTokenInternalError();
			esitoNegoziazioneToken.setDetails(e.getMessage());
			esitoNegoziazioneToken.setEccezioneProcessamento(e);
    	}
		
		if(datiRichiesta!=null && datiRichiesta.getPrepareRequest()!=null) {
			datiRichiesta.setProcessComplete(DateManager.getDate());
		}
		return esitoNegoziazioneToken;
	}
	
	static String buildPrefixCacheKeyNegoziazione(String policy, String funzione) {
		StringBuilder bf = new StringBuilder(funzione);
    	bf.append("_");
    	bf.append(policy);
    	bf.append("_");
    	return bf.toString();
	}
	static String buildCacheKeyNegoziazione(String policy, String funzione, boolean portaDelegata, NegoziazioneTokenDynamicParameters dynamicParameters) {
    	StringBuilder bf = new StringBuilder();
    	bf.append(buildPrefixCacheKeyNegoziazione(policy, funzione));
    	if(portaDelegata){ // serve per non aver classcast exception nei risultati
    		bf.append("PD");
    	}
    	else {
    		bf.append("PA");
    	}
    	bf.append("_");
    	
    	String dynamicParametersKeyCache = dynamicParameters.toString("_", true);
    	bf.append(dynamicParametersKeyCache);
    	
    	return bf.toString();
    }	
	static void validazioneInformazioniNegoziazioneToken(boolean checkPerRinegoziazione, EsitoNegoziazioneToken esitoNegoziazioneToken, boolean saveErrorInCache) {
		
		Date now = DateManager.getDate();
		
		if(esitoNegoziazioneToken.isValido()) {
			esitoNegoziazioneToken.setDateValide(true); // tanto le ricontrollo adesso
		}
		
		if(esitoNegoziazioneToken.isValido() &&		
			esitoNegoziazioneToken.getInformazioniNegoziazioneToken().getExpiresIn()!=null) {			
				
			if(checkPerRinegoziazione) {
				int secondsPreExpire = -1;
				OpenSPCoop2Properties properties = OpenSPCoop2Properties.getInstance();
				if(properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_percent()!=null && properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_percent()>0) {
					int percent = properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_percent();
					long secondsExpire = esitoNegoziazioneToken.getInformazioniNegoziazioneToken().getExpiresIn().getTime() / 1000l;
					long secondsIat = esitoNegoziazioneToken.getInformazioniNegoziazioneToken().getRetrievedIn().getTime() / 1000l;
					long secondsDiff = secondsExpire - secondsIat;
					if(secondsDiff>0) {
						float perc1 = Float.parseFloat(secondsDiff+"") / 100f;
						float perc2 = perc1 * Float.parseFloat(percent+"");
						int s = Math.round(perc2);
						if(s>0) {
							secondsPreExpire = s;
						}
					}
				}
				else if(properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_seconds()!=null && properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_seconds()>0) {
					secondsPreExpire = properties.getGestioneRetrieveToken_refreshTokenBeforeExpire_seconds();
				}
				if(secondsPreExpire>0) {
					now = new Date(now.getTime() + (secondsPreExpire*1000));
					/**System.out.println("Controllo scadenza per rinegoziazione now+tollerance("+secondsPreExpire+")["+org.openspcoop2.utils.date.DateUtils.getSimpleDateFormatMs().format(now)+"] exp["+
					//		org.openspcoop2.utils.date.DateUtils.getSimpleDateFormatMs().format(esitoNegoziazioneToken.getInformazioniNegoziazioneToken().getExpiresIn())+"]");*/
				}
			}
			
			/*
			 *  The lifetime in seconds of the access token.  For example, the value "3600" denotes that the access token will
			 * expire in one hour from the time the response was generated.
			 * If omitted, the authorization server SHOULD provide the expiration time via other means or document the default value. 
			 **/
			if(!now.before(esitoNegoziazioneToken.getInformazioniNegoziazioneToken().getExpiresIn())){
				esitoNegoziazioneToken.setTokenScaduto();
				esitoNegoziazioneToken.setDateValide(false);
				esitoNegoziazioneToken.setDetails("AccessToken expired");	
			}
			
		}
			
		if(!esitoNegoziazioneToken.isValido()) {
			esitoNegoziazioneToken.setNoCache(!saveErrorInCache);
		}
	}
	
	static Map<String, Object> buildDynamicNegoziazioneTokenMap(Busta busta, 
			RequestInfo requestInfo, PdDContext pddContext, Logger log) {
		return TokenUtilities.buildDynamicMap(busta, 
				requestInfo, pddContext, log);
	}
	
	private static HttpResponse http(boolean debug, Logger log, PolicyNegoziazioneToken policyNegoziazioneToken,
			Busta busta, RequestInfo requestInfo, TipoPdD tipoPdD,
			NegoziazioneTokenDynamicParameters dynamicParameters, IProtocolFactory<?> protocolFactory,
			IState state, boolean delegata, String idModulo, PortaApplicativa pa, PortaDelegata pd,
			IDSoggetto idDominio, IDServizio idServizio,
			boolean refreshModeEnabled, String refreshToken,
			InformazioniNegoziazioneToken_DatiRichiesta datiRichiesta) throws TokenException, ProviderException, ProviderValidationException, UtilsException, SecurityException {
		
		
		// *** Raccola Parametri ***
		
		String endpoint = dynamicParameters.getEndpoint();
		if(datiRichiesta!=null) {
			datiRichiesta.setEndpoint(endpoint);
		}
		
		HttpRequestMethod httpMethod = dynamicParameters.getHttpMethod();
		
		// Nell'endpoint config ci finisce i timeout e la configurazione proxy
		Properties endpointConfig = policyNegoziazioneToken.getProperties().get(Costanti.POLICY_ENDPOINT_CONFIG);
		
		boolean https = policyNegoziazioneToken.isEndpointHttps();
		boolean httpsClient = false;
		Properties sslConfig = null;
		Properties sslClientConfig = null;
		if(https) {
			sslConfig = policyNegoziazioneToken.getProperties().get(Costanti.POLICY_ENDPOINT_SSL_CONFIG);
			httpsClient = policyNegoziazioneToken.isHttpsAuthentication();
			if(httpsClient) {
				sslClientConfig = policyNegoziazioneToken.getProperties().get(Costanti.POLICY_ENDPOINT_SSL_CLIENT_CONFIG);
			}
		}
		
		boolean basic = policyNegoziazioneToken.isBasicAuthentication();
		boolean basicAsAuthorizationHeader = policyNegoziazioneToken.isBasicAuthenticationAsAuthorizationHeader();
		String username = null;
		String password = null;
		if(basic) {
			username = dynamicParameters.getBasicUsername();
			password = dynamicParameters.getBasicPassword();
			if(datiRichiesta!=null) {
				datiRichiesta.setClientId(username);
			}
		}
		
		boolean bearer = policyNegoziazioneToken.isBearerAuthentication();
		String bearerToken = null;
		if(bearer) {
			bearerToken = dynamicParameters.getBearerToken();
			if(datiRichiesta!=null) {
				datiRichiesta.setClientToken(bearerToken);
			}
		}
		
		
		
		// *** Definizione Connettore ***
		
		ConnettoreMsg connettoreMsg = new ConnettoreMsg();
		ConnettoreBaseHTTP connettore = null;
		if(https) {
			connettoreMsg.setTipoConnettore(TipiConnettore.HTTPS.getNome());
			connettore = new ConnettoreHTTPS();
		}
		else {
			connettoreMsg.setTipoConnettore(TipiConnettore.HTTP.getNome());
			connettore = new ConnettoreHTTP();
		}
		connettoreMsg.setIdModulo(idModulo);
		connettoreMsg.setState(state);
		PolicyTimeoutConfig policyConfig = new PolicyTimeoutConfig();
		policyConfig.setPolicyNegoziazione(policyNegoziazioneToken.getName());
		connettoreMsg.setPolicyTimeoutConfig(policyConfig);
		
		ForwardProxy forwardProxy = null;
		ConfigurazionePdDManager configurazionePdDManager = ConfigurazionePdDManager.getInstance(state);
		if(configurazionePdDManager.isForwardProxyEnabled(requestInfo)) {
			try {
				IDGenericProperties policy = new IDGenericProperties();
				policy.setTipologia(CostantiConfigurazione.GENERIC_PROPERTIES_TOKEN_TIPOLOGIA_RETRIEVE);
				policy.setNome(policyNegoziazioneToken.getName());
				if(delegata) {
					forwardProxy = configurazionePdDManager.getForwardProxyConfigFruizione(idDominio, idServizio, policy, requestInfo);
				}
				else {
					forwardProxy = configurazionePdDManager.getForwardProxyConfigErogazione(idDominio, idServizio, policy, requestInfo);
				}
			}catch(Exception e) {
				throw new TokenException(GestoreToken.getMessageErroreGovWayProxy(e),e);
			}
		}
		if(forwardProxy!=null && forwardProxy.isEnabled() && forwardProxy.getConfigToken()!=null && forwardProxy.getConfigToken().isTokenRetrieveEnabled()) {
			connettoreMsg.setForwardProxy(forwardProxy);
		}
		
		connettore.setForceDisable_proxyPassReverse(true);
		connettore.init(dynamicParameters.getPddContext(), protocolFactory);
		connettore.setRegisterSendIntoContext(false);
		
		if(basic && basicAsAuthorizationHeader){
			InvocazioneCredenziali credenziali = new InvocazioneCredenziali();
			credenziali.setUser(username);
			credenziali.setPassword(password);
			connettoreMsg.setCredenziali(credenziali);
		}
		
		connettoreMsg.setConnectorProperties(new java.util.HashMap<>());
		connettoreMsg.getConnectorProperties().put(CostantiConnettori.CONNETTORE_LOCATION, endpoint);
		OpenSPCoop2Properties properties = OpenSPCoop2Properties.getInstance();
		Boolean debugTramiteProperties = properties.getGestioneRetrieveToken_debug();
		if(debugTramiteProperties!=null) {
			if(debugTramiteProperties) {
				connettoreMsg.getConnectorProperties().put(CostantiConnettori.CONNETTORE_DEBUG, true+"");
			}
		}
		else if(debug) { // impostazione del connettore
			connettoreMsg.getConnectorProperties().put(CostantiConnettori.CONNETTORE_DEBUG, true+"");
		}
		connettoreMsg.getConnectorProperties().put(CostantiConnettori.CONNETTORE_HTTP_DATA_TRANSFER_MODE, TransferLengthModes.CONTENT_LENGTH.getNome());
		GestoreToken.addProperties(connettoreMsg, endpointConfig);
		if(https) {
			GestoreToken.addProperties(connettoreMsg, sslConfig);
			if(httpsClient) {
				GestoreToken.addProperties(connettoreMsg, sslClientConfig);
			}
		}
		
		
		TransportRequestContext transportRequestContext = new TransportRequestContext(log);
		transportRequestContext.setRequestType(httpMethod.name());
		transportRequestContext.setHeaders(new HashMap<>());
		if(bearer) {
			String authorizationHeader = HttpConstants.AUTHORIZATION_PREFIX_BEARER+bearerToken;
			TransportUtils.setHeader(transportRequestContext.getHeaders(),HttpConstants.AUTHORIZATION, authorizationHeader);
		}
		
		Map<String, List<String>> pContent = new HashMap<>();
		boolean custom = false;
		if(refreshModeEnabled) {
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE, ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE_REFRESH_TOKEN);
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REFRESH_TOKEN, refreshToken);
		}
		else if(policyNegoziazioneToken.isClientCredentialsGrant()) {
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE, ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE_CLIENT_CREDENTIALS_GRANT);
		}
		else if(policyNegoziazioneToken.isRfc7523x509Grant() || policyNegoziazioneToken.isRfc7523ClientSecretGrant()) {
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE, ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE_CLIENT_CREDENTIALS_GRANT);
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_CLIENT_ASSERTION_TYPE, ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_CLIENT_ASSERTION_TYPE_RFC_7523);
		}
		else if(policyNegoziazioneToken.isUsernamePasswordGrant()) {
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE, ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_GRANT_TYPE_RESOURCE_OWNER_PASSWORD_CREDENTIALS_GRANT);
		}
		else if(policyNegoziazioneToken.isCustomGrant()) {
			custom = true;
		}
		else {
			throw new TokenException("Nessuna modalità definita");
		}
		
		transportRequestContext.removeHeader(HttpConstants.CONTENT_TYPE);
		String contentType = null;
		if(custom) {
			contentType = dynamicParameters.getHttpContentType();
			if(contentType!=null && StringUtils.isNotEmpty(contentType)) {
				TransportUtils.setHeader(transportRequestContext.getHeaders(),HttpConstants.CONTENT_TYPE, contentType);
			}
		}
		else {
			contentType = HttpConstants.CONTENT_TYPE_X_WWW_FORM_URLENCODED;
			TransportUtils.setHeader(transportRequestContext.getHeaders(),HttpConstants.CONTENT_TYPE, contentType);
		}
		
		if(!custom && basic && !basicAsAuthorizationHeader){
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_CLIENT_ID, username);
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_CLIENT_SECRET, password);
		}
		
		if(policyNegoziazioneToken.isUsernamePasswordGrant()) {
			String usernamePasswordGrantUsername = dynamicParameters.getUsernamePasswordGrantUsername();
			String usernamePasswordGrantPassword = dynamicParameters.getUsernamePasswordGrantPassword();
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_USERNAME, usernamePasswordGrantUsername);
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_PASSWORD, usernamePasswordGrantPassword);
			if(datiRichiesta!=null) {
				datiRichiesta.setUsername(usernamePasswordGrantUsername);
			}
		}
		
		if(policyNegoziazioneToken.isRfc7523x509Grant() || policyNegoziazioneToken.isRfc7523ClientSecretGrant()) {
			String jwt = buildJwt(policyNegoziazioneToken,
					busta, tipoPdD,
					dynamicParameters,
					protocolFactory, requestInfo);
			String signedJwt = signJwt(policyNegoziazioneToken, jwt, contentType, dynamicParameters, 
					busta, requestInfo, log);
			TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_CLIENT_ASSERTION, signedJwt);
			if(datiRichiesta!=null) {
				boolean infoNormalizzate = properties.isGestioneRetrieveToken_grantType_rfc7523_saveClientAssertionJWTInfo_transazioniRegistrazioneInformazioniNormalizzate();
				datiRichiesta.setJwtClientAssertion(new InformazioniJWTClientAssertion(log, signedJwt, infoNormalizzate));
			}
		}
		
		if(!custom) {
			List<String> scopes = policyNegoziazioneToken.getScopes(dynamicParameters);
			if(scopes!=null && !scopes.isEmpty()) {
				StringBuilder bf = new StringBuilder();
				for (String scope : scopes) {
					if(bf.length()>0) {
						bf.append(" ");
					}
					bf.append(scope);
				}
				if(bf.length()>0) {
					TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_SCOPE, bf.toString());
					if(datiRichiesta!=null) {
						datiRichiesta.setScope(scopes);
					}
				}
			}
			
			String aud = dynamicParameters.getAudience();
			if(aud!=null && !"".equals(aud)) {
				TransportUtils.setParameter(pContent,ClaimsNegoziazione.OAUTH2_RFC_6749_REQUEST_AUDIENCE, aud);
				if(datiRichiesta!=null) {
					datiRichiesta.setAudience(aud);
				}
			}
		}
		
		if(policyNegoziazioneToken.isPDND()) {
			String formClientId = dynamicParameters.getFormClientId();
			if(formClientId!=null && !"".equals(formClientId) && !Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(formClientId)) {
				TransportUtils.setParameter(pContent,Costanti.PDND_OAUTH2_RFC_6749_REQUEST_CLIENT_ID, formClientId);
				if(datiRichiesta!=null && datiRichiesta.getClientId()==null) { // dovrebbe essere null poiche' il tipo PDND non e' abilitabile per il tipo clientId/Secret
					datiRichiesta.setClientId(formClientId);
				}
			}
			
			String formResource = dynamicParameters.getFormResource();
			if(formResource!=null && !"".equals(formResource) && !Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(formResource)) {
				TransportUtils.setParameter(pContent,Costanti.PDND_OAUTH2_RFC_6749_REQUEST_RESOURCE, formResource);
			}
		}
		
		String parameters = dynamicParameters.getParameters();
		if(!custom && parameters!=null && !"".equals(parameters)) {
			Properties convertTextToProperties = PropertiesUtilities.convertTextToProperties(parameters);
			if(convertTextToProperties!=null && !convertTextToProperties.isEmpty()) {
				Map<String, String> mapParameters = new HashMap<>();
				Enumeration<Object> keys = convertTextToProperties.keys();
				while (keys.hasMoreElements()) {
					String nome = (String) keys.nextElement();
					if(nome!=null && !"".equals(nome)) {
						String valore = convertTextToProperties.getProperty(nome);
						if(valore!=null) {
							TransportUtils.setParameter(pContent, nome, valore);
							mapParameters.put(nome, valore);
						}
					}
				}
				if(datiRichiesta!=null && !mapParameters.isEmpty()) {
					datiRichiesta.setParameters(mapParameters);
				}
			}
		}
		
		String httpHeaders = dynamicParameters.getHttpHeaders();
		if(httpHeaders!=null && !"".equals(httpHeaders)) {
			Properties convertTextToProperties = PropertiesUtilities.convertTextToProperties(httpHeaders);
			if(convertTextToProperties!=null && !convertTextToProperties.isEmpty()) {
				Map<String, String> mapHttpHeaders = new HashMap<>();
				Enumeration<Object> keys = convertTextToProperties.keys();
				while (keys.hasMoreElements()) {
					String nome = (String) keys.nextElement();
					if(nome!=null && !"".equals(nome)) {
						String valore = convertTextToProperties.getProperty(nome);
						if(valore!=null) {
							TransportUtils.setHeader(transportRequestContext.getHeaders(),nome, valore);
							mapHttpHeaders.put(nome, valore);
						}
					}
				}
				if(datiRichiesta!=null && !mapHttpHeaders.isEmpty()) {
					datiRichiesta.setHttpHeaders(mapHttpHeaders);
				}
			}
		}

		String prefixUrl = "PREFIX?";
		byte [] content = null;
		if(custom) {
			String contentString = dynamicParameters.getHttpPayload();
			if(contentString!=null && StringUtils.isNotEmpty(contentString)) {
				content = contentString.getBytes();
				connettoreMsg.forceSendContent();
			}
		}
		else {
			String contentString = TransportUtils.buildUrlWithParameters(pContent, prefixUrl , log);
			contentString = contentString.substring(prefixUrl.length());
			if(contentString.startsWith("&") && contentString.length()>1) {
				contentString = contentString.substring(1);
			}
			content = contentString.getBytes();
		}
			
		try {
			OpenSPCoop2MessageParseResult pr = OpenSPCoop2MessageFactory.getDefaultMessageFactory().createMessage(MessageType.BINARY, transportRequestContext, content);
			OpenSPCoop2Message msg = pr.getMessage_throwParseException();
			connettoreMsg.setRequestMessage(msg);
			connettoreMsg.setGenerateErrorWithConnectorPrefix(false);
						
			connettore.setHttpMethod(msg);
			connettore.setPa(pa);
			connettore.setPd(pd);
		}catch(Exception e) {
			throw new TokenException(e.getMessage(),e);
		}
		
		ResponseCachingConfigurazione responseCachingConfigurazione = new ResponseCachingConfigurazione();
		responseCachingConfigurazione.setStato(StatoFunzionalita.DISABILITATO);
		String prefixConnettore = "[EndpointNegoziazioneToken: "+endpoint+"] ";
		if(endpointConfig.containsKey(CostantiConnettori.CONNETTORE_HTTP_PROXY_HOSTNAME)) {
			String hostProxy = endpointConfig.getProperty(CostantiConnettori.CONNETTORE_HTTP_PROXY_HOSTNAME);
			String portProxy = endpointConfig.getProperty(CostantiConnettori.CONNETTORE_HTTP_PROXY_PORT);
			prefixConnettore = prefixConnettore+GestoreToken.getMessageViaProxy(hostProxy, portProxy);
		}
		if(datiRichiesta!=null && datiRichiesta.getPrepareRequest()!=null) {
			datiRichiesta.setSendRequest(DateManager.getDate());
		}
		boolean send = connettore.send(responseCachingConfigurazione, connettoreMsg);
		if(datiRichiesta!=null && datiRichiesta.getPrepareRequest()!=null) {
			datiRichiesta.setReceiveResponse(DateManager.getDate());
		}
		if(!send) {
			if(connettore.getEccezioneProcessamento()!=null) {
				throw new TokenException(prefixConnettore+connettore.getErrore(), connettore.getEccezioneProcessamento());
			}
			else {
				throw new TokenException(prefixConnettore+connettore.getErrore());
			}
		}
		
		OpenSPCoop2Message msgResponse = connettore.getResponse();
		ByteArrayOutputStream bout = null;
		if(msgResponse!=null) {
			try {
				bout = new ByteArrayOutputStream();
				if(msgResponse!=null) {
					msgResponse.writeTo(bout, true);
				}
				bout.flush();
				bout.close();
			}catch(Exception e) {
				throw new TokenException(e.getMessage(),e);
			}
		}
		
		HttpResponse httpResponse = new HttpResponse();
		httpResponse.setResultHTTPOperation(connettore.getCodiceTrasporto());
		
		if(connettore.getCodiceTrasporto() >= 200 &&  connettore.getCodiceTrasporto() < 299) {
			String msgSuccess = prefixConnettore+GestoreToken.getMessageConnettoreConnessioneSuccesso(connettore);
			if(bout!=null && bout.size()>0) {
				log.debug(msgSuccess);
				httpResponse.setContent(bout.toByteArray());
				return httpResponse;
			}
			else {
				throw new TokenException(msgSuccess+GestoreToken.CONNETTORE_RISPOSTA_NON_PERVENUTA);
			}
		}
		else {
			String msgError = prefixConnettore+GestoreToken.getMessageConnettoreConnessioneErrore(connettore);
			if(bout!=null && bout.size()>0) {
				String e = msgError+": "+bout.toString();
				log.debug(e);
				httpResponse.setContent(bout.toByteArray());
				return httpResponse;
			}
			else {
				log.error(msgError);
				throw new TokenException(msgError);
			}
		}
		
		
	}
	
	private static String buildJwt(PolicyNegoziazioneToken policyNegoziazioneToken,
			Busta busta, TipoPdD tipoPdD,
			NegoziazioneTokenDynamicParameters dynamicParameters, 
			IProtocolFactory<?> protocolFactory, RequestInfo requestInfo) throws TokenException, UtilsException {
		
		// https://datatracker.ietf.org/doc/html/rfc7523
		
		OpenSPCoop2Properties op2Properties = OpenSPCoop2Properties.getInstance();
		JSONUtils jsonUtils = JSONUtils.getInstance(false);
		
		ObjectNode jwtPayload = jsonUtils.newObjectNode();

		// add iat, nbf, exp
		long nowMs = DateManager.getTimeMillis();
		long nowSeconds = nowMs/1000;
		
		String issuer = dynamicParameters.getSignedJwtIssuer();
		if(issuer==null) {
			if(TipoPdD.APPLICATIVA.equals(tipoPdD) && busta!=null && busta.getDestinatario()!=null) {
				issuer = busta.getDestinatario();
			}
			else if(TipoPdD.DELEGATA.equals(tipoPdD) && busta!=null && busta.getMittente()!=null) {
				issuer = busta.getMittente();
			}
			else {
				issuer = op2Properties.getIdentitaPortaDefault(protocolFactory!=null ? protocolFactory.getProtocol() : null, requestInfo).getNome();
			}
		}
		if(!Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(issuer)) {
			jwtPayload.put(Claims.OIDC_ID_TOKEN_ISSUER, issuer);
		}
		
		// For client authentication, the subject MUST be the "client_id" of the OAuth client.
		String clientId = dynamicParameters.getSignedJwtClientId();
		if(clientId==null) {
			throw new TokenException("ClientID undefined");
		}
		if(!Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(clientId)) {
			jwtPayload.put(Claims.INTROSPECTION_RESPONSE_RFC_7662_CLIENT_ID, clientId);
		}
		
		String subject = dynamicParameters.getSignedJwtSubject();
		if(StringUtils.isNotEmpty(subject)) {
			if(!Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(subject)) {
				jwtPayload.put(Claims.OIDC_ID_TOKEN_SUBJECT, subject);
			}
		}
		else {
			if(!Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(clientId)) {
				jwtPayload.put(Claims.OIDC_ID_TOKEN_SUBJECT, clientId);
			}
		}
		
		// The JWT MUST contain an "aud" (audience) claim containing a value that identifies the authorization server as an intended audience. 
		String jwtAudience = dynamicParameters.getSignedJwtAudience();
		if(jwtAudience==null) {
			throw new TokenException("JWT-Audience undefined");
		}
		if(!Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(jwtAudience)) {
			jwtPayload.put(Claims.OIDC_ID_TOKEN_AUDIENCE, jwtAudience);
		}
		
		// The JWT MAY contain an "iat" (issued at) claim that identifies the time at which the JWT was issued. 
		jwtPayload.put(Claims.JSON_WEB_TOKEN_RFC_7519_ISSUED_AT, nowSeconds);
		
		// The JWT MAY contain an "nbf" (not before) claim that identifies the time before which the token MUST NOT be accepted for processing.
		jwtPayload.put(Claims.JSON_WEB_TOKEN_RFC_7519_NOT_TO_BE_USED_BEFORE, nowSeconds);
		
		// The JWT MUST contain an "exp" (expiration time) claim that limits the time window during which the JWT can be used.
		int ttl = -1;
		try {
			ttl = policyNegoziazioneToken.getJwtTtlSeconds();
		}catch(Exception e) {
			throw new TokenException("Invalid JWT-TimeToLive value: "+e.getMessage(),e);
		}
		long expired = nowSeconds+ttl;
		jwtPayload.put(Claims.JSON_WEB_TOKEN_RFC_7519_EXPIRED, expired);
		
		// The JWT MAY contain a "jti" (JWT ID) claim that provides a unique identifier for the token.
		String jti = dynamicParameters.getSignedJwtJti();
		if(StringUtils.isNotEmpty(jti)) {
			if(!Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(jti)) {
				jwtPayload.put(Claims.JSON_WEB_TOKEN_RFC_7519_JWT_ID, jti);
			}
		}
		else {
			// per default genero un uuid
			String uuid = null;
			try {
				uuid = UniqueIdentifierManager.newUniqueIdentifier().toString();
			}catch(Exception e) {
				throw new TokenException("Invalid JWT-TimeToLive value: "+e.getMessage(),e);
			}
			jwtPayload.put(Claims.JSON_WEB_TOKEN_RFC_7519_JWT_ID, uuid);
		}
		
		if(policyNegoziazioneToken.isPDND()) {
			
			// purposeId
			String jwtPurposeId = dynamicParameters.getSignedJwtPurposeId();
			if(jwtPurposeId==null) {
				throw new TokenException("JWT-PurposeId undefined");
			}
			jwtPayload.put(Costanti.PDND_PURPOSE_ID, jwtPurposeId);
			
			// sessionInfo
			String sessionInfo = dynamicParameters.getSignedJwtSessionInfo();
			if(sessionInfo!=null && !"".equals(sessionInfo)) {
				Properties convertTextToProperties = PropertiesUtilities.convertTextToProperties(sessionInfo);
				ObjectNode sessionInfoPayload = null;
				if(convertTextToProperties!=null && !convertTextToProperties.isEmpty()) {
					Enumeration<Object> keys = convertTextToProperties.keys();
					while (keys.hasMoreElements()) {
						String nome = (String) keys.nextElement();
						if(nome!=null && !"".equals(nome)) {
							String valore = convertTextToProperties.getProperty(nome);
							if(valore!=null) {
								if(sessionInfoPayload ==null) {
									sessionInfoPayload = jsonUtils.newObjectNode();
								}
								jsonUtils.putValue(sessionInfoPayload, nome, valore);
							}
						}
					}
				}
				if(sessionInfoPayload!=null) {
					jwtPayload.set(Costanti.PDND_SESSION_INFO, sessionInfoPayload);
				}
			}
		}
		
		// digest Audit
		String digestAudit = dynamicParameters.getSignedJwtAuditDigest();
		if(digestAudit!=null && StringUtils.isNotEmpty(digestAudit)) {
			String algorithm = dynamicParameters.getSignedJwtAuditDigestAlgo();
			if(algorithm==null || StringUtils.isEmpty(algorithm)) {
				algorithm = Costanti.PDND_DIGEST_ALG_DEFAULT_VALUE;
			}
			
			ObjectNode digest = jsonUtils.newObjectNode();
			digest.put(Costanti.PDND_DIGEST_ALG, algorithm);
			digest.put(Costanti.PDND_DIGEST_VALUE, digestAudit);
			jwtPayload.set(Costanti.PDND_DIGEST, digest);
		}
		
		// claims
		String claims = dynamicParameters.getSignedJwtClaims();
		if(claims!=null && !"".equals(claims)) {
			Properties convertTextToProperties = PropertiesUtilities.convertTextToProperties(claims);
			if(convertTextToProperties!=null && !convertTextToProperties.isEmpty()) {
				Enumeration<Object> keys = convertTextToProperties.keys();
				while (keys.hasMoreElements()) {
					String nome = (String) keys.nextElement();
					if(nome!=null && !"".equals(nome)) {
						String valore = convertTextToProperties.getProperty(nome);
						if(valore!=null) {
							jsonUtils.putValue(jwtPayload, nome, valore);
						}
					}
				}
			}
		}
		
		return jsonUtils.toString(jwtPayload);
	}
	
	public static KeystoreParams readKeystoreParams(PolicyNegoziazioneToken policyNegoziazioneToken) throws TokenException {
		
		KeystoreParams kp = new KeystoreParams();
		
		String keystoreType = policyNegoziazioneToken.getJwtSignKeystoreType();
		if(keystoreType==null) {
			throw new TokenException(GestoreToken.KEYSTORE_TYPE_UNDEFINED);
		}
		kp.setType(keystoreType);
		
		fillKeystoreParams(policyNegoziazioneToken, keystoreType, kp);
				
		String keyAlias = policyNegoziazioneToken.getJwtSignKeyAlias();
		if(keyAlias==null && 
				!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
				!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
			throw new TokenException(GestoreToken.KEY_ALIAS_UNDEFINED);
		}
		kp.setKeyAlias(keyAlias);
		
		String keyPassword = policyNegoziazioneToken.getJwtSignKeyPassword();
		if(keyPassword==null && 
				!SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType) && 
				!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
				!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
			throw new TokenException(GestoreToken.KEY_PASSWORD_UNDEFINED);
		}
		kp.setKeyPassword(keyPassword);
		
		return kp;
	}
	private static void fillKeystoreParams(PolicyNegoziazioneToken policyNegoziazioneToken, String keystoreType, KeystoreParams kp) throws TokenException {
		String keystoreFile = null;
		String keystoreFilePublicKey = null;
		String keyPairAlgorithm = null;
		if(SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType)) {
			keystoreFile = policyNegoziazioneToken.getJwtSignKeystoreFile();
			if(keystoreFile==null) {
				throw new TokenException(GestoreToken.KEYSTORE_PRIVATE_KEY_UNDEFINED);
			}
			kp.setPath(keystoreFile);
			
			keystoreFilePublicKey = policyNegoziazioneToken.getJwtSignKeystoreFilePublicKey();
			if(keystoreFilePublicKey==null) {
				throw new TokenException(GestoreToken.KEYSTORE_PUBLIC_KEY_UNDEFINED);
			}
			kp.setKeyPairPublicKeyPath(keystoreFilePublicKey);
			
			keyPairAlgorithm = policyNegoziazioneToken.getJwtSignKeyPairAlgorithm();
			if(keyPairAlgorithm==null) {
				throw new TokenException(GestoreToken.KEYSTORE_KEY_PAIR_ALGORITHM_UNDEFINED);
			}
			kp.setKeyPairAlgorithm(keyPairAlgorithm);
		}
		else {
			keystoreFile = policyNegoziazioneToken.getJwtSignKeystoreFile();
			if(keystoreFile==null) {
				throw new TokenException(GestoreToken.KEYSTORE_KEYSTORE_FILE_UNDEFINED);
			}
			kp.setPath(keystoreFile);
		}
		
		String keystorePassword = policyNegoziazioneToken.getJwtSignKeystorePassword();
		if(keystorePassword==null && 
				!SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType) && 
				!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
				!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
			throw new TokenException(GestoreToken.KEYSTORE_KEYSTORE_PASSWORD_UNDEFINED);
		}
		kp.setPassword(keystorePassword);
		
		String keystoreByokPolicy = policyNegoziazioneToken.getJwtSignKeystoreByokPolicy();
		kp.setByokPolicy(keystoreByokPolicy);
	}
	
	private static String signJwt(PolicyNegoziazioneToken policyNegoziazioneToken, String payload, String contentType,
			NegoziazioneTokenDynamicParameters dynamicParameters,
			Busta busta, RequestInfo requestInfo, Logger log) throws TokenException, SecurityException, UtilsException {
		
		String signAlgo = policyNegoziazioneToken.getJwtSignAlgorithm();
		if(signAlgo==null) {
			throw new TokenException("SignAlgorithm undefined");
		}
		
		KeyStore ks = null;
		KeyPairStore keyPairStore = null;
		JWKSetStore jwtStore = null;
		String keyAlias = null;
		String keyPassword = null;
		if(policyNegoziazioneToken.isRfc7523x509Grant()) {
			
			if(policyNegoziazioneToken.isJwtSignKeystoreApplicativoModI()) {
				
				Map<String, Object> dynamicMap = null;
				if(dynamicParameters.isByokPolicyDefinedApplicativoModI()) {
					dynamicMap = DynamicMapBuilderUtils.buildDynamicMap(busta, 
							requestInfo, dynamicParameters.getPddContext(), log);
				}
				
				String keystoreType = dynamicParameters.getTipoKeystoreApplicativoModI();
				if(keystoreType==null) {
					throw new TokenException(GestoreToken.KEYSTORE_TYPE_UNDEFINED);
				}
				if(SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType)) {
					keyPairStore = dynamicParameters.getKeyPairStoreApplicativoModI(dynamicMap);
					if(keyPairStore==null) {
						throw new TokenException(GestoreToken.KEYSTORE_KEYPAIR_UNDEFINED);
					}
				}
				else if(SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType)) {
					jwtStore =  dynamicParameters.getJWKSetStoreApplicativoModI(dynamicMap);
					if(jwtStore==null) {
						throw new TokenException(GestoreToken.KEYSTORE_KEYSTORE_UNDEFINED);
					}
				}
				else {
					ks = dynamicParameters.getKeystoreApplicativoModI(dynamicMap);
					if(ks==null) {
						throw new TokenException(GestoreToken.KEYSTORE_KEYSTORE_UNDEFINED);
					}
				}
				
				keyAlias = dynamicParameters.getKeyAliasApplicativoModI();
				if(keyAlias==null && 
						!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
						!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
					throw new TokenException(GestoreToken.KEY_ALIAS_UNDEFINED);
				}
				keyPassword = dynamicParameters.getKeyPasswordApplicativoModI();
				if(keyPassword==null && 
						!SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType) && 
						!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
						!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
					throw new TokenException(GestoreToken.KEY_PASSWORD_UNDEFINED);
				}
			}
			else if(policyNegoziazioneToken.isJwtSignKeystoreFruizioneModI()) {
				
				Map<String, Object> dynamicMap = null;
				if(dynamicParameters.isByokPolicyDefinedFruizioneModI()) {
					dynamicMap = DynamicMapBuilderUtils.buildDynamicMap(busta, 
							requestInfo, dynamicParameters.getPddContext(), log);
				}
				
				String keystoreType = dynamicParameters.getTipoKeystoreFruizioneModI();
				if(keystoreType==null) {
					throw new TokenException(GestoreToken.KEYSTORE_TYPE_UNDEFINED);
				}
				if(SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType)) {
					keyPairStore = dynamicParameters.getKeyPairStoreFruizioneModI(dynamicMap);
					if(keyPairStore==null) {
						throw new TokenException(GestoreToken.KEYSTORE_KEYPAIR_UNDEFINED);
					}
				}
				else if(SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType)) {
					jwtStore =  dynamicParameters.getJWKSetStoreFruizioneModI(dynamicMap);
					if(jwtStore==null) {
						throw new TokenException(GestoreToken.KEYSTORE_KEYSTORE_UNDEFINED);
					}
				}
				else {
					ks = dynamicParameters.getKeystoreFruizioneModI(dynamicMap);
					if(ks==null) {
						throw new TokenException(GestoreToken.KEYSTORE_KEYSTORE_UNDEFINED);
					}
				}
				
				keyAlias = dynamicParameters.getKeyAliasFruizioneModI();
				if(keyAlias==null && 
						!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
						!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
					throw new TokenException(GestoreToken.KEY_ALIAS_UNDEFINED);
				}
				keyPassword = dynamicParameters.getKeyPasswordFruizioneModI();
				if(keyPassword==null && 
						!SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType) && 
						!SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType) && 
						!SecurityConstants.KEYSTORE_TYPE_PUBLIC_KEY_VALUE.equalsIgnoreCase(keystoreType)) {
					throw new TokenException(GestoreToken.KEY_PASSWORD_UNDEFINED);
				}
			}
			else {
				KeystoreParams kp = readKeystoreParams(policyNegoziazioneToken);

				String keystoreType = kp.getType();	
				String keystoreFile = kp.getPath();
				String keystoreFilePublicKey = kp.getKeyPairPublicKeyPath();
				String keyPairAlgorithm = kp.getKeyPairAlgorithm();
				String keystorePassword = kp.getPassword();
				
				keyAlias = kp.getKeyAlias();				
				keyPassword = kp.getKeyPassword();
				
				String keystoreByokPolicy = kp.getByokPolicy();
				BYOKRequestParams byokParams = null;
				if(BYOKProvider.isPolicyDefined(keystoreByokPolicy)) {
					Map<String, Object> dynamicMap = DynamicMapBuilderUtils.buildDynamicMap(busta, 
							requestInfo, dynamicParameters.getPddContext(), log);
					byokParams = BYOKProvider.getBYOKRequestParamsByUnwrapBYOKPolicy(keystoreByokPolicy, 
							dynamicMap );
				}
				
				if(SecurityConstants.KEYSTORE_TYPE_KEY_PAIR_VALUE.equalsIgnoreCase(keystoreType)) {
					keyPairStore = GestoreKeystoreCache.getKeyPairStore(requestInfo, keystoreFile, keystoreFilePublicKey, keyPassword, keyPairAlgorithm, byokParams);
				}
				else if(SecurityConstants.KEYSTORE_TYPE_JWK_VALUE.equalsIgnoreCase(keystoreType)) {
					jwtStore = GestoreKeystoreCache.getJwkSetStore(requestInfo, keystoreFile, byokParams);
				}
				else {
					MerlinKeystore merlinKs = GestoreKeystoreCache.getMerlinKeystore(requestInfo, keystoreFile, keystoreType, keystorePassword, byokParams);
					if(merlinKs==null) {
						throw new TokenException("Accesso al keystore '"+keystoreFile+"' non riuscito");
					}
					ks = merlinKs.getKeyStore();
				}
			}
		}
		
		JWSOptions options = new JWSOptions(JOSESerialization.COMPACT);
		
		JwtHeaders jwtHeaders = new JwtHeaders();
		if(policyNegoziazioneToken.isJwtSignIncludeKeyIdWithKeyAlias()) {
			jwtHeaders.setKid(keyAlias);
		}
		else if(policyNegoziazioneToken.isJwtSignIncludeKeyIdWithClientId()) {
			String clientId = dynamicParameters.getSignedJwtClientId();
			jwtHeaders.setKid(clientId);
		}
		else if(policyNegoziazioneToken.isJwtSignIncludeKeyIdCustom()) {
			String customId = dynamicParameters.getSignedJwtCustomId();
			jwtHeaders.setKid(customId);
		}
		else if(policyNegoziazioneToken.isJwtSignIncludeKeyIdApplicativoModI()) {
			String clientId = dynamicParameters.getKidApplicativoModI();
			jwtHeaders.setKid(clientId);
		}
		else if(policyNegoziazioneToken.isJwtSignIncludeKeyIdFruizioneModI()) {
			String clientId = dynamicParameters.getKidFruizioneModI();
			jwtHeaders.setKid(clientId);
		}
		if(policyNegoziazioneToken.isJwtSignIncludeX509Cert()) {
			jwtHeaders.setAddX5C(true);
		}
		String url = dynamicParameters.getSignedJwtX509Url();
		if(url!=null && !"".equals(url)) {
			try {
				jwtHeaders.setX509Url(new URI(url));
			}catch(Exception e) {
				throw new TokenException(e.getMessage(),e);
			}
		}
		if(policyNegoziazioneToken.isJwtSignIncludeX509CertSha1()) {
			jwtHeaders.setX509IncludeCertSha1(true);
		}
		if(policyNegoziazioneToken.isJwtSignIncludeX509CertSha256()) {
			jwtHeaders.setX509IncludeCertSha256(true);
		}
		if(policyNegoziazioneToken.isJwtSignJoseContentType() &&
			contentType!=null && !"".equals(contentType)) {
			jwtHeaders.setContentType(contentType);
		}
		String type = policyNegoziazioneToken.getJwtSignJoseType();
		if(type!=null && !"".equals(type) && !Costanti.POLICY_RETRIEVE_TOKEN_JWT_CLAIM_UNDEFINED.equals(type)) { // funzionalita' undefined undocumented
			jwtHeaders.setType(type);
		}
		if(ks!=null) {
			Certificate cert = ks.getCertificate(keyAlias);
			if(cert instanceof X509Certificate) {
				jwtHeaders.addX509cert((X509Certificate)cert);
			}
		}
		
		if(policyNegoziazioneToken.isRfc7523ClientSecretGrant()) {
		
			String clientSecret = policyNegoziazioneToken.getJwtClientSecret();
			if(clientSecret==null) {
				throw new TokenException("ClientSecret undefined");
			}
			
			JsonSignature jsonSignature = new JsonSignature(clientSecret, signAlgo, jwtHeaders, options);
			return jsonSignature.sign(payload);
		}
		else if(policyNegoziazioneToken.isRfc7523x509Grant()) {
						
			JsonSignature jsonSignature = null;
			if(keyPairStore!=null || jwtStore!=null) {
				JsonWebKeys jwk = null;
				if(keyPairStore!=null) {
					jwk = keyPairStore.getJwkSet().getJsonWebKeys();
					keyAlias = keyPairStore.getJwkSetKid();
				}
				else {
					jwk = jwtStore.getJwkSet().getJsonWebKeys();
				}
				jsonSignature = new JsonSignature(jwk, false, keyAlias, signAlgo, jwtHeaders, options);
			}
			else {
				jsonSignature = new JsonSignature(ks, false, keyAlias,  keyPassword, signAlgo, jwtHeaders, options);
			}
			return jsonSignature.sign(payload);
		}
		else {
			throw new TokenException("JWT Signed mode unknown");
		}
		
	}
}