001: /*
002: * BEGIN_HEADER - DO NOT EDIT
003: *
004: * The contents of this file are subject to the terms
005: * of the Common Development and Distribution License
006: * (the "License"). You may not use this file except
007: * in compliance with the License.
008: *
009: * You can obtain a copy of the license at
010: * https://open-esb.dev.java.net/public/CDDLv1.0.html.
011: * See the License for the specific language governing
012: * permissions and limitations under the License.
013: *
014: * When distributing Covered Code, include this CDDL
015: * HEADER in each file and include the License file at
016: * https://open-esb.dev.java.net/public/CDDLv1.0.html.
017: * If applicable add the following below this CDDL HEADER,
018: * with the fields enclosed by brackets "[]" replaced with
019: * your own identifying information: Portions Copyright
020: * [year] [name of copyright owner]
021: */
022:
023: /*
024: * @(#)HttpSecurityHandler.java
025: * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
026: *
027: * END_HEADER - DO NOT EDIT
028: */
029: /**
030: * HttpSecurityHandler.java
031: *
032: * SUN PROPRIETARY/CONFIDENTIAL.
033: * This software is the proprietary information of Sun Microsystems, Inc.
034: * Use is subject to license terms.
035: *
036: * Created on October 19, 2004, 5:53 PM
037: */package com.sun.jbi.internal.security.https;
038:
039: import com.sun.jbi.StringTranslator;
040: import com.sun.jbi.binding.security.Endpoint;
041: import com.sun.jbi.binding.security.HttpErrorResponseException;
042: import com.sun.jbi.internal.security.SecurityService;
043: import com.sun.jbi.internal.security.ThreadLocalContext;
044:
045: import java.net.URL;
046: import java.net.URLConnection;
047: import java.security.cert.X509Certificate;
048: import java.util.logging.Logger;
049:
050: import javax.net.ssl.HostnameVerifier;
051: import javax.net.ssl.HttpsURLConnection;
052: import javax.net.ssl.SSLContext;
053:
054: import javax.security.auth.Subject;
055: import javax.servlet.http.HttpServletRequest;
056:
057: /**
058: * HttpsSecurityHandler defines a set of methods which can be used by a Http binding.
059: *
060: * @author Sun Microsystems, Inc.
061: */
062: public class HttpSecurityHandler implements
063: com.sun.jbi.binding.security.HttpSecurityHandler {
064: /** The Logger to use */
065: private Logger mLogger = null;
066:
067: /** The SSL Helper. */
068: private SSLHelper mSSLHelper;
069:
070: /** The String Translator to use. */
071: private StringTranslator mTranslator;
072:
073: /**
074: * Constructor.
075: *
076: * @param sslHelper is the SSLHelper instance.
077: */
078: public HttpSecurityHandler(SSLHelper sslHelper) {
079: mSSLHelper = sslHelper;
080: mTranslator = SecurityService
081: .getStringTranslator(HttpConstants.PACKAGE);
082: mLogger = Logger
083: .getLogger(com.sun.jbi.internal.security.Constants.PACKAGE);
084: }
085:
086: /**
087: * Authenticate a HttpServletRequest.
088: *
089: * If the Endpoint requires SSL Client Authentication, this method gets the Client
090: * Certificate from the request and authenticates the Sender. If a Client Certificate
091: * is missing an exception is thrown.
092: *
093: * If the Endpoint does not require SSL Client Authentication none of the above steps
094: * are performed and an empty Subject is returned. This method does not return a null
095: * Subject to avoid NullPOinterExceptions.
096: *
097: * @param request is the HttpServletRequest.
098: * @param endpoint is the targeted Endpoint
099: * @param subject is the Sender subject, if the subject is null a new one is
100: * created.
101: * @throws HttpErrorResponseException if a HttpErrorResponse is to be sent back to the
102: * client.
103: * @return the authenticated Sender Subject.
104: */
105: public Subject authenticateSenderRequest(
106: HttpServletRequest request, Endpoint endpoint,
107: Subject subject) throws HttpErrorResponseException {
108:
109: if (subject == null) {
110: // -- If there is a Subject associated with the current thread use that.
111: Subject subj = ThreadLocalContext.getLocalSubject();
112: subject = ((subj == null) ? new Subject() : subj);
113: }
114:
115: TlsContext endptTlsCtx = mSSLHelper.getTlsContext(endpoint);
116:
117: if (endptTlsCtx != null) {
118: if (endptTlsCtx.isClientAuthRequired()) {
119: X509Certificate cert = getClientCertificate(request);
120: if (cert == null) {
121: throwClientCertRequiredException();
122: }
123: updateSenderSubject(cert, subject);
124: }
125: }
126:
127: return subject;
128:
129: }
130:
131: /**
132: * Authenticate the Sender Request by getting the Sender identity from the
133: * Certificate.
134: *
135: * @param cert is the trusted X.509 Certificate.
136: * @param endpoint is the targeted Endpoint
137: * @param subject is the Subject to be updated.
138: * @throws HttpErrorResponseException if authentication of the request fails.
139: * @return the authenticated Sender Subject
140: */
141: public Subject authenticateSenderRequest(X509Certificate cert,
142: Endpoint endpoint, Subject subject)
143: throws HttpErrorResponseException {
144: if (subject == null) {
145: subject = new Subject();
146: }
147:
148: TlsContext endptTlsCtx = mSSLHelper.getTlsContext(endpoint);
149:
150: if (endptTlsCtx != null) {
151: if (endptTlsCtx.isClientAuthRequired()) {
152: if (cert == null) {
153: throwClientCertRequiredException();
154: }
155: updateSenderSubject(cert, subject);
156: }
157: }
158:
159: return subject;
160:
161: }
162:
163: /**
164: * Get the Client Certificate from the request
165: *
166: * @param request is the HttpServletRequest
167: * @return the X.509 Client certificate
168: * @throws HttpErrorResponseException if the Client Certificate is not found.
169: */
170: private X509Certificate getClientCertificate(
171: HttpServletRequest request)
172: throws HttpErrorResponseException {
173: if (request.isSecure()) {
174: X509Certificate[] certs = (X509Certificate[]) request
175: .getAttribute(HttpConstants.X509CERT_SERVLET_REQUEST_ATTRIB);
176: if (certs != null) {
177: if (certs[0] == null) {
178: throwClientCertRequiredException(request);
179: }
180:
181: return certs[0];
182:
183: }
184: }
185: throwClientCertRequiredException(request);
186:
187: // -- Should never reach here
188: return null;
189: }
190:
191: /**
192: * Throws a HttpErrorResponseException for Client Certificate Required.
193: *
194: * @param request is the HttpServletRequest which does not have the certificate.
195: * @throws HttpErrorResponseException with the required ErrorCode and ErrMsg
196: */
197: private void throwClientCertRequiredException(
198: HttpServletRequest request)
199: throws HttpErrorResponseException {
200: HttpErrorResponseException httpError = new HttpErrorResponseException(
201: mTranslator
202: .getString(
203: HttpConstants.BC_ERR_CLIENT_CERT_REQUIRED_DETAIL,
204: request.getRequestURL().toString()));
205:
206: httpError
207: .setErrorCode(HttpConstants.CLIENT_CERTIFICATE_REQUIRED);
208: httpError.setErrorMessage(mTranslator
209: .getString(HttpConstants.BC_ERR_CLIENT_CERT_REQUIRED));
210:
211: throw httpError;
212: }
213:
214: /**
215: * Throws a HttpErrorResponseException for Client Certificate Required.
216: *
217: * @throws HttpErrorResponseException with the required ErrorCode and ErrMsg
218: */
219: private void throwClientCertRequiredException()
220: throws HttpErrorResponseException {
221: HttpErrorResponseException httpError = new HttpErrorResponseException(
222: mTranslator
223: .getString(HttpConstants.BC_ERR_CLIENT_CERT_REQUIRED));
224:
225: httpError
226: .setErrorCode(HttpConstants.CLIENT_CERTIFICATE_REQUIRED);
227: httpError.setErrorMessage(mTranslator
228: .getString(HttpConstants.BC_ERR_CLIENT_CERT_REQUIRED));
229:
230: throw httpError;
231: }
232:
233: /**
234: * Throws a HttpErrorResponseException for User Authentication failures.
235: *
236: * @param userName is the name of the User that failed authentication.
237: * @throws HttpErrorResponseException with the required ErrorCode and ErrMsg
238: */
239: private void throwAuthenticationFailedException(String userName)
240: throws HttpErrorResponseException {
241: HttpErrorResponseException httpError = new HttpErrorResponseException(
242: mTranslator
243: .getString(
244: HttpConstants.BC_ERR_CLIENT_CERT_AUTH_FAILED_DETAIL,
245: userName));
246:
247: httpError
248: .setErrorCode(HttpConstants.CLIENT_CERTIFICATE_INVALID);
249: httpError
250: .setErrorMessage(mTranslator
251: .getString(HttpConstants.BC_ERR_CLIENT_CERT_AUTH_FAILED));
252:
253: throw httpError;
254:
255: }
256:
257: /**
258: * Update the Subject based on the Certificate.
259: *
260: * @param cert is the X.509 Certificate
261: * @param subject is the Subject to update.
262: */
263: private void updateSenderSubject(X509Certificate cert,
264: Subject subject) {
265: subject.getPrincipals().add(
266: new com.sun.jbi.security.SSLClientPrincipal(cert
267: .getSubjectX500Principal()));
268: subject.getPublicCredentials().add(cert);
269:
270: // -- Associate the Subject with the Current Thread.
271: if (ThreadLocalContext.getLocalSubject() == null) {
272: ThreadLocalContext.setLocalSubject(subject);
273: }
274: }
275:
276: /**
277: * Make this a secure Connection. The choice of using SSL3.0/TLS and the TrustStore
278: * Keystore details should come from the endpoint/operation details.
279: *
280: * @param serverURL is the URL of the server to which a secure HTTPs connection is
281: * to be established.
282: * @param endpoint is the Endpoint on behalf of which the secure connection
283: * is being made.
284: * @throws java.io.IOException on IO realted errors.
285: * @return the secure HTTPs URL Connection
286: */
287: public URLConnection createSecureClientConnection(URL serverURL,
288: Endpoint endpoint) throws java.io.IOException {
289: URLConnection connection = serverURL.openConnection();
290:
291: if (connection instanceof HttpsURLConnection) {
292:
293: // -- Set the SSL Socket Factory for this instance
294: SSLContext sslCtx = getSSLContext(endpoint);
295:
296: if (sslCtx != null) {
297: ((HttpsURLConnection) connection)
298: .setSSLSocketFactory(sslCtx.getSocketFactory());
299: }
300:
301: HostnameVerifier hv = new DefaultHostnameVerifier(
302: mTranslator);
303:
304: // -- Set the HostNameVerifier for the instance
305: ((HttpsURLConnection) connection).setHostnameVerifier(hv);
306: }
307: return connection;
308: }
309:
310: /**
311: *
312: * @param endpoint is the Endpoint whose SSL Context is to be retrieved.
313: * @return a initialized SSLContext, null if the default SSLContext is to be
314: * used for the endpoint. If the Endpoint deployment registration has not yet
315: * occured, there would be no TLS Context for it, in whicha case too a null is
316: * returned.
317: */
318: private SSLContext getSSLContext(Endpoint endpoint) {
319: TlsContext tlsCtx = mSSLHelper.getTlsContext(endpoint);
320:
321: SSLContext sslc = null;
322:
323: if (tlsCtx != null) {
324: sslc = tlsCtx.getSSLContext();
325: }
326: return sslc;
327: }
328:
329: }
|