| /* | |
| * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code 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 | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package javax.rmi.ssl; | |
| import java.io.IOException; | |
| import java.net.ServerSocket; | |
| import java.net.Socket; | |
| import java.rmi.server.RMIServerSocketFactory; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| import javax.net.ssl.SSLContext; | |
| import javax.net.ssl.SSLServerSocketFactory; | |
| import javax.net.ssl.SSLSocket; | |
| import javax.net.ssl.SSLSocketFactory; | |
| /** | |
| * <p>An <code>SslRMIServerSocketFactory</code> instance is used by the RMI | |
| * runtime in order to obtain server sockets for RMI calls via SSL.</p> | |
| * | |
| * <p>This class implements <code>RMIServerSocketFactory</code> over | |
| * the Secure Sockets Layer (SSL) or Transport Layer Security (TLS) | |
| * protocols.</p> | |
| * | |
| * <p>This class creates SSL sockets using the default | |
| * <code>SSLSocketFactory</code> (see {@link | |
| * SSLSocketFactory#getDefault}) or the default | |
| * <code>SSLServerSocketFactory</code> (see {@link | |
| * SSLServerSocketFactory#getDefault}) unless the | |
| * constructor taking an <code>SSLContext</code> is | |
| * used in which case the SSL sockets are created using | |
| * the <code>SSLSocketFactory</code> returned by | |
| * {@link SSLContext#getSocketFactory} or the | |
| * <code>SSLServerSocketFactory</code> returned by | |
| * {@link SSLContext#getServerSocketFactory}. | |
| * | |
| * When an <code>SSLContext</code> is not supplied all the instances of this | |
| * class share the same keystore, and the same truststore (when client | |
| * authentication is required by the server). This behavior can be modified | |
| * by supplying an already initialized <code>SSLContext</code> instance. | |
| * | |
| * @see javax.net.ssl.SSLSocketFactory | |
| * @see javax.net.ssl.SSLServerSocketFactory | |
| * @see javax.rmi.ssl.SslRMIClientSocketFactory | |
| * @since 1.5 | |
| */ | |
| public class SslRMIServerSocketFactory implements RMIServerSocketFactory { | |
| /** | |
| * <p>Creates a new <code>SslRMIServerSocketFactory</code> with | |
| * the default SSL socket configuration.</p> | |
| * | |
| * <p>SSL connections accepted by server sockets created by this | |
| * factory have the default cipher suites and protocol versions | |
| * enabled and do not require client authentication.</p> | |
| */ | |
| public SslRMIServerSocketFactory() { | |
| this(null, null, false); | |
| } | |
| /** | |
| * <p>Creates a new <code>SslRMIServerSocketFactory</code> with | |
| * the specified SSL socket configuration.</p> | |
| * | |
| * @param enabledCipherSuites names of all the cipher suites to | |
| * enable on SSL connections accepted by server sockets created by | |
| * this factory, or <code>null</code> to use the cipher suites | |
| * that are enabled by default | |
| * | |
| * @param enabledProtocols names of all the protocol versions to | |
| * enable on SSL connections accepted by server sockets created by | |
| * this factory, or <code>null</code> to use the protocol versions | |
| * that are enabled by default | |
| * | |
| * @param needClientAuth <code>true</code> to require client | |
| * authentication on SSL connections accepted by server sockets | |
| * created by this factory; <code>false</code> to not require | |
| * client authentication | |
| * | |
| * @exception IllegalArgumentException when one or more of the cipher | |
| * suites named by the <code>enabledCipherSuites</code> parameter is | |
| * not supported, when one or more of the protocols named by the | |
| * <code>enabledProtocols</code> parameter is not supported or when | |
| * a problem is encountered while trying to check if the supplied | |
| * cipher suites and protocols to be enabled are supported. | |
| * | |
| * @see SSLSocket#setEnabledCipherSuites | |
| * @see SSLSocket#setEnabledProtocols | |
| * @see SSLSocket#setNeedClientAuth | |
| */ | |
| public SslRMIServerSocketFactory( | |
| String[] enabledCipherSuites, | |
| String[] enabledProtocols, | |
| boolean needClientAuth) | |
| throws IllegalArgumentException { | |
| this(null, enabledCipherSuites, enabledProtocols, needClientAuth); | |
| } | |
| /** | |
| * <p>Creates a new <code>SslRMIServerSocketFactory</code> with the | |
| * specified <code>SSLContext</code> and SSL socket configuration.</p> | |
| * | |
| * @param context the SSL context to be used for creating SSL sockets. | |
| * If <code>context</code> is null the default <code>SSLSocketFactory</code> | |
| * or the default <code>SSLServerSocketFactory</code> will be used to | |
| * create SSL sockets. Otherwise, the socket factory returned by | |
| * <code>SSLContext.getSocketFactory()</code> or | |
| * <code>SSLContext.getServerSocketFactory()</code> will be used instead. | |
| * | |
| * @param enabledCipherSuites names of all the cipher suites to | |
| * enable on SSL connections accepted by server sockets created by | |
| * this factory, or <code>null</code> to use the cipher suites | |
| * that are enabled by default | |
| * | |
| * @param enabledProtocols names of all the protocol versions to | |
| * enable on SSL connections accepted by server sockets created by | |
| * this factory, or <code>null</code> to use the protocol versions | |
| * that are enabled by default | |
| * | |
| * @param needClientAuth <code>true</code> to require client | |
| * authentication on SSL connections accepted by server sockets | |
| * created by this factory; <code>false</code> to not require | |
| * client authentication | |
| * | |
| * @exception IllegalArgumentException when one or more of the cipher | |
| * suites named by the <code>enabledCipherSuites</code> parameter is | |
| * not supported, when one or more of the protocols named by the | |
| * <code>enabledProtocols</code> parameter is not supported or when | |
| * a problem is encountered while trying to check if the supplied | |
| * cipher suites and protocols to be enabled are supported. | |
| * | |
| * @see SSLSocket#setEnabledCipherSuites | |
| * @see SSLSocket#setEnabledProtocols | |
| * @see SSLSocket#setNeedClientAuth | |
| * @since 1.7 | |
| */ | |
| public SslRMIServerSocketFactory( | |
| SSLContext context, | |
| String[] enabledCipherSuites, | |
| String[] enabledProtocols, | |
| boolean needClientAuth) | |
| throws IllegalArgumentException { | |
| // Initialize the configuration parameters. | |
| // | |
| this.enabledCipherSuites = enabledCipherSuites == null ? | |
| null : enabledCipherSuites.clone(); | |
| this.enabledProtocols = enabledProtocols == null ? | |
| null : enabledProtocols.clone(); | |
| this.needClientAuth = needClientAuth; | |
| // Force the initialization of the default at construction time, | |
| // rather than delaying it to the first time createServerSocket() | |
| // is called. | |
| // | |
| this.context = context; | |
| final SSLSocketFactory sslSocketFactory = | |
| context == null ? | |
| getDefaultSSLSocketFactory() : context.getSocketFactory(); | |
| SSLSocket sslSocket = null; | |
| if (this.enabledCipherSuites != null || this.enabledProtocols != null) { | |
| try { | |
| sslSocket = (SSLSocket) sslSocketFactory.createSocket(); | |
| } catch (Exception e) { | |
| final String msg = "Unable to check if the cipher suites " + | |
| "and protocols to enable are supported"; | |
| throw new IllegalArgumentException(msg, e); | |
| } | |
| } | |
| // Check if all the cipher suites and protocol versions to enable | |
| // are supported by the underlying SSL/TLS implementation and if | |
| // true create lists from arrays. | |
| // | |
| if (this.enabledCipherSuites != null) { | |
| sslSocket.setEnabledCipherSuites(this.enabledCipherSuites); | |
| enabledCipherSuitesList = Arrays.asList(this.enabledCipherSuites); | |
| } | |
| if (this.enabledProtocols != null) { | |
| sslSocket.setEnabledProtocols(this.enabledProtocols); | |
| enabledProtocolsList = Arrays.asList(this.enabledProtocols); | |
| } | |
| } | |
| /** | |
| * <p>Returns the names of the cipher suites enabled on SSL | |
| * connections accepted by server sockets created by this factory, | |
| * or <code>null</code> if this factory uses the cipher suites | |
| * that are enabled by default.</p> | |
| * | |
| * @return an array of cipher suites enabled, or <code>null</code> | |
| * | |
| * @see SSLSocket#setEnabledCipherSuites | |
| */ | |
| public final String[] getEnabledCipherSuites() { | |
| return enabledCipherSuites == null ? | |
| null : enabledCipherSuites.clone(); | |
| } | |
| /** | |
| * <p>Returns the names of the protocol versions enabled on SSL | |
| * connections accepted by server sockets created by this factory, | |
| * or <code>null</code> if this factory uses the protocol versions | |
| * that are enabled by default.</p> | |
| * | |
| * @return an array of protocol versions enabled, or | |
| * <code>null</code> | |
| * | |
| * @see SSLSocket#setEnabledProtocols | |
| */ | |
| public final String[] getEnabledProtocols() { | |
| return enabledProtocols == null ? | |
| null : enabledProtocols.clone(); | |
| } | |
| /** | |
| * <p>Returns <code>true</code> if client authentication is | |
| * required on SSL connections accepted by server sockets created | |
| * by this factory.</p> | |
| * | |
| * @return <code>true</code> if client authentication is required | |
| * | |
| * @see SSLSocket#setNeedClientAuth | |
| */ | |
| public final boolean getNeedClientAuth() { | |
| return needClientAuth; | |
| } | |
| /** | |
| * <p>Creates a server socket that accepts SSL connections | |
| * configured according to this factory's SSL socket configuration | |
| * parameters.</p> | |
| */ | |
| public ServerSocket createServerSocket(int port) throws IOException { | |
| final SSLSocketFactory sslSocketFactory = | |
| context == null ? | |
| getDefaultSSLSocketFactory() : context.getSocketFactory(); | |
| return new ServerSocket(port) { | |
| public Socket accept() throws IOException { | |
| Socket socket = super.accept(); | |
| SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket( | |
| socket, socket.getInetAddress().getHostName(), | |
| socket.getPort(), true); | |
| sslSocket.setUseClientMode(false); | |
| if (enabledCipherSuites != null) { | |
| sslSocket.setEnabledCipherSuites(enabledCipherSuites); | |
| } | |
| if (enabledProtocols != null) { | |
| sslSocket.setEnabledProtocols(enabledProtocols); | |
| } | |
| sslSocket.setNeedClientAuth(needClientAuth); | |
| return sslSocket; | |
| } | |
| }; | |
| } | |
| /** | |
| * <p>Indicates whether some other object is "equal to" this one.</p> | |
| * | |
| * <p>Two <code>SslRMIServerSocketFactory</code> objects are equal | |
| * if they have been constructed with the same SSL context and | |
| * SSL socket configuration parameters.</p> | |
| * | |
| * <p>A subclass should override this method (as well as | |
| * {@link #hashCode()}) if it adds instance state that affects | |
| * equality.</p> | |
| */ | |
| public boolean equals(Object obj) { | |
| if (obj == null) return false; | |
| if (obj == this) return true; | |
| if (!(obj instanceof SslRMIServerSocketFactory)) | |
| return false; | |
| SslRMIServerSocketFactory that = (SslRMIServerSocketFactory) obj; | |
| return (getClass().equals(that.getClass()) && checkParameters(that)); | |
| } | |
| private boolean checkParameters(SslRMIServerSocketFactory that) { | |
| // SSL context | |
| // | |
| if (context == null ? that.context != null : !context.equals(that.context)) | |
| return false; | |
| // needClientAuth flag | |
| // | |
| if (needClientAuth != that.needClientAuth) | |
| return false; | |
| // enabledCipherSuites | |
| // | |
| if ((enabledCipherSuites == null && that.enabledCipherSuites != null) || | |
| (enabledCipherSuites != null && that.enabledCipherSuites == null)) | |
| return false; | |
| if (enabledCipherSuites != null && that.enabledCipherSuites != null) { | |
| List<String> thatEnabledCipherSuitesList = | |
| Arrays.asList(that.enabledCipherSuites); | |
| if (!enabledCipherSuitesList.equals(thatEnabledCipherSuitesList)) | |
| return false; | |
| } | |
| // enabledProtocols | |
| // | |
| if ((enabledProtocols == null && that.enabledProtocols != null) || | |
| (enabledProtocols != null && that.enabledProtocols == null)) | |
| return false; | |
| if (enabledProtocols != null && that.enabledProtocols != null) { | |
| List<String> thatEnabledProtocolsList = | |
| Arrays.asList(that.enabledProtocols); | |
| if (!enabledProtocolsList.equals(thatEnabledProtocolsList)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /** | |
| * <p>Returns a hash code value for this | |
| * <code>SslRMIServerSocketFactory</code>.</p> | |
| * | |
| * @return a hash code value for this | |
| * <code>SslRMIServerSocketFactory</code>. | |
| */ | |
| public int hashCode() { | |
| return getClass().hashCode() + | |
| (context == null ? 0 : context.hashCode()) + | |
| (needClientAuth ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode()) + | |
| (enabledCipherSuites == null ? 0 : enabledCipherSuitesList.hashCode()) + | |
| (enabledProtocols == null ? 0 : enabledProtocolsList.hashCode()); | |
| } | |
| // We use a static field because: | |
| // | |
| // SSLSocketFactory.getDefault() always returns the same object | |
| // (at least on Sun's implementation), and we want to make sure | |
| // that the Javadoc & the implementation stay in sync. | |
| // | |
| // If someone needs to have different SslRMIServerSocketFactory | |
| // factories with different underlying SSLSocketFactory objects | |
| // using different keystores and truststores, he/she can always | |
| // use the constructor that takes an SSLContext as input. | |
| // | |
| private static SSLSocketFactory defaultSSLSocketFactory = null; | |
| private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() { | |
| if (defaultSSLSocketFactory == null) | |
| defaultSSLSocketFactory = | |
| (SSLSocketFactory) SSLSocketFactory.getDefault(); | |
| return defaultSSLSocketFactory; | |
| } | |
| private final String[] enabledCipherSuites; | |
| private final String[] enabledProtocols; | |
| private final boolean needClientAuth; | |
| private List<String> enabledCipherSuitesList; | |
| private List<String> enabledProtocolsList; | |
| private SSLContext context; | |
| } |