WrappedLogSSLSocket.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.transport.http;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.security.cert.Certificate;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

import org.openspcoop2.utils.certificate.CertificateUtils;
import org.openspcoop2.utils.resources.Charset;
import org.slf4j.Logger;

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

	private SSLSocket delegate;
    private Logger log;
    private String prefixLog;
    private boolean clientCertificateConfigurated;
    private String clientCertificateConfiguratedPath;

    public WrappedLogSSLSocket(SSLSocket s, Logger log, String prefixLog, String clientCertificateConfigurated) {
    	this.delegate = s;
        this.log = log;
        this.prefixLog = prefixLog;
        this.clientCertificateConfiguratedPath = clientCertificateConfigurated;
        this.clientCertificateConfigurated = this.clientCertificateConfiguratedPath!=null;
    }

    @Override
	public OutputStream getOutputStream() throws IOException {
        return new WrappedLogOutputStream(this.delegate.getOutputStream(), this.log, this.prefixLog);
    }

    private static class WrappedLogOutputStream extends FilterOutputStream {

		private static final String CHARSET = Charset.UTF_8.getValue();
        private Logger log;
        private String prefixLog;
        private StringBuilder sb;

        public WrappedLogOutputStream(OutputStream out, Logger log, String prefixLog) {
            super(out);
            this.log = log;
            this.prefixLog = prefixLog;
            this.sb = new StringBuilder(this.prefixLog);
        }

        @Override
		public void write(byte[] b, int off, int len)
                throws IOException {
        	this.sb.append(new String(b, off, len, CHARSET));
            this.out.write(b, off, len);
        }

        @Override
		public void write(int b) throws IOException {
        	this.sb.append(b);
            this.out.write(b);
        }
        
        @Override
		public void write(byte[] b) throws IOException {
        	if(b!=null) {
        		this.sb.append(new String(b));
        		this.out.write(b);
        	}
		}

        @Override
        public void close() throws IOException {
            this.log.info(this.sb.toString());
            super.close();
        }
        
		@Override
		public void flush() throws IOException {
			super.flush();
		}
    }
  
   	@Override
   	public void startHandshake() throws IOException {
   		StringBuilder sb = new StringBuilder(this.prefixLog);
   		sb.append("startHandshake");
   		this.log.info(sb.toString());
   		this.delegate.startHandshake();
   	}
    
    private WrappedLogHandshakeCompletedListener handshakeCompletedListener = null;
	@Override
   	public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
    	this.handshakeCompletedListener = new WrappedLogHandshakeCompletedListener(this, listener);
    	this.delegate.addHandshakeCompletedListener(this.handshakeCompletedListener);
   	}
   	@Override
   	public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
   		this.delegate.removeHandshakeCompletedListener(this.handshakeCompletedListener);
   	}
    
    private static class WrappedLogHandshakeCompletedListener implements HandshakeCompletedListener {

    	private HandshakeCompletedListener delegate;
    	private WrappedLogSSLSocket sslSocket;
        private Logger log;
        private String prefixLog;
        private StringBuilder sb;
        private StringBuilder sbError;
            	
		public WrappedLogHandshakeCompletedListener(WrappedLogSSLSocket sslSocket, HandshakeCompletedListener delegate) {
    		this.delegate = delegate;
    		this.sslSocket = sslSocket;
    		this.log = sslSocket.log;
    		this.prefixLog = sslSocket.prefixLog;
    		this.sb = new StringBuilder(this.prefixLog);
    		this.sbError = new StringBuilder(); // prefisso aggiunto dopo
    	}
    	
		@Override
		public void handshakeCompleted(HandshakeCompletedEvent event) {
			this.delegate.handshakeCompleted(event);
			
//			boolean needClientAuth = this.sslSocket.getNeedClientAuth();
//			boolean useClientMode = this.sslSocket.getUseClientMode();
//			boolean wClientAuth = this.sslSocket.getWantClientAuth();
			
			this.sb.append("handshakeCompleted");
			if(event!=null) {
				
				if(event.getCipherSuite()!=null) {
					this.sb.append("\nCipherSuite: "+event.getCipherSuite());
				}
				
				if(event.getLocalPrincipal()!=null) {
					this.sb.append("\nLocalPrincipal: "+event.getLocalPrincipal().getName());
				}
				else if(this.sslSocket.clientCertificateConfigurated){
					this.sbError.append("LocalPrincipal");
				}
				if(event.getLocalCertificates()!=null && event.getLocalCertificates().length>0) {
					this.sb.append("\nLocalCertificates: "+event.getLocalCertificates().length);
					this.sb.append("\n");
					print(event.getLocalCertificates(), "LocalCertificate");
				}
				else if(this.sslSocket.clientCertificateConfigurated){
					if(this.sbError.length()>0) {
						this.sbError.append(",");
					}
					this.sbError.append("LocalCertificates");
				}
				
				try {
					if(event.getPeerPrincipal()!=null) {
						this.sb.append("\nPeerPrincipal: "+event.getPeerPrincipal().getName());
					}
				}catch(Exception e) {
					this.sb.append("\nPeerPrincipal: "+e.getMessage());
				}
				try {
					if(event.getPeerCertificates()!=null && event.getPeerCertificates().length>0) {
						this.sb.append("\nPeerCertificates: "+event.getPeerCertificates().length);
						this.sb.append("\n");
						print(event.getPeerCertificates(), "PeerCertificate");
					}
				}catch(Exception e) {
					this.sb.append("\nPeerCertificates: "+e.getMessage());
				}
				
			}
			
			this.log.info(this.sb.toString());
			if(this.sbError.length()>0) {
				this.sbError.append(" non inviato, nonostante sia configurato un certificato client (keystore: "+this.sslSocket.clientCertificateConfiguratedPath+")");
				this.log.error(this.prefixLog+this.sbError.toString());
			}
		}
		
		private void print(Certificate [] certs, String tipo) {
			for (int j = 0; j < certs.length; j++) {
				if(certs[j] instanceof java.security.cert.X509Certificate) {
					java.security.cert.X509Certificate x509 = (java.security.cert.X509Certificate) certs[j];
					CertificateUtils.printCertificate(this.sb, x509, tipo+"-"+j, true);
				}
				else {
					this.sb.append("#### Certificate["+tipo+"-"+j+"]\n");
					this.sb.append("Certificate["+tipo+"-"+j+"] non รจ X509");
				}
			}
		}
   	
    }
    

   	@Override
   	public boolean getEnableSessionCreation() {
   		boolean returnValue = this.delegate.getEnableSessionCreation(); 
   		StringBuilder sb = new StringBuilder(this.prefixLog);
   		sb.append("getEnableSessionCreation=").append(returnValue);
   		this.log.info(sb.toString());
   		return returnValue;
   	}

   	@Override
   	public String[] getEnabledCipherSuites() {
   		String[] cipherSuites = this.delegate.getEnabledCipherSuites();
   		if(cipherSuites!=null && cipherSuites.length>0) {
   			StringBuilder sb = new StringBuilder(this.prefixLog);
   			sb.append("EnabledCipherSuites: ");
   			for (String cs : cipherSuites) {
   				if(sb.length()>0) {
   					sb.append(",");
   				}
   				sb.append(cs);
			}
   			this.log.info(sb.toString());
   		} 
   		return cipherSuites;
   	}
   	
   	@Override
   	public String[] getSupportedCipherSuites() {
   		String[] cipherSuites = this.delegate.getSupportedCipherSuites();
   		if(cipherSuites!=null && cipherSuites.length>0) {
   			StringBuilder sb = new StringBuilder(this.prefixLog);
   			sb.append("SupportedCipherSuites: ");
   			for (String cs : cipherSuites) {
   				if(sb.length()>0) {
   					sb.append(",");
   				}
   				sb.append(cs);
			}
   			this.log.info(sb.toString());
   		} 
   		return cipherSuites;
   	}

   	@Override
   	public String[] getEnabledProtocols() {
   		String[] enabledProtocols = this.delegate.getEnabledProtocols();
   		if(enabledProtocols!=null && enabledProtocols.length>0) {
   			StringBuilder sb = new StringBuilder(this.prefixLog);		
   			sb.append("EnabledProtocols: ");
   			for (String ep : enabledProtocols) {
   				if(sb.length()>0) {
   					sb.append(",");
   				}
   				sb.append(ep);
			}
   			this.log.info(sb.toString());
   		} 
   		return enabledProtocols;
   	}
   	
   	@Override
   	public String[] getSupportedProtocols() {
   		String[] supportedProtocols = this.delegate.getSupportedProtocols();
   		if(supportedProtocols!=null && supportedProtocols.length>0) {
   			StringBuilder sb = new StringBuilder(this.prefixLog);		
   			sb.append("SupportedProtocols: ");
   			for (String ep : supportedProtocols) {
   				if(sb.length()>0) {
   					sb.append(",");
   				}
   				sb.append(ep);
			}
   			this.log.info(sb.toString());
   		} 
   		return supportedProtocols;
   	}

   	@Override
   	public boolean getNeedClientAuth() {
   		boolean returnValue = this.delegate.getNeedClientAuth(); 
   		StringBuilder sb = new StringBuilder(this.prefixLog);
   		sb.append("getNeedClientAuth=").append(returnValue);
   		this.log.info(sb.toString());
   		return returnValue;
   	}
   	
   	@Override
   	public boolean getUseClientMode() {
   		boolean returnValue = this.delegate.getUseClientMode(); 
   		StringBuilder sb = new StringBuilder(this.prefixLog);
   		sb.append("getUseClientMode=").append(returnValue);
   		this.log.info(sb.toString());
   		return returnValue;
   	}
   	
   	@Override
   	public boolean getWantClientAuth() {
   		boolean returnValue = this.delegate.getWantClientAuth(); 
   		StringBuilder sb = new StringBuilder(this.prefixLog);
   		sb.append("getWantClientAuth=").append(returnValue);
   		this.log.info(sb.toString());
   		return returnValue;
   	}
   	
   	@Override
   	public SSLSession getHandshakeSession() {
   		return this.delegate.getHandshakeSession();
   	}

   	@Override
   	public SSLParameters getSSLParameters() {
   		return this.delegate.getSSLParameters();
   	}

   	@Override
   	public SSLSession getSession() {
   		return this.delegate.getSession();
   	}

   	@Override
   	public void setEnableSessionCreation(boolean flag) {
   		this.delegate.setEnableSessionCreation(flag);
   	}

   	@Override
   	public void setEnabledCipherSuites(String[] suites) {
   		this.delegate.setEnabledCipherSuites(suites);
   	}

   	@Override
   	public void setEnabledProtocols(String[] protocols) {
   		this.delegate.setEnabledProtocols(protocols);
   	}

   	@Override
   	public void setNeedClientAuth(boolean need) {
   		this.delegate.setNeedClientAuth(need);
   	}

   	@Override
   	public void setSSLParameters(SSLParameters params) {
   		this.delegate.setSSLParameters(params);
   	}

   	@Override
   	public void setUseClientMode(boolean mode) {
   		this.delegate.setUseClientMode(mode);
   	}

   	@Override
   	public void setWantClientAuth(boolean want) {
   		this.delegate.setWantClientAuth(want);
   	}

   	// METODI NON MODIFICATI
   	
   	@Override
	public void connect(SocketAddress endpoint) throws IOException {
		this.delegate.connect(endpoint);
	}

	@Override
	public void connect(SocketAddress endpoint, int timeout) throws IOException {
		this.delegate.connect(endpoint, timeout);
	}

	@Override
	public void bind(SocketAddress bindpoint) throws IOException {
		this.delegate.bind(bindpoint);
	}

	@Override
	public InetAddress getInetAddress() {
		return this.delegate.getInetAddress();
	}

	@Override
	public InetAddress getLocalAddress() {
		return this.delegate.getLocalAddress();
	}

	@Override
	public int getPort() {
		return this.delegate.getPort();
	}

	@Override
	public int getLocalPort() {
		return this.delegate.getLocalPort();
	}

	@Override
	public SocketAddress getRemoteSocketAddress() {
		return this.delegate.getRemoteSocketAddress();
	}

	@Override
	public SocketAddress getLocalSocketAddress() {
		return this.delegate.getLocalSocketAddress();
	}

	@Override
	public SocketChannel getChannel() {
		return this.delegate.getChannel();
	}

	@Override
	public InputStream getInputStream() throws IOException {
		return this.delegate.getInputStream();
	}

	@Override
	public void setTcpNoDelay(boolean on) throws SocketException {
		this.delegate.setTcpNoDelay(on);
	}

	@Override
	public boolean getTcpNoDelay() throws SocketException {
		return this.delegate.getTcpNoDelay();
	}

	@Override
	public void setSoLinger(boolean on, int linger) throws SocketException {
		this.delegate.setSoLinger(on, linger);
	}

	@Override
	public int getSoLinger() throws SocketException {
		return this.delegate.getSoLinger();
	}

	@Override
	public void sendUrgentData(int data) throws IOException {
		this.delegate.sendUrgentData(data);
	}

	@Override
	public void setOOBInline(boolean on) throws SocketException {
		this.delegate.setOOBInline(on);
	}

	@Override
	public boolean getOOBInline() throws SocketException {
		return this.delegate.getOOBInline();
	}

	@Override
	public synchronized void setSoTimeout(int timeout) throws SocketException {
		this.delegate.setSoTimeout(timeout);
	}

	@Override
	public synchronized int getSoTimeout() throws SocketException {
		return this.delegate.getSoTimeout();
	}

	@Override
	public synchronized void setSendBufferSize(int size) throws SocketException {
		this.delegate.setSendBufferSize(size);
	}

	@Override
	public synchronized int getSendBufferSize() throws SocketException {
		return this.delegate.getSendBufferSize();
	}

	@Override
	public synchronized void setReceiveBufferSize(int size) throws SocketException {
		this.delegate.setReceiveBufferSize(size);
	}

	@Override
	public synchronized int getReceiveBufferSize() throws SocketException {
		return this.delegate.getReceiveBufferSize();
	}

	@Override
	public void setKeepAlive(boolean on) throws SocketException {
		this.delegate.setKeepAlive(on);
	}

	@Override
	public boolean getKeepAlive() throws SocketException {
		return this.delegate.getKeepAlive();
	}

	@Override
	public void setTrafficClass(int tc) throws SocketException {
		this.delegate.setTrafficClass(tc);
	}

	@Override
	public int getTrafficClass() throws SocketException {
		return this.delegate.getTrafficClass();
	}

	@Override
	public void setReuseAddress(boolean on) throws SocketException {
		this.delegate.setReuseAddress(on);
	}

	@Override
	public boolean getReuseAddress() throws SocketException {
		return this.delegate.getReuseAddress();
	}

	@Override
	public synchronized void close() throws IOException {
		this.delegate.close();
	}

	@Override
	public void shutdownInput() throws IOException {
		this.delegate.shutdownInput();
	}

	@Override
	public void shutdownOutput() throws IOException {
		this.delegate.shutdownOutput();
	}

	@Override
	public String toString() {
		return this.delegate.toString();
	}

	@Override
	public boolean isConnected() {
		return this.delegate.isConnected();
	}

	@Override
	public boolean isBound() {
		return this.delegate.isBound();
	}

	@Override
	public boolean isClosed() {
		return this.delegate.isClosed();
	}

	@Override
	public boolean isInputShutdown() {
		return this.delegate.isInputShutdown();
	}

	@Override
	public boolean isOutputShutdown() {
		return this.delegate.isOutputShutdown();
	}

	@Override
	public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
		this.delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
	}

	@Override
	public int hashCode() {
		return this.delegate.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return this.delegate.equals(obj);
	}
}