What is proper way to add encryption/decryption in spring-ws (wss4j)? - java

I have deployed 2 web apps, one representing web service and other representing ws client. When using SIGNING and TIMESTAMP-ing, everything works fine, client stamps message(but i think that he doesn't override the default 300s ttl), signs the message with his x509 cert, and sends it to ws. He, in the other hand, recives message and is able to valiadate timestamp and certificate/signature against clients trusted cert in his keystore.
Problem arises when i add Encrypt operation to my configuration. Client seems to be able to encrypt the message, but ws seems not to be intrested in decrypting the message. He just sees that there is no endpoint mapping for
[SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData]
and throws
WebServiceTransportException: Not Found [404] exception.
SO can someone explain what I need to do in order to achieve timestamping,signing with x509 and encryption, again with x509?
part of server app-context:
<bean id="wss4jSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<!-- valiadation -->
<property name="validationActions" value="Timestamp Signature Encrypt"/>
<property name="enableSignatureConfirmation" value="true"/>
<property name="validationSignatureCrypto">
<ref bean="keystore"/>
</property>
<property name="validationDecryptionCrypto">
<ref bean="keystore"/>
</property>
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j.callback.KeyStoreCallbackHandler">
<property name="privateKeyPassword" value="password"/>
</bean>
</property>
<!-- timestamp options -->
<property name="timestampStrict" value="true"/>
<property name="timeToLive" value="30"/>
<property name="timestampPrecisionInMilliseconds" value="true"/>
<!-- signing and encryption -->
<property name="securementActions" value="Timestamp Signature Encrypt"/>
<property name="securementUsername" value="wsserver"/>
<property name="securementPassword" value="password"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference"/>
<property name="securementSignatureCrypto">
<ref bean="keystore"/>
</property>
<property name="securementEncryptionUser" value="wsclient"/>
<property name="securementEncryptionCrypto">
<ref bean="keystore"/>
</property>
</bean>
<!-- keystore -->
<bean id="keystore" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="password"/>
<property name="keyStoreLocation" value="WEB-INF/MyTruststore.jks"/>
</bean>
<!-- interceptors -->
<sws:interceptors>
<ref bean="wss4jSecurityInterceptor"/>
<bean id="validatingInterceptor"
class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schema" value="/WEB-INF/person.xsd"/>
<property name="validateRequest" value="true"/>
<property name="validateResponse" value="true"/>
</bean>
<bean id ="loggingInterceptor" class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor">
</bean>
</sws:interceptors>
Client basicly uses the same config, except that he uses server public key for encryption, and his private key for decryption.
Keystores are ok, i guess, because signing works ok...Everything juust falls apart when i add Encrypt action, part of server log says:
DEBUG [org.springframework.ws.server.MessageTracing.recei ved] - Received request [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData]
DEBUG [org.springframework.ws.server.endpoint.mapping.Pay loadRootAnnotationMethodEndpointMapping] - Looking up endpoint for [{http://www.w3.org/2001/04/xmlenc#}EncryptedData]
DEBUG [org.springframework.ws.soap.server.SoapMessageDisp atcher] - Endpoint mapping [org.springframework.ws.server.endpoint.mapping.Pay loadRootAnnotationMethodEndpointMapping#30a14083] has no mapping for request
...
No endpoint mapping found for [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData]
org.springframework.ws.client.WebServiceTransportE xception: Not Found [404]
...
I think I must somehow instruct ws to decrypt SOAP body before it starts to look for an endpoint for message,but I don't know how. Suggestions?

Since your comments were helpful but kind of incomplete, I took a shoot of answering with a bit more of a detail.
In the spring tutorial, the endpoint method is annotated with #PayloadRoot: #PayloadRoot(localPart = "orderInput", namespace = "http://samples")
This works fine when the soap message is not encrypted. PayloadRootAnnotationMethodEndpointMapping is able to map to soap message to the corresponding method.
When the soap message is encrypted, the PayloadRootAnnotationMethodEndpointMapping is unable to map the soap message because The security interceptor did not have yet the time to decipher it. The solution is to replace #PayloadRoot with #SoapAction.
When a soap message is received, spring-ws calls first the PayloadRootAnnotationMethodEndpointMapping then SoapActionAnnotationMethodEndpointMapping. You can use both in order to be full compatible with non-spring client (axis for example or .net):
#PayloadRoot(localPart = "orderInput", namespace = "http://samples")
#SoapAction("http://samples/order")
Last but not least: If you are using a spring client with secured soap message, spring does not send soap action automatically. Your server will not be able to map the soap message with the appropriate action. In order to solve this problem, you should use a WebServiceMessageCallback:
ClientMessageCallBack callBack = new ClientMessageCallBack(
"http://samples/order");
Object output = wsTemplate.marshalSendAndReceive(inputObject, callBack);
where ClientMessageCallBack class is
public final class ClientMessageCallBack
implements WebServiceMessageCallback {
/**the soapAction to be appended to the soap message.*/
private String soapAction;
/**constructor.
* #param action the soapAction to be set.*/
public ClientMessageCallBack(final String action) {
this.soapAction = action;
}
#Override
public void doWithMessage(final WebServiceMessage message)
throws IOException, TransformerException {
if (message instanceof SoapMessage) {
SoapMessage soapMessage = (SoapMessage) message;
soapMessage.setSoapAction(soapAction);
}
}
}

this happens because you did not defined securementEncryptionParts property. It causes to encrypt whole body and made to this Error

Is there anyway to decrypt request before it triggers #Endpoint? issue is that client can not add soapaction.

Related

Multiple AssertionConsumerService in SP with differend 2nd level domains

I am implementing a multitenant application with the spring security saml extention.
I have a Service Provider (SP) for each tenant.
All SPs runs on the same server exposed with SP-specific 2nd level domain:
sp1.myapp.com/myapi/1/
sp1.myapp.com/myapi/2/
In each SP metadata file I have configured the tenant-specific AssertionConsumerService.
When I test the SSO Login, I get a KO on SP side when it gets the response of the Identity Provider (IDP).
On Log side i see only:
ERROR [BaseSAMLMessageDecoder] SAML message intended destination endpoint 'https://sp1.myapp.com/myapi/saml/SSO' did not match the recipient endpoint 'https://default.myapp.com/myapi/saml/SSO'
Where the 'https://default.myapp.com/myapi/saml/SSO' is the URL set as serverName of the load balancer context provider:
<bean id="lbContextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB" init-method="afterPropertiesSet">
<property name="metadata" ref="metadata" />
<property name="keyManager" ref="keyManager" />
<property name="scheme" value="https" />
<property name="serverName" value="default.myapp.com" />
<property name="contextPath" value="/myapi" />
<property name="serverPort" value="443" />
<property name="includeServerPortInRequestURL" value="true" />
</bean>
Question
In the docs.spring.io/spring-security-saml I see that
Service provider can now define multiple assertion consumer endpoints with same binding
How can I configure it?
Does it conflict with load balancer context provider?
Can I provide multiple AssertionConsumerService with different 2nd level domains without reproduction this conflict?
I already tested:
This question seems to be fixed with the LB, but anyone knows if I can provide multiple serverName to load balancer context provider (maybe with a dynamic pick)?
Disable the checking of the InResponseToField as suggested at ch.13 docs.spring.io/spring-security-saml and for this and this question.
Configure the defaultTargetUrl of the successRedirectHandler (where I am using a custom superclass of org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler) as suggested for this question. In addition this solution is not multitenant.
<bean id="successRedirectHandler" class="org.MySamlAuthenticationSuccessHandler"
init-method="afterPropertiesSet">
<property name="contextPath" value="/myapi" />
<property name="defaultTargetUrl" value="https://default.myapp.com/myapi/saml/SSO"/>
<property name="requireProxyWrapping" value="false"/>
</bean>
Customize SAMLContextProviderLB by extending SamlContextProviderLB.
In custom class, add constructor and initialize with default values.
Override getLocalAndPeerEntity/getLocalEntity/populateLocalEntityId. In each of this method set lbDomain based on domain in requestURL.
above approach worked for me.

Generate web service client secure policy from .wsdl for java

I have been researching how to implement a web service client policies from a .wsdl file.
The policies of the web services implicates a signature and encryption using a .jks file with the necessary keys (asymmetric privateKey for signing, and a symmetric privateKey for encryption). The policy is: username:oracle/wss10_username_token_with_message_protection_service_policy.
I am able to make the .xsd files (request, response and service objects) using the wsimport tool for java (or with cxf or axis2). What i can't resolve is how to make the correct policy.
Is there any way to automatically generate the policies from the .wsdl or do i have to make them by myself
The username:oracle/wss10_username_token_with_message_protection_service_policy is solved with spring ws this way:
<!-- == Ougoing interceptor == -->
<bean id="loginOutgoingWss4jSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp Signature Encrypt" />
<!-- == Set Outgoing Signature properties == -->
<property name="securementUsername" value="alias"/>
<property name="securementPassword" value="aliasPass"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference"/>
<property name="securementSignatureCrypto" ref="cryptoFactoryBean" />
<property name="securementSignatureParts" value="{Element}{}Body;{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;" />
<!-- == Set Outgoing Encryption properties == -->
<property name="securementEncryptionUser" value="alias"/>
<property name="securementEncryptionCrypto" ref="cryptoFactoryBean" />
<property name="securementEncryptionKeyIdentifier" value="DirectReference"/>
<property name="securementEncryptionParts" value="{Content}{}Body;" />
</bean>
<!-- == Incoming interceptor == -->
<bean id="loginIncomingWss4jSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor">
<property name="validationActions" value="Timestamp Signature Encrypt" />
<!-- == Set Validations Response, This validate signature and decrypts response == -->
<property name="validateResponse" value="true" />
<!-- The lower operation validation. Less time consume-->
<property name="validateRequest" value="false" />
<property name="enableSignatureConfirmation" value="false"/>
<!-- == Set Incoming Signature/Decryption keystore == -->
<property name="validationDecryptionCrypto" ref="cryptoFactoryBean" />
<property name="validationSignatureCrypto" ref="cryptoFactoryBean" />
<!-- Sets the {#link org.apache.ws.security.WSPasswordCallback} handler to use when validating messages -->
<property name="validationCallbackHandler">
<bean class="org.springframework.ws.soap.security.wss4j2.callback.KeyStoreCallbackHandler">
<property name="privateKeyPassword" value="aliasPass"/>
</bean>
</property>
</bean>
If you are using policies in WS-SecurityPolicy (1.1 or later) in your wsdl, no need to generate policies nor make them on client side with Apache CXF. With WS-SecurityPolicy, CXF's security runtime is policy driven.
1) You follow CXF's WSDL-first approach to generate the client code, using either wsdl2java command-line tool or Maven cxf-codegen-plugin (wsdl2java goal). This is described in CXF doc's How to develop a client.
2) Following CXF's doc on WS-SecurityPolicy usage, you configure the client security properties for the wsdl port you want to use, either using JAX-WS API (on the client's RequestContext) or Spring XML configuration. For the list of possible properties, there are the generic XML security ones and WS-Security-specific ones. Example with Spring XML for UsernameToken policy (from Glen Mazza's blog samples ):
<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">
<jaxws:client name="{http://www.example.org/contract/DoubleIt}DoubleItPort" createdFromAPI="true">
<!-- Use this for the UsernameToken Symmetric Binding w/X.509 for secret key derivation -->
<jaxws:properties>
<entry key="ws-security.username" value="alice"/>
<entry key="ws-security.callback-handler" value="client.ClientPasswordCallback"/>
<entry key="ws-security.encryption.properties" value="clientKeystore.properties"/>
<entry key="ws-security.encryption.username" value="myservicekey"/>
</jaxws:properties>
<!-- Use this for the UsernameToken Symmetric Binding w/UT password for secret key derivation -->
<!--jaxws:properties>
<entry key="ws-security.username" value="alice"/>
<entry key="ws-security.callback-handler" value="client.ClientPasswordCallback"/>
</jaxws:properties-->
</jaxws:client>
</beans>
Put this in /cxf.xml on the class path. Warning: the example is using a CallbackHandler subclass (client.ClientPasswordCallback in this example) to provide the password. So you'll need to provide your own implementation.
3) Back to CXF doc's How to develop a client - last part - in the application code, initialize the client using JAX-WS API with arguments: a) the location of the WSDL (URL) having the WS-SecurityPolicy policies (you already have that, as far as I understand); b) service and port's QNames to be used by the client, as defined in the WSDL:
final Service service = Service.create(wsdlLocation, SERVICE_QNAME);
final DoubleItPortType transportPort = service.getPort(PORT_QNAME, DoubleItPortType.class);
4) Make sure you have cxf-rt-ws-policy and cxf-rt-ws-security modules on the classpath at runtime to enable WS-SecurityPolicy support.

SOAP KeyInfo failed to recognise by IIS Server and client is java

I am getting following error while consuming .Net Web Service from java Spring
Web Service uses Certificate based authentication.
I tried java(Server) to java(Client) working locally using HTTPS and without https
Even .Net Side Server and client working.
org.springframework.ws.soap.client.SoapFaultClientException: Referenced security token could not be retrieved --->
WSE590: Failed to resolve the following Key Info
<KeyInfo Id="KI-A65A1CE3CB2EB7AE6114286591136763" xmlns="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509IssuerSerial>
<ds:X509IssuerName>CN=VXX,OU=NXX,O=iXXX,L=Cty,ST=MH,C=IN</ds:X509IssuerName>
<ds:X509SerialNumber>1711043036</ds:X509SerialNumber>
</ds:X509IssuerSerial>
</ds:X509Data>
</wsse:SecurityTokenReference>
</KeyInfo>.
I am using org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor
to intercept request and response.
I don't understand whats happening at .Net side as its developed by other team.
But .Net Team told request is not reached to applcation, IIS server itself not able to process this.
Please find .Net client calling code snippet.
// TODO: Replace with a valid path to your certificate
string certPath = #"C:\mypath\certClient1.cer";
Secured.TSRService objsecserv = new Secured.TSRService();
objsecserv.Url = #"https://xyz/service.asmx";
//objsecserv.Url = #"http://xyz:97/service.asmx";
objsecserv.ClientCertificates.Add(X509Certificate.CreateFromCertFile(certPath));
objsecserv.Timeout = Timeout.Infinite;
string strresult1 = objsecserv.GetCertDetails();
rtxtresult.Text = strresult1;
And Here is my java side code snippet
<bean id="countryWsClSecInterId" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp Signature" /> <!-- Encrypt -->
<!-- Key alias for signature -->
<property name="securementUsername" value="client" />
<property name="securementPassword" value="mypasswd" />
<property name="securementSignatureCrypto" ref="countryCrypto"/>
<property name="securementEncryptionCrypto" ref="countryCrypto"/>
<!-- Key alias for encryption -->
<property name="securementEncryptionUser" value="server"/>
<!-- Validation config -->
<property name="validationActions" value="Signature" />
<property name="validationSignatureCrypto" ref="countryCrypto"/>
</bean>
Please guide me , I searched a lot and made changes according but nothing worked.

Spring HTTP invoker waits one second before processing next request

I have set up an spring http invoker example as described here http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/remoting.html in section 20.4
If i do several service calls in a row (see my for-loop), between the single calls is one second though the server processes the method in less than 4ms.
Any ideas.
Stefan
Here the config and call:
<!-- server side -->
<bean name="configurationServiceExporter"
class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter">
<property name="service" ref="configurationService" />
<property name="serviceInterface"
value="remote.service.ConfigurationService" />
</bean>
<bean id="httpServer"
class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
<property name="contexts">
<util:map>
<entry key="/remoting/ConfigurationService" value-ref="configurationServiceExporter" />
</util:map>
</property>
<property name="port" value="${port.httpinvoker}" />
</bean>
<!-- client side -->
<bean id="configurationServiceProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl"
value="http://localhost:7777/remoting/ConfigurationService" />
<property name="serviceInterface"
value="remote.service.ConfigurationService" />
</bean>
/** here the service call*/
#Component
public class ServiceConsumer {
private ConfigurationService configurationService;
public void do(){
for (int i = 0; i < 10; i++)
this.configurationService.getConfigurationValue(SMTP_HOST);
}
I just encountered the very same problem:
Spring Remoting
Delays of "exactly" 1 second
Java 7
Unfortunately, I was unable to find a reason for this odd behavior, but there exists a workaround: use jetty instead of the SimpleHttpServerFactoryBean.
This boils down to changing the xml configuration a little, how exactly is described here.
The delays disappeared; firing requests even seems to have sped up compared to using SimpleHttpServerFactoryBean in Java 6.
Found the Problem. It was not connected to Spring HTTP Invoker. I updated to Java 7. When i ran my app with Java 6 it works as before the update (without waiting one second between the requests. If i knew anything more, i come back.

Problem configuring Spring's MailSender for our SMTP-server (but GMail works)

I have some problems sending mails through SMTP using Spring's MailSender interface and the concrete implementation JavaMailSenderImpl. I'm able to send mail through GMail, but not through our company SMTP-server (Postfix).
Correct configurations
To see that I have the correct configuration I used the excellent mail sender ssmtp. It's a simple utility (which is able to emulate Sendmail) and is used solely for sending mail through SMTP.
Below are the two commands I used to send mail. The first one is for GMail and the second one for our company SMTP-server. Both mails arrived as they should and thus the configuration files that follow are correct.
$ ssmtp -C gmail-smtp.conf john.doe#gmail.com < gmail-message.txt
$ ssmtp -C other-smtp.conf john.doe#thecompany.net < other-message.txt
The contents of the ssmtp configuration files and the message files are listed below. How the configuration file is structured can be seen at: http://linux.die.net/man/5/ssmtp.conf:
gmail-message.txt:
To: john.doe#gmail.com
From: john.doe#gmail.com
Subject: Sent using the SMTP-server of GMail
Some content.
gmail-smtp.conf:
mailhub=smtp.gmail.com:587
UseSTARTTLS=yes
AuthUser=john.doe#gmail.com
AuthPass=john_password
other-message.txt:
To: john.doe#thecompany.net
From: john.doe#thecompany.net
Subject: Sent using the SMTP-server of TheCompany
Some content.
other-smtp.conf:
# No username or password = no authentication
hostname=thecompany.net
mailhub=mail.thecompany.net:25
MailSender configuration which works against GMail
I'm sucessful in sending mail through GMail using this Spring MailSender configuration:
...
<bean id="mailSender" class ="org.springframework.mail.javamail.JavaMailSenderImpl" >
<property name="host" value="smtp.gmail.com" />
<property name="port" value="587" />
<property name="username" value="john.doe#gmail.com" />
<property name="password" value="john_password" />
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.starttls.enable">true</prop>
</props>
</property>
</bean>
...
The problem (sending through the company SMTP-server)
With this MailSender configuration:
...
<bean id="mailSender" class ="org.springframework.mail.javamail.JavaMailSenderImpl" >
<property name="host" value="mail.thecompany.net" />
<property name="port" value="25" />
</bean>
...
I get this exception:
org.springframework.mail.MailSendException; nested exceptions (1) are:
Failed message 1: javax.mail.SendFailedException: Invalid Addresses;
nested exception is:
com.sun.mail.smtp.SMTPAddressFailedException: 504 5.5.2 <rat>: Helo command rejected: need fully-qualified hostname
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:422)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:308)
at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:297)
... (The rest are methods I've created, which are irrelevant)
I also get 504 5.5.2 : Helo command rejected: need fully-qualified hostname if I remove hostname=thecompany.net from other-smtp.conf using ssmtp. I guess I have to provide the hostname somehow. My computers name is rat but it seems like it wants thecompany.net.
Any and all help appreciated!
To add to complete the previous answer by matt b, the correct option of the javamail is smtp.mail.localhost
<property name="javaMailProperties">
<props>
<prop key="mail.smtp.localhost">thecompany.net</prop>
</props>
</property>
Try to add the property "mail.smtp.localhost" with correct value (thecompany.net).
Can you pass in the extra properties (hostname) you need to set with your SMTP server in the javaMailProperties property?
<bean id="mailSender" class ="org.springframework.mail.javamail.JavaMailSenderImpl" >
<property name="host" value="mail.thecompany.net" />
<property name="port" value="25" />
<!-- addition: -->
<property name="javaMailProperties">
<props>
<prop key="hostname">thecompany.net</prop>
</props>
</property>
</bean>
Not quite sure what the correct key name is for hostname in the JavaMail API.
I think its an address resolution problem in your case. make sure the mail server is accessible using the given name from the machine you've deployed this code on. if not just add an entry to your the hosts file.
SMTPTransport mailFrom() sets the sender's address in the following order:
SMTPMessage.getEnvelopeFrom()
mail.smtp.from property
From: header in the message
System username using the InternetAddress.getLocalAddress() method
You just need to add property from in javaMailProperies to your email address

Categories

Resources