FELIX-4230 Convert X-Forwarded-SSL-Certificate header to javax.servlet.request.X509Certificate request attribute for consumption by the web application.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1533418 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java
index f6736b0..2807901 100644
--- a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java
+++ b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilter.java
@@ -19,7 +19,6 @@
package org.apache.felix.http.sslfilter.internal;
import java.io.IOException;
-
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -37,6 +36,9 @@
// value indicating an SSL endpoint proxy
private static final String X_FORWARD_SSL_VALUE = "on";
+ // request header indicating an SSL client certificate (if available)
+ private static final String X_FORWARD_SSL_CERTIFICATE_HEADER = "X-Forwarded-SSL-Certificate";
+
public void init(FilterConfig config)
{
}
@@ -44,15 +46,25 @@
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException
{
-
HttpServletRequest httpReq = (HttpServletRequest) req;
-
if (X_FORWARD_SSL_VALUE.equalsIgnoreCase(httpReq.getHeader(X_FORWARD_SSL_HEADER)))
{
- httpReq = new SslFilterRequest(httpReq);
+ httpReq = new SslFilterRequest(httpReq, httpReq.getHeader(X_FORWARD_SSL_CERTIFICATE_HEADER));
}
- chain.doFilter(httpReq, res);
+ // forward the request making sure any certificate is removed
+ // again after the request processing gets back here
+ try
+ {
+ chain.doFilter(httpReq, res);
+ }
+ finally
+ {
+ if (httpReq instanceof SslFilterRequest)
+ {
+ ((SslFilterRequest) httpReq).done();
+ }
+ }
}
public void destroy()
diff --git a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java
index e3cf616..3c1c15d 100644
--- a/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java
+++ b/http/sslfilter/src/main/java/org/apache/felix/http/sslfilter/internal/SslFilterRequest.java
@@ -18,6 +18,15 @@
*/
package org.apache.felix.http.sslfilter.internal;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.regex.Pattern;
+
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
@@ -30,11 +39,59 @@
// The HTTP scheme prefix in an URL
private static final String HTTP_SCHEME_PREFIX = "http://";
+ // pattern to convert the header to a PEM certificate for parsing
+ // by replacing spaces with line breaks
+ private static Pattern HEADER_TO_CERT = Pattern.compile("(?! CERTIFICATE)(?= ) ");
+
+ // character encoding for the client certificate header
+ private static final String UTF_8 = "UTF-8";
+
+ /**
+ * If there is an SSL certificate associated with the request, it must be
+ * exposed by the servlet container to the servlet programmer as an array of
+ * objects of type java.security.cert.X509Certificate and accessible via a
+ * ServletRequest attribute of javax.servlet.request.X509Certificate.
+ * <p>
+ * The order of this array is defined as being in ascending order of trust.
+ * The first certificate in the chain is the one set by the client, the next
+ * is the one used to authenticate the first, and so on.
+ */
+ private static final String ATTR_SSL_CERTIFICATE = "javax.servlet.request.X509Certificate";
+
private String requestURL;
- SslFilterRequest(HttpServletRequest request)
+ SslFilterRequest(final HttpServletRequest request, final String clientCertHeader)
{
super(request);
+
+ if (clientCertHeader != null && clientCertHeader.length() > 0)
+ {
+
+ final String clientCert = HEADER_TO_CERT.matcher(clientCertHeader).replaceAll("\n");
+
+ try
+ {
+ InputStream instream = new ByteArrayInputStream(clientCert.getBytes(UTF_8));
+ CertificateFactory fac = CertificateFactory.getInstance("X.509");
+ @SuppressWarnings("unchecked")
+ Collection<X509Certificate> certs = (Collection<X509Certificate>) fac.generateCertificates(instream);
+ request.setAttribute(ATTR_SSL_CERTIFICATE, certs.toArray(new X509Certificate[certs.size()]));
+ }
+ catch (CertificateException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ void done() {
+ getRequest().removeAttribute(ATTR_SSL_CERTIFICATE);
}
public String getScheme()
@@ -64,4 +121,8 @@
return new StringBuffer(this.requestURL);
}
+
+ private final void provideCertificate(final HttpServletRequest request) throws UnsupportedEncodingException
+ {
+ }
}
diff --git a/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterRequestTest.java b/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterRequestTest.java
index 0db95a6..83ecfc0 100644
--- a/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterRequestTest.java
+++ b/http/sslfilter/src/test/java/org/apache/felix/http/sslfilter/internal/SslFilterRequestTest.java
@@ -34,7 +34,7 @@
public void test_isSecure()
{
HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
- SslFilterRequest sreq = new SslFilterRequest(req);
+ SslFilterRequest sreq = new SslFilterRequest(req, null);
when(req.isSecure()).thenReturn(false);
TestCase.assertFalse(req.isSecure());
@@ -51,7 +51,7 @@
public void test_getScheme()
{
HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
- SslFilterRequest sreq = new SslFilterRequest(req);
+ SslFilterRequest sreq = new SslFilterRequest(req, null);
when(req.getScheme()).thenReturn("http");
TestCase.assertEquals("http", req.getScheme());
@@ -68,7 +68,7 @@
public void test_getRequestURL()
{
HttpServletRequest req = Mockito.mock(HttpServletRequest.class);
- SslFilterRequest sreq = new SslFilterRequest(req);
+ SslFilterRequest sreq = new SslFilterRequest(req, null);
when(req.getRequestURL()).thenReturn(new StringBuffer("http://some/page"));
TestCase.assertEquals("http://some/page", req.getRequestURL().toString());