1. /*
  2. * @(#)file SslRMIClientSocketFactory.java
  3. * @(#)author Sun Microsystems, Inc.
  4. * @(#)version 1.16
  5. * @(#)date 04/06/01
  6. *
  7. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  8. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  9. */
  10. package javax.rmi.ssl;
  11. import java.io.IOException;
  12. import java.io.Serializable;
  13. import java.net.Socket;
  14. import java.rmi.server.RMIClientSocketFactory;
  15. import java.util.StringTokenizer;
  16. import javax.net.SocketFactory;
  17. import javax.net.ssl.SSLSocket;
  18. import javax.net.ssl.SSLSocketFactory;
  19. /**
  20. * <p>An <code>SslRMIClientSocketFactory</code> instance is used by the RMI
  21. * runtime in order to obtain client sockets for RMI calls via SSL.</p>
  22. *
  23. * <p>This class implements <code>RMIClientSocketFactory</code> over
  24. * the Secure Sockets Layer (SSL) or Transport Layer Security (TLS)
  25. * protocols.</p>
  26. *
  27. * <p>This class creates SSL sockets using the default
  28. * <code>SSLSocketFactory</code> (see {@link
  29. * SSLSocketFactory#getDefault}). All instances of this class are
  30. * functionally equivalent. In particular, they all share the same
  31. * truststore, and the same keystore when client authentication is
  32. * required by the server. This behavior can be modified in
  33. * subclasses by overriding the {@link #createSocket(String,int)}
  34. * method; in that case, {@link #equals(Object) equals} and {@link
  35. * #hashCode() hashCode} may also need to be overridden.</p>
  36. *
  37. * <p>If the system property
  38. * <code>javax.rmi.ssl.client.enabledCipherSuites</code> is specified,
  39. * the {@link #createSocket(String,int)} method will call {@link
  40. * SSLSocket#setEnabledCipherSuites(String[])} before returning the
  41. * socket. The value of this system property is a string that is a
  42. * comma-separated list of SSL/TLS cipher suites to enable.</p>
  43. *
  44. * <p>If the system property
  45. * <code>javax.rmi.ssl.client.enabledProtocols</code> is specified,
  46. * the {@link #createSocket(String,int)} method will call {@link
  47. * SSLSocket#setEnabledProtocols(String[])} before returning the
  48. * socket. The value of this system property is a string that is a
  49. * comma-separated list of SSL/TLS protocol versions to enable.</p>
  50. *
  51. * @see javax.net.ssl.SSLSocketFactory
  52. * @see javax.rmi.ssl.SslRMIServerSocketFactory
  53. * @since 1.5
  54. */
  55. public class SslRMIClientSocketFactory
  56. implements RMIClientSocketFactory, Serializable {
  57. /**
  58. * <p>Creates a new <code>SslRMIClientSocketFactory</code>.</p>
  59. */
  60. public SslRMIClientSocketFactory() {
  61. // We don't force the initialization of the default SSLSocketFactory
  62. // at construction time - because the RMI client socket factory is
  63. // created on the server side, where that initialization is a priori
  64. // meaningless, unless both server and client run in the same JVM.
  65. // We could possibly override readObject() to force this initialization,
  66. // but it might not be a good idea to actually mix this with possible
  67. // deserialization problems.
  68. // So contrarily to what we do for the server side, the initialization
  69. // of the SSLSocketFactory will be delayed until the first time
  70. // createSocket() is called - note that the default SSLSocketFactory
  71. // might already have been initialized anyway if someone in the JVM
  72. // already called SSLSocketFactory.getDefault().
  73. //
  74. }
  75. /**
  76. * <p>Creates an SSL socket.</p>
  77. *
  78. * <p>If the system property
  79. * <code>javax.rmi.ssl.client.enabledCipherSuites</code> is
  80. * specified, this method will call {@link
  81. * SSLSocket#setEnabledCipherSuites(String[])} before returning
  82. * the socket. The value of this system property is a string that
  83. * is a comma-separated list of SSL/TLS cipher suites to
  84. * enable.</p>
  85. *
  86. * <p>If the system property
  87. * <code>javax.rmi.ssl.client.enabledProtocols</code> is
  88. * specified, this method will call {@link
  89. * SSLSocket#setEnabledProtocols(String[])} before returning the
  90. * socket. The value of this system property is a string that is a
  91. * comma-separated list of SSL/TLS protocol versions to
  92. * enable.</p>
  93. */
  94. public Socket createSocket(String host, int port) throws IOException {
  95. // Retrieve the SSLSocketFactory
  96. //
  97. final SocketFactory sslSocketFactory = getDefaultClientSocketFactory();
  98. // Create the SSLSocket
  99. //
  100. final SSLSocket sslSocket = (SSLSocket)
  101. sslSocketFactory.createSocket(host, port);
  102. // Set the SSLSocket Enabled Cipher Suites
  103. //
  104. final String enabledCipherSuites = (String)
  105. System.getProperty("javax.rmi.ssl.client.enabledCipherSuites");
  106. if (enabledCipherSuites != null) {
  107. StringTokenizer st = new StringTokenizer(enabledCipherSuites, ",");
  108. int tokens = st.countTokens();
  109. String enabledCipherSuitesList[] = new String[tokens];
  110. for (int i = 0 ; i < tokens; i++) {
  111. enabledCipherSuitesList[i] = st.nextToken();
  112. }
  113. try {
  114. sslSocket.setEnabledCipherSuites(enabledCipherSuitesList);
  115. } catch (IllegalArgumentException e) {
  116. throw (IOException)
  117. new IOException(e.getMessage()).initCause(e);
  118. }
  119. }
  120. // Set the SSLSocket Enabled Protocols
  121. //
  122. final String enabledProtocols = (String)
  123. System.getProperty("javax.rmi.ssl.client.enabledProtocols");
  124. if (enabledProtocols != null) {
  125. StringTokenizer st = new StringTokenizer(enabledProtocols, ",");
  126. int tokens = st.countTokens();
  127. String enabledProtocolsList[] = new String[tokens];
  128. for (int i = 0 ; i < tokens; i++) {
  129. enabledProtocolsList[i] = st.nextToken();
  130. }
  131. try {
  132. sslSocket.setEnabledProtocols(enabledProtocolsList);
  133. } catch (IllegalArgumentException e) {
  134. throw (IOException)
  135. new IOException(e.getMessage()).initCause(e);
  136. }
  137. }
  138. // Return the preconfigured SSLSocket
  139. //
  140. return sslSocket;
  141. }
  142. /**
  143. * <p>Indicates whether some other object is "equal to" this one.</p>
  144. *
  145. * <p>Because all instances of this class are functionally equivalent
  146. * (they all use the default
  147. * <code>SSLSocketFactory</code>), this method simply returns
  148. * <code>this.getClass().equals(obj.getClass())</code>.</p>
  149. *
  150. * <p>A subclass should override this method (as well
  151. * as {@link #hashCode()}) if its instances are not all
  152. * functionally equivalent.</p>
  153. */
  154. public boolean equals(Object obj) {
  155. if (obj == null) return false;
  156. if (obj == this) return true;
  157. return this.getClass().equals(obj.getClass());
  158. }
  159. /**
  160. * <p>Returns a hash code value for this
  161. * <code>SslRMIClientSocketFactory</code>.</p>
  162. *
  163. * @return a hash code value for this
  164. * <code>SslRMIClientSocketFactory</code>.
  165. */
  166. public int hashCode() {
  167. return this.getClass().hashCode();
  168. }
  169. // We use a static field because:
  170. //
  171. // SSLSocketFactory.getDefault() always returns the same object
  172. // (at least on Sun's implementation), and we want to make sure
  173. // that the Javadoc & the implementation stay in sync.
  174. //
  175. // If someone needs to have different SslRMIClientSocketFactory factories
  176. // with different underlying SSLSocketFactory objects using different key
  177. // and trust stores, he can always do so by subclassing this class and
  178. // overriding createSocket(String host, int port).
  179. //
  180. private static SocketFactory defaultSocketFactory = null;
  181. private static synchronized SocketFactory getDefaultClientSocketFactory() {
  182. if (defaultSocketFactory == null)
  183. defaultSocketFactory = SSLSocketFactory.getDefault();
  184. return defaultSocketFactory;
  185. }
  186. private static final long serialVersionUID = -8310631444933958385L;
  187. }