You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by dk...@apache.org on 2008/11/13 01:49:28 UTC

svn commit: r713584 - in /cxf/trunk: api/src/main/java/org/apache/cxf/endpoint/ rt/core/src/main/java/org/apache/cxf/configuration/spring/ rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/support/ rt/transports/http/src/main/java/org/apache/cxf/tra...

Author: dkulp
Date: Wed Nov 12 16:49:27 2008
New Revision: 713584

URL: http://svn.apache.org/viewvc?rev=713584&view=rev
Log:
Install a real HostnameVerifier that should work (need to make this more configurable)
Make the Configuration wildcarding actually use regex wildcards.
Update to HttpConduits to configure based on the address URL as well as endpoint name.

Added:
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java   (with props)
    cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml   (with props)
Modified:
    cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
    cxf/trunk/rt/core/src/main/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java
    cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/support/ServiceDelegateAccessor.java
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/AbstractHTTPTransportFactory.java
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java
    cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/HTTPSClientTest.java
    cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java

Modified: cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java (original)
+++ cxf/trunk/api/src/main/java/org/apache/cxf/endpoint/AbstractConduitSelector.java Wed Nov 12 16:49:27 2008
@@ -24,6 +24,7 @@
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.BusException;
+import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.message.Exchange;
 import org.apache.cxf.message.Message;
@@ -32,6 +33,8 @@
 import org.apache.cxf.transport.ConduitInitiator;
 import org.apache.cxf.transport.ConduitInitiatorManager;
 import org.apache.cxf.transport.MessageObserver;
+import org.apache.cxf.ws.addressing.AttributedURIType;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
 
 
 /**
@@ -70,7 +73,17 @@
                     ConduitInitiator conduitInitiator =
                         conduitInitiatorMgr.getConduitInitiator(transportID);
                     if (conduitInitiator != null) {
-                        selectedConduit = conduitInitiator.getConduit(ei);
+                        String add = (String)message.get(Message.ENDPOINT_ADDRESS);
+                        if (StringUtils.isEmpty(add)
+                            || add.equals(ei.getAddress())) {
+                            selectedConduit = conduitInitiator.getConduit(ei);
+                        } else {
+                            EndpointReferenceType epr = new EndpointReferenceType();
+                            AttributedURIType ad = new AttributedURIType();
+                            ad.setValue(add);
+                            epr.setAddress(ad);
+                            selectedConduit = conduitInitiator.getConduit(ei, epr);
+                        }
                         MessageObserver observer = 
                             exchange.get(MessageObserver.class);
                         if (observer != null) {

Modified: cxf/trunk/rt/core/src/main/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/core/src/main/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/rt/core/src/main/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java (original)
+++ cxf/trunk/rt/core/src/main/java/org/apache/cxf/configuration/spring/ConfigurerImpl.java Wed Nov 12 16:49:27 2008
@@ -20,12 +20,16 @@
 package org.apache.cxf.configuration.spring;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.configuration.Configurable;
@@ -47,7 +51,17 @@
     private static final Logger LOG = LogUtils.getL7dLogger(ConfigurerImpl.class);
 
     private Set<ApplicationContext> appContexts;
-    private final Map<String, String> wildCardBeanDefinitions = new HashMap<String, String>();
+    private final Map<String, List<MatcherHolder>> wildCardBeanDefinitions
+        = new HashMap<String, List<MatcherHolder>>();
+    
+    static class MatcherHolder {
+        Matcher matcher;
+        String wildCardId;
+        public MatcherHolder(String orig, Matcher matcher) {
+            wildCardId = orig;
+            this.matcher = matcher;
+        }
+    }
     
     public ConfigurerImpl() {
         // complete
@@ -67,14 +81,19 @@
                         BeanDefinition bd = bdr.getBeanDefinition(n);
                         String className = bd.getBeanClassName();
                         if (null != className) {
-                            if (!wildCardBeanDefinitions.containsKey(className)) {
-                                wildCardBeanDefinitions.put(className, n);
-                            } else {
-                                LogUtils.log(LOG, Level.WARNING, "ONE_WILDCARD_BEAN_ID_PER_CLASS_MSG", 
-                                             new String[]{wildCardBeanDefinitions.get(className),
-                                                          className,
-                                                          n});   
+                            String orig = n;
+                            if (n.charAt(0) == '*') {
+                                //old wildcard
+                                n = "." + n.replaceAll("\\.", "\\."); 
                             }
+                            Matcher matcher = Pattern.compile(n).matcher("");
+                            List<MatcherHolder> m = wildCardBeanDefinitions.get(className);
+                            if (m == null) {
+                                m = new ArrayList<MatcherHolder>();
+                                wildCardBeanDefinitions.put(className, m);
+                            }
+                            MatcherHolder holder = new MatcherHolder(orig, matcher);
+                            m.add(holder);
                         } else {
                             LogUtils.log(LOG, Level.WARNING, "WILDCARD_BEAN_ID_WITH_NO_CLASS_MSG", n); 
                         }
@@ -138,21 +157,24 @@
     private void configureWithWildCard(String bn, Object beanInstance) {
         if (!wildCardBeanDefinitions.isEmpty() && !isWildcardBeanName(bn)) {
             String className = beanInstance.getClass().getName();
-            if (wildCardBeanDefinitions.containsKey(className)) {
-                String wildCardBeanId = wildCardBeanDefinitions.get(className);
-                if (bn.endsWith(stripStar(wildCardBeanId))) {
-                    configureBean(wildCardBeanId, beanInstance);
-                }       
+            List<MatcherHolder> matchers = wildCardBeanDefinitions.get(className);
+            if (matchers != null) {
+                for (MatcherHolder m : matchers) {
+                    synchronized (m.matcher) {
+                        m.matcher.reset(bn);
+                        if (m.matcher.matches()) {
+                            configureBean(m.wildCardId, beanInstance);
+                            return;
+                        }
+                    }
+                }
             }
         }
     }
 
     private boolean isWildcardBeanName(String bn) {
-        return bn.charAt(0) == '*';
-    }
-
-    private String stripStar(String wildCardBeanId) {
-        return wildCardBeanId.substring(1);
+        return bn.indexOf('*') != -1 || bn.indexOf('?') != -1
+            || (bn.indexOf('(') != -1 && bn.indexOf(')') != -1);
     }
 
     protected String getBeanName(Object beanInstance) {

Modified: cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/support/ServiceDelegateAccessor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/support/ServiceDelegateAccessor.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/support/ServiceDelegateAccessor.java (original)
+++ cxf/trunk/rt/frontend/jaxws/src/main/java/org/apache/cxf/jaxws/support/ServiceDelegateAccessor.java Wed Nov 12 16:49:27 2008
@@ -39,6 +39,7 @@
     private static final Logger LOG = LogUtils.getL7dLogger(ServiceDelegateAccessor.class);
 
     private static final String DELEGATE_FIELD_NAME = "delegate";
+    private static final String DELEGATE_FIELD_NAME2 = "_delegate";
 
     private ServiceDelegateAccessor() {        
     }
@@ -59,11 +60,18 @@
             delegateField.setAccessible(true);
             delegate = (ServiceImpl)delegateField.get(service);
         } catch (Exception e) {
-            WebServiceException wse = new WebServiceException("Failed to access Field named "
-                                                              + DELEGATE_FIELD_NAME + " of Service instance "
-                                                              + service, e);
-            LOG.log(Level.SEVERE, e.getMessage(), e);
-            throw wse;
+            try {
+                Field delegateField = Service.class.getDeclaredField(DELEGATE_FIELD_NAME2);
+                delegateField.setAccessible(true);
+                delegate = (ServiceImpl)delegateField.get(service);
+            } catch (Exception e2) {
+                WebServiceException wse = new WebServiceException("Failed to access Field named "
+                                                                  + DELEGATE_FIELD_NAME 
+                                                                  + " of Service instance "
+                                                                  + service, e);
+                LOG.log(Level.SEVERE, e.getMessage(), e);
+                throw wse;                
+            }
         }
         return delegate;
     }

Modified: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/AbstractHTTPTransportFactory.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/AbstractHTTPTransportFactory.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/AbstractHTTPTransportFactory.java (original)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/AbstractHTTPTransportFactory.java Wed Nov 12 16:49:27 2008
@@ -158,9 +158,8 @@
         HTTPConduit conduit = target == null
             ? new HTTPConduit(bus, endpointInfo)
             : new HTTPConduit(bus, endpointInfo, target);
-        
         // Spring configure the conduit.  
-        configure(conduit);
+        configure(conduit, conduit.getAddress());
         conduit.finalizeConfig();
         return conduit;
     }
@@ -218,9 +217,15 @@
      * @param bean
      */
     protected void configure(Object bean) {
+        configure(bean, null);
+    }
+    protected void configure(Object bean, String extraName) {
         Configurer configurer = bus.getExtension(Configurer.class);
         if (null != configurer) {
             configurer.configureBean(bean);
+            if (extraName != null) {
+                configurer.configureBean(extraName, bean);
+            }
         }
     }
 

Modified: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java (original)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java Wed Nov 12 16:49:27 2008
@@ -679,8 +679,10 @@
         String pathInfo = (String)message.get(Message.PATH_INFO);
         String queryString = (String)message.get(Message.QUERY_STRING);
         if (result == null) {
+            if (pathInfo == null && queryString == null) {
+                return getURL();
+            }
             result = getURL().toString();
-            
         }
         
         // REVISIT: is this really correct?

Added: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java?rev=713584&view=auto
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java (added)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java Wed Nov 12 16:49:27 2008
@@ -0,0 +1,590 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+package org.apache.cxf.transport.https;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * ************************************************************************
+ * Copied from the not-yet-commons-ssl project at http://juliusdavies.ca/commons-ssl/
+ * As the above project is accepted into Apache and its JARs become available in
+ * the Maven 2 repos, we will have to switch to using the JARs instead
+ * ************************************************************************
+ * <p/>
+ * Interface for checking if a hostname matches the names stored inside the
+ * server's X.509 certificate.  Correctly implements
+ * javax.net.ssl.HostnameVerifier, but that interface is not recommended.
+ * Instead we added several check() methods that take SSLSocket,
+ * or X509Certificate, or ultimately (they all end up calling this one),
+ * String.  (It's easier to supply JUnit with Strings instead of mock
+ * SSLSession objects!)
+ * </p><p>Our check() methods throw exceptions if the name is
+ * invalid, whereas javax.net.ssl.HostnameVerifier just returns true/false.
+ * <p/>
+ * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and
+ * HostnameVerifier.ALLOW_ALL implementations.  We also provide the more
+ * specialized HostnameVerifier.DEFAULT_AND_LOCALHOST, as well as
+ * HostnameVerifier.STRICT_IE6.  But feel free to define your own
+ * implementations!
+ * <p/>
+ * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the
+ * HttpClient "contrib" repository.
+ *
+ * @author Julius Davies
+ * @author <a href="mailto:hauer@psicode.com">Sebastian Hauer</a>
+ * @since 8-Dec-2006
+ */
+
+public interface CertificateHostnameVerifier extends javax.net.ssl.HostnameVerifier {
+
+
+
+    /**
+     * The DEFAULT HostnameVerifier works the same way as Curl and Firefox.
+     * <p/>
+     * The hostname must match either the first CN, or any of the subject-alts.
+     * A wildcard can occur in the CN, and in any of the subject-alts.
+     * <p/>
+     * The only difference between DEFAULT and STRICT is that a wildcard (such
+     * as "*.foo.com") with DEFAULT matches all subdomains, including
+     * "a.b.foo.com".
+     */
+    CertificateHostnameVerifier DEFAULT =
+        new AbstractVerifier() {
+            public final void check(final String[] hosts, final String[] cns,
+                final String[] subjectAlts)
+                throws SSLException {
+                check(hosts, cns, subjectAlts, false, false);
+            }
+
+            public final String toString() {
+                return "DEFAULT";
+            }
+        };
+
+
+    /**
+     * The DEFAULT_AND_LOCALHOST HostnameVerifier works like the DEFAULT
+     * one with one additional relaxation:  a host of "localhost",
+     * "localhost.localdomain", "127.0.0.1", "::1" will always pass, no matter
+     * what is in the server's certificate.
+     */
+    CertificateHostnameVerifier DEFAULT_AND_LOCALHOST =
+        new AbstractVerifier() {
+            public final void check(final String[] hosts, final String[] cns,
+                final String[] subjectAlts)
+                throws SSLException {
+                if (isLocalhost(hosts[0])) {
+                    return;
+                }
+                check(hosts, cns, subjectAlts, false, false);
+            }
+
+            public final String toString() {
+                return "DEFAULT_AND_LOCALHOST";
+            }
+        };
+
+    /**
+     * The STRICT HostnameVerifier works the same way as java.net.URL in Sun
+     * Java 1.4, Sun Java 5, Sun Java 6.  It's also pretty close to IE6.
+     * This implementation appears to be compliant with RFC 2818 for dealing
+     * with wildcards.
+     * <p/>
+     * The hostname must match either the first CN, or any of the subject-alts.
+     * A wildcard can occur in the CN, and in any of the subject-alts.  The
+     * one divergence from IE6 is how we only check the first CN.  IE6 allows
+     * a match against any of the CNs present.  We decided to follow in
+     * Sun Java 1.4's footsteps and only check the first CN.
+     * <p/>
+     * A wildcard such as "*.foo.com" matches only subdomains in the same
+     * level, for example "a.foo.com".  It does not match deeper subdomains
+     * such as "a.b.foo.com".
+     */
+    CertificateHostnameVerifier STRICT =
+        new AbstractVerifier() {
+            public final void check(final String[] host, final String[] cns,
+                final String[] subjectAlts)
+                throws SSLException {
+                check(host, cns, subjectAlts, false, true);
+            }
+
+            public final String toString() {
+                return "STRICT";
+            }
+        };
+
+    /**
+     * The STRICT_IE6 HostnameVerifier works just like the STRICT one with one
+     * minor variation:  the hostname can match against any of the CN's in the
+     * server's certificate, not just the first one.  This behaviour is
+     * identical to IE6's behaviour.
+     */
+    CertificateHostnameVerifier STRICT_IE6 =
+        new AbstractVerifier() {
+            public final void check(final String[] host, final String[] cns,
+                final String[] subjectAlts)
+                throws SSLException {
+                check(host, cns, subjectAlts, true, true);
+            }
+
+            public final String toString() {
+                return "STRICT_IE6";
+            }
+        };
+
+    /**
+     * The ALLOW_ALL HostnameVerifier essentially turns hostname verification
+     * off.  This implementation is a no-op, and never throws the SSLException.
+     */
+    CertificateHostnameVerifier ALLOW_ALL =
+        new AbstractVerifier() {
+            public final void check(final String[] host, final String[] cns,
+                final String[] subjectAlts) {
+                // Allow everything - so never blowup.
+            }
+
+            public final String toString() {
+                return "ALLOW_ALL";
+            }
+        };
+
+        
+
+    boolean verify(String host, SSLSession session);
+
+    void check(String host, SSLSocket ssl) throws IOException;
+
+    void check(String host, X509Certificate cert) throws SSLException;
+
+    void check(String host, String[] cns, String[] subjectAlts)
+        throws SSLException;
+
+    void check(String[] hosts, SSLSocket ssl) throws IOException;
+
+    void check(String[] hosts, X509Certificate cert) throws SSLException;
+
+
+    /**
+     * Checks to see if the supplied hostname matches any of the supplied CNs
+     * or "DNS" Subject-Alts.  Most implementations only look at the first CN,
+     * and ignore any additional CNs.  Most implementations do look at all of
+     * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards
+     * according to RFC 2818.
+     *
+     * @param cns         CN fields, in order, as extracted from the X.509
+     *                    certificate.
+     * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted
+     *                    from the X.509 certificate.
+     * @param hosts       The array of hostnames to verify.
+     * @throws SSLException If verification failed.
+     */
+    void check(String[] hosts, String[] cns, String[] subjectAlts)
+        throws SSLException;
+        
+    abstract class AbstractVerifier implements CertificateHostnameVerifier {
+
+        /**
+         * This contains a list of 2nd-level domains that aren't allowed to
+         * have wildcards when combined with country-codes.
+         * For example: [*.co.uk].
+         * <p/>
+         * The [*.co.uk] problem is an interesting one.  Should we just hope
+         * that CA's would never foolishly allow such a certificate to happen?
+         * Looks like we're the only implementation guarding against this.
+         * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check.
+         */
+        private static final String[] BAD_COUNTRY_2LDS = {
+            "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
+            "lg", "ne", "net", "or", "org"};
+
+        private static final String[] LOCALHOSTS = { 
+            "::1", "127.0.0.1",
+            "localhost",
+            "localhost.localdomain"};
+
+
+        static {
+            // Just in case developer forgot to manually sort the array.  :-)
+            Arrays.sort(BAD_COUNTRY_2LDS);
+            Arrays.sort(LOCALHOSTS);
+        }
+
+        protected AbstractVerifier() {
+        }
+
+        /**
+         * The javax.net.ssl.HostnameVerifier contract.
+         *
+         * @param host    'hostname' we used to create our socket
+         * @param session SSLSession with the remote server
+         * @return true if the host matched the one in the certificate.
+         */
+        public boolean verify(String host, SSLSession session) {
+            try {
+                Certificate[] certs = session.getPeerCertificates();
+                X509Certificate x509 = (X509Certificate) certs[0];
+                check(new String[]{host}, x509);
+                return true;
+            } catch (SSLException e) {
+                return false;
+            }
+        }
+
+        public void check(String host, SSLSocket ssl) throws IOException {
+            check(new String[]{host}, ssl);
+        }
+
+        public void check(String host, X509Certificate cert)
+            throws SSLException {
+            check(new String[]{host}, cert);
+        }
+
+        public void check(String host, String[] cns, String[] subjectAlts)
+            throws SSLException {
+            check(new String[]{host}, cns, subjectAlts);
+        }
+
+        public void check(String host[], SSLSocket ssl)
+            throws IOException {
+            if (host == null) {
+                throw new NullPointerException("host to verify is null");
+            }
+
+            SSLSession session = ssl.getSession();
+            if (session == null) {
+                // In our experience this only happens under IBM 1.4.x when
+                // spurious (unrelated) certificates show up in the server'
+                // chain.  Hopefully this will unearth the real problem:
+                InputStream in = ssl.getInputStream();
+                in.available();
+                /*
+                  If you're looking at the 2 lines of code above because
+                  you're running into a problem, you probably have two
+                  options:
+
+                    #1.  Clean up the certificate chain that your server
+                         is presenting (e.g. edit "/etc/apache2/server.crt"
+                         or wherever it is your server's certificate chain
+                         is defined).
+
+                                               OR
+
+                    #2.   Upgrade to an IBM 1.5.x or greater JVM, or switch
+                          to a non-IBM JVM.
+                */
+
+                // If ssl.getInputStream().available() didn't cause an
+                // exception, maybe at least now the session is available?
+                session = ssl.getSession();
+                if (session == null) {
+                    // If it's still null, probably a startHandshake() will
+                    // unearth the real problem.
+                    ssl.startHandshake();
+
+                    // Okay, if we still haven't managed to cause an exception,
+                    // might as well go for the NPE.  Or maybe we're okay now?
+                    session = ssl.getSession();
+                }
+            }
+            Certificate[] certs;
+            try {
+                certs = session.getPeerCertificates();
+            } catch (SSLPeerUnverifiedException spue) {
+                InputStream in = ssl.getInputStream();
+                in.available();
+                // Didn't trigger anything interesting?  Okay, just throw
+                // original.
+                throw spue;
+            }
+            X509Certificate x509 = (X509Certificate) certs[0];
+            check(host, x509);
+        }
+
+        public void check(String[] host, X509Certificate cert)
+            throws SSLException {
+
+            String[] cns = Certificates.getCNs(cert);
+            String[] subjectAlts = Certificates.getDNSSubjectAlts(cert);
+            check(host, cns, subjectAlts);
+
+        }
+
+        public void check(final String[] hosts, final String[] cns,
+            final String[] subjectAlts, final boolean ie6,
+            final boolean strictWithSubDomains)
+            throws SSLException {
+            // Build up lists of allowed hosts For logging/debugging purposes.
+            StringBuffer buf = new StringBuffer(32);
+            buf.append('<');
+            for (int i = 0; i < hosts.length; i++) {
+                String h = hosts[i];
+                h = h != null ? h.trim().toLowerCase() : "";
+                hosts[i] = h;
+                if (i > 0) {
+                    buf.append('/');
+                }
+                buf.append(h);
+            }
+            buf.append('>');
+            String hostnames = buf.toString();
+            // Build the list of names we're going to check.  Our DEFAULT and
+            // STRICT implementations of the HostnameVerifier only use the
+            // first CN provided.  All other CNs are ignored.
+            // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way).
+            Set<String> names = new TreeSet<String>();
+            if (cns != null && cns.length > 0 && cns[0] != null) {
+                names.add(cns[0]);
+                if (ie6) {
+                    for (int i = 1; i < cns.length; i++) {
+                        names.add(cns[i]);
+                    }
+                }
+            }
+            if (subjectAlts != null) {
+                for (int i = 0; i < subjectAlts.length; i++) {
+                    if (subjectAlts[i] != null) {
+                        names.add(subjectAlts[i]);
+                    }
+                }
+            }
+            if (names.isEmpty()) {
+                String msg = "Certificate for " + hosts[0] + " doesn't contain CN or DNS subjectAlt";
+                throw new SSLException(msg);
+            }
+
+            // StringBuffer for building the error message.
+            buf = new StringBuffer();
+
+            boolean match = false;
+        out:
+            for (Iterator<String> it = names.iterator(); it.hasNext();) {
+                // Don't trim the CN, though!
+                String cn = it.next();
+                cn = cn.toLowerCase();
+                // Store CN in StringBuffer in case we need to report an error.
+                buf.append(" <");
+                buf.append(cn);
+                buf.append('>');
+                if (it.hasNext()) {
+                    buf.append(" OR");
+                }
+
+                // The CN better have at least two dots if it wants wildcard
+                // action.  It also can't be [*.co.uk] or [*.co.jp] or
+                // [*.org.uk], etc...
+                boolean doWildcard = cn.startsWith("*.") 
+                    && cn.lastIndexOf('.') >= 0 
+                    && !isIP4Address(cn) 
+                    && acceptableCountryWildcard(cn);
+
+                for (int i = 0; i < hosts.length; i++) {
+                    final String hostName = hosts[i].trim().toLowerCase();
+                    if (doWildcard) {
+                        match = hostName.endsWith(cn.substring(1));
+                        if (match && strictWithSubDomains) {
+                            // If we're in strict mode, then [*.foo.com] is not
+                            // allowed to match [a.b.foo.com]
+                            match = countDots(hostName) == countDots(cn);
+                        }
+                    } else {
+                        match = hostName.equals(cn);
+                    }
+                    if (match) {
+                        break out;
+                    }
+                }
+            }
+            if (!match) {
+                throw new SSLException("hostname in certificate didn't match: " + hostnames + " !=" + buf);
+            }
+        }
+
+        public static boolean isIP4Address(final String cn) {
+            boolean isIP4 = true;
+            String tld = cn;
+            int x = cn.lastIndexOf('.');
+            // We only bother analyzing the characters after the final dot
+            // in the name.
+            if (x >= 0 && x + 1 < cn.length()) {
+                tld = cn.substring(x + 1);
+            }
+            for (int i = 0; i < tld.length(); i++) {
+                if (!Character.isDigit(tld.charAt(0))) {
+                    isIP4 = false;
+                    break;
+                }
+            }
+            return isIP4;
+        }
+
+        public static boolean acceptableCountryWildcard(final String cn) {
+            int cnLen = cn.length();
+            if (cnLen >= 7 && cnLen <= 9) {
+                // Look for the '.' in the 3rd-last position:
+                if (cn.charAt(cnLen - 3) == '.') {
+                    // Trim off the [*.] and the [.XX].
+                    String s = cn.substring(2, cnLen - 3);
+                    // And test against the sorted array of bad 2lds:
+                    int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
+                    return x < 0;
+                }
+            }
+            return true;
+        }
+
+        public static boolean isLocalhost(String host) {
+            host = host != null ? host.trim().toLowerCase() : "";
+            if (host.startsWith("::1")) {
+                int x = host.lastIndexOf('%');
+                if (x >= 0) {
+                    host = host.substring(0, x);
+                }
+            }
+            int x = Arrays.binarySearch(LOCALHOSTS, host);
+            return x >= 0;
+        }
+
+        /**
+         * Counts the number of dots "." in a string.
+         *
+         * @param s string to count dots from
+         * @return number of dots
+         */
+        public static int countDots(final String s) {
+            int count = 0;
+            for (int i = 0; i < s.length(); i++) {
+                if (s.charAt(i) == '.') {
+                    count++;
+                }
+            }
+            return count;
+        }
+    }
+
+    final class Certificates {
+        private Certificates() {
+            //utility class
+        }
+        public static String[] getCNs(X509Certificate cert) {
+            List<String> cnList = new LinkedList<String>();
+            /*
+           Sebastian Hauer's original StrictSSLProtocolSocketFactory used
+           getName() and had the following comment:
+
+              Parses a X.500 distinguished name for the value of the
+              "Common Name" field.  This is done a bit sloppy right
+              now and should probably be done a bit more according to
+              <code>RFC 2253</code>.
+
+            I've noticed that toString() seems to do a better job than
+            getName() on these X500Principal objects, so I'm hoping that
+            addresses Sebastian's concern.
+
+            For example, getName() gives me this:
+            1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
+
+            whereas toString() gives me this:
+            EMAILADDRESS=juliusdavies@cucbc.com
+
+            Looks like toString() even works with non-ascii domain names!
+            I tested it with "&#x82b1;&#x5b50;.co.jp" and it worked fine.
+           */
+            String subjectPrincipal = cert.getSubjectX500Principal().toString();
+            StringTokenizer st = new StringTokenizer(subjectPrincipal, ",");
+            while (st.hasMoreTokens()) {
+                String tok = st.nextToken();
+                int x = tok.indexOf("CN=");
+                if (x >= 0) {
+                    cnList.add(tok.substring(x + 3));
+                }
+            }
+            if (!cnList.isEmpty()) {
+                String[] cns = new String[cnList.size()];
+                cnList.toArray(cns);
+                return cns;
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Extracts the array of SubjectAlt DNS names from an X509Certificate.
+         * Returns null if there aren't any.
+         * <p/>
+         * Note:  Java doesn't appear able to extract international characters
+         * from the SubjectAlts.  It can only extract international characters
+         * from the CN field.
+         * <p/>
+         * (Or maybe the version of OpenSSL I'm using to test isn't storing the
+         * international characters correctly in the SubjectAlts?).
+         *
+         * @param cert X509Certificate
+         * @return Array of SubjectALT DNS names stored in the certificate.
+         */
+        public static String[] getDNSSubjectAlts(X509Certificate cert) {
+            List<String> subjectAltList = new LinkedList<String>();
+            Collection<List<?>> c = null;
+            try {
+                c = cert.getSubjectAlternativeNames();
+            } catch (CertificateParsingException cpe) {
+                // Should probably log.debug() this?
+                cpe.printStackTrace();
+            }
+            if (c != null) {
+                Iterator it = c.iterator();
+                while (it.hasNext()) {
+                    List list = (List) it.next();
+                    int type = ((Integer) list.get(0)).intValue();
+                    // If type is 2, then we've got a dNSName
+                    if (type == 2) {
+                        String s = (String) list.get(1);
+                        subjectAltList.add(s);
+                    }
+                }
+            }
+            if (!subjectAltList.isEmpty()) {
+                String[] subjectAlts = new String[subjectAltList.size()];
+                subjectAltList.toArray(subjectAlts);
+                return subjectAlts;
+            } else {
+                return null;
+            }
+        }
+    }
+}

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/CertificateHostnameVerifier.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java (original)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/https/HttpsURLConnectionFactory.java Wed Nov 12 16:49:27 2008
@@ -30,10 +30,8 @@
 import java.util.logging.Logger;
 
 import javax.imageio.IIOException;
-import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocketFactory;
 
 import org.apache.cxf.common.logging.LogUtils;
@@ -60,8 +58,6 @@
     private static final Logger LOG =
         LogUtils.getL7dLogger(HttpsURLConnectionFactory.class);
     
-    private static final HostnameVerifier DISABLE_HOSTNAME_VERIFIER = new AlwaysTrueHostnameVerifier();
-    
     /*
      *  For development and testing only
      */
@@ -151,25 +147,6 @@
 
         return connection;
     }
-
-    /**
-     * This "accept all" hostname verifier is activated when the 
-     * disableCNCheck TLS client configuration parameter is set to 
-     * true (not recommended for production use).  The default of
-     * false makes sure the Common Name (CN) on the server 
-     * certificate equals that of the https:// URL provided by
-     * the SOAP client.
-     */
-    private static class AlwaysTrueHostnameVerifier implements HostnameVerifier {
-
-        public boolean verify(
-            String      hostname,
-            SSLSession  sslSession
-        ) {
-            return true;
-        }
-
-    }
     
     /**
      * This method assigns the various TLS parameters on the HttpsURLConnection
@@ -209,7 +186,9 @@
                                                         tlsClientParameters.getSecureSocketProtocol());
         }
         if (tlsClientParameters.isDisableCNCheck()) {
-            connection.setHostnameVerifier(DISABLE_HOSTNAME_VERIFIER);
+            connection.setHostnameVerifier(CertificateHostnameVerifier.ALLOW_ALL);
+        } else {
+            connection.setHostnameVerifier(CertificateHostnameVerifier.DEFAULT);
         }
         connection.setSSLSocketFactory(socketFactory);
     }

Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/HTTPSClientTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/HTTPSClientTest.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/HTTPSClientTest.java (original)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/HTTPSClientTest.java Wed Nov 12 16:49:27 2008
@@ -82,7 +82,6 @@
         startServers();
         SOAPService service = new SOAPService();
         assertNotNull("Service is null", service);   
-        
         final Greeter port = service.getHttpsPort();
         assertNotNull("Port is null", port);
         
@@ -118,4 +117,9 @@
         testSuccessfulCall("resources/resource-key-spec.xml",
                            "https://localhost:9004/SoapContext/HttpsPort");
     }
+    @Test
+    public final void testResourceKeySpecEndpointURL() throws Exception {
+        testSuccessfulCall("resources/resource-key-spec-url.xml",
+                           "https://localhost:9005/SoapContext/HttpsPort");
+    }
 }

Added: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml?rev=713584&view=auto
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml (added)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml Wed Nov 12 16:49:27 2008
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:http="http://cxf.apache.org/transports/http/configuration"
+       xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
+       xmlns:jaxws="http://cxf.apache.org/jaxws"
+       xmlns:sec="http://cxf.apache.org/configuration/security"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans                 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+        http://cxf.apache.org/jaxws                                 http://cxf.apache.org/schemas/jaxws.xsd
+        http://cxf.apache.org/transports/http/configuration         http://cxf.apache.org/schemas/configuration/http-conf.xsd
+        http://cxf.apache.org/transports/http-jetty/configuration   http://cxf.apache.org/schemas/configuration/http-jetty.xsd
+        http://cxf.apache.org/configuration/security                http://cxf.apache.org/schemas/configuration/security.xsd
+        ">
+
+    <!-- -->
+    <!-- This Spring config file is designed to represent a minimal -->
+    <!-- configuration for spring-loading a CXF servant, where the -->
+    <!-- servant listens using HTTP/S as the transport protocol. -->
+    <!-- -->
+    <!-- Note that the service endpoint is spring-loaded.  In the -->
+    <!-- scenario in which this config is designed to run, the -->
+    <!-- server application merely instantiates a Bus, and does not -->
+    <!-- publish any services programmatically -->
+    <!-- -->
+
+    <!-- -->
+    <!-- Spring-load an HTTPS servant -->
+    <!-- -->
+    <jaxws:endpoint 
+        id="JaxwsHttpsEndpoint"
+        implementor="org.apache.cxf.systest.http.GreeterImpl"
+        address="https://localhost:9005/SoapContext/HttpsPort"
+        serviceName="s:SOAPService"
+        endpointName="e:HttpsPort"
+        xmlns:e="http://apache.org/hello_world/services"
+        xmlns:s="http://apache.org/hello_world/services"
+        depends-on="port-9005-tls-config"/>
+
+    <!-- -->
+    <!-- TLS Port configuration parameters for port 9005 -->
+    <!-- -->
+    <!-- This test exercises the resource attribute in a keyStore element -->
+    <!-- -->
+    <httpj:engine-factory id="port-9005-tls-config">
+        <httpj:engine port="9005">
+            <httpj:tlsServerParameters>
+               <sec:keyManagers keyPassword="password">
+               <sec:keyStore type="JKS" password="password" 
+                    resource="org/apache/cxf/systest/http/resources/Bethal.jks"/>
+                </sec:keyManagers>
+                <sec:trustManagers>
+                <sec:keyStore type="JKS" password="password"
+                   resource="org/apache/cxf/systest/http/resources/Truststore.jks"/>
+                </sec:trustManagers>
+            </httpj:tlsServerParameters>
+        </httpj:engine>
+    </httpj:engine-factory>
+
+    <!-- -->
+    <!-- HTTP/S configuration for clients -->
+    <!-- -->
+    <!-- This test exercises the resource attribute in a keyStore and certStore element -->
+    <!-- -->
+    <http:conduit name="https://localhost:9005/SoapContext/HttpsPort">
+        <http:tlsClientParameters disableCNCheck="true">
+            <sec:keyManagers keyPassword="password">
+               <sec:keyStore type="pkcs12" password="password" 
+                    resource="org/apache/cxf/systest/http/resources/Morpit.p12"/>
+               </sec:keyManagers>
+            <sec:trustManagers>
+               <sec:certStore
+                   resource="org/apache/cxf/systest/http/resources/Truststore.pem"/>
+            </sec:trustManagers>
+        </http:tlsClientParameters>
+    </http:conduit>
+
+</beans>

Propchange: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Propchange: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/http/resources/resource-key-spec-url.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Modified: cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java?rev=713584&r1=713583&r2=713584&view=diff
==============================================================================
--- cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java (original)
+++ cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxws/ClientServerTest.java Wed Nov 12 16:49:27 2008
@@ -829,6 +829,15 @@
         String realAddress = "http://localhost:9015/SoapContext/SoapPort";
         SOAPServiceBogusAddressTest service = new SOAPServiceBogusAddressTest();
         Greeter greeter = service.getSoapPort();
+        try {
+            greeter.greetMe("test");
+            fail("Should fail");
+        } catch (WebServiceException f) {
+            // expected
+        }
+
+        
+        
         BindingProvider bp = (BindingProvider)greeter;
         bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                                    realAddress);