I have a java application that interacts with a SOAP service. I used the WSDL to generate a java client via CXF, but I need to authenticate my calls using ws-security. I am looking for a code-only way to do this, and I don't have any xml configurations. This is what I have tried:
Map ctx = ((BindingProvider)port).getRequestContext();
ctx.put("ws-security.username", "joe");
ctx.put("ws-security.password", "joespassword");
port.makeSoapCall();
But I get a parse error for invalid WS-Security header. What is the right way to do this?
In SOAP UI, I can do this easily by right-clicking the soap header, clicking "Add WSS UsernameToken", and selecting "Password Text"
You are using WS-SecurityPolicy as per the code you shared. How about using WS-Security only and sending across the usernametoken using WSS4JOutInterceptor?
Check the section "Adding the interceptors via the API" in apache cfx ws-security guide here : http://cxf.apache.org/docs/ws-security.html
This is what needs to be done as per the above apache cxf documenation above. You might only need the out interceptor path.
On the client side, you can obtain a reference to the CXF endpoint using the ClientProxy helper:
import org.apache.cxf.frontend.ClientProxy;
...
GreeterService gs = new GreeterService();
Greeter greeter = gs.getGreeterPort();
...
org.apache.cxf.endpoint.Client client = ClientProxy.getClient(greeter);
org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();
Now you're ready to add the interceptors:
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
...
Map<String,Object> inProps = new HashMap<String,Object>();
... // how to configure the properties is outlined below;
WSS4JInInterceptor wssIn = new WSS4JInInterceptor(inProps);
cxfEndpoint.getInInterceptors().add(wssIn);
Map<String,Object> outProps = new HashMap<String,Object>();
outProps.put("action", "UsernameToken Timestamp");
outProps.put("passwordType", "PasswordDigest"); //remove this line if want to use plain text password
outProps.put("user", "abcd");
outProps.put("passwordCallbackClass", "demo.wssec.client.UTPasswordCallback");
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
cxfEndpoint.getOutInterceptors().add(wssOut);
You will need to write password callback class (UTPasswordCallback) in the example above.
Apache cxf has a complete sample for UserName token here: http://svn.apache.org/repos/asf/cxf/trunk/distribution/src/main/release/samples/ws_security/ut/
From the above link browse to client folder (src/main/java/demo/wssec/client) for user name token and UTPasswordCallback code.
EDIT: If your wsdl expects password as plain text then just remove this line from the code:
outProps.put("passwordType", "PasswordDigest");
You could take a look at the "ws-security/ut" demo that ships with CXF, this shows how to add a UsernameToken programmatically. Here is the client code:
https://github.com/apache/cxf/blob/master/distribution/src/main/release/samples/ws_security/ut/src/main/java/demo/wssec/client/Client.java
Colm.
Related
I am using java spring webservice to send soap requests to a server. However I get an error from the server saying
The security context token is expired or is not valid
So then I added authentication to the SOAP headers, resulting in request xml something like this:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<UsernameToken>
<Username>XXXXXXXX</Username>
<Password>XXXXXXXX</Password>
</UsernameToken>
</Security>
</env:Header>
<env:Body>
The body is in the correct format here
</env:Body>
</env:Envelope>
But I still get that error. From what I understand, the client sends authentication credentials to the server, the server sends back requestToken which is used to keep connection alive between client and server and then client using the token received from the server can make any other API calls such as login, buy, sell (or whatever mentioned in API)
Am I right in this assumption? If yes, how can this be implemented using Java Spring WebServices. Do I need to generate fields on client side like BinarySecret and package that under RequestSecurityContext?
For adding the SOAP headers, I wrote a class which implements WebServiceMessageCallback and overrides doWithMessage method and writes headers in that for username and password (i.e security)
Any help would be appreciated! Thank You!
So after 2 days of finding out, I was able to figure out the solution!
1) Generate the classes from wsdl using CXF wsdl2java
2) Make sure in the pom file, you point to the correct wsdlLocation
3) Get the stub from the service class generated and inject username and password provided and then it should work. Something like this:
final YourService service = new YourService();
final YourStub stub = service.getService();
final Map ctx = ((BindingProvider)stub).getRequestContext();
ctx.put("ws-security.username", userName);
ctx.put("ws-security.password", password);
stub.callYourMethod();
PS: Please make sure you have the right libraries, I just used cxf-bundle and nothing else from cxf and it worked! Earlier it was not working as I had individually included libraries from cxf.
I have a dot net application that call a java web service. I am trying to implement authentication by passing credentials to the java service. Here is the dot net code setting the credentials. How can I get these credentials in my java application? They aren't set in the headers...
System.Net.NetworkCredential serviceCredentials = new NetworkCredential("user", "pass");
serviceInstance.Credentials = serviceCredentials;
serviceInstance is an instance of SoapHttpClientProtocol.
I've tried injecting the WebServiceContext like so
#Resource
WebServiceContext wsctx;
and pulling the crentials from the headers but they aren't there.
You are not passing the credentials to your service the correct way. In order to get the Authorize http request header do the following:
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("user", "pass");
Uri uri = new Uri(serviceInstance.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
serviceInstance.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
serviceInstance.PreAuthenticate = true;
Note: Be sure to set PreAuthenticate to true or else authentication will not be sent.
see this article for more information.
I had to dig-up some old code for this one :)
Update:
After inspecting the request/response headers using fiddler as suggested in the comments below a WWW-Authenticate header was missing at the Java Web Service side.
A more elegant way of implementing "JAX-WS Basic authentication" can be found in this article here using a SoapHeaderInterceptor (Apache CXF Interceptors)
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?
I have registered my web application for use with oath2 using the following instructions:
http://code.google.com/apis/accounts/docs/OAuth2.html
This means my client is created with a client ID, client secret and Redirect URI.
Once I have configured my web application as per
http://code.google.com/apis/accounts/docs/OAuth2WebServer.html
I recieve a code in a request parameter from google, which I can then use to request an access token, which comes in a JSON in a format along the lines of:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer"
}
Once this is done, I can use that access token to access a google api on behalf of the user:
GET https://www.googleapis.com/oauth2/v1/userinfo?access_token=1/fFBGRNJru1FQd44AzqT3Zg
This as documented is done by simply passing the access token as a request parameter.
However when I move onto using a Java API (In this case google contacts) I get the following in the documentation for HMAC-SHA1:
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setOAuthToken(ACCESS_TOKEN);
oauthParameters.setOAuthTokenSecret(TOKEN_SECRET);
DocsService client = new DocsService("yourCompany-YourAppName-v1");
client.setOAuthCredentials(oauthParameters, new OAuthHmacSha1Signer());
URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full");
DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
System.out.println(entry.getTitle().getPlainText());
}
Or the following for RSA-SHA1
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setOAuthToken(ACCESS_TOKEN);
PrivateKey privKey = getPrivateKey("/path/to/your/rsakey.pk8"); // See above for the defintion of getPrivateKey()
DocsService client = new DocsService("yourCompany-YourAppName-v1");
client.setOAuthCredentials(oauthParameters, new OAuthRsaSha1Signer(privKey));
URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full");
DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
System.out.println(entry.getTitle().getPlainText());
}
First off, it seems that if I was doing standard http rather than the java wrapper, all I would need to provide is an access token. Am I missing something or where have these additional parameters come from? Mainly TOKEN_SECRET, which there is no mention of in the docunentation. There is also no mention of having to provide CONSUMER_KEY and CONSUMER_SECRET. I am presuming they are alternative names for client id and client secret, but I do not understand why I am now having to provide them. Finally when setting up my application using the google API's console, there was no mention whatsoever of the two different encryption types, and which one I am going to be using, am I missing something here aswell?
The Java code examples you show are based on OAuth 1.0 (not OAuth 2.0) which has some crypto requirements which were simplified in OAuth 2.0. In some cases with the Google Contacts API you need OAuth 1.0. See: http://code.google.com/apis/contacts/docs/3.0/developers_guide.html#GettingStarted
I'm trying to consume a .NET 2.0 web service using Axis.
I generated the web services client using Eclipse WST Plugin and it seems ok so far.
Here the expected SOAP header:
<soap:Header>
<Authentication xmlns="http://mc1.com.br/">
<User>string</User>
<Password>string</Password>
</Authentication>
</soap:Header>
I didn't find any documentation on how to configure this header from an Axis client.
When I generated the client using Visual Studio C# Express 2008, it generates a class named Authentication with two String attributes (User and Password) and all the client methods receive an object of this class as first parameter, but it did not happen with Axis WS client.
How can I set this header in my client calls?
Maybe you can use org.apache.axis.client.Stub.setHeader method? Something like this:
MyServiceLocator wsLocator = new MyServiceLocator();
MyServiceSoap ws = wsLocator.getMyServiceSoap(new URL("http://localhost/MyService.asmx"));
//add SOAP header for authentication
SOAPHeaderElement authentication = new SOAPHeaderElement("http://mc1.com.br/","Authentication");
SOAPHeaderElement user = new SOAPHeaderElement("http://mc1.com.br/","User", "string");
SOAPHeaderElement password = new SOAPHeaderElement("http://mc1.com.br/","Password", "string");
authentication.addChild(user);
authentication.addChild(password);
((Stub)ws).setHeader(authentication);
//now you can use ws to invoke web services...
If you have an object representing the Authentication container with userid and password, you can do it like so:
import org.apache.axis.client.Stub;
//...
MyAuthObj authObj = new MyAuthObj("userid","password");
((Stub) yourServiceObject).setHeader("urn://your/name/space/here", "partName", authObj);
I have the same issue and solved by the below fragement:
ServiceSoapStub clientStub = (ServiceSoapStub)new ServiceLocator().getServiceSoap(url);
org.apache.axis.message.SOAPHeaderElement header = new org.apache.axis.message.SOAPHeaderElement("http://www.abc.com/SSsample/","AuthHeader");
SOAPElement node = header.addChildElement("Username");
node.addTextNode("aat");
SOAPElement node2 = header.addChildElement("Password");
node2.addTextNode("sd6890");
((ServiceSoapStub) clientStub).setHeader(header);
I used almost the same solution as mentioned before:
SOAPHeaderElement cigWsHeader = new SOAPHeaderElement("https://ws.creditinfo.com", "CigWsHeader");
cigWsHeader.addChildElement("UserName").setValue("username");
cigWsHeader.addChildElement("Password").setValue("password");
var port = new ServiceLocator().getServiceSoap(someUrl);
((Stub) port).setHeader(cigWsHeader);
And spent hours searching why "Security header not found". And the aswer is... redundant symbol "/" at the namespace: "https://ws.creditinfo.com/" . Seriously! Keep it in mind.