/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb.server;

import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.StringConverter;

The default secure socket factory implementation.
Author:Campbell Burnet (campbell-burnet@users dot sourceforge.net), Blaine Simpson (blaine dot simpson at admc dot com)
Version:2.5.0
Since:1.7.2
/** * The default secure socket factory implementation. * * @author Campbell Burnet (campbell-burnet@users dot sourceforge.net) * @author Blaine Simpson (blaine dot simpson at admc dot com) * * @version 2.5.0 * @since 1.7.2 */
public final class HsqlSocketFactorySecure extends HsqlSocketFactory implements HandshakeCompletedListener { // --------------------------------- members -----------------------------------
The underlying socket factory implementation.
/** The underlying socket factory implementation. */
protected Object socketFactory;
The underlying server socket factory implementation.
/** The underlying server socket factory implementation. */
protected Object serverSocketFactory;
Monitor object to guard against concurrent modification of the underlying socket factory implementation member.
/** * Monitor object to guard against concurrent modification * of the underlying socket factory implementation member. */
protected final Object socket_factory_mutex = new Object();
Monitor object to guard against concurrent modification of the underlying server socket factory implementation member.
/** * Monitor object to guard against concurrent modification of * the underlying server socket factory implementation member. */
protected final Object server_socket_factory_mutex = new Object(); // ------------------------------ constructors ---------------------------------
External construction disabled. New factory instances are retrieved through the newHsqlSocketFactory method instead.
/** * External construction disabled. New factory instances are retrieved * through the newHsqlSocketFactory method instead. */
protected HsqlSocketFactorySecure() throws Exception { super(); } // ----------------------------- subclass overrides ---------------------------- public void configureSocket(Socket socket) { SSLSocket s; super.configureSocket(socket); s = (SSLSocket) socket; s.addHandshakeCompletedListener(this); }
Creates a secure server socket bound to the specified port. The socket is configured with the socket options given to this factory.
Params:
  • port – the port to which to bind the secure ServerSocket
Throws:
  • Exception – if a network or security provider error occurs
Returns:the secure ServerSocket
/** * Creates a secure server socket bound to the specified port. * The socket is configured with the socket options * given to this factory. * * @return the secure ServerSocket * @param port the port to which to bind the secure ServerSocket * @throws Exception if a network or security provider error occurs */
public ServerSocket createServerSocket(int port) throws Exception { SSLServerSocket ss; ss = (SSLServerSocket) getServerSocketFactoryImpl().createServerSocket( port); if (Error.TRACESYSTEMOUT) { Error.printSystemOut("[" + this + "]: createServerSocket()"); Error.printSystemOut("capabilities for " + ss + ":"); Error.printSystemOut("----------------------------"); dump("supported cipher suites", ss.getSupportedCipherSuites()); dump("enabled cipher suites", ss.getEnabledCipherSuites()); } return ss; }
Creates a secure server socket bound to the specified port. The socket is configured with the socket options given to this factory.
Params:
  • port – the port to which to bind the secure ServerSocket
Throws:
  • Exception – if a network or security provider error occurs
Returns:the secure ServerSocket
/** * Creates a secure server socket bound to the specified port. * The socket is configured with the socket options * given to this factory. * * @return the secure ServerSocket * @param port the port to which to bind the secure ServerSocket * @throws Exception if a network or security provider error occurs */
public ServerSocket createServerSocket(int port, String address) throws Exception { SSLServerSocket ss; InetAddress addr; addr = InetAddress.getByName(address); ss = (SSLServerSocket) getServerSocketFactoryImpl().createServerSocket( port, 128, addr); if (Error.TRACESYSTEMOUT) { Error.printSystemOut("[" + this + "]: createServerSocket()"); Error.printSystemOut("capabilities for " + ss + ":"); Error.printSystemOut("----------------------------"); dump("supported cipher suites", ss.getSupportedCipherSuites()); dump("enabled cipher suites", ss.getEnabledCipherSuites()); } return ss; } private static void dump(String title, String[] as) { Error.printSystemOut(title); Error.printSystemOut("----------------------------"); for (int i = 0; i < as.length; i++) { Error.printSystemOut(String.valueOf(as[i])); } Error.printSystemOut("----------------------------"); }
if socket argument is not null, creates a secure Socket as a wrapper for the normal, non-SSL socket. If the socket is null, create a new secure socket. The secure socket is configured using the socket options established for this factory.
Params:
  • socket – the existing socket
  • host – the server host
  • port – the server port
Throws:
  • Exception – if a network or security provider error occurs
Returns:the socket
/** * if socket argument is not null, creates a secure Socket as a wrapper for * the normal, non-SSL socket. If the socket is null, create a new secure * socket. The secure socket is configured using the * socket options established for this factory. * * @return the socket * @param socket the existing socket * @param host the server host * @param port the server port * @throws Exception if a network or security provider error occurs */
public Socket createSocket(Socket socket, String host, int port) throws Exception { SSLSocket sslSocket; if (socket == null) { return createSocket(host, port); } sslSocket = (SSLSocket) getSocketFactoryImpl().createSocket(socket, host, port, true); sslSocket.addHandshakeCompletedListener(this); sslSocket.startHandshake(); verify(host, sslSocket.getSession()); return sslSocket; }
Creates a secure Socket and connects it to the specified remote host at the specified remote port. This socket is configured using the socket options established for this factory.
Params:
  • host – the server host
  • port – the server port
Throws:
  • Exception – if a network or security provider error occurs
Returns:the socket
/** * Creates a secure Socket and connects it to the specified remote host * at the specified remote port. This socket is configured using the * socket options established for this factory. * * @return the socket * @param host the server host * @param port the server port * @throws Exception if a network or security provider error occurs */
public Socket createSocket(String host, int port) throws Exception { SSLSocket socket; socket = (SSLSocket) getSocketFactoryImpl().createSocket(host, port); socket.addHandshakeCompletedListener(this); socket.startHandshake(); // unsaved@users // For https protocol, the protocol handler should do this verification // (Sun's implementation does), but if we do not use the Protocol // handler (which is only available in Java >= 1.4), then we need to do // the verification: hostname == cert CN // // campbell-burnet@users 20030503: // CHEKME/TODO: // // Stricter verify? Either require SunJSSE (assume its trust manager properly // verifies whole chain), or implement our own TrustManager layer? // // What about v1/v3 and signing checks (re: man-in-the-middle attack), // CRL check, basic constraints? notBefore? notAfter? // // Reference: http://www.securitytracker.com/alerts/2002/Aug/1005030.html // // That is, we can't guarantee that installed/preferred provider trust manager // implementations verify the whole chain properly and there are still // v1 certs out there (i.e. have no basic constraints, etc.), meaning that // we should check for and reject any intermediate certs that are not v3+ // (cannot be checked for basic constraints). Only root and intermediate // certs found in the trust store should be allowed to be v1 (since we must // be trusting them for them to be there). All other intermediate signers, // however, should be required to be v3+, otherwise anybody with any kind // of cert issued somehow via a trust chain from the root can pose as an // intermediate signing CA and hence leave things open to man-in-the-middle // style attack. Also, we should really check CRLs, just in case // it turns out that trust chain has been breached and thus issuer has revoked // on some cert(s). Of course, this really begs the question, as it is not // guaranteed that all CAs in trust store have valid, working CRL URL // // So what to do? // // Maybe best to leave this all up to DBA? verify(host, socket.getSession()); return socket; }
Retrieves whether this factory produces secure sockets.
Returns:true iff this factory creates secure sockets
/** * Retrieves whether this factory produces secure sockets. * * @return true iff this factory creates secure sockets */
public boolean isSecure() { return true; } // ----------------------- internal implementation -----------------------------
Retrieves the underlying javax.net.ssl.SSLServerSocketFactory.
Throws:
  • Exception – if there is a problem retrieving the underlying factory
Returns:the underlying javax.net.ssl.SSLServerSocketFactory
/** * Retrieves the underlying javax.net.ssl.SSLServerSocketFactory. * * @throws Exception if there is a problem retrieving the * underlying factory * @return the underlying javax.net.ssl.SSLServerSocketFactory */
protected SSLServerSocketFactory getServerSocketFactoryImpl() throws Exception { Object factory; synchronized (server_socket_factory_mutex) { factory = serverSocketFactory; if (factory == null) { factory = SSLServerSocketFactory.getDefault(); serverSocketFactory = factory; } } return (SSLServerSocketFactory) factory; }
Retrieves the underlying javax.net.ssl.SSLSocketFactory.
Throws:
  • Exception – if there is a problem retrieving the underlying factory
Returns:the underlying javax.net.ssl.SSLSocketFactory
/** * Retrieves the underlying javax.net.ssl.SSLSocketFactory. * * @throws Exception if there is a problem retrieving the * underlying factory * @return the underlying javax.net.ssl.SSLSocketFactory */
protected SSLSocketFactory getSocketFactoryImpl() throws Exception { Object factory; synchronized (socket_factory_mutex) { factory = socketFactory; if (factory == null) { factory = SSLSocketFactory.getDefault(); socketFactory = factory; } } return (SSLSocketFactory) factory; }
Verifies the certificate chain presented by the server to which a secure Socket has just connected. Specifically, the provided host name is checked against the Common Name of the server certificate; additional checks may or may not be performed.
Params:
  • host – the requested host name
  • session – SSLSession used on the connection to host
Throws:
  • Exception – if the certificate chain cannot be verified
/** * Verifies the certificate chain presented by the server to which * a secure Socket has just connected. Specifically, the provided host * name is checked against the Common Name of the server certificate; * additional checks may or may not be performed. * * @param host the requested host name * @param session SSLSession used on the connection to host * @throws Exception if the certificate chain cannot be verified */
protected void verify(String host, SSLSession session) throws Exception { Certificate[] chain; X509Certificate certificate; Principal principal; String DN; String CN; int start; int end; chain = session.getPeerCertificates(); if (chain == null || chain.length == 0) { throw new UnknownHostException( Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_1)); } if (!(chain[0] instanceof X509Certificate)) { throw new UnknownHostException( Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_1)); } certificate = (X509Certificate) chain[0]; principal = certificate.getSubjectDN(); DN = String.valueOf(principal); start = DN.indexOf("CN="); if (start < 0) { throw new UnknownHostException( Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_1)); } start += 3; end = DN.indexOf(',', start); CN = DN.substring(start, (end > -1) ? end : DN.length()); if (CN.length() < 1) { throw new UnknownHostException( Error.getMessage(ErrorCode.M_SERVER_SECURE_VERIFY_2)); } if (!CN.equalsIgnoreCase(host)) { // TLS_HOSTNAME_MISMATCH throw new UnknownHostException( Error.getMessage( ErrorCode.M_SERVER_SECURE_VERIFY_3, 0, new Object[] { CN, host })); } } public void handshakeCompleted(HandshakeCompletedEvent evt) { SSLSession session; String sessionId; SSLSocket socket; if (Error.TRACESYSTEMOUT) { socket = evt.getSocket(); session = evt.getSession(); Error.printSystemOut("SSL handshake completed:"); Error.printSystemOut( "------------------------------------------------"); Error.printSystemOut("socket: : " + socket); Error.printSystemOut("cipher suite : " + session.getCipherSuite()); sessionId = StringConverter.byteArrayToHexString(session.getId()); Error.printSystemOut("session id : " + sessionId); Error.printSystemOut( "------------------------------------------------"); } } }