Bonjour,
Je rencontre un souci au travers d'une configuration cxf 2.2 et spring 2.5.6

La demande est de refondre des web services cxf de facon a les rendre sécurisés.
Le serveur doit pouvoir authentifier de manière unique les différents clients au travers de certificats.

Quelques recherches m'ont conduit a un tuto d'apache :
http://www.jroller.com/gmazza/entry/cxf_x509_profile

Ce tuto fonctionne tres bien sur un service type helloWorld. Chaque client s'authentifie avec un certif qui lui est propre. Le serveur verifiant la validité au travers de son keystore.

Mais impossible a adapter sur l'existant...

Coté serveur (spring)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans.xsd 
	http://cxf.apache.org/jaxws 
	http://cxf.apache.org/schemas/jaxws.xsd">
 
  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
 
 
 
 <jaxws:endpoint id="ConsultationService" 
  bindingUri="http://schemas.xmlsoap.org/wsdl/soap/http"
    implementor="fr.xxxxxxxxxxxxxxxxxxxxx.services.ConsultationImpl" 
    address="/ConsultationService" 
    serviceName="kxxxxxx:ServiceDeConsultation"
    xmlns:karajan="http://xxxxxxxxxxxxxxxxxxxxxxx/services/ConsultationService">
 
 
 
		<jaxws:outInterceptors>
			<ref bean="TimestampSignEncrypt_Response"/>
		</jaxws:outInterceptors>
 
	    <jaxws:inInterceptors>
	        <ref bean="SecurityInterceptor"/>
	        <ref bean="TimestampSignEncrypt_Request"/>
	    </jaxws:inInterceptors>
 
  </jaxws:endpoint>  	
 
 
 
 
           <bean id="TimestampSignEncrypt_Response"  class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
		        <constructor-arg>
		            <map>
		                <entry key="action" value="Timestamp Signature Encrypt"/>
		                <entry key="user" value="myservicekey"/>
		                <entry key="signaturePropFile" value="serviceKeystore.properties"/>
		                <entry key="encryptionPropFile" value="serviceKeystore.properties"/>
		                <entry key="encryptionUser" value="useReqSigCert"/>
		                <entry key="passwordCallbackClass" value="fr.xxxxxxxxxxxxxxxxxxxxxxxxxxxxx.ServerPasswordCallback"/>
		                <entry key="signatureParts" value="{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
               		<entry key="encryptionParts" value="{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
               		<entry key="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
		            </map>
		        </constructor-arg>
    		</bean>
 
         <bean id="TimestampSignEncrypt_Request"   class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
	        <constructor-arg>
	            <map>
	                <entry key="action" value="Timestamp Signature Encrypt"/>
	                <entry key="signaturePropFile" value="serviceKeystore.properties"/>
	                <entry key="decryptionPropFile" value="serviceKeystore.properties"/>
	                <entry key="passwordCallbackClass" value="fr.xxxxxxxxxxxxxxxxxxxxxxx.handler.ServerPasswordCallback"/>
	            </map>
	        </constructor-arg>
    	</bean>
        <bean id="SecurityInterceptor"  class="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.security.SecurityInterceptor"/> 
 
</beans>

web.xml
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
	                 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
  <display-name>Kxxxn</display-name>
 
 
	<!-- CXF -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>WEB-INF/beans.xml</param-value>
	</context-param>
 
 
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
 
 
  <!-- WEBSERVICES CXF -->
 
  <servlet>
    <servlet-name>cxf</servlet-name> 
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>3</load-on-startup>
  </servlet>
 
 
  <!-- Mapping CXF -->
  <servlet-mapping>
    <servlet-name>cxf</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
 
 
  <!--  Maintien de session à 10 minutes -->
  <session-config>
    <session-timeout>10</session-timeout>
  </session-config>
  <welcome-file-list>
    <welcome-file>lb.jsp</welcome-file>
  </welcome-file-list>
</web-app>
mon Callback coté serveur : passwordCallbackClass
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
public class ServerPasswordCallback implements CallbackHandler {
 
	private Map<String, String> passwords = new HashMap<String, String>();
 
    public ServerPasswordCallback() {
        passwords.put("myservicekey", "skpass");
    }
 
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
 
    	for (int i = 0; i < callbacks.length; i++) {
            WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
 
            String pass = passwords.get(pc.getIdentifier());
            if (pass != null) {
                pc.setPassword(pass);
                return;
            }
        }
    }   
}
le properties
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=serviceKeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=sspass
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
coté client : un simple main

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
 * This class was generated by Apache CXF 2.6.0
 * 2012-05-03T13:58:41.267+02:00
 * Generated source version: 2.6.0
 * 
 */
public final class Consultation_ConsultationImplPort_Client {
 
    private static final QName SERVICE_NAME = new QName("http://xxxxxxxxxxxxxxxxxxr/services/ConsultationService", "ServiceDeConsultation");
 
    private Consultation_ConsultationImplPort_Client() {
    }
 
    public static void main(String args[]) throws java.lang.Exception {
        URL wsdlURL = ServiceDeConsultation.WSDL_LOCATION;
        if (args.length > 0 && args[0] != null && !"".equals(args[0])) { 
            File wsdlFile = new File(args[0]);
            try {
                if (wsdlFile.exists()) {
                    wsdlURL = wsdlFile.toURI().toURL();
                } else {
                    wsdlURL = new URL(args[0]);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
 
        ServiceDeConsultation ss = new ServiceDeConsultation(wsdlURL, SERVICE_NAME);
        Consultation port = ss.getConsultationImplPort();  
 
    	System.out.println("MainTEstClient generate--------------");
 
 
		org.apache.cxf.endpoint.Client client = org.apache.cxf.frontend.ClientProxy.getClient(port);
 
		HTTPConduit conduit = (HTTPConduit) client.getConduit();
		HTTPClientPolicy policy = conduit.getClient();
		policy.setReceiveTimeout(30000);
		policy.setAllowChunking(false);
 
		Map<String, Object> outProps = new HashMap<String, Object>();
		outProps.put(WSHandlerConstants.ACTION,	WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPT);
		outProps.put(WSHandlerConstants.USER, "myclientkey");
		outProps.put(WSHandlerConstants.SIG_PROP_FILE, "clientKeystore.properties");
		outProps.put(WSHandlerConstants.ENC_PROP_FILE, "clientKeystore.properties");
		outProps.put(WSHandlerConstants.ENCRYPTION_USER, "myservicekey");
		outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,	ClientPasswordCallback.class.getName());
		//String bodyPart = "{Content}{}Body";
		outProps.put(WSHandlerConstants.SIGNATURE_PARTS,	"{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body");
		outProps.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");
		outProps.put(WSHandlerConstants.ENC_SYM_ALGO, "http://www.w3.org/2001/04/xmlenc#tripledes-cbc");
 
 
		WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
 
 
		Map<String, Object> inProps = new HashMap<String, Object>();
		inProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.SIGNATURE + " " + WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.ENCRYPT);
		inProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());
		inProps.put(WSHandlerConstants.SIG_PROP_FILE, "clientKeystore.properties");
		inProps.put(WSHandlerConstants.DEC_PROP_FILE, "clientKeystore.properties");
 
 
		WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
 
		org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();
 
		cxfEndpoint.getOutInterceptors().add(wssOut);
		cxfEndpoint.getOutInterceptors().add(new SAAJOutInterceptor());
 
		cxfEndpoint.getInInterceptors().add(wssIn);
		cxfEndpoint.getInInterceptors().add(new SAAJInInterceptor());
 
 
        {
        System.out.println("Invoking recuperPersonne...");
        Context _recuperPersonne_contextApplicatif = new  Context();
        _recuperPersonne_contextApplicatif.setCanal("Canal982244408");
        _recuperPersonne_contextApplicatif.setInstance("Instance921856946");
        _recuperPersonne_contextApplicatif.setLogin("1111111111111");
        java.lang.String _recuperPersonne_codePartenaire = "93282";
        java.lang.String _recuperPersonne_idtPersonneExterne = "_recuperPersonne_idtPersonneExterne-155186809";
 
       try {
            Boolean _recuperPersonne__return = port.authentification(_recuperPersonne_contextApplicatif, _recuperPersonne_codePartenaire, _recuperPersonne_idtPersonneExterne, "Demo2012");
            System.out.println("recuperPersonne.result=" + _recuperPersonne__return);
 
        } catch (BusinessException e) { 
            System.out.println("Expected exception: BusinessException has occurred.");
            System.out.println(e.toString());
        } catch (TechnicalException e) { 
            System.out.println("Expected exception: TechnicalException has occurred.");
            System.out.println(e.toString());
        }
            }
 
 
        System.exit(0);
    }
 
}
le callback coté client

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ClientPasswordCallback implements CallbackHandler {
 
	private Map<String, String> passwords = new HashMap<String, String>();
 
	public ClientPasswordCallback() {
		passwords.put("myclientkey", "ckpass");
	}
 
	public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
 
		for (int i = 0; i < callbacks.length; i++) {
			WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
 
			String pass = passwords.get(pc.getIdentifier());
			if (pass != null) {
				pc.setPassword(pass);
				return;
			}
		}
 
	}
}
le properties coté client

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=clientKeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=cspass
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
Je pense que coté client ca se passe bien mais le souci est coté serveur, l'intercepteur fait bien son boulot : on passe dedans, mais l'objet callback est vide alors qu'il devrait contenir dans la propriété Identifier la valeur "myservicekey" declaré dans le bean xml. C'est d'ailleurs ce qui se passe coté client et sur mes projets de test helloworld.
Ici une exception est levé


Code : Sélectionner tout - Visualiser dans une fenêtre à part
18:00:30,277 WARN  [org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor] : org.apache.ws.security.WSSecurityException: General security error (WSSecurityEngine: Callback supplied no password for: null)