JsonVerifySignature.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.security;

import java.io.File;
import java.security.PublicKey;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Properties;

import org.apache.cxf.common.util.Base64UrlUtility;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKey;
import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
import org.apache.cxf.rs.security.jose.jws.JwsCompactConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
import org.apache.cxf.rs.security.jose.jws.JwsJsonConsumer;
import org.apache.cxf.rs.security.jose.jws.JwsJsonProducer;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
import org.apache.cxf.rs.security.jose.jws.JwsUtils;
import org.apache.cxf.rt.security.rs.RSSecurityConstants;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.certificate.ArchiveLoader;
import org.openspcoop2.utils.certificate.CertificateInfo;
import org.openspcoop2.utils.certificate.JWK;
import org.openspcoop2.utils.certificate.JWKSet;
import org.openspcoop2.utils.certificate.KeyStore;
import org.openspcoop2.utils.certificate.remote.IRemoteStoreProvider;
import org.openspcoop2.utils.certificate.remote.RemoteKeyType;
import org.openspcoop2.utils.certificate.remote.RemoteStoreConfig;
import org.openspcoop2.utils.io.Base64Utilities;
import org.openspcoop2.utils.transport.http.HttpResponse;
import org.openspcoop2.utils.transport.http.HttpUtilities;
import org.openspcoop2.utils.transport.http.IOCSPValidator;

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

	private JwsSignatureVerifier provider;
	private Properties propsConfig;
	private JWTOptions options;
	private Properties properties;
	private boolean dynamicProvider;
	
	private JwsHeaders jwsDecodedHeaders;
	private String decodedHeader;
	
	private String decodedPayload;
	private byte[] decodedPayloadAsByte;
	
	private KeyStore trustStoreCertificatiX509; // per verificare i certificati presenti nell'header
	public KeyStore getTrustStoreCertificatiX509() {
		return this.trustStoreCertificatiX509;
	}
	private JsonWebKeys jsonWebKeys; // dove prendere il certificato per validare rispetto al kid
	
	private IRemoteStoreProvider remoteStoreProvider; // dove prendere il certificato remoto per validare rispetto al kid
	private RemoteKeyType remoteStoreKeyType; 
	private RemoteStoreConfig remoteStoreConfig;
	
	private KeyStore trustStoreHttps; // per accedere ad un endpoint https dove scaricare i certificati
	public KeyStore getTrustStoreHttps() {
		return this.trustStoreHttps;
	}
	private CertStore crlHttps; // per verificare i certificati http server rispetto alle crl
	public void setCrlHttps(CertStore crlHttps) {
		this.crlHttps = crlHttps;
	}
	private IOCSPValidator ocspValidatorHttps; // per verificare i certificati http server rispetto a servizi OCSP
	public void setOcspValidatorHttps(IOCSPValidator ocspValidator) {
		this.ocspValidatorHttps = ocspValidator;
	}
	
	private CertStore crlX509; // per verificare i certificati rispetto alle crl
	public void setCrlX509(CertStore crlX509) {
		this.crlX509 = crlX509;
	}
	private IOCSPValidator ocspValidatorX509; // per verificare i certificati rispetto a servizi OCSP
	public void setOcspValidatorX509(IOCSPValidator ocspValidator) {
		this.ocspValidatorX509 = ocspValidator;
	}

	private CertificateValidityCheck validityCheck = CertificateValidityCheck.ENABLED; // validazione (date) del certificato utilizzato per firmare il token 	
	public void setValidityCheck(CertificateValidityCheck validityCheck) {
		this.validityCheck = validityCheck;
	}

	public JsonVerifySignature(Properties props, JWTOptions options) throws UtilsException{
		try {
			this.propsConfig = props;
			this.dynamicProvider = JsonUtils.isDynamicProvider(props); // rimuove l'alias
			if(this.dynamicProvider) {
				this.properties = props;
			}
			else {
				this.provider = loadProviderFromProperties(props, null); // nel caso di jceks deve essere definito l'algoritmo se non e' dinamico
			}
			this.options = options;
		}catch(Exception t) {
			throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}

	private JwsSignatureVerifier loadProviderFromProperties(Properties props, org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm signatureAlgorithm) throws UtilsException {
		File fTmp = null;
		try {
			fTmp = JsonUtils.normalizeProperties(props); // in caso di url http viene letta la risorsa remota e salvata in tmp
			/**java.util.Enumeration<?> en = props.keys();
			while (en.hasMoreElements()) {
				String key = (String) en.nextElement();
				System.out.println("- ["+key+"] ["+props.getProperty(key)+"]");
			}*/
			
			JwsSignatureVerifier providerReturn = null;
			if(signatureAlgorithm!=null) {
				providerReturn = JsonUtils.getJwsSignatureVerifier(props, signatureAlgorithm);
			}
			else {
				providerReturn = JsonUtils.getJwsSignatureVerifier(props);
			}
			if(providerReturn==null) {
				providerReturn = JwsUtils.loadSignatureVerifier(JsonUtils.newMessage(), props, new JwsHeaders());
			}
			if(providerReturn==null) {
				throw new UtilsException("JwsSignatureVerifier provider not found");
			}
			return providerReturn;
		}finally {
			try {
				if(fTmp!=null) {
					java.nio.file.Files.delete(fTmp.toPath());
				}
			}catch(Exception t) {
				// ignore
			}
		}
	}
	
	public JsonVerifySignature(java.security.KeyStore keystore, String alias, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		this(new KeyStore(keystore), alias, signatureAlgorithm, options);
	}
	public JsonVerifySignature(KeyStore keystore, String alias, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		try {
			org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
			this.provider = JwsUtils.getPublicKeySignatureVerifier((X509Certificate) keystore.getCertificate(alias), algo);
			this.options=options;
			
			this.trustStoreCertificatiX509 = keystore; // per abilitare la validazione crl o ocsp
			
		}catch(Exception t) {
			throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}
	
	public JsonVerifySignature(java.security.KeyStore keystore, String alias, String passwordPrivateKey, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		this(new KeyStore(keystore), alias, passwordPrivateKey, signatureAlgorithm, options);
	}
	public JsonVerifySignature(KeyStore keystore, String alias, String passwordPrivateKey, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		try {
			org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
			this.provider = JwsUtils.getHmacSignatureVerifier(keystore.getSecretKey(alias, passwordPrivateKey).getEncoded(), algo);
			if(this.provider==null) {
				throw new UtilsException("(JCEKS) JwsSignatureVerifier init failed; check signature algorithm ("+signatureAlgorithm+")");
			}
			this.options=options;
			
			this.trustStoreCertificatiX509 = keystore; // per abilitare la validazione crl o ocsp
			
		}catch(Exception t) {
			throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}
	
	public JsonVerifySignature(JsonWebKeys jsonWebKeys, boolean secretKey, String alias, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		this(JsonUtils.readKey(jsonWebKeys, alias), secretKey, signatureAlgorithm, options);
	}	
	public JsonVerifySignature(JsonWebKey jsonWebKey, boolean secretKey, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		try {
			org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
			if(secretKey) {
				this.provider = JwsUtils.getSignatureVerifier(jsonWebKey, algo);
				if(this.provider==null) {
					throw new UtilsException("(JsonWebKey) JwsSignatureVerifier init failed; check signature algorithm ("+signatureAlgorithm+")");
				}
			}
			else {
				this.provider = JwsUtils.getPublicKeySignatureVerifier(JwkUtils.toRSAPublicKey(jsonWebKey), algo);
			}
			this.options=options;
		}catch(Exception t) {
			throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}
	
	public JsonVerifySignature(String secret, String signatureAlgorithm, JWTOptions options) throws UtilsException{
		try {
			org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
			this.provider = JwsUtils.getHmacSignatureVerifier(secret.getBytes(), algo);
			if(this.provider==null) {
				throw new UtilsException("(Secret) JwsSignatureVerifier init failed; check signature algorithm ("+signatureAlgorithm+")");
			}
			this.options=options;
		}catch(Exception t) {
			throw JsonUtils.convert(options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}
	
	
	public JsonVerifySignature(JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(null, null, 
				null, options);
	}
	public JsonVerifySignature(Properties propsTrustStoreHttps, java.security.KeyStore trustStoreVerificaCertificato, JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(propsTrustStoreHttps, null, 
				new KeyStore(trustStoreVerificaCertificato), options);
	}
	public JsonVerifySignature(Properties propsTrustStoreHttps, KeyStore trustStore, JWTOptions options){
		initVerifyCertificatiHeaderJWTEngine(propsTrustStoreHttps, null,
				trustStore, options);
	}
	public JsonVerifySignature(java.security.KeyStore trustStoreHttps, java.security.KeyStore trustStoreVerificaCertificato, JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(null, new KeyStore(trustStoreHttps), 
				new KeyStore(trustStoreVerificaCertificato), options);
	}
	public JsonVerifySignature(KeyStore trustStoreHttps, KeyStore trustStore, JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(null, trustStoreHttps,
				trustStore, options);
	}
	public JsonVerifySignature(java.security.KeyStore trustStoreVerificaCertificato, JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(null, null, 
				new KeyStore(trustStoreVerificaCertificato), options);
	}
	public JsonVerifySignature(KeyStore trustStore, JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(null, null, 
				trustStore, options);
	}
	private void initVerifyCertificatiHeaderJWTEngine(Properties propsTrustStoreHttps, KeyStore trustStoreHttps,
			KeyStore trustStoreVerificaCertificato, JWTOptions options) {
		// verra usato l'header per validare ed ottenere il certificato
		this.options=options;
		this.properties = propsTrustStoreHttps; // le proprieta' servono per risolvere le url https
		this.trustStoreHttps = trustStoreHttps;
		this.trustStoreCertificatiX509 = trustStoreVerificaCertificato;
	}
	
	public JsonVerifySignature(JsonWebKeys jsonWebKeys, JWTOptions options) {
		initVerifyCertificatiHeaderJWTEngine(jsonWebKeys, options);
	}
	private void initVerifyCertificatiHeaderJWTEngine(JsonWebKeys jsonWebKeys, JWTOptions options) {
		// verra usato l'header per validare ed ottenere il certificato
		this.options=options;
		this.jsonWebKeys = jsonWebKeys;
	}

	
	public JsonVerifySignature(IRemoteStoreProvider remoteStoreProvider, RemoteKeyType remoteStoreKeyType, RemoteStoreConfig remoteStoreConfig, JWTOptions options) {
		initVerifyCertificatiHeaderRemoteStoreEngine(remoteStoreProvider, remoteStoreKeyType, remoteStoreConfig, options);
	}
	private void initVerifyCertificatiHeaderRemoteStoreEngine(IRemoteStoreProvider remoteStoreProvider, RemoteKeyType remoteStoreKeyType, RemoteStoreConfig remoteStoreConfig, JWTOptions options) {
		// verra usato l'header per validare ed ottenere il certificato
		this.options=options;
		this.remoteStoreProvider = remoteStoreProvider;
		this.remoteStoreKeyType = remoteStoreKeyType;
		this.remoteStoreConfig = remoteStoreConfig;
	}
	

	public boolean verify(String jsonString) throws UtilsException{
		try {
			switch(this.options.getSerialization()) {
				case JSON: return verifyJson(jsonString);
				case COMPACT: return verifyCompact(jsonString);
				default: throw new UtilsException("Unsupported serialization '"+this.options.getSerialization()+"'");
			}
		}
		catch(Exception t) {
			throw JsonUtils.convert(this.options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}

	public boolean verifyDetached(String jsonDetachedSignature, String jsonDetachedPayload) throws UtilsException{
		try {
			switch(this.options.getSerialization()) {
				case JSON: return verifyDetachedJson(jsonDetachedSignature, jsonDetachedPayload);
				case COMPACT: return verifyDetachedCompact(jsonDetachedSignature, jsonDetachedPayload);
				default: throw new UtilsException("Unsupported serialization '"+this.options.getSerialization()+"'");
			}
		}
		catch(Exception t) {
			throw JsonUtils.convert(this.options.getSerialization(), JsonUtils.SIGNATURE,JsonUtils.RECEIVER,t);
		}
	}

	private boolean verifyDetachedJson(String jsonDetachedSignature, String jsonDetachedPayload) throws UtilsException {
		JwsJsonProducer producer = new JwsJsonProducer(jsonDetachedPayload);
		JwsJsonConsumer consumer = new JwsJsonConsumer(jsonDetachedSignature, producer.getUnsignedEncodedPayload());
		return this.verifyJsonEngine(consumer);
	}
	private boolean verifyJson(String jsonString) throws UtilsException {
		JwsJsonConsumer consumer = new JwsJsonConsumer(jsonString);
		return this.verifyJsonEngine(consumer);
	}	
	private boolean verifyJsonEngine(JwsJsonConsumer consumer) throws UtilsException {
		
		JwsHeaders jwsHeaders = null;
		if(consumer.getSignatureEntries()!=null && !consumer.getSignatureEntries().isEmpty() &&
			// prendo la prima entry
			consumer.getSignatureEntries().get(0)!=null
			) {
			jwsHeaders = consumer.getSignatureEntries().get(0).getProtectedHeader();
		}
		JwsSignatureVerifier providerInternal = getProvider(jwsHeaders);
		
		boolean result = consumer.verifySignatureWith(providerInternal);
		this.decodedPayload = consumer.getDecodedJwsPayload();
		this.decodedPayloadAsByte = consumer.getDecodedJwsPayloadBytes();
		return result;
	}

	private boolean verifyDetachedCompact(String jsonDetachedSignature, String jsonDetachedPayload) throws UtilsException {
		return verifyCompactEngine(jsonDetachedSignature, jsonDetachedPayload);
	}
	private boolean verifyCompact(String jsonSignature) throws UtilsException {
		return verifyCompactEngine(jsonSignature, null);
	}
	private boolean verifyCompactEngine(String jsonSignature, String jsonDetachedPayload) throws UtilsException {
		
		JwsCompactConsumer consumer = null;
		if(jsonDetachedPayload==null) {
			consumer = new JwsCompactConsumer(jsonSignature);
		}
		else {
			consumer = new JwsCompactConsumer(jsonSignature, Base64UrlUtility.encode(jsonDetachedPayload));
		}
		
		this.jwsDecodedHeaders = consumer.getJwsHeaders();
		try {
			this.decodedHeader = consumer.getDecodedJsonHeaders();
		}catch(Exception e) {
			// ignore
		}
		
		JwsSignatureVerifier providerInternal = getProvider(this.jwsDecodedHeaders);
				
		boolean result = consumer.verifySignatureWith(providerInternal);
		if(jsonDetachedPayload!=null) {
			this.decodedPayload = jsonDetachedPayload;
			this.decodedPayloadAsByte = jsonDetachedPayload.getBytes();
		}
		else {
			this.decodedPayload = consumer.getDecodedJwsPayload();
			this.decodedPayloadAsByte = consumer.getDecodedJwsPayloadBytes();
		}
		return result;
	}
	
	public String getDecodedPayload() {
		return this.decodedPayload;
	}

	public byte[] getDecodedPayloadAsByte() {
		return this.decodedPayloadAsByte;
	}
	
	public JwsHeaders getJwsDecodedHeaders() {
		return this.jwsDecodedHeaders;
	}
	
	public String getDecodedHeader() {
		return this.decodedHeader;
	}
	
	private X509Certificate x509Certificate;
	private PublicKey publicKey;
	private String kid;
	public X509Certificate getX509Certificate() {
		return this.x509Certificate;
	}
	public PublicKey getRsaPublicKey() {
		return this.publicKey;
	}
	public String getKid() {
		return this.kid;
	}

	private String getProcessErrorMsg(String mode, Exception e) {
		return "Process '"+mode+"' error: "+e.getMessage();
	}
	private JwsSignatureVerifier getProvider(JwsHeaders jwsHeaders) throws UtilsException {
		
		JwsSignatureVerifier providerReturn = this.provider;
		if(jwsHeaders==null) {
			return providerReturn;
		}
				
		if(this.dynamicProvider) {
			/** String alias = JsonUtils.readAlias(jsonSignature); */
			String alias = jwsHeaders.getKeyId();
			Properties pNew = new Properties();
			pNew.putAll(this.properties);
			/** System.out.println("ALIAS ["+alias+"]"); */
			pNew.put(RSSecurityConstants.RSSEC_KEY_STORE_ALIAS, alias);
			providerReturn = loadProviderFromProperties(pNew, jwsHeaders.getSignatureAlgorithm());
		}
		
		if(providerReturn==null) {
			org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo = jwsHeaders.getSignatureAlgorithm();
			if(algo==null) {
				throw new UtilsException("SignatureAlgorithm not found");
			}
			
			if(jwsHeaders.getX509Chain()!=null && !jwsHeaders.getX509Chain().isEmpty() && this.options.isPermitUseHeaderX5C()) {
				try {
					// https://tools.ietf.org/html/rfc7515#section-4.1.6: The certificate containing the public key corresponding to the key used to digitally sign the JWS MUST be the first certificate.
					byte [] cer = Base64Utilities.decode(jwsHeaders.getX509Chain().get(0));
					CertificateInfo certificatoInfo = ArchiveLoader.load(cer).getCertificate();
					if(this.trustStoreCertificatiX509!=null) {
						JsonUtils.validate(certificatoInfo, 
								this.trustStoreCertificatiX509, this.crlX509, this.ocspValidatorX509, JwtHeaders.JWT_HDR_X5C, 
								true,
								this.validityCheck);
					}
					this.x509Certificate = certificatoInfo.getCertificate();
					providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.x509Certificate, algo);
				}catch(Exception e) {
					throw new UtilsException(getProcessErrorMsg(JwtHeaders.JWT_HDR_X5C,e),e);
				}
			}
			else if(jwsHeaders.getJsonWebKey()!=null && this.options.isPermitUseHeaderJWK()) {
				try {
					this.publicKey = JwkUtils.toRSAPublicKey(jwsHeaders.getJsonWebKey());
					providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.publicKey, algo);
				}catch(Exception e) {
					throw new UtilsException(getProcessErrorMsg(JwtHeaders.JWT_HDR_JWK,e),e);
				}
			}
			else if(
					(jwsHeaders.getX509Url()!=null && this.options.isPermitUseHeaderX5U()) 
					||
					(jwsHeaders.getJsonWebKeysUrl()!=null && this.options.isPermitUseHeaderJKU())
					) {
				
				boolean x509 = true;
				String path = jwsHeaders.getX509Url();
				String hdr = JwtHeaders.JWT_HDR_X5U;
				if(path==null) {
					path=jwsHeaders.getJsonWebKeysUrl();
					x509 = false;
					hdr = JwtHeaders.JWT_HDR_JKU;
				}
				try {
					String prefixPath = "Resource '"+path+"'";
					byte [] cer = null;
					if(this.properties!=null) {
						this.properties.put(RSSecurityConstants.RSSEC_KEY_STORE_FILE, path);
						cer = JsonUtils.readKeystoreFromURI(this.properties);
					}
					else {
						HttpResponse httpResponse = getHttpResponse(path, prefixPath);
						if(httpResponse==null || httpResponse.getContent()==null) {
							throw new UtilsException(prefixPath+" unavailable");
						}
						if(httpResponse.getResultHTTPOperation()!=200) {
							throw new UtilsException("Retrieve '"+path+"' failed (returnCode:"+httpResponse.getResultHTTPOperation()+")");
						}
						cer = httpResponse.getContent();
					}
					if(cer==null) {
						throw new UtilsException(prefixPath+" unavailable");
					}
					if(x509) {
						CertificateInfo certificatoInfo = ArchiveLoader.load(cer).getCertificate();
						if(this.trustStoreCertificatiX509!=null) {
							JsonUtils.validate(certificatoInfo, 
									this.trustStoreCertificatiX509, this.crlX509, this.ocspValidatorX509, hdr, 
									true,
									this.validityCheck);
						}
						this.x509Certificate = certificatoInfo.getCertificate();
						providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.x509Certificate, algo);
					}
					else {
						JWKSet set = new JWKSet(new String(cer));
						JsonWebKeys jsonWebKeysInternal = set.getJsonWebKeys();
						JsonWebKey jsonWebKey = null;
						if(jsonWebKeysInternal.size()==1) {
							jsonWebKey = jsonWebKeysInternal.getKeys().get(0);
						}
						else {
							if(jwsHeaders.getKeyId()==null) {
								throw new UtilsException("Kid non definito e JwkSet contiene più di un certificato");
							}
							jsonWebKey = jsonWebKeysInternal.getKey(jwsHeaders.getKeyId());
						}
						if(jsonWebKey==null) {
							throw new UtilsException("JsonWebKey non trovata");
						}
						this.publicKey = JwkUtils.toRSAPublicKey(jsonWebKey);
						providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.publicKey, algo);
					}
				}catch(Exception e) {
					throw new UtilsException(getProcessErrorMsg(hdr,e),e);
				}
			}
			else if(jwsHeaders.getKeyId()!=null && this.options.isPermitUseHeaderKID()) {
				try {
					this.kid = jwsHeaders.getKeyId();
					if(this.jsonWebKeys!=null) {
						JsonWebKey jsonWebKey = getJsonWebKeyByKidIgnoreException();
						if(jsonWebKey!=null) {
							this.publicKey = JwkUtils.toRSAPublicKey(jsonWebKey);
							providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.publicKey, algo);
						}
					}
					if(providerReturn==null &&
						this.trustStoreCertificatiX509!=null &&
						this.trustStoreCertificatiX509.existsAlias(this.kid)) {
						Certificate cer = this.trustStoreCertificatiX509.getCertificate(this.kid);
						if(cer instanceof X509Certificate) {
							this.x509Certificate = (X509Certificate) cer;
							
							// La validazione serve per verificare la data e il crl
							JsonUtils.validate(new CertificateInfo(this.x509Certificate, this.kid), 
									this.trustStoreCertificatiX509, this.crlX509, this.ocspValidatorX509, JwtHeaders.JWT_HDR_KID, 
									false,
									this.validityCheck);
							
							providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.x509Certificate, algo);
						}
					}
					if(providerReturn==null &&
							this.remoteStoreProvider!=null && this.remoteStoreConfig!=null && this.remoteStoreKeyType!=null) {
						providerReturn = getProviderByRemoteStore(algo);
					}
					if(providerReturn==null) {
						throw new UtilsException("Certificato, corrispondente al kid '"+this.kid+"', non trovato nel TrustStore dei certificati");
					}
				}catch(Exception e) {
					throw new UtilsException(getProcessErrorMsg(JwtHeaders.JWT_HDR_KID,e),e);
				}
			}
			else if(
					(jwsHeaders.getX509ThumbprintSHA256()!=null && this.options.isPermitUseHeaderX5T_256())
					||
					(jwsHeaders.getX509Thumbprint()!=null && this.options.isPermitUseHeaderX5T())
					) {
				String hdr = JwtHeaders.JWT_HDR_X5T;
				if (jwsHeaders.getX509ThumbprintSHA256()!=null) {
					hdr = JwtHeaders.JWT_HDR_X5t_S256;
				}
				try {
					if(this.trustStoreCertificatiX509==null) {
						throw new UtilsException("TrustStore dei certificati non fornito"); 
					}
					Certificate cer = null;
					if(jwsHeaders.getX509ThumbprintSHA256()!=null) {
						cer = this.trustStoreCertificatiX509.getCertificateByDigestSHA256UrlEncoded(jwsHeaders.getX509ThumbprintSHA256());
					}
					else{
						cer = this.trustStoreCertificatiX509.getCertificateByDigestSHA1UrlEncoded(jwsHeaders.getX509Thumbprint());
					}
					if(cer==null) {
						throw new UtilsException("Certificato, corrispondente al digest indicato, non trovato nel TrustStore dei certificati");
					}	
					if(cer instanceof X509Certificate) {
						this.x509Certificate = (X509Certificate) cer;
						
						// La validazione serve per verificare la data e il crl
						JsonUtils.validate(new CertificateInfo(this.x509Certificate, hdr), 
								this.trustStoreCertificatiX509, this.crlX509, this.ocspValidatorX509, hdr, 
								false,
								this.validityCheck);
						
						providerReturn = JwsUtils.getPublicKeySignatureVerifier(this.x509Certificate, algo);
					}
					else {
						throw new UtilsException("Certificato indicato non è nel formato X.509");
					}
				}catch(Exception e) {
					throw new UtilsException(getProcessErrorMsg(hdr,e),e);
				}
			}
			else {
				List<String> hdrNotPermitted = this.options.headersNotPermitted(jwsHeaders);
				String notPermitted = "";
				if(hdrNotPermitted!=null && !hdrNotPermitted.isEmpty()) {
					notPermitted = "; header trovati ma non abilitati all'utilizzo: "+hdrNotPermitted;
				}
				throw new UtilsException("Non è stato trovato alcun header che consentisse di recuperare il certificato per effettuare la validazione"+notPermitted);
			}
		}
		else {
			if(this.x509Certificate==null && providerReturn instanceof org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier) {
				org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier certVer = (org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier) providerReturn;
				this.x509Certificate = certVer.getX509Certificate();
				
				KeyStore trustStore = this.trustStoreCertificatiX509;
				if(trustStore==null &&
					this.propsConfig!=null) {
					trustStore = JsonUtils.getKeyStore(this.propsConfig);
				}
				
				if(trustStore!=null) {
					try {
						CertificateInfo certificatoInfo = new CertificateInfo(this.x509Certificate, "x509");
						JsonUtils.validate(certificatoInfo, 
								trustStore, this.crlX509, this.ocspValidatorX509, null, true,
								this.validityCheck);
					}catch(Exception e) {
						throw new UtilsException(e.getMessage(),e);
					}
				}
			}
		}
		
		return providerReturn;
	}
	
	private JsonWebKey getJsonWebKeyByKidIgnoreException(){
		try {
			return this.jsonWebKeys.getKey(this.kid);
		}catch(Exception e) {
			// key not found
		}
		return null;
	}
	
	private HttpResponse getHttpResponse(String path, String prefixPath) throws UtilsException{
		HttpResponse httpResponse = null;
		try {
			if(this.trustStoreHttps!=null) {
				httpResponse = HttpUtilities.getHTTPSResponse(path, this.trustStoreHttps.getKeystore(), this.crlHttps, this.ocspValidatorHttps);
			}
			else {
				httpResponse = HttpUtilities.getHTTPResponse(path);
			}
		}catch(Exception e) {
			throw new UtilsException(prefixPath+" unavailable: "+e.getMessage(),e);
		}
		return httpResponse;
	}
	
	private JwsSignatureVerifier getProviderByRemoteStore(org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm algo) throws UtilsException {
		switch (this.remoteStoreKeyType) {
		case JWK:
			JWK jwk = this.remoteStoreProvider.readJWK(this.kid, this.remoteStoreConfig);
			if(jwk==null) {
				throw this.newUtilsExceptionErrorKidUnavailable(true);
			}
			JsonWebKey jsonWebKey = jwk.getJsonWebKey();
			if(jsonWebKey!=null) {
				this.publicKey = JwkUtils.toRSAPublicKey(jsonWebKey);
				return JwsUtils.getPublicKeySignatureVerifier(this.publicKey, algo);
			}
			else {
				throw this.newUtilsExceptionErrorKidUnavailable(true);
			}
		case PUBLIC_KEY:
			this.publicKey = this.remoteStoreProvider.readPublicKey(this.kid, this.remoteStoreConfig);
			if(this.publicKey==null) {
				throw this.newUtilsExceptionErrorKidUnavailable(true);
			}
			return JwsUtils.getPublicKeySignatureVerifier(this.publicKey, algo);
		case X509:
			org.openspcoop2.utils.certificate.Certificate certificate = this.remoteStoreProvider.readX509(this.kid, this.remoteStoreConfig);
			if(certificate==null) {
				throw this.newUtilsExceptionErrorKidUnavailable(false);
			}
			this.x509Certificate = certificate.getCertificate().getCertificate();
			return JwsUtils.getPublicKeySignatureVerifier(this.x509Certificate, algo);
		default:
			throw new UtilsException("RemoteStoreKeyType '"+this.remoteStoreKeyType+"' unknown");
		}
	}
	private UtilsException newUtilsExceptionErrorKidUnavailable(boolean publicKey) {
		String prefix = publicKey? "Public Key" : "Certificate";
		return new UtilsException(prefix+" with kid '"+this.kid+"' unavailable");
	}
}