package com.hwlcn.ldap.ldap.sdk.extensions; import javax.net.ssl.SSLContext; import com.hwlcn.ldap.ldap.sdk.Control; import com.hwlcn.ldap.ldap.sdk.ExtendedRequest; import com.hwlcn.ldap.ldap.sdk.ExtendedResult; import com.hwlcn.ldap.ldap.sdk.InternalSDKHelper; import com.hwlcn.ldap.ldap.sdk.LDAPConnection; import com.hwlcn.ldap.ldap.sdk.LDAPException; import com.hwlcn.ldap.ldap.sdk.ResultCode; import com.hwlcn.core.annotation.NotMutable; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.util.ThreadSafetyLevel; import com.hwlcn.ldap.util.ssl.SSLUtil; import static com.hwlcn.ldap.ldap.sdk.extensions.ExtOpMessages.*; import static com.hwlcn.ldap.util.Debug.*; /** * This class provides an implementation of the LDAP StartTLS extended request * as defined in <A HREF="http://www.ietf.org/rfc/rfc4511.txt">RFC 4511</A> * section 4.14. It may be used to establish a secure communication channel * over an otherwise unencrypted connection. * <BR><BR> * Note that when using the StartTLS extended operation, you should establish * a connection to the server's unencrypted LDAP port rather than its secure * port. Then, you can use the StartTLS extended request in order to secure * that connection. * <BR><BR> * <H2>Example</H2> * The following example attempts to use the StartTLS extended request in order * to secure communication on a previously insecure connection. In this case, * it will use the {@link com.hwlcn.ldap.util.ssl.SSLUtil} class in conjunction * with the {@link com.hwlcn.ldap.util.ssl.TrustAllTrustManager} class to * simplify the process of performing the SSL negotiation by blindly trusting * whatever certificate the server might happen to present. In real-world * applications, if stronger verification is required then it is recommended * that you use an {@link javax.net.ssl.SSLContext} that is configured to perform an * appropriate level of validation. * <PRE> * SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager()); * SSLContext sslContext = sslUtil.createSSLContext(); * ExtendedResult extendedResult = connection.processExtendedOperation( * new StartTLSExtendedRequest(sslContext)); * * // NOTE: The processExtendedOperation method will only throw an exception * // if a problem occurs while trying to send the request or read the * // response. It will not throw an exception because of a non-success * // response. * * if (extendedResult.getResultCode() == ResultCode.SUCCESS) * { * System.out.println("Communication with the server is now secure."); * } * else * { * System.err.println("An error occurred while attempting to perform " + * "StartTLS negotiation. The connection can no longer be used."); * connection.close(); * } * </PRE> */ @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class StartTLSExtendedRequest extends ExtendedRequest { public static final String STARTTLS_REQUEST_OID = "1.3.6.1.4.1.1466.20037"; private static final long serialVersionUID = -3234194603452821233L; private final SSLContext sslContext; public StartTLSExtendedRequest() throws LDAPException { this(null, null); } public StartTLSExtendedRequest(final Control[] controls) throws LDAPException { this(null, controls); } public StartTLSExtendedRequest(final SSLContext sslContext) throws LDAPException { this(sslContext, null); } public StartTLSExtendedRequest(final SSLContext sslContext, final Control[] controls) throws LDAPException { super(STARTTLS_REQUEST_OID, controls); if (sslContext == null) { try { this.sslContext = SSLContext.getInstance(SSLUtil.getDefaultSSLProtocol()); this.sslContext.init(null, null, null); } catch (Exception e) { debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_STARTTLS_REQUEST_CANNOT_CREATE_DEFAULT_CONTEXT.get(e), e); } } else { this.sslContext = sslContext; } } public StartTLSExtendedRequest(final ExtendedRequest extendedRequest) throws LDAPException { this(extendedRequest.getControls()); if (extendedRequest.hasValue()) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_STARTTLS_REQUEST_HAS_VALUE.get()); } } @Override() public ExtendedResult process(final LDAPConnection connection, final int depth) throws LDAPException { InternalSDKHelper.setSoTimeout(connection, 50); final ExtendedResult result = super.process(connection, depth); if (result.getResultCode() == ResultCode.SUCCESS) { InternalSDKHelper.convertToTLS(connection, sslContext); } return result; } @Override() public StartTLSExtendedRequest duplicate() { return duplicate(getControls()); } @Override() public StartTLSExtendedRequest duplicate(final Control[] controls) { try { final StartTLSExtendedRequest r = new StartTLSExtendedRequest(sslContext, controls); r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); return r; } catch (Exception e) { debugException(e); return null; } } @Override() public String getExtendedRequestName() { return INFO_EXTENDED_REQUEST_NAME_START_TLS.get(); } @Override() public void toString(final StringBuilder buffer) { buffer.append("StartTLSExtendedRequest("); final Control[] controls = getControls(); if (controls.length > 0) { buffer.append("controls={"); for (int i=0; i < controls.length; i++) { if (i > 0) { buffer.append(", "); } buffer.append(controls[i]); } buffer.append('}'); } buffer.append(')'); } }