Me ha tocado realizar un cliente para este Servicio Web del Estado (Español).
En los documentos adjuntos, hay mucha informacion relativa a lo que hace y como quiere los datos.
Sobre seguridad no indica nada mas que permite: WS-Security, XMLDSig y Xades. Que se tiene que firmar, pero no indica, que algoritmos hay que usar, como firmar las cabeceras, que partes hay que firmar, etc. Cosa sorprendente cuando ves que hay varias maneras de firmar y de hacer las cosas. Pero te dan una peticion y una respuesta tipo. En base a esa peticion he ido realizando pruebas y puedo afirmar que ya lo tengo.
En CXF recomiendan el uso de WS-Policy siempre y cuando el WSDL contenga estos apartados, pero en este caso tenemos que usar los interceptors de CXF. En el cliente antes de enviar la peticion SOAP, tenemos que firmar el soap:body. Esto se hace en el soap:header, con WS-Security y los Interceptors.
Por lo que he deducido las caracteristicas de WS-Security son:
- no debe aparecer la etiqueta: mustUnderstand=»1″
- tipo de firma: «DirectReference«
- algoritmo de firma: http://www.w3.org/2000/09/xmldsig#rsa-sha1
- digest de la firma: http://www.w3.org/2000/09/xmldsig#sha1
- algoritmo de canonización XML: http://www.w3.org/2001/10/xml-exc-c14n
- parte a firmar: soap:envelope
Dejo mi fichero de configuración de spring para CXF y mi fichero de propiedades:
<?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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:util="http://www.springframework.org/schema/util" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core" xmlns:http="http://cxf.apache.org/transports/http/configuration" xmlns:p="http://cxf.apache.org/policy" xmlns:wsa="http://cxf.apache.org/ws/addressing" xmlns:wsp="http://www.w3.org/2006/07/ws-policy" xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xsi:schemaLocation=" http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/ws/addressing http://cxf.apache.org/schema/ws/addressing.xs http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://www.w3.org/2006/07/ws-policy http://www.w3.org/2006/07/ws-policy.xsd http://cxf.apache.org/policy http://cxf.apache.org/schemas/policy.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath*:spring-xsd.xml" /> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <http:conduit name="https://localhost:.*"> <http:tlsClientParameters disableCNCheck="true"> <!-- <sec:trustManagers> --> <!-- <sec:keyStore type="jks" password="cspass" resource="clientKeystore.jks" /> --> <!-- </sec:trustManagers> --> </http:tlsClientParameters> </http:conduit> <cxf:bus> <cxf:features> <cxf:logging /> </cxf:features> <cxf:inInterceptors> <ref bean="SignResponse" /> <bean class="org.apache.cxf.ws.security.wss4j.DefaultCryptoCoverageChecker" /> </cxf:inInterceptors> <cxf:outInterceptors> <ref bean="SignRequest" /> </cxf:outInterceptors> </cxf:bus> <util:map id="mapKeystorePasswords"> <entry key="${org.apache.ws.security.crypto.merlin.keystore.alias}" value="${org.apache.ws.security.crypto.merlin.keystore.password}" /> </util:map> <bean id="clientPasswordCallback" class="es.una.ruta.callback.ClientPasswordCallback" > </bean> <bean id="url" class="java.lang.String"> <constructor-arg value="https://${paxase.host}:${paxase.port}/${paxase.wsname}" /> </bean> <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="serviceClass" value="es.map.xml_schemas.PeticionPortType" /> <property name="address" value="#{url}/consultarIdentidad" /> </bean> <bean id="clientConsultaIdentidad" class="es.map.xml_schemas.PeticionPortType" factory-bean="clientFactory" factory-method="create" /> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor" id="SignRequest"> <constructor-arg> <map> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).IS_BSP_COMPLIANT}" value="#{T(java.lang.Boolean).FALSE.toString()}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).MUST_UNDERSTAND}" value="#{T(java.lang.Boolean).FALSE.toString()}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).ACTION}" value="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).USER}" value="${org.apache.ws.security.crypto.merlin.keystore.alias}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_PROP_FILE}" value="client_sign.properties" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_KEY_ID}" value="${signature.key.identifier}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_ALGO}" value="#{T(org.apache.ws.security.WSConstants).RSA_SHA1}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_DIGEST_ALGO}" value="#{T(org.apache.ws.security.WSConstants).SHA1}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_C14N_ALGO}" value="#{T(org.apache.ws.security.WSConstants).C14N_EXCL_OMIT_COMMENTS}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE_PARTS}" value="#{'{}{' + T(org.apache.ws.security.WSConstants).URI_SOAP11_ENV + '}' + T(org.apache.ws.security.WSConstants).ELEM_BODY + ';'}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).PW_CALLBACK_REF}" value-ref="clientPasswordCallback" /> </map> </constructor-arg> </bean> <bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor" id="SignResponse"> <constructor-arg> <map> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).ACTION}" value="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIGNATURE}" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_PROP_FILE}" value="client_sign.properties" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).PW_CALLBACK_REF}" value-ref="clientPasswordCallback" /> <entry key="#{T(org.apache.ws.security.handler.WSHandlerConstants).SIG_ALGO}" value="#{T(org.apache.ws.security.WSConstants).RSA_SHA1}" /> </map> </constructor-arg> </bean> </beans>
En mi fichero de propiedades del keystore tengo puesto el tipo de firma:
# Defines which key identifier type to use for signature. # Valid values are: - "IssuerSerial" - "DirectReference" - "X509KeyIdentifier" - "Thumbprint" - "SKIKeyIdentifier" - "KeyValue" - "EmbeddedKeyName" - "EncryptedKeySHA1" signature.key.identifier =DirectReference # 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