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
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
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>
le properties
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; } } } }
coté client : un simple main
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
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
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 properties 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; } } } }
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.
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
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)
Partager