MessageSecurityReceiver_soapbox.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.security.message.soapbox;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPPart;

import org.adroitlogic.soapbox.MessageSecurityContext;
import org.adroitlogic.ultraesb.core.MessageImpl;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.OpenSPCoop2SoapMessage;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.exception.MessageException;
import org.openspcoop2.message.exception.MessageNotSupportedException;
import org.openspcoop2.message.soap.reference.Reference;
import org.openspcoop2.protocol.sdk.Busta;
import org.openspcoop2.protocol.sdk.constants.CodiceErroreCooperazione;
import org.openspcoop2.security.SecurityException;
import org.openspcoop2.security.message.AbstractSOAPMessageSecurityReceiver;
import org.openspcoop2.security.message.SubErrorCodeSecurity;
import org.openspcoop2.security.message.constants.SecurityConstants;
import org.openspcoop2.security.message.engine.MessageUtilities;
import org.openspcoop2.security.message.engine.WSSUtilities;
import org.openspcoop2.security.message.utils.EncryptionBean;
import org.openspcoop2.security.message.utils.KeystoreUtils;
import org.openspcoop2.security.message.utils.SignatureBean;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.certificate.KeyStore;
import org.openspcoop2.utils.resources.ClassLoaderUtilities;
import org.w3c.dom.Document;



/**
 * WSSContext_soapbox
 *
 * @author Andrea Poli (apoli@link.it)
 * @author Giovanni Bussu (bussu@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class MessageSecurityReceiver_soapbox extends AbstractSOAPMessageSecurityReceiver{

	private X509Certificate [] certificates = null;
	

	@Override
	public void process(org.openspcoop2.security.message.MessageSecurityContext messageSecurityContext,OpenSPCoop2Message messageParam,Busta busta,org.openspcoop2.utils.Map<Object> ctx) throws SecurityException{
		try{
			
			if(ServiceBinding.SOAP.equals(messageParam.getServiceBinding())==false){
				throw new SecurityException("SoapBox Engine usable only with SOAP Binding");
			}
			OpenSPCoop2SoapMessage message = messageParam.castAsSoap();
			
			
			
			// ********** Leggo operazioni ***************
			boolean decrypt = false;
			boolean signature = false;
			boolean timestamp = false;

			String[]actions = ((String)messageSecurityContext.getIncomingProperties().get(SecurityConstants.ACTION)).split(" ");
			for (int i = 0; i < actions.length; i++) {
				if(SecurityConstants.isActionEncryption(actions[i].trim())){
					decrypt = true;
				}
				else if(SecurityConstants.SIGNATURE_ACTION.equals(actions[i].trim())){
					signature = true;
				}
				else if(SecurityConstants.TIMESTAMP_ACTION.equals(actions[i].trim())){
					timestamp = true;
				}
			}
			// rilasciato vincolo di abilitazione signature o encryption per abilitare il timestamp
//			if(!signature && !decrypt && timestamp){
//				throw new WSSException("La funzionalita' "+WSSConstants.TIMESTAMP_ACTION+" richiede per essere abilitata almeno una delle seguenti altre funzionalita': "+
//						WSSConstants.ENCRYPT_ACTION+","+WSSConstants.SIGNATURE_ACTION);
//			}
			
			
			
			
			// ********** Inizializzo Header WSS ***************

			SOAPPart sp = message.getSOAPPart();
			//Document d = sp;
			Document d = sp.getDocumentElement().getOwnerDocument();
			Object mustUnderstandObject = messageSecurityContext.getIncomingProperties().get(SecurityConstants.MUST_UNDERSTAND);
			boolean mustUnderstand = false;
			if(mustUnderstandObject!=null){
				mustUnderstand = SecurityConstants.TRUE.equals(mustUnderstandObject);
			}
			MessageSecurityContext msgSecCtx = new MessageSecurityContext(d, new MessageImpl(true, null, "http"));
			Iterator<?> it = message.getAttachments();
			if(it!=null){
				while(it.hasNext()) {
					AttachmentPart part = (AttachmentPart) it.next();
					String contentId = part.getContentId();
					if(contentId.startsWith("<"))
						contentId = contentId.substring(1);
					if(contentId.endsWith(">"))
						contentId = contentId.substring(0,contentId.length()-1);
					msgSecCtx.setProperty(contentId, part); //tolgo < e > dal nome eventualmente
				}
			}
			
			
			
			
			
			
			// **************** Inizializzo process per validare timestamp **************************
			
			ProcessTimestampedMessage verifyTimestampedProc = null;
			if(timestamp){
				verifyTimestampedProc = new ProcessTimestampedMessage();
				
				//future time to live
				Object futureTtlObject = messageSecurityContext.getIncomingProperties().get(SecurityConstants.TIMESTAMP_FUTURE_TTL);
				if(futureTtlObject==null){
					futureTtlObject = SecurityConstants.TIMESTAMP_SOAPBOX_FUTURE_TTL_DEFAULT;
				}
				String ttl = (String) futureTtlObject; 
				long futureTtlLong = -1;
				try{
					futureTtlLong = Long.parseLong(ttl);
				}catch(Exception e){
					throw new Exception("Indicazione "+SecurityConstants.TIMESTAMP_FUTURE_TTL+" non corretta: "+e.getMessage());
				}

				msgSecCtx.setProperty(SecurityConstants.TIMESTAMP_FUTURE_TTL, futureTtlLong * 1000l);

				//strict Timestamp handling
				Object strictObject = messageSecurityContext.getIncomingProperties().get(SecurityConstants.TIMESTAMP_STRICT);
				if(strictObject==null){
					strictObject = SecurityConstants.TRUE;
				}
				String strict = (String) strictObject; 
				boolean strictBoolean = false;
				try{
					strictBoolean = Boolean.parseBoolean(strict);
				}catch(Exception e){
					throw new Exception("Indicazione "+SecurityConstants.TIMESTAMP_STRICT+" non corretta: "+e.getMessage());
				}

				msgSecCtx.setProperty(SecurityConstants.TIMESTAMP_STRICT, strictBoolean);
			}
			
			
			
			
			
			
			// **************** Inizializzo process per decifrare **************************

			ProcessPartialEncryptedMessage decryptMsgProc = null;
			if(decrypt){
				decryptMsgProc = (ProcessPartialEncryptedMessage) ClassLoaderUtilities.newInstance(message.getProcessPartialEncryptedMessageClass());
				decryptMsgProc.setMessage(message);
				decryptMsgProc.setActor(messageSecurityContext.getActor());
				decryptMsgProc.setMustUnderstand(mustUnderstand);
			}
			
			
			
			
			
			
			
			// **************** Inizializzo process per validare la firma **************************
			
			ProcessSignedMessage signMsgProc = null;
			if(signature){
				signMsgProc = new ProcessSignedMessage();
				signMsgProc.setMessage(message);
				signMsgProc.setActor(messageSecurityContext.getActor());
				signMsgProc.setMustUnderstand(mustUnderstand);
			}
			
			
			
			
			
			
			
			
			
			// **************** Leggo parametri decryption store **************************
			KeyStore decryptionKS = null;
			KeyStore decryptionTrustStoreKS = null;
			String aliasDecryptUser = null;
			String aliasDecryptPassword = null;
			boolean decryptionSymmetric = false;
			if(decrypt){
				EncryptionBean bean = KeystoreUtils.getReceiverEncryptionBean(messageSecurityContext,ctx);
				
				decryptionKS = bean.getKeystore();
				decryptionTrustStoreKS = bean.getTruststore();
				decryptionSymmetric = bean.isEncryptionSimmetric();
				aliasDecryptUser = bean.getUser();
				aliasDecryptPassword = bean.getPassword();
			
			}
			
			
			
			
			// **************** Leggo parametri signature store **************************
			KeyStore signatureKS = null;
			KeyStore signatureTrustStoreKS = null;
			String aliasSignatureUser = null;
			String aliasSignaturePassword = null;
			String crlPath = null;
			if(signature){
				
				SignatureBean bean = KeystoreUtils.getReceiverSignatureBean(messageSecurityContext,ctx);
				
				signatureKS = bean.getKeystore();
				signatureTrustStoreKS = bean.getTruststore();
				aliasSignatureUser = bean.getUser();
				aliasSignaturePassword = bean.getPassword();
				crlPath = bean.getCrlPath();

			}
			
			
			
			
			
			
			
			
			
			
			// **************** Inizializzo Secure Context for encryption **************************
			org.openspcoop2.security.message.soapbox.SoapBoxSecurityConfig securityConfig_decryption = null;
			if(decrypt){			
				msgSecCtx.getEncryptionRequest().setCertAlias(aliasDecryptUser);
				
				Map<String, String> passwordMap_decryption = new HashMap<>();
				passwordMap_decryption.put(aliasDecryptUser, aliasDecryptPassword);
				if(decryptionTrustStoreKS==null){
					decryptionTrustStoreKS = decryptionKS;
				}
				securityConfig_decryption = new org.openspcoop2.security.message.soapbox.SoapBoxSecurityConfig(decryptionKS, decryptionTrustStoreKS, passwordMap_decryption,ctx);
				securityConfig_decryption.setSymmetricSharedKey(decryptionSymmetric);
			}
			
			
			
			
			
			
			
			
			
			// **************** Inizializzo Secure Context for signature **************************
			org.openspcoop2.security.message.soapbox.SoapBoxSecurityConfig securityConfig_signature = null;
			if(signature){			
				Map<String, String> passwordMap_signature = new HashMap<>();
				passwordMap_signature.put(aliasSignatureUser, aliasSignaturePassword);
				if(signatureTrustStoreKS==null){
					signatureTrustStoreKS = signatureKS;
				}
				securityConfig_signature = new org.openspcoop2.security.message.soapbox.SoapBoxSecurityConfig(signatureKS, signatureTrustStoreKS, passwordMap_signature,crlPath,ctx);
			}	
			
			
			
			
			
			
			
			
			
			// **************** Process **************************

			// Devo rileggerle per eseguire nell'ordine
			actions = ((String)messageSecurityContext.getIncomingProperties().get(SecurityConstants.ACTION)).split(" ");
			for (int i = actions.length-1; i >= 0; i--) {
				if(SecurityConstants.isActionEncryption(actions[i].trim()) || SecurityConstants.isActionDecryption(actions[i].trim())){
					decryptMsgProc.process(securityConfig_decryption, msgSecCtx);
					//refreshAttachments(message); // per impostare il nuovo contenuto degli attachment, una volta decriptati (non serve se non si imposta il CONTENT_TRANSFER_ENCODING a base64!!)
				}
				else if(SecurityConstants.SIGNATURE_ACTION.equals(actions[i].trim())){
					signMsgProc.process(securityConfig_signature, msgSecCtx);
					this.certificates = signMsgProc.getCertificates();
				}
				else if(SecurityConstants.TIMESTAMP_ACTION.equals(actions[i].trim())){
					if(securityConfig_signature!=null){
						verifyTimestampedProc.process(securityConfig_signature, msgSecCtx);
					}
					else if(securityConfig_decryption!=null){
						verifyTimestampedProc.process(securityConfig_decryption, msgSecCtx);
					} else {
						verifyTimestampedProc.process(null, msgSecCtx);
					}
				}
			}
			
			
			
			
			
			
			
			
			
		} catch (Exception e) {
			
			SecurityException wssException = new SecurityException(e.getMessage(), e);
			
			
			/* **** MESSAGGIO ***** */
			String msg = Utilities.getInnerNotEmptyMessageException(e).getMessage();
			
			Throwable innerExc = Utilities.getLastInnerException(e);
			String innerMsg = null;
			if(innerExc!=null){
				innerMsg = innerExc.getMessage();
			}
			
			String messaggio = null;
			if(msg!=null){
				messaggio = new String(msg);
				if(innerMsg!=null && !innerMsg.equals(msg)){
					messaggio = messaggio + " ; " + innerMsg;
				}
			}
			else{
				if(innerMsg!=null){
					messaggio = innerMsg;
				}
			}
			
			wssException.setMsgErrore(messaggio);
			
			

			/* ***** CODICE **** */
			
			boolean signature = false;
			boolean encrypt = false;
			try{
				ByteArrayOutputStream bout = new ByteArrayOutputStream();
				PrintStream printStream = new PrintStream(bout);
				e.printStackTrace(printStream);
				bout.flush();
				printStream.flush();
				bout.close();
				printStream.close();
				
				if(bout.toString().contains("ProcessSignedMessage")){
					signature = true;
				}
				else if(bout.toString().contains("Signature verification failed")){
					signature = true;
				}
				else if(bout.toString().contains("Message is not signed")){
					signature = true;
				}
				else if(bout.toString().contains("ProcessPartialEncryptedMessage")){
					encrypt = true;
				}
				
			}catch(Exception eClose){}
			
			if(signature){
				wssException.setCodiceErrore(CodiceErroreCooperazione.SICUREZZA_FIRMA_NON_VALIDA);
			}
			else if(encrypt){
				wssException.setCodiceErrore(CodiceErroreCooperazione.SICUREZZA_CIFRATURA_NON_VALIDA);
			}
			else {
				wssException.setCodiceErrore(CodiceErroreCooperazione.SICUREZZA);
			}
			
			
			
			throw wssException;
		}

	}

	@SuppressWarnings("unused")
	private void refreshAttachments(OpenSPCoop2SoapMessage openspcoop2Message) throws MessageException, MessageNotSupportedException, SOAPException{
		java.util.Iterator<?> itAp = openspcoop2Message.getAttachments();
		List<AttachmentPart> v = new ArrayList<AttachmentPart>();
	    while(itAp.hasNext()){
	    	AttachmentPart ap = 
	    		(AttachmentPart) itAp.next();
	    	v.add(ap);
	    }
		openspcoop2Message.removeAllAttachments();
		while(v.size()>0){
			AttachmentPart ap = v.remove(0);
			AttachmentPart apNew = openspcoop2Message.createAttachmentPart();
			apNew.setDataHandler(ap.getDataHandler());
			Iterator<?> itMhs = ap.getAllMimeHeaders();
			while (itMhs.hasNext()) {
				javax.xml.soap.MimeHeader mh = (javax.xml.soap.MimeHeader) itMhs.next();
				//System.out.println("TIPO["+mh.getName()+"] VALUE["+mh.getValue()+"]");
				if(!"Content-Transfer-Encoding".equals(mh.getName())){
					apNew.addMimeHeader(mh.getName(), mh.getValue());
				}
			}
			openspcoop2Message.addAttachmentPart(apNew);
		}
	} 
	

	
	@Override
	public String getCertificate() throws SecurityException{
		if(this.certificates!=null &&
			this.certificates.length > 0){
			return this.certificates[0].getSubjectX500Principal().toString();
		}
		return null;
	}
	
	@Override
	public X509Certificate getX509Certificate() throws SecurityException {
		if(this.certificates!=null &&
			this.certificates.length > 0){
			return this.certificates[0];
		}
		return null;
	}

	@Override
	public PublicKey getPublicKey() {
		if(this.certificates!=null &&
			this.certificates.length > 0){
			return this.certificates[0].getPublicKey();
		}
		return null;
	}
	
	@Override
	public String getCertificateId() throws SecurityException{
		return null;
	}
	

	@Override
	public List<Reference> getDirtyElements(
			org.openspcoop2.security.message.MessageSecurityContext messageSecurityContext,
			OpenSPCoop2SoapMessage message) throws SecurityException {
		return WSSUtilities.getDirtyElements(messageSecurityContext, message);
	}

	@Override
	public Map<QName, QName> checkEncryptSignatureParts(
			org.openspcoop2.security.message.MessageSecurityContext messageSecurityContext,
			List<Reference> elementsToClean, OpenSPCoop2SoapMessage message,
			List<SubErrorCodeSecurity> codiciErrore) throws SecurityException {
		return MessageUtilities.checkEncryptSignatureParts(messageSecurityContext, elementsToClean, message, codiciErrore, SecurityConstants.QNAME_WSS_ELEMENT_SECURITY);
	}

	@Override
	public void checkEncryptionPartElements(Map<QName, QName> notResolved,
			OpenSPCoop2SoapMessage message,
			List<SubErrorCodeSecurity> erroriRilevati) throws SecurityException {
		MessageUtilities.checkEncryptionPartElements(notResolved, message, erroriRilevati);
	}

	@Override
	public void cleanDirtyElements(
			org.openspcoop2.security.message.MessageSecurityContext messageSecurityContext,
			OpenSPCoop2SoapMessage message, List<Reference> elementsToClean,
			boolean detachHeaderWSSecurity, boolean removeAllIdRef)
			throws SecurityException {
		WSSUtilities.cleanDirtyElements(messageSecurityContext, message, elementsToClean, detachHeaderWSSecurity,removeAllIdRef);
		
	}

	
}