35
36 import javax.net.ssl.SSLSession;
37 import javax.net.ssl.SSLSocket;
38 import javax.net.ssl.SSLSocketFactory;
39 import javax.net.ssl.SSLPeerUnverifiedException;
40 import javax.net.ssl.HostnameVerifier;
41 import sun.security.util.HostnameChecker;
42
43 import javax.naming.ldap.*;
44 import com.sun.jndi.ldap.Connection;
45
46 /**
47 * This class implements the LDAPv3 Extended Response for StartTLS as
48 * defined in
49 * <a href="http://www.ietf.org/rfc/rfc2830.txt">Lightweight Directory
50 * Access Protocol (v3): Extension for Transport Layer Security</a>
51 *
52 * The object identifier for StartTLS is 1.3.6.1.4.1.1466.20037
53 * and no extended response value is defined.
54 *
55 *<p>
56 * The Start TLS extended request and response are used to establish
57 * a TLS connection over the existing LDAP connection associated with
58 * the JNDI context on which <tt>extendedOperation()</tt> is invoked.
59 *
60 * @see StartTlsRequest
61 * @author Vincent Ryan
62 */
63 final public class StartTlsResponseImpl extends StartTlsResponse {
64
65 private static final boolean debug = false;
66
67 /*
68 * The dNSName type in a subjectAltName extension of an X.509 certificate
69 */
70 private static final int DNSNAME_TYPE = 2;
71
72 /*
73 * The server's hostname.
74 */
75 private transient String hostname = null;
76
77 /*
78 * The LDAP socket.
107
108 /*
109 * The hostname verifier callback.
110 */
111 private transient HostnameVerifier verifier = null;
112
113 /*
114 * The flag to indicate that the TLS connection is closed.
115 */
116 private transient boolean isClosed = true;
117
118 private static final long serialVersionUID = -1126624615143411328L;
119
120 // public no-arg constructor required by JDK's Service Provider API.
121
122 public StartTlsResponseImpl() {}
123
124 /**
125 * Overrides the default list of cipher suites enabled for use on the
126 * TLS connection. The cipher suites must have already been listed by
127 * <tt>SSLSocketFactory.getSupportedCipherSuites()</tt> as being supported.
128 * Even if a suite has been enabled, it still might not be used because
129 * the peer does not support it, or because the requisite certificates
130 * (and private keys) are not available.
131 *
132 * @param suites The non-null list of names of all the cipher suites to
133 * enable.
134 * @see #negotiate
135 */
136 public void setEnabledCipherSuites(String[] suites) {
137 // The impl does accept null suites, although the spec requires
138 // a non-null list.
139 this.suites = suites == null ? null : suites.clone();
140 }
141
142 /**
143 * Overrides the default hostname verifier used by <tt>negotiate()</tt>
144 * after the TLS handshake has completed. If
145 * <tt>setHostnameVerifier()</tt> has not been called before
146 * <tt>negotiate()</tt> is invoked, <tt>negotiate()</tt>
147 * will perform a simple case ignore match. If called after
148 * <tt>negotiate()</tt>, this method does not do anything.
149 *
150 * @param verifier The non-null hostname verifier callback.
151 * @see #negotiate
152 */
153 public void setHostnameVerifier(HostnameVerifier verifier) {
154 this.verifier = verifier;
155 }
156
157 /**
158 * Negotiates a TLS session using the default SSL socket factory.
159 * <p>
160 * This method is equivalent to <tt>negotiate(null)</tt>.
161 *
162 * @return The negotiated SSL session
163 * @throw IOException If an IO error was encountered while establishing
164 * the TLS session.
165 * @see #setEnabledCipherSuites
166 * @see #setHostnameVerifier
167 */
168 public SSLSession negotiate() throws IOException {
169
170 return negotiate(null);
171 }
172
173 /**
174 * Negotiates a TLS session using an SSL socket factory.
175 * <p>
176 * Creates an SSL socket using the supplied SSL socket factory and
177 * attaches it to the existing connection. Performs the TLS handshake
178 * and returns the negotiated session information.
179 * <p>
180 * If cipher suites have been set via <tt>setEnabledCipherSuites</tt>
181 * then they are enabled before the TLS handshake begins.
182 * <p>
183 * Hostname verification is performed after the TLS handshake completes.
184 * The default check performs a case insensitive match of the server's
185 * hostname against that in the server's certificate. The server's
186 * hostname is extracted from the subjectAltName in the server's
187 * certificate (if present). Otherwise the value of the common name
188 * attribute of the subject name is used. If a callback has
189 * been set via <tt>setHostnameVerifier</tt> then that verifier is used if
190 * the default check fails.
191 * <p>
192 * If an error occurs then the SSL socket is closed and an IOException
193 * is thrown. The underlying connection remains intact.
194 *
195 * @param factory The possibly null SSL socket factory to use.
196 * If null, the default SSL socket factory is used.
197 * @return The negotiated SSL session
198 * @throw IOException If an IO error was encountered while establishing
199 * the TLS session.
200 * @see #setEnabledCipherSuites
201 * @see #setHostnameVerifier
202 */
203 public SSLSession negotiate(SSLSocketFactory factory) throws IOException {
204
205 if (isClosed && sslSocket != null) {
206 throw new IOException("TLS connection is closed.");
207 }
208
209 if (factory == null) {
210 factory = getDefaultFactory();
211 }
212
213 if (debug) {
214 System.out.println("StartTLS: About to start handshake");
215 }
216
217 SSLSession sslSession = startHandshake(factory).getSession();
218
235 isClosed = false;
236 return sslSession;
237 }
238
239 // Verification failed
240 close();
241 sslSession.invalidate();
242 if (verifExcep == null) {
243 verifExcep = new SSLPeerUnverifiedException(
244 "hostname of the server '" + hostname +
245 "' does not match the hostname in the " +
246 "server's certificate.");
247 }
248 throw verifExcep;
249 }
250
251 /**
252 * Closes the TLS connection gracefully and reverts back to the underlying
253 * connection.
254 *
255 * @throw IOException If an IO error was encountered while closing the
256 * TLS connection
257 */
258 public void close() throws IOException {
259
260 if (isClosed) {
261 return;
262 }
263
264 if (debug) {
265 System.out.println("StartTLS: replacing SSL " +
266 "streams with originals");
267 }
268
269 // Replace SSL streams with the original streams
270 ldapConnection.replaceStreams(
271 originalInputStream, originalOutputStream);
272
273 if (debug) {
274 System.out.println("StartTLS: closing SSL Socket");
275 }
|
35
36 import javax.net.ssl.SSLSession;
37 import javax.net.ssl.SSLSocket;
38 import javax.net.ssl.SSLSocketFactory;
39 import javax.net.ssl.SSLPeerUnverifiedException;
40 import javax.net.ssl.HostnameVerifier;
41 import sun.security.util.HostnameChecker;
42
43 import javax.naming.ldap.*;
44 import com.sun.jndi.ldap.Connection;
45
46 /**
47 * This class implements the LDAPv3 Extended Response for StartTLS as
48 * defined in
49 * <a href="http://www.ietf.org/rfc/rfc2830.txt">Lightweight Directory
50 * Access Protocol (v3): Extension for Transport Layer Security</a>
51 *
52 * The object identifier for StartTLS is 1.3.6.1.4.1.1466.20037
53 * and no extended response value is defined.
54 *
55 * <p>
56 * The Start TLS extended request and response are used to establish
57 * a TLS connection over the existing LDAP connection associated with
58 * the JNDI context on which {@code extendedOperation()} is invoked.
59 *
60 * @see StartTlsRequest
61 * @author Vincent Ryan
62 */
63 final public class StartTlsResponseImpl extends StartTlsResponse {
64
65 private static final boolean debug = false;
66
67 /*
68 * The dNSName type in a subjectAltName extension of an X.509 certificate
69 */
70 private static final int DNSNAME_TYPE = 2;
71
72 /*
73 * The server's hostname.
74 */
75 private transient String hostname = null;
76
77 /*
78 * The LDAP socket.
107
108 /*
109 * The hostname verifier callback.
110 */
111 private transient HostnameVerifier verifier = null;
112
113 /*
114 * The flag to indicate that the TLS connection is closed.
115 */
116 private transient boolean isClosed = true;
117
118 private static final long serialVersionUID = -1126624615143411328L;
119
120 // public no-arg constructor required by JDK's Service Provider API.
121
122 public StartTlsResponseImpl() {}
123
124 /**
125 * Overrides the default list of cipher suites enabled for use on the
126 * TLS connection. The cipher suites must have already been listed by
127 * {@code SSLSocketFactory.getSupportedCipherSuites()} as being supported.
128 * Even if a suite has been enabled, it still might not be used because
129 * the peer does not support it, or because the requisite certificates
130 * (and private keys) are not available.
131 *
132 * @param suites The non-null list of names of all the cipher suites to
133 * enable.
134 * @see #negotiate
135 */
136 public void setEnabledCipherSuites(String[] suites) {
137 // The impl does accept null suites, although the spec requires
138 // a non-null list.
139 this.suites = suites == null ? null : suites.clone();
140 }
141
142 /**
143 * Overrides the default hostname verifier used by {@code negotiate()}
144 * after the TLS handshake has completed. If
145 * {@code setHostnameVerifier()} has not been called before
146 * {@code negotiate()} is invoked, {@code negotiate()}
147 * will perform a simple case ignore match. If called after
148 * {@code negotiate()}, this method does not do anything.
149 *
150 * @param verifier The non-null hostname verifier callback.
151 * @see #negotiate
152 */
153 public void setHostnameVerifier(HostnameVerifier verifier) {
154 this.verifier = verifier;
155 }
156
157 /**
158 * Negotiates a TLS session using the default SSL socket factory.
159 * <p>
160 * This method is equivalent to {@code negotiate(null)}.
161 *
162 * @return The negotiated SSL session
163 * @throws IOException If an IO error was encountered while establishing
164 * the TLS session.
165 * @see #setEnabledCipherSuites
166 * @see #setHostnameVerifier
167 */
168 public SSLSession negotiate() throws IOException {
169
170 return negotiate(null);
171 }
172
173 /**
174 * Negotiates a TLS session using an SSL socket factory.
175 * <p>
176 * Creates an SSL socket using the supplied SSL socket factory and
177 * attaches it to the existing connection. Performs the TLS handshake
178 * and returns the negotiated session information.
179 * <p>
180 * If cipher suites have been set via {@code setEnabledCipherSuites}
181 * then they are enabled before the TLS handshake begins.
182 * <p>
183 * Hostname verification is performed after the TLS handshake completes.
184 * The default check performs a case insensitive match of the server's
185 * hostname against that in the server's certificate. The server's
186 * hostname is extracted from the subjectAltName in the server's
187 * certificate (if present). Otherwise the value of the common name
188 * attribute of the subject name is used. If a callback has
189 * been set via {@code setHostnameVerifier} then that verifier is used if
190 * the default check fails.
191 * <p>
192 * If an error occurs then the SSL socket is closed and an IOException
193 * is thrown. The underlying connection remains intact.
194 *
195 * @param factory The possibly null SSL socket factory to use.
196 * If null, the default SSL socket factory is used.
197 * @return The negotiated SSL session
198 * @throws IOException If an IO error was encountered while establishing
199 * the TLS session.
200 * @see #setEnabledCipherSuites
201 * @see #setHostnameVerifier
202 */
203 public SSLSession negotiate(SSLSocketFactory factory) throws IOException {
204
205 if (isClosed && sslSocket != null) {
206 throw new IOException("TLS connection is closed.");
207 }
208
209 if (factory == null) {
210 factory = getDefaultFactory();
211 }
212
213 if (debug) {
214 System.out.println("StartTLS: About to start handshake");
215 }
216
217 SSLSession sslSession = startHandshake(factory).getSession();
218
235 isClosed = false;
236 return sslSession;
237 }
238
239 // Verification failed
240 close();
241 sslSession.invalidate();
242 if (verifExcep == null) {
243 verifExcep = new SSLPeerUnverifiedException(
244 "hostname of the server '" + hostname +
245 "' does not match the hostname in the " +
246 "server's certificate.");
247 }
248 throw verifExcep;
249 }
250
251 /**
252 * Closes the TLS connection gracefully and reverts back to the underlying
253 * connection.
254 *
255 * @throws IOException If an IO error was encountered while closing the
256 * TLS connection
257 */
258 public void close() throws IOException {
259
260 if (isClosed) {
261 return;
262 }
263
264 if (debug) {
265 System.out.println("StartTLS: replacing SSL " +
266 "streams with originals");
267 }
268
269 // Replace SSL streams with the original streams
270 ldapConnection.replaceStreams(
271 originalInputStream, originalOutputStream);
272
273 if (debug) {
274 System.out.println("StartTLS: closing SSL Socket");
275 }
|