/*
 * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved.
 * See LICENCE file for licensing information.
 *
 * Derived from the code copyrighted and licensed as follows:
 * 
 * Copyright (c) Members of the EGEE Collaboration. 2004.
 * See http://www.eu-egee.org/partners/ for details on the copyright
 * holders.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 *    
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.emi.security.authn.x509.impl;

import java.security.cert.Certificate;
import java.security.cert.X509Certificate;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;

import eu.emi.security.authn.x509.helpers.ssl.HostnameToCertificateChecker;

/**
 * Abstract implementation of the JSSE {@link HandshakeCompletedListener} 
 * which can be registered on a {@link SSLSocket} to verify if a peer's 
 * host name matches a DN of its certificate. It is useful on client side
 * when connecting to a server.
 * <p>
 * By default the implementation checks the certificate's Subject Alternative Name 
 * and Common Name, following the server identity part of RFC 2818. Additionally the
 * 'service/hostname' syntax is supported (the service prefix is simply ignored).
 * <p> 
 * If there is a name mismatch the nameMismatch() method is called. 
 * User of this class must extend it and provide the application specific reaction 
 * in this method.
 * <p>
 * Note that this class should be used only on SSL connections which are
 * authenticated with X.509 certificates.
 *
 * @deprecated Use {@link SocketFactoryCreator#connectWithHostnameChecking(SSLSocket, HostnameMismatchCallback)} 
 * instead. This class is not perfect as the {@link HandshakeCompletedListener} is invoked (at least in reference JDK)
 * in a separate thread, what can easily lead to a situation when the connection is opened and made available,
 * before this implementation finishes checking. 
 * @author Joni Hahkala
 * @author K. Benedyczak
 * 
 */
@Deprecated
public abstract class AbstractHostnameToCertificateChecker implements HandshakeCompletedListener
{
	static 
	{
		CertificateUtils.configureSecProvider();
	}

	public void handshakeCompleted(HandshakeCompletedEvent hce)
	{
		X509Certificate cert;
		try
		{
			Certificate[] serverChain = hce.getPeerCertificates();
			if (serverChain == null || serverChain.length == 0)
			{
				processingError(hce, new Exception("JDK BUG? Got null or empty peer certificate array"));
				return;
			}
			if (!(serverChain[0] instanceof X509Certificate))
			{
				processingError(hce, new ClassCastException("Peer certificate should be " +
						"an X.509 certificate, but is " + serverChain[0].getClass().getName()));
				return;
			}
			cert = (X509Certificate) serverChain[0];
		} catch (SSLPeerUnverifiedException e)
		{
			processingError(hce, new Exception("Peer is unverified " +
					"when handshake is completed - is it really an X.509-authenticated connection?", e));
			return;
		}
		String hostname = hce.getSocket().getInetAddress().getHostName();
		
		try
		{
			HostnameToCertificateChecker checker = new HostnameToCertificateChecker();
			if (!checker.checkMatching(hostname, cert))
				nameMismatch(hce, cert, hostname);
		} catch (Exception e)
		{
			processingError(hce, e);
			return;
		}
	}

	/**
	 * This method is called whenever peer's host name is not matching the peer's 
	 * certificate DN. Note that throwing exceptions from this method doesn't make any sense. 
	 * @param hce the original event object
	 * @param peerCertificate peer's certificate (for convenience) 
	 * @param hostName peer's host name (for convenience)
	 * @throws SSLException SSL exception
	 */
	protected abstract void nameMismatch(HandshakeCompletedEvent hce, X509Certificate peerCertificate,
			String hostName) throws SSLException;
	
	/**
	 * This method is called whenever there is an error when processing the peer's certificate
	 * and hostname. Generally it should never happen, and the implementation should simply 
	 * close the socket and report the error. The default implementation simply throws an 
	 * {@link IllegalStateException}. 
	 * @param hce the original event object
	 * @param e error
	 */
	protected void processingError(HandshakeCompletedEvent hce, Exception e)
	{
		throw new IllegalStateException("Error occured when verifying if the SSL peer's " +
				"hostname matches its certificate", e);
	}
}









