I'm trying to develop a web service that uses WS-Security using Websphere 7 and JAX-WS. Looking through some guides, it appears that I MUST create a application server user registry and maintain username/passwords inside of that server. Is there anyway to avoid having to create usernames in the server itself and somehow capture the header and do validation based upon another a custom security configuration like a single sign-on?
I'm able to create a handler to get the header, but when mustUnderstands is set to 1 in the request (which is mandatory), it gets rejected before my handler sees the message.
I'm only looking to use the UsernameToken part of WS-Security.
Any help is appreciated.
An example of my request
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" Id="unt_20">
<wsse:Username>some_username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">some_password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
...body...
</soapenv:Body>
</soapenv:Envelope>
Is it possible to create a custom security implementation so I can use my existing user validation scheme?
It would appear that I can implement a custom user registry that can interact with the security implementation of my choice. A link to the IBM article:
http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.base.doc/info/aes/ae/tsec_useregistry.html
Another possible answer is to create a Trust Association Interceptor (TAI). This basically extends your current security.
Here is a useful link to get started:
http://www.ibm.com/developerworks/websphere/techjournal/0508_benantar/0508_benantar.html
You can use the out-of-box WS-Security runtime with policy/bindings to achieve this, but you must write custom code to override the default behavior of checking the user registry for UsernameTokens.
See this article for using your own authentication mechanism when consuming the UsernameToken:
Configuring a UsernameToken caller configuration with no registry interaction
See this article if you want to also create WebSphere credentials based on the user name in that token:
Replacing the authentication method of the UsernameToken consumer using a stacked JAAS login module
Can you elaborate on what you want to achieve?
The WAS Server needs to validate the username and password that comes in the header against its user registry (which could an LDAP, File based registry etc).
LTPA tokens (which are used by WebSphere and related products for SSO) can be used too.
If you spell out your requirements, folks here will be able to help you out.
HTH
Manglu
JAX-WS should allow you to have a custom interceptor.
Take a look at this spring config to see how I have added an interceptor to the service endpoint.
<jaxws:endpoint id="pqdws"
implementor="#Atypon"
address="/pqdws"
publishedEndpointUrl="#ws_webapp_url_ext#">
<jaxws:properties>
<entry key="exceptionMessageCauseEnabled" value="true"/>
<entry key="Content-length"
</jaxws:properties>
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="passwordCallbackRef">
<ref bean="passwordCallback"/>
</entry>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>
<bean id="passwordCallback"
class="access.ws.ServerPasswordCallback">
<property name="username" value="#ws_sec_username#"/>
<property name="password" value="#ws_sec_password#"/>
</bean>
The interceptor can then do whatever you wish including calling out to an external service for authentication.
Related
I am updating the client of an existing SOAP webservice since the service provider has made some changes to thier security headers in the request.
The requirement is to digitally sign the Timestamp which should be present in the request header and the body should not be digitally signed. I am using XML config to create my SOAP request header and digitally sign the Timestamp.
I am basically using org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor as an interceptor. The problem is that Timestamp gets created in the request header however the SignatureValue and DigestValue tags are empty
I have referred https://docs.spring.io/spring-ws/site/reference/html/security.html#security-wss4j-digital-signatures
Versions:
Spring-ws-core --> 2.0.0.RELEASE
spring-ws-security --> 2.0.0.RELEASE
<bean id="wsClientSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference" />
<property name="securementUsername" value="username" />
<property name="securementPassword" value="keystorepassword" />
<property name="securementSignatureCrypto" ref="clientCrypto"/>
<property name="securementSignatureUser" value="username" />
<property name="securementSignatureParts" value="{}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp"/>
</bean>
<bean id="clientCrypto" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="keystorepassword" />
<property name="keyStoreLocation" value="file:${key.store.location}"/>
<property name="keyStoreType" value="jks" />
<property name="keyStoreProvider" value="IBMJCE" />
</bean>
Though the timestamp gets added to the wsse:Security element in the header, the DigestValue and SignatureValue elements belonging to the xmlns:ds="http://www.w3.org/2000/09/xmldsig#" namespace are always empty
This does not happen if I only sign the Body
I also tried using another Interceptor XwsSecurityInterceptor but that does not work without the Wss4jSecurityInterceptor and gives me the same result when used with Wss4jSecurityInterceptor
<bean id="xwsSecurityInterceptor" class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler"/>
</list>
</property>
</bean>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="keystorepassword"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="file:${key.store.location}"/>
<property name="password" value="keystorepassword"/>
</bean>
After referring a number of IBM support posts, I have finally come to the solution and have received a QA signoff too on the implementation. I had to replace all my spring mvc config for creating SSL context with configurations on the WAS server itself.You can configure all this using either scripts or using the WAS console manually.
Steps:
Add an entry to ur applications web.xml.Please refer this post https://www.ibm.com/support/knowledgecenter/en/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/twbs_jaxwsclientdd.html
The link below will give you a step by step guide as to how to configure the request payload to have a signed timestamp on WAS server.
http://pglezen.github.io/was-config/html/signts.html#sec.signts.keystores.consumer
It basically focusses on :
Policy Set Creation:This will Specify the element or elements to be signed or encrypted in this message part.In our case its the timestamp in the header
Client Policy Set Bindings. If you are the provider you have to refer the provider Policy set bindings section.This involves creation of truststore and keystore required to configure your SSL context. Also has configurations for any proxy settings that you might require.
Finally take a JNDI reference of the service whereever you need to call a particular operation in the service.Refer https://www.ibm.com/support/knowledgecenter/SSAW57_9.0.5/com.ibm.websphere.nd.multiplatform.doc/ae/twbs_jaxwsclientdd.html
Once this done, Simply navigate to Services--> Service Clients and you should have your service refs visible there. You should now attach the Policy set and the bindings created above to the Service client references created in WAS due to point 1 above.You might have to write a script to attach the polict set and binding since after deployment you dont want to do this step manually
Please note: this solution came into picture sicne the version of WAS 8.5.5.16,spring -3.0.5.RELEASE and spring-ws-2.0.0.RELEASE did not support signing of timestamp in the request payload of the SOAP service request. Hope this helps someone !!
I am working on a spring integration component where I posting data to external third-party URL. and Its is working fine with the below code.
<!-- language: lang-xml -->
<int:chain id="channe.id"
input-channel="request.in"
output-channel="reply.channel">
<int-ws:header-enricher>
<int-ws:soap-action
value="${service.soapaction}" />
</int-ws:header-enricher>
<int-ws:outbound-gateway
id="invoker.ws.outbound.gateway"
ignore-empty-responses="true" message-sender="message.sender"
interceptors="${SecurityInterceptor}"
message-factory="${mmessageFactory}"
uri="${protocol}://${host}:${port}/{endpoint}">
<int-ws:uri-variable name="endpoint"
expression="headers.endpoint" />
<int-ws:request-handler-advice-chain>
<ref bean="commonRetryAdviceBean" />
</int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>
</int:chain>
Below is payload received by third part api.
<MessageLogTraceRecord>
<HttpRequest xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<Method>POST</Method>
<QueryString></QueryString>
<WebHeaders>
<Content-Length>9381</Content-Length>
<Content-Type>text/xml; charset=UTF-8</Content-Type>
<Accept>text/xml</Accept>
<Accept-Encoding>gzip</Accept-Encoding>
<Host>myhost</Host>
<User-Agent>Jakarta Commons-HttpClient/3.1</User-Agent>
<SOAPAction>"http://www.mysoap.com/action/update"</SOAPAction>
</WebHeaders>
Now, I have to add an additional security feature and send the API key in the HTTP header or soap header. SO I modified my code as below. Now I can see API key is sent as soap header but some how SOAPAction is going empty, not sure why.
below is the modified code to send api ket as part of soap header.
<int:chain id="channe.id"
input-channel="request.in"
output-channel="reply.channel">
<int-ws:header-enricher>
<int-ws:soap-action
value="${service.soapaction}" />
</int-ws:header-enricher>
<int-ws:outbound-gateway
id="invoker.ws.outbound.gateway"
ignore-empty-responses="true" message-sender="message.sender"
interceptors="${SecurityInterceptor}"
message-factory="${mmessageFactory}"
mapped-request-headers="soapHeaderMapper"
uri="${protocol}://${host}:${port}/{endpoint}">
<int-ws:uri-variable name="endpoint"
expression="headers.endpoint" />
<int-ws:request-handler-advice-chain>
<ref bean="commonRetryAdviceBean" />
</int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>
</int:chain>
<bean id="soapHeaderMapper"
class="org.springframework.integration.ws.DefaultSoapHeaderMapper">
<property name="requestHeaderNames">
<list>
<value>api-key</value>
</list>
</property>
</bean>
After adding mapped-request-headers now I am getting
org.springframework.messaging.MessagingException: The message could not be processed because the action '' is invalid or unrecognized.; nested exception is org.springframework.ws.soap.client.SoapFaultClientException: The message could not be processed because the action '' is invalid or unrecognized., failedMessage=GenericMessage
when I checked the payload received by thirdparty api I can see SOAP action is empty I am not sure why.
Please help me.
Thanks.
<QueryString></QueryString>
<WebHeaders>
<Content-Length>9463</Content-Length>
<Content-Type>text/xml; charset=UTF-8</Content-Type>
<Accept>text/xml</Accept>
<Accept-Encoding>gzip</Accept-Encoding>
<Host>myhost</Host>
<User-Agent>Jakarta Commons-HttpClient/3.1</User-Agent>
<SOAPAction>""</SOAPAction>
</WebHeaders>
</HttpRequest>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header api-key="dummy-123455555uuuuuuuuuuuqwert">
The mapped-request-headers="soapHeaderMapper" is wrong configuration. It is exactly about names, but in your case you try to make a reference to the DefaultSoapHeaderMapper bean definition.
Consider to use:
<xsd:attribute name="header-mapper">
<xsd:annotation>
<xsd:documentation>
Reference to a SoapHeaderMapper implementation
that this gateway will use to map between Spring Integration
MessageHeaders and the SoapHeader.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.ws.SoapHeaderMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
instead.
Also there is the with your header names to map: when you configure some custom header names, all the standard headers are missed. So, alongside with the api-key, you need to consider include the mentioned ws_soapAction name as well.
I implemented the Spring SAML sample application using ssocircle and it worked fine. Now I have been trying to implement it for the client's ADFS. Following is the configuration I think that is required, please correct me if I am wrong:
Change the first parameter below, to the federationMetadata.xml url provided by client
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<constructor-arg>
<value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value>
</constructor-arg>
<constructor-arg>
<value type="int">5000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
Replace the entity id of SP metadata below:
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityId" value="replaceWithUniqueIdentifier"/>
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="signMetadata" value="false"/>
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
</bean>
I haven't been able to figure out the following:
All I have received is a url to adfs/../federationMetadata.xml, who is supposed to create the SP metadata?
Am I supposed to create SP metadata and provide to the client, to add it in adfs? Because, that's what I did using sample application. I added the generated metadata to ssocircle
Is my understanding, that point 1 would be adfs url, and point 2 will be SP entity id, correct?
I would be grateful if you could clarify the above to me, also if possible, point me to straightforward tutorial that helps in integrating SAML with Spring security enabled application as I haven't been able to find the same.
Many thanks
To make SAML between SP and IdP (ADFS) work, you have to mutually exchange metadata.
The ADFS metadata are available on the URL https://adfs-host/FederationMetadata/2007-06/FederationMetadata.xml and you can register them in your SP either with HTTPMetadataProvider, or download them and read them from classpath, or file system with ResourceBackedMetadataProvider.
For SP metadata, you have to configure MetadataGenerator (as you have it in your question) and then expose it via FilterChainProxy. Here is a Java configuration (it's equivalent for XML):
#Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
chains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/saml/metadata/**"), metadataDisplayFilter()));
return new FilterChainProxy(chains);
}
Than, you can access SP metadata on the URL https://sp-host/saml/metadata and register them on ADFS as a Relying Party Trust. Again, you can do this either via URL, or import data from the (downloaded) file.
Basically, you should be fine if you follow Spring Security SAML Reference Documentation which uses XML configuration. In case, you'll need to switch to Java configuration, you can find handy either referenced vdenotaris/spring-boot-security-saml-sample, or my working prototype sw-samuraj/blog-spring-security.
So I am looking for a programmatic configuration way of using form based authentication and basic authentication on the same resource. What I mean is if I fill out the login form and authenticate I should be able to use the site. At the same time if I authentication using basic auth I should be able to access the same resources. Is it possible? I was even thinking maybe using a DelegatingAuthenticationEntryPoint where if there is a certain header specified it will use basic otherwise it will use form based login. Anyway any help would be appreciated?
I think you should look at the request-matcher-ref attribute of the http element in the Spring Security Current Reference Documentation. It enables you to specify custom conditions under which a particular FilterChain will be used or not. For example, you can determine which authentication mechanism to use, according to the host header of incoming request.
<bean id="customMatcher1" class="org.springframework.security.web.util.matcher.ELRequestMatcher">
<constructor-arg value="hasHeader('host', 'stackoverflow.com')"/>
</bean>
<bean id="customMatcher2" class="org.springframework.security.web.util.matcher.ELRequestMatcher">
<constructor-arg value="hasHeader('host', 'careers.stackoverflow.com')"/>
</bean>
<sec:http request-matcher-ref="customMatcher1">
<sec:http-basic />
</sec:http>
<sec:http request-matcher-ref="customMatcher2">
<sec:form-login />
</sec:http>
Look and choose one of existing implementations or look and write own implementation of the RequestMatcher interface.
In applicationContext.xml, it is valid to defined mmultiple security:custom-authentication-provider ?
for example
<bean id="dummyAuthenticationProvider"
class="com.user.sample.gwtspring.server.security.JDBCDummyAuthenticationProvider">
<security:custom-authentication-provider />
</bean>
<bean id="dummyAuthenticationProvider2"
class="com.user.sample.gwtspring.server.security.OpenIdDummyAuthenticationProvider2">
<security:custom-authentication-provider />
</bean>
will both be registered inside authenticationManager? I am thinking of using dummyAuthenticationProvider2 as openId. what other metaconfig i need to put inside applicationContext.xml?
Yes, both authentication providers will be registered with authentication manager. No other config is required.
It should register both providers with the authentication manager. When automatically configured the auth manager is set up with a list of providers to perform authentication (anonymous provider, remember me provider, etc..)
http://static.springsource.org/spring-security/site/docs/2.0.x/reference/authentication-common-auth-services.html