NTLM authentication in axis2 client return error 401 - axis2

I am using axis2 to create client code and access the wcf webservice with NTLM authentication. My client code is
Service1Stub stub = new Service1Stub();
Options options = stub._getServiceClient().getOptions();
HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
auth.setUsername("administrator");
auth.setPassword("passwrd");
auth.setHost("172.16.12.25");
auth.setDomain("MY-PC");
List<String> authSchemes = new ArrayList<String>();
authSchemes.add(HttpTransportProperties.Authenticator.NTLM);
auth.setAuthSchemes(authSchemes);
options.setProperty(HTTPConstants.AUTHENTICATE, auth);
options.setProperty(HTTPConstants.CHUNKED, Boolean.FALSE);
stub._getServiceClient().setOptions(options);
when I run my client code it returns the following error
org.apache.axis2.AxisFault: Transport error: 401 Error: Unauthorized
at org.apache.axis2.transport.http.HTTPSender.handleResponse(HTTPSender.java:310)
at org.apache.axis2.transport.http.HTTPSender.sendViaPost(HTTPSender.java:194)
at org.apache.axis2.transport.http.HTTPSender.send(HTTPSender.java:75)
at org.apache.axis2.transport.http.CommonsHTTPTransportSender.writeMessageWithCommons(CommonsHTTPTransportSender.java:404)
at org.apache.axis2.transport.http.CommonsHTTPTransportSender.invoke(CommonsHTTPTransportSender.java:231)
at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:443)
at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:406)
at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:229)
at org.apache.axis2.client.OperationClient.execute(OperationClient.java:165)
at org.tempuri.Service1Stub.welcomeData(Service1Stub.java:473)
at ws.client.Client.myservice(Client.java:159)
at ws.client.Client.main(Client.java:50)
my header log is
>> "POST /Service1/Service1.svc HTTP/1.1[\r][\n]"
>> "Content-Type: text/xml; charset=UTF-8[\r][\n]"
>> "SOAPAction: "http://tempuri.org/IService1/WelcomeData"[\r][\n]"
>> "User-Agent: Axis2[\r][\n]"
>> "Content-Length: 278[\r][\n]"
>> "Authorization: NTLM TlRMTVNTUAADAAAAGAAYAGMAAAAAAAAAewAAAAkACQBAAAAADQANAEkAAAANAA0AVgAAAAAAAAB7AAAABlIAAFZJTk9USC1QQ0FETUlOSVNUUkFUT1IxNzIuMTYuMTIuMjQ11kmkEIwyUVitHBvTPwhExpcylZ9vkdwd[\r][\n]"
>> "Host: 172.16.12.25[\r][\n]"
>> "[\r][\n]"
>> "<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><ns1:WelcomeData xmlns:ns1="http://tempuri.org/"><ns1:helloservice>Hello Servie</ns1:helloservice></ns1:WelcomeData></soapenv:Body></soapenv:Envelope>"
<< "HTTP/1.1 401 Unauthorized[\r][\n]"
<< "HTTP/1.1 401 Unauthorized[\r][\n]"
<< "Content-Type: text/html[\r][\n]"
<< "Server: Microsoft-IIS/7.5[\r][\n]"
<< "WWW-Authenticate: NTLM[\r][\n]"
<< "X-Powered-By: ASP.NET[\r][\n]"
<< "Date: Thu, 10 May 2012 19:30:20 GMT[\r][\n]"
<< "Content-Length: 1293[\r][\n]"
<< "[\r][\n]"
<< "<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">[\r][\n]"
<< "<html xmlns="http://www.w3.org/1999/xhtml">[\r][\n]"
<< "<head>[\r][\n]"
<< "<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>[\r][\n]"
<< "<title>401 - Unauthorized: Access is denied due to invalid credentials.</title>[\r][\n]"
<< "<style type="text/css">[\r][\n]"
<< "<!--[\r][\n]"
<< "body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}[\r][\n]"
<< "fieldset{padding:0 15px 10px 15px;} [\r][\n]"
<< "h1{font-size:2.4em;margin:0;color:#FFF;}[\r][\n]"
<< "h2{font-size:1.7em;margin:0;color:#CC0000;} [\r][\n]"
<< "h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;} [\r][\n]"
<< "#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;[\r][\n]"
<< "background-color:#555555;}[\r][\n]"
<< "#content{margin:0 0 0 2%;position:relative;}[\r][\n]"
<< ".content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}[\r][\n]"
<< "-->[\r][\n]"
<< "</style>[\r][\n]"
<< "</head>[\r][\n]"
<< "<body>[\r][\n]"
<< "<div id="header"><h1>Server Error</h1></div>[\r][\n]"
<< "<div id="content">[\r][\n]"
<< " <div cla"
<< "ss="content-container"><fieldset>[\r][\n]"
<< " <h2>401 - Unauthorized: Access is denied due to invalid credentials.</h2>[\r][\n]"
<< " <h3>You do not have permission to view this directory or page using the credentials that you supplied.</h3>[\r][\n]"
<< " </fieldset></div>[\r][\n]"
<< "</div>[\r][\n]"
<< "</body>[\r][\n]"
<< "</html>[\r][\n]
I don't know where I made mistake.

As far as I know, the standard release of Axis2 1.6 still uses HTTPClient 3.1 and thus NTLMv1, which most Windows servers have disabled by default. Changing this requires either patching Axis2 or changing the registry settings on the server.
Here's a link to the development thread with a patch as recent as 25-05-2012:
https://issues.apache.org/jira/browse/AXIS2-4318

Not sure if you have figured out a way to access WCF via NTLM authentication.. but this is what I did to fix this issue..
HttpClient doesnt support NTLM v2 hence I use JCIFS library to return NTLM v1,2,3 message type as described in this website
http://devsac.blogspot.com/2010/10/supoprt-for-ntlmv2-with-apache.html
I just used the JCIFS_NTLMScheme.java file from the above website to register the auth scheme and it worked !!!!
Sample client:
List authSchema = new ArrayList();
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, org.tempuri.JCIFS_NTLMScheme.class);
HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
auth.setUsername("");
auth.setPassword("");
auth.setDomain("");
auth.setHost("");
auth.setPort();
List authPrefs = new ArrayList(1);
authPrefs.add(AuthPolicy.NTLM);
auth.setAuthSchemes(authPrefs);
stub._getServiceClient().getOptions().setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);

As #WLPhoenix pointed out, Axis2 uses the old Apache Commons HTTP, which only supports an old, reverse-engineered NTLM implementation. In the new Apache HTTPComponents 4.2.3, support was added for the new, openly-documented NTLM standard, which works with newer versions of Windows Server and IIS (source).
Here is a way to backport the new Apache HTTPComponents 4 NTLMScheme for use in Axis2 using a custom Apache Commons HTTP AuthScheme.
public class BackportedNTLMScheme extends org.apache.http.impl.auth.NTLMScheme implements org.apache.commons.httpclient.auth.AuthScheme {
#Override
public String authenticate(final Credentials credentials, final HttpMethod method) throws AuthenticationException {
org.apache.commons.httpclient.NTCredentials oldCredentials;
try {
oldCredentials = (org.apache.commons.httpclient.NTCredentials) credentials;
} catch (final ClassCastException e) {
throw new InvalidCredentialsException(
"Credentials cannot be used for NTLM authentication: "
+ credentials.getClass().getName());
}
final org.apache.http.auth.Credentials adaptedCredentials = new NTCredentials(oldCredentials.getUserName(), oldCredentials.getPassword(), oldCredentials.getHost(), oldCredentials.getDomain());
try {
final Header header = super.authenticate(adaptedCredentials, null);
return header.getValue();
} catch (final org.apache.http.auth.AuthenticationException e) {
throw new AuthenticationException("AuthenticationException", e);
}
}
#Override
public void processChallenge(final String challenge) throws MalformedChallengeException {
final String s = AuthChallengeParser.extractScheme(challenge);
if (!s.equalsIgnoreCase(getSchemeName())) {
throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
}
int challengeIdx = challenge.indexOf(' ');
final CharArrayBuffer challengeBuffer;
if(challengeIdx != -1){
challengeBuffer = new CharArrayBuffer(challenge.length());
challengeBuffer.append(challenge);
} else {
challengeBuffer = new CharArrayBuffer(0);
challengeIdx = 0;
}
try {
parseChallenge(challengeBuffer, challengeIdx, challengeBuffer.length());
} catch (final org.apache.http.auth.MalformedChallengeException e) {
throw new MalformedChallengeException("MalformedChallengeException", e);
}
}
#Override
#Deprecated
public String getID() {
throw new RuntimeException("deprecated BackportedNTLMScheme.getID()");
}
#Override
#Deprecated
public String authenticate(final Credentials credentials, final String method, final String uri) throws AuthenticationException {
throw new RuntimeException("deprecated BackportedNTLMScheme.authenticate(Credentials, String, String)");
}
}
Usage
// given a stubbed AXIS SOAP client called MyAxisClient:
MyAxisClientStub myAxisClient = new MyAxisClientStub();
ServiceClient serviceClient = myAxisClient._getServiceClient();
// use new NTLM
AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, BackportedNTLMScheme.class);
Authenticator authenticator = new Authenticator();
authenticator.setAuthSchemes(Arrays.asList(AuthPolicy.NTLM));
authenticator.setDomain("my-auth-domain");
authenticator.setHost("my-auth-host");
authenticator.setUsername("my-username");
authenticator.setPassword("my-password");
serviceClient.getOptions().setProperty(HTTPConstants.AUTHENTICATE, authenticator);
//call MyAxisClient methods
I tested this on IIS 7.5 on Windows Server 2008 R2.

I could not get this to work until a coworker found this which fixed 401 Unauthorized.
import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
import org.apache.commons.httpclient.auth.CredentialsProvider;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.NTCredentials;
final NTCredentials credentials = new NTCredentials(username, password, host, domain);
final CredentialsProvider myCredentialsProvider = new CredentialsProvider() {
public Credentials getCredentials(final AuthScheme scheme, final String host, int port, boolean proxy) throws CredentialsNotAvailableException {
return credentials;
}
};
DefaultHttpParams.getDefaultParams().setParameter("http.authentication.credential-provider", myCredentialsProvider);

Related

keycloak unknown error while creating a new resource via rest api

I am trying to create a new resource in keycloak
on keycloak UI I looged in with admin account then I created a realm called demo and a user abc in it and created a client clientA after that I have created a new resource named resourceA with scopes and policy and permissions
while on keycloak ui everything is working fine resource was created the user abc has realm roles as admin, uma_authorization and client role of the client clienA is uma_protection
when i try to create a resource via rest api i am getting {"error":"unknown_error"}
Here are the steps I am following
obtained a pat as shown here
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=client_credentials&client_id=clientA&client_secret=given client secret' \
"http://localhost:8080/auth/realms/demo/protocol/openid-connect/token"
I got the access token (pat) then I followed next step
curl -v -X POST \
http://localhost:8080/auth/realms/demo/authz/protection/resource_set \
-H 'Authorization: Bearer '$here i am adding the pat i received from the previous step \
-H 'Content-Type: application/json' \
-d '{
"resource_scopes":[
"read-public",
"post-updates",
"read-private",
"http://www.example.com/scopes/all"
],
"icon_uri":"http://www.example.com/icons/sharesocial.png",
"name":"Tweedl Social Service",
"type":"http://www.example.com/rsrcs/socialstream/140-compatible"
}'
I got {"error":"unknown_error"} as a response with status code 400 Bad request. What am I missing here ?
I know what it feels like :)
Reason
org.keycloak.services.error.KeycloakErrorHandler gets error text from WebApplicationException::getMessage method result.
public Response toResponse(Throwable throwable) {
//...
error.setError(getErrorCode(throwable));
//...
}
private String getErrorCode(Throwable throwable) {
if (throwable instanceof WebApplicationException && throwable.getMessage() != null) {
return throwable.getMessage();
}
return "unknown_error";
}
And if caused exception is another type of RuntimeException you will get "unknown_error"
Solution (short path):
Extend WebApplicationException and implement your own constructor and getMessage method like that:
public class CustomException extends WebApplicationException {
private final String message;
public AbstractUserException(Throwable throwable) {
// log exception if you want
this.message = throwable.getMessage();
}
#Override
public String getMessage() {
return message;
}
}
Wrap the main code of your resource in the try catch block, which always will throw your custom Exception
try {
// your code
} catch (Exception e) {
throw new CustomException(e);
}
Voila! You will get a readable error in response
P.S. If you want to go further - you can throw your exception with a complete HTTP Response object, which may contain custom HTTP status code, etc

How to pass data in Poco HTTPRequest

This is regarding to my project, Where I am write Poco SSL client to communicate with a server.
I am able to do (i) Basic Auth (ii) Cert exchange. But after sending post request I am facing "Error 500".
Let me explain it=>
I have a working curl:
curl -d '{"name":"com.my.session.value"}' -H 'Content-Type: application/json' -H 'Accept: application/json' -E MyCert.pem --key MyKey.pem -u testuser -k -vvv https://<server-ip>:<port>/Internal/directory/path
using this I am able to print data on console. So tried to write same in Poco/C++:
// I have handler for keyPassparse, cert, invalid-cert-handler and used them in below line
SSLManager::instance().initializeClient(parse_str, Cert, ptrContext);
URI uri(argv[1]);
Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort());
session.setKeepAlive(true);
Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_POST, uri.getPath(), Poco::Net::HTTPMessage::HTTP_1_1);
HTTPBasicCredentials cred("testuser", "secret");
//Here I tried to add headers and data
cred.authenticate(req);
req.add("Content-Type","application/json");
req.add("Accept","application/json");
req.add("data","com.my.session.value"); // try-1 to add data
req.setKeepAlive(true);
std::ostream& myOStream = session.sendRequest(req);
std::string body("name=com.my.session.value"); // try-2 to add data
myOStream << body;
Poco::Net::HTTPResponse response;
std::istream& rs = session.receiveResponse(response);
std::cout << response.getStatus() << " " << response.getReason() << std::endl;
}
catch (Exception& exc)
{
std::cerr << exc.displayText() << std::endl;
return 1;
}
return 0;
}
This is always returning Error:500 (Internal server error)
Which means my data section is not reaching properly.
Please suggest me a way to pass proper "data" section to server.
Thanks in advance.
I found the solution for this:
1) I sent data as json:
Poco::JSON::Object obj;
obj.set("name", "com.my.session.value");
std::stringstream ss;
obj.stringify(ss);
2) Content herders should not be added by "add", used below for them:
req.setContentType("application/json");
req.setContentLength(ss.str().size());
3) Now sending body like this:
std::ostream& myOStream = session.sendRequest(req);
obj.stringify(myOStream);
Approach used:
I wrote code for http.
Sent same data by curl and exe, captured packets for both.
Compared and fixed gaps one by one.
I hope this will help someone in future.

Interact with a JSF application which use basic authentication programmatically

I am having difficulties interacting with a website which use basic authentication to authenticate the user.
I am working on visual basic and i have already tried to use
Dim req As HttpWebRequest = HttpWebRequest.Create("http://url.to.website.com")
adding the headers directly to the web request:
req.Headers.Add("Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes("user" & ":" & "password")))
or using the network credentials:
req.Credentials = New Net.NetworkCredential("user", "password")
receiving always the same response code: 401 Unauthorized
Using Firefox developer tools i can analyze and resend some web requests and only using Firefox i am able to authenticate correctly.
Firefox report these headers:
Host: url.to.website.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http.//url.to.website.com/portal/data/pub
DNT: 1
Authorization: Basic ZmFrZTpwYXNzd29yZA==
Connection: keep-alive
So i have tried to set it manaually this way:
req.Host = "url.to.website.com"
req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0"
req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
req.Referer = "https://url.to.website.com/some/path/to/file.jsf"
req.ContentType = "application/x-www-form-urlencoded"
req.KeepAlive = True
req.PreAuthenticate = True
req.Method = "POST"
req.Headers.Add("Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes("user" & ":" & "password")))
with no success (receiving always the same response code: 401 Unauthorized)
Another try was with a web-browser:
WebBrowser1.Navigate("url", Nothing, Nothing, "Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes(AUTH_USER & ":" & AUTH_PASSWORD)))
My objective is to authenticate, then query some pages and collect responses in order to parse them and use it later in the application.
How can i solve the issue about authentication?
The website is written using JSF and i have no control over it.
Update:
My problem is about authentication, not yet about the jsf application.
While using Firefox all work fine (I can send a request to the website and it will authenticate me right) but while using the HttpWebRequest the authentication fails, even if I set the same headers, as Written before .
I have to figure out the difference between the two requests
I had to get this working for Dukes Forest Java EE Tutorial Port to Wildfly. The code was already written, but the header was case sensitive. Anyway, the code used there is as follows:
/* Client filter for basic HTTP auth */
class AuthClientRequestFilter implements ClientRequestFilter {
private final String user;
private final String password;
public AuthClientRequestFilter(String user, String password) {
this.user = user;
this.password = password;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
try {
requestContext.getHeaders().add(
"Authorization",
"Basic " + DatatypeConverter.printBase64Binary(
(user+":"+password).getBytes("UTF-8"))
);
} catch (UnsupportedEncodingException ex) { }
}
}
The DatatypeConverter is imported from javax.xml.bind. This code was called from the following routine, which has the HTTPClient:
Client client = ClientBuilder.newClient();
client.register(new AuthClientRequestFilter("jack#example.com", "1234"));
Response resp = client.target(ENDPOINT)
.request(MediaType.APPLICATION_XML)
.post(Entity.entity(order, MediaType.APPLICATION_XML), Response.class);
int status = resp.getStatus();
if (status == 200) {
success = true;
}
logger.log(Level.INFO, "[PaymentHandler] Response status {0}", status);
client.close();
return success;
This client code posts to a RESTful service.

Which PEM file should I provide when uploading to S3 using HTTP PUT

I'm trying to put a file in S3 using a presigned signature my Java web server provides
http://docs.amazonwebservices.com/AmazonS3/latest/dev/PresignedUrlUploadObjectDotNetSDK.html
I need my uploading client (currently my windows 7 using C++) to have a handshake with amazon servers and I don't know how to do it.
When I tried to send the request with a "default context" (naively) it printed a "self signed certificate in certificate chain" error and asked me to accept or not the certificate.
Then I tried to figure out how to add a certificate and found this code:
POCO C++ - NET SSL - how to POST HTTPS request
The problem is that I'm not sure which pem file is needed here.
I tried providing the pem files I've downloaded from x.509 in Amazon Web Services Console but it raised an SSL exception: SSL3_GET_SERVER_CERTIFICATE
My Code:
URI uri("https://BUCKET.s3.amazonaws.com/nosigfile?Expires=1959682330&AWSAccessKeyId=ACCESSKEY&Signature=DgOifWPmQi%2BASAIDaIOGXla10%2Fw%3D");
const Poco::Net::Context::Ptr context( new Poco::Net::Context( Poco::Net::Context::CLIENT_USE, "", "", "cert(x509).pem") );
Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort(), context );
HTTPRequest req(HTTPRequest::HTTP_PUT, uri.getPathAndQuery(), HTTPMessage::HTTP_1_1);
req.setContentLength(contentLength);
session.sendRequest(req) << streamToSend;
Thanks
Poco includes certificates in the project.
You will need any.pem, rootcert.pem, yourappname.xml which you can find in the poco test suite for the SSL side.
./poco-1.4.1p1-all/NetSSL_OpenSSL/testsuite/{any.pem,rootcert.pem,testsuite.xml}
Once you include the two pem files, your xml, which is used during the initializeSSL phase you will not get the warning for self-signed certificates.
class MySSLApp: public Poco::Util::Application
{
public:
MySSLApp()
{
Poco::Net::initializeSSL();
Poco::Net::HTTPStreamFactory::registerFactory();
Poco::Net::HTTPSStreamFactory::registerFactory();
}
~MySSLApp()
{
Poco::Net::uninitializeSSL();
}
protected:
void initialize(Poco::Util::Application& self)
{
loadConfiguration(); // load default configuration files, if present
Poco::Util::Application::initialize(self);
}
void myUpload(...) {
...
FilePartSource* pFPS = new FilePartSource(szFilename);
std::string szHost = "BUCKET.s3.amazonaws.com";
std::string szPath = "/";
int nRespCode = 201;
try{
HTTPClientSession s(szHost);
HTTPRequest request(HTTPRequest::HTTP_POST, szPath, HTTPMessage::HTTP_1_1);
HTMLForm pocoForm(HTMLForm::ENCODING_MULTIPART);
pocoForm.set("AWSAccessKeyId", ACCESSKEY);
pocoForm.set("acl", "public-read");
pocoForm.set("success_action_status", toString(nRespCode));
pocoForm.set("Content-Type", m_szContentType);
pocoForm.set("key", m_szPath + "/" + m_szDestFileName);
pocoForm.set("policy", m_szPolicy);
pocoForm.set("signature", m_szSignature);
pocoForm.addPart("file", pFPS);
pocoForm.prepareSubmit(request);
std::ostringstream oszMessage;
pocoForm.write(oszMessage);
std::string szMessage = oszMessage.str();
//AWS requires a ContentLength set EVEN though it is chunked!
request.setContentLength((int) szMessage.length());
s.sendRequest(request) << szMessage;
//or:
//pocoForm.write(s.sendRequest(request));
HTTPResponse response;
std::istream& rs = s.receiveResponse(response);
int code = response.getStatus();
if (code != nRespCode) {
stringstream s;
s << "HTTP Error " << code;
throw Poco::IOException(s.str());
}
} catch (Exception& exc) {
std::cout << exc.displayText() << endl;
return;
}
return;
}
}
The xml file will look something like this:
<AppConfig>
<openSSL>
<server>
<privateKeyFile>${application.configDir}any.pem</privateKeyFile>
<caConfig>${application.configDir}rootcert.pem</caConfig>
<verificationMode>none</verificationMode>
<verificationDepth>9</verificationDepth>
<loadDefaultCAFile>true</loadDefaultCAFile>
<cypherList>ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH</cypherList>
<privateKeyPassphraseHandler>
<name>KeyFileHandler</name>
<options>
<password>secret</password>
</options>
</privateKeyPassphraseHandler>
<invalidCertificateHandler>
<name>AcceptCertificateHandler</name>
<options>
</options>
</invalidCertificateHandler>
</server>
<client>
<privateKeyFile>${application.configDir}any.pem</privateKeyFile>
<caConfig>${application.configDir}rootcert.pem</caConfig>
<verificationMode>relaxed</verificationMode>
<verificationDepth>9</verificationDepth>
<loadDefaultCAFile>true</loadDefaultCAFile>
<cypherList>ALL:!ADH:!LOW:!EXP:!MD5:#STRENGTH</cypherList>
<privateKeyPassphraseHandler>
<name>KeyFileHandler</name>
<options>
<password>secret</password>
</options>
</privateKeyPassphraseHandler>
<invalidCertificateHandler>
<name>AcceptCertificateHandler</name>
<options>
</options>
</invalidCertificateHandler>
</client>
</openSSL>
</AppConfig>

Metro client hangs when calling WCF webserver with wsHttpBinding

I have generated a webservice client with a local wsdl using Metro 1.2 this way:
./wsimport.sh -extension -verbose -wsdllocation service.wsdl -s src -d target service.wsdl -Xendorsed
The wsdl uses SOAP 1.2 and wsHttpBinding. It's supposed to connect to a WCF server that's using NTLM as authentication method.
I have created an Authenticator to handle the NTLM authentication:
public class NtlmAuthenticator extends Authenticator
{
private String username = "";
private String password = "";
public NtlmAuthenticator(String username, String password) {
this.username = username;
this.password = password;
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
}
Which I set before each webservice method is called:
#WebEndpoint(name = "WSHttpBinding_ICustomerService")
public ICustomerService getWSHttpBindingICustomerService() {
ICustomerService service =
super.getPort(new QName("http://xmlns.example.com/services/Customer",
"WSHttpBinding_ICustomerService"), ICustomerService.class);
NtlmAuthenticator auth = new NtlmAuthenticator(username, password);
Authenticator.setDefault(auth);
return service;
}
If I use the wrong username/password, I get a 401 Unauthorized back, which is well and all, but when I use the correct username/password, the call hangs and I never get a response!
The request looks like this (captured it with netcat, so host is different, and no https):
POST / HTTP/1.1
Content-type: application/soap+xml;charset="utf-8";action="http://xmlns.example.com/services/ICustomerService/GetCustomer"
Password: [password]
Authorization: Basic [auth]
Username: [username]
Accept: application/soap+xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: JAX-WS RI 2.1.7-b01-
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:5500
Connection: keep-alive
Content-Length: 603
[xml follows]
I have also tried with wget 1.12 (heard that 1.11 had problem with NTLM), but it too never yields a response, just waits.
[...]
---request end---
[writing POST file customerRequest.xml ... done]
HTTP request sent, awaiting response...
I've seen that others have gotten this behaviour before, but I have not been able to find out why. Can anyone shed some light on this? JDK 1.6 on linux.
I found that I missed a line in my generated client code that enabled Addressing and pass it to the getPort super method:
WebServiceFeature wsAddressing = new AddressingFeature(true);
ICustomerService service =
super.getPort(new QName("http://xmlns.example.com/services/Customer",
"WSHttpBinding_ICustomerService"), ICustomerService.class,
wsAddressing);
Why metro didn't generate this is beyond me. The method looked like this in the end:
#WebEndpoint(name = "WSHttpBinding_ICustomerService")
public ICustomerService getWSHttpBindingICustomerService() {
WebServiceFeature wsAddressing = new AddressingFeature(true);
ICustomerService service =
super.getPort(new QName("http://xmlns.example.com/services/Customer",
"WSHttpBinding_ICustomerService"), ICustomerService.class,
wsAddressing);
NtlmAuthenticator auth = new NtlmAuthenticator(username, password);
Authenticator.setDefault(auth);
return service;
}
This in turn added a SOAP header to the message:
<S:Header>
<To xmlns="http://www.w3.org/2005/08/addressing">https://services.example.com/CustomerService.svc</To>
<Action xmlns="http://www.w3.org/2005/08/addressing">http://xmlns.example.com/services/ICustomerService/GetCustomer</Action>
<ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
<MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:d33c2888-abfa-474d-8729-95d2bcd17a96</MessageID>
</S:Header>