Https WebService message: Message did not contain a valid Security Element - java

I am using axis 2 webservice client.
The first https call to the webservice throws a exception with the message: "Message did not contain a valid Security Element".
I think that the problem could be the security mode: maybe it has to be message level security. In this case, how can I configure it in axis?.
The code:
System.setProperty("javax.net.ssl.keyStore", jksFile);
System.setProperty("javax.net.ssl.keyStorePassword", jksPassword);
MyServicePortProxy proxy = new MyServicePortProxy();
Stub stub = (Stub) proxy.getMyServicePort();
proxy.setEndpoint(endpoint);
stub.setUsername(username);
stub.setPassword(password);
// throws exception with the above message:
proxy.serviceMethod(...);

Take a look at http://ws.apache.org/wss4j/package.html we had to define a client-config.wsdd that told axis to include username/password (if it is ws-security you are using). I am a little bit weak on the different standards and what separates them.

It's possible that in your XML service definition you need to create a service policy -[http://schemas.xmlsoap.org/ws/2004/09/policy/]
Take a look at these articles:
http://www.javaranch.com/journal/200603/Journal200603.jsp#a2
http://www.javaranch.com/journal/200709/web-services-authentication-axis2.html

Watch your clock-skew. If the timestamp in the security element provided by the client is too far into the past or the future from the server's perspective, it may reject it with exactly this message.

Related

Problem consuming Webservice with certificate

I am having problems consuming a SOAP WebService with security that uses a certificate to encrypt the data.
I am using GeneXus 17 (but I did this same test with GeneXus X Evolution 2 and 3) generating Java.
I did all the necessary steps to generate the certificate and the keystore, then I added everything in the KB and in the object as specified there but it gives me the following error when executing it:
"C:\Program Files\Java\jdk1.8.0_241\bin\java.exe" com.rendiciongastos17.aobtnerrendicionespendientessoap
Exception in thread "main" java.lang.RuntimeException: DOCTYPE is disallowed when the feature "http://apache.org/xml/features/disallow-doctype-decl" set to true.(-5)
at com.rendiciongastos17.SdtClients.getrendicionespendientesintegracion(SdtClients.java:386)
at com.rendiciongastos17.aobtnerpendingrendicionessoap.privateExecute(aobtnerpendingrendicionessoap.java:61)
at com.renditionexpenses17.aobtnerpendingrenditionssoap.execute_int(aobtnerpendingrenditionssoap.java:46)
at com.renditionexpenses17.aobtnerrenditionspendingsoap.execute(aobtnerrenditionspendingsoap.java:38)
at com.surrenderpending17.aobtnerrenderpendingsoap.executeCmdLine(aobtnerrenderpendingsoap.java:22)
at com.renditionexpenses17.aobtnerrenditionspendingsoap.main(aobtnerrenditionspendingsoap.java:15)
Failed: Execution
Now if I import the WSDL into SOAPUI and run it, it does fine.
The only code I have is:
java System.setProperty("javax.net.ssl.trustStore", "d:\\caolix");
java System.setProperty("javax.net.ssl.trustStorePassword", "Riogas1710");
&location = GetLocation('Clients')
&location.Authentication = 1
&location.AuthenticationMethod = 0
&location.AuthenticationRealm = "UnRealm"
&location.AuthenticationUser = "riogas"
&location.AuthenticationPassword = "xcrtdymx"
&RendicionesPendientesDeIntegracion = &wsClients.GetRendicionesPendientesIntegracion()
The location of the keystoke is correct and I can't see what it can be.
This error could be related with the SAC numbers #48044, #49588 or #51075, take a look at them before do anything. If it's not, then the error comes because the endpoint is not returning an XML, or something broke in between. You're talking about "a certificate to encrypt the data", but there is no code where you "encrypt the data" itself, setting the property to a trustStore is not enought, you have to use the Security API to encrypt the data.
And last but not least, I do see that the web service provider uses realm-based authentication, check if the parameters are ok. It's very rare to see endpoints with realm-based authentication but, who knows...

Adding soap header authentication to wsdl2java generated code

I'm in the process of creating a Java web services client from a wsdl. I used Eclipses's Dynamic Web Project and new Web Services Client to generate the code with wsdl2java with Apache Axis 1.4. I need to add SOAP authentication to this code in order for it to work with the service. I couldn't find a place to do that in the generated code. After copious research I found this, which I've used as the backbone for my code so far.
Adding ws-security to wsdl2java generated classes
Before I was getting a "Error occurred while processing security for the message" or something along those lines. Now I am getting
"Exception: Did not understand "MustUnderstand" header(s):{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security Message: null"
I've tried many things to get past this exception. This is the code I've arrived at now.
javax.xml.namespace.QName headerName = new javax.xml.namespace.QName(
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security");
org.apache.axis.message.SOAPHeaderElement header = new org.apache.axis.message.SOAPHeaderElement(headerName);
header.setActor(null);
header.setMustUnderstand(true);
// Add the UsernameToken element to the WS-Security header
javax.xml.soap.SOAPElement utElem = header.addChildElement("UsernameToken");
utElem.setAttribute("Id", "uuid-3453f017-d595-4a5b-bc16-da53e5831cd1-1");
javax.xml.soap.SOAPElement userNameElem = utElem.addChildElement("Username");
userNameElem.setValue("username");
javax.xml.soap.SOAPElement passwordElem = utElem.addChildElement("Password");
passwordElem.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
passwordElem.setValue("password");
header.setProcessed(true);
// Finally, attach the header to the binding.
setHeader(header)
This code is located in my Binding_ServiceStub class (in its' createCall method).
We have created clients in both C# and VB with this wsdl, and there it's as easy as just changing the ClientCredentials variable which is an extension of the proxy class generated. I was hoping for something similar here.
Here's the security policy from the wsdl code as well.
<wsp:Policy><sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"><wsp:Policy><sp:WssUsernameToken10/></wsp:Policy></sp:UsernameToken></wsp:Policy>
Does anyone know what else I can do here? Why this exception is happening? I've tried many different combinations of prefixes and setProcesses and setMustUnderstand values all in vain (and based on my research of this exception).
And if anyone knows a way in which to add Soap header authentication to wsdl2java code I would take that too. Just need this to work and you would think something like this would be a little more straightforward or at least have more examples out there.
Update-
Confirmed that the same header passed using SOAPUI works fine. Must be something with the framework? I created a custom handler to process the SOAP Message but that didn't help. Is Axis 1.4 and JAX-RPC the problem? (I know they're outdated but still...)
Cool. I decided to just use Apache CXF as my framework and using this it's as easy as adding
javax.xml.ws.BindingProvider bp = (javax.xml.ws.BindingProvider) port;
bp.getRequestContext().put("ws-security.username", username);
bp.getRequestContext().put("ws-security.password", password);
Wow that's much better. Don't use Axis 1.4 lesson learned.

How do I specify url in JAX-WS call and avoid initial network connection?

I'm using JAX-WS standard stuff with wsimport http://localhost/Order.wsdl to generate client stub classes.
The live web service is on a different host, so I need to supply a url when I make the service call. My approach so far has been like this (classes below are generated from wsimport):
1. OrderService s = new OrderService (
new URL("https://live/WS/Order"),
new QName(...));
2. OrderServicePort port = s.getOrderServicePort();
3. configureHttpCertificatesStuff(port) // Set up ssl stuff with the port
4. port.placeOrder(args); // The actual ws call
First: Is this the correct way of specifying the url?
Second: It seems the constructor in line 1 actually makes a network call to the new URL! This results in an exception (due to https not being configured), so I never get to the next line.
Background: I am implementing two-way ssl auth as outlined in this question. This means I need to configure ssl stuff in the port before the service call. I can't have the constructor make any connection before I've configured the ssl layer correctly for obvious reasons...
Update:
Apparenty the url is to the WSDL, not the endpoint when using jax-ws standard. This tripped me up. Loading the WSDL directly from file solved that problem.
Setting the endpoint url is done like this:
BindingProvider b = (BindingProvider) port;
b.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointUrl);
One solution would be to have your build process arrange for the WSDL file processed by wsimport to become a class path resource for your app. There are any number of ways to do this, but lets assume you take a JAR-per-service approach. So, you'd run Order.wsdl through wsimport and take the resulting classes, like OrderService and OrderServicePort, and stuff them into order-service.jar. The other thing you could do would be to stuff a copy of Order.wsdl into that same JAR at META-INF/wsdl/Order.wsdl. Assuming that JAR file is then part of the class path for your app, you can get the WSDL's URL by doing:
URL wsdlLocation = Thread.currentThread().getContextClassLoader().getResource("META-INF/wsdl/Order.wsdl");

How to digitally sign SOAP request using Eclipse generated proxy (axis 1.4) via WSS4J?

I have been provided a WSDL for a webservice. I am now required to digitally sign that request. The previous developer utilized the Eclipse feature to generate proxy classes. Add the WSDL to the project, then right click on it, click "Web Service", then "Generate Client".
This worked fine until we were required to digitally sign the request. I did some digging and it looks like Axis 1.4 doesn't allow you to sign requests. You can use WSS4J to do that. I mavened in WSS4j 1.5 into my project.
I'm at a loss on how to digitally sign the request. Here is my existing code that uses the proxy classes:
XiSecureWSServiceLocator service = new XiSecureWSServiceLocator();
service.setXiSecureWSServicePortEndpointAddress(paymetricPortAddress);
XiSecureWSPortType proxy = service.getXiSecureWSServicePort();
((Stub) proxy).setTimeout(paymetricTimeOutinMillisec);
SDecrypt_InputType sdi = new SDecrypt_InputType();
sdi.setStrToken(ccNumber);
sdi.setStrUserID(user);
SDecrypt_OutputType sdo = null;
sdo = proxy.pm_SingleDecrypt(sdi);
What I want to do is something similar to this article. Here is a function they used:
public Message signSOAPEnvelope(SOAPEnvelope
unsignedEnvelope) throws Exception
{
WSSignEnvelope signer = new WSSignEnvelope();
String alias = "16c73ab6-b892-458f-abf5-2f875f74882e";
String password = "security";
signer.setUserInfo(alias, password);
Document doc = unsignedEnvelope.getAsDocument();
Document signedDoc = signer.build(doc, crypto);
// Convert the signed document into a SOAP message.
Message signedSOAPMsg =
(org.apache.axis.Message)AxisUtil.toSOAPMessage(signedDoc);
return signedSOAPMsg;
}
How can i get the Soap Envelope to be signed when all of the code to create it is hidden in the generated proxy classes?
This JavaRanch Thread explains implementing Security and Encryption with WSS4J using Axis handlers.
It looks like you have to do a number of things:
Configure/write a wsdd
Call the web service with an EngineConfiguration pointing to your wsdd file
Write a password callback class
Write a crypto.properties file
ensure the crypto.properties file and the key store containing the certificate are on the class path of the app.
Props to John Farrel on the JavaRanch forum for figuring all this out.
All in all kind of a pain in the butt. If there's a way to obtain the underlying SOAP Message itself from the Axis proxy class, that might be a quicker way to do it, but I don't have a lot of experience with Axis 1.
Hello from /r/java, by the way!
Found this:
Can anyone recommend to me or point me somewhere that describes
a simple, straightforward way to sign a SOAP request with a Digital
Signature within the Axis framework?
"Have a look at the WSS4J project. They provide Axis handlers for signing and encrypting as it is described in WS Security."
http://mail-archives.apache.org/mod_mbox/axis-java-user/200403.mbox/%3CCGEOIPKACAGJDDPKCDIHEEKACCAA.abhinavm#contata.co.in%3E -
Does that help?

Write proxy/wrapper class for own service in jersey

I want to access a full rest service with basic http auth running.
However there is no way to for the javascript browser client to suppress the authenticate box when a wrong credential is provided.
I thought about different methods to solve this problem
someone suggested to remove the WWW-Authenticate Header with a filter (i dont think this is a clean approach)
i could rewrite my app to not use Basic Http Auth at all (i think this is too much trouble)
i could write a proxy that talks to my regular service
I like the last approach the best.
I keep my regular Rest Interface, but also have the option to use this interface with clients that are not that flexible.
Furthermore I can later proxy Http Requests unsupported by some browsers.
The idea is to have a /api/proxy/{request} path that proxies to /api/{request} and returns a Facebook-Graph-like JSON query { data: {data}, error: {error}}
This is the stub of the Proxy class
#Path("proxy")
public class ProxyResource {
#GET()
#Path("{url: [a-zA-Z/]*}")
public String get(#Context Request request, #PathParam("url") String url) {
// remove proxy/ from path
// resend request
// verify result
}
}
I can access the Request (which seems to be a ContainerRequest). How can I modify the request without building it from scratch to resend it.
Edit: when somebody knows a better approach i am delighted to hear about it.
As I started to digg deeper into this, i found out that not the 401 was the problem. The www-authenticate header sent back from the server caused the browser to open the login box.
If somebody is interested I've written a little nodejs proxy to remove a www-authenticate from all server requests.
https://gist.github.com/ebb9a5052575b0a3f41f
As this is not the answer to my original question I will leave it open.

Categories