java soap connect via https - java

I'm trying to connect to https url - https://rtpubcommission.api.cj.com/wsdl/version2/realtimeCommissionServiceV2.wsdl
But getting the errors (listed only chain of errors, without full stacktrace):
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: java.security.PrivilegedActionException: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Message send failed
Caused by: java.security.PrivilegedActionException: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Message send failed
Caused by: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: algorithm check failed: MD2withRSA is disabled
Caused by: java.security.cert.CertPathValidatorException: algorithm check failed: MD2withRSA is disabled
Here is my code:
private void processCommonRequest(String url, HashMap<String, String> params) throws Exception {
URL endpoint = new URL(url);
//MessageDigest md = MessageDigest.getInstance("MD5");
//System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
//Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
// Create SOAP connection
SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection connection = scf.createConnection();
// Create a message from the message factory.
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();
// Get the SOAP Part from the message
SOAPPart soapPart = msg.getSOAPPart();
// Get the SOAP Envelope from the SOAP Part
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/");
envelope.addNamespaceDeclaration("xsd", "http://www.w3.org/1999/XMLSchema");
envelope.addNamespaceDeclaration("xsi", "http://www.w3.org/1999/XMLSchema-instance-instance");
envelope.addNamespaceDeclaration("tns", "http://api.cj.com");
envelope.setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");
// Remove empty header from the Envelope
envelope.getHeader().detachNode();
// Create a soap body from the Envelope.
SOAPBody body = envelope.getBody();
body.addNamespaceDeclaration("soap-env", "http://schemas.xmlsoap.org/soap/encoding/");
// SOAPBodyElement item = body.addBodyElement(envelope.createName("GeScore") );
SOAPBodyElement item = body.addBodyElement(envelope.createName(
"GeScore", "soap-env", "http://schemas.xmlsoap.org/soap/encoding/"));
for (String keyMap : params.keySet()) {
addItem(envelope, keyMap, params.get(keyMap), item);
}
System.out.println("\nContent of the message: \n"); // FIXME
msg.writeTo(System.out);
// Send the SOAP message and get reply
System.err.println("\nSending message to URL: " + endpoint); // XXX
SOAPMessage reply = connection.call(msg, endpoint);
// ... nevermind what later ... .call function throws error ....
connection.close();
}

The MD2 is not secure any more, so Sun disabled its use in certification path validation. In the Release Notes of security update 6u17, http://java.sun.com/javase/6/webnotes/6u17.html, it is said that the vulnerability is addressed, "6861062: Disable MD2 in certificate chain validation".
You could either run your code on older version of JRE (before 6u17) or try latest version instead, as there are some reports that it might work (https://forums.oracle.com/forums/thread.jspa?threadID=1237743).
Alternatively you could try to use custom TrustManager, although it's not recommended if security is important for you.

Related

SOAP Post works with Postman but not with Java

I have a very strange thing going on.
My code gives me Connection Timeout when I call -> SOAPMessage resp = conn.call(msg, url);
java.net.ConnectException: Connection timed out: connect
So I thought my url for the request or login was wrong but I copy paste the msg from the
LOGGER.info(msg);
and paste it in the Postman and it works, I get the response.
Any idea what might be causing this issue? This seems to be an issue only with the code.
public void updateTicket(String ticketID, String user, String password, String connection, String nameSpace, Map < String, Object > values, Map < String, Object > articleValues, Map < String, Object > dynamicFieldsValues) throws UnsupportedOperationException, SOAPException, IOException, Exception {
// Create the connection
SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection conn = scf.createConnection();
// Create message
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();
// Object for message parts
SOAPPart sp = msg.getSOAPPart();
SOAPEnvelope env = sp.getEnvelope();
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
env.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
env.addNamespaceDeclaration("soap", "http://www.w3.org/2003/05/soap-envelope");
env.addNamespaceDeclaration("con", nameSpace);
SOAPBody body = env.getBody();
SOAPElement ticketUpdate = body.addChildElement("TicketUpdate", "con");
ticketUpdate.addChildElement("UserLogin", "con").addTextNode(user);
ticketUpdate.addChildElement("Password", "con").addTextNode(password);
ticketUpdate.addChildElement("TicketID", "con").addTextNode(ticketID);
LOGGER.info("SOAP Request");
LOGGER.info(msg);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
msg.writeTo(stream);
URL url = new URL(connection);
SOAPMessage resp = conn.call(msg, url);
Edit: The connection variable contains the correct SOAP endpoint

Java client using SAAJ error: SEVERE: SAAJ0008: Bad Response; Unauthorized

I have written a Java client code using SAAJ for Primevera P6 webservices. I am getting the below authentication error. I am providing the http username + password to the code but it gives error: SEVERE: SAAJ0008: Bad Response; Unauthorized. Could some one please help me with this issue. I am stuck in this problem since a long time. The wsdl of the web service is: https://sunpower-p6.oracleindustry.com/p6ws-token/services/ProjectService?wsdl.
ERROR:
Request SOAP Message =
11106
Jul 18, 2016 1:03:19 PM com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection post
SEVERE: SAAJ0008: Bad Response; Unauthorized
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Bad response: (401Unauthorized
My code:
public class TestClient {
/*
Method used to create the SOAP Request
*/
private static SOAPMessage createSOAPRequest() throws Exception {
// Next, create the actual message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
String serverURI = "http://xmlns.oracle.com/Primavera/P6/WS/Project/V2";
// SOAP Envelope
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("ProjectService", serverURI);
//start: setting HTTP headers - optional, comment out if not needed
String authorization = Base64Coder.encodeString("xyz:abc");
MimeHeaders hd = soapMessage.getMimeHeaders();
hd.addHeader("Authorization", "Basic " + authorization);
//end: setting HTTP headers
// Create and populate the body
SOAPBody soapBody = envelope.getBody();
// Create the main element and namespace
SOAPElement soapBodyElem = soapBody.addChildElement("ReadProjects", "ProjectService");
SOAPElement soapBodyElem1 = soapBodyElem.addChildElement("Field", "ProjectService");
SOAPElement soapBodyElem2 = soapBodyElem1.addChildElement("Id", "ProjectService");
soapBodyElem2.addTextNode("11106");
hd.addHeader("SOAPAction", serverURI + "ReadProjects");
// Save the message
soapMessage.saveChanges();
// Check the input
System.out.println("Request SOAP Message = ");
soapMessage.writeTo(System.out);
System.out.println();
return soapMessage;
}
/**
* Method used to print the SOAP Response
*/
private static void printSOAPResponse(SOAPMessage soapResponse) throws Exception {
// Create the transformer
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
// Extract the content of the reply
Source sourceContent = soapResponse.getSOAPPart().getContent();
// Set the output for the transformation
System.out.println("\nResponse SOAP Message = ");
StreamResult result = new StreamResult(System.out);
transformer.transform(sourceContent, result);
System.out.println();
}
public static void main(String[] args) throws Exception {
try {
// First create the connection
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection soapConnection = soapConnectionFactory.createConnection();
//System.out.println(soapConnection);
//Send SOAP Message to SOAP Server
String url = "https://sunpower-p6.oracleindustry.com/p6ws-token/services/ProjectService?wsdl";
// Send the message and get the reply
SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);
// Process the SOAP Response
printSOAPResponse(soapResponse);
soapConnection.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
I realize this is an old question but would like to leave some assistance to anyone in the future who ends up here (like I did). There is a few likely situations that I have recently experienced regarding this.
Check that your details are correct, or that the connection is possible. Be 100% sure before moving on.
You will receive an unauthorized error if your database is not correctly configured for some setups (for instance, on JBoss). In my situation the data-source file was not being read properly when it attempted to connect causing the Unauthorized error on the client side.

Consuming an authenticated web service using SAAJ?

I am trying to consume an authenticated webservice using SAAJ. This is the code I have so far:
import java.io.ByteArrayOutputStream;
import javax.xml.soap.*;
import biz.source_code.base64Coder.*;
public class Client {
private static String endpoint = "https://example.com/xxx.php",
username = "xxx", password = "xxx";
private static SOAPMessage getRequest() throws Exception{
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();
//set authorization as a HTTP header
String authorization = Base64Coder.encodeString(username + ":" + password);
MimeHeaders hd = message.getMimeHeaders();
hd.addHeader("Authorization", "Basic " + authorization);
//Call getReportList operation
return message;
}
public static void main(String[] args) throws Exception {
SOAPConnectionFactory connFactory = SOAPConnectionFactory.newInstance();
SOAPConnection connection = connFactory.createConnection();
// create request message and give it content
SOAPMessage request = Client.getRequest();
// call the API endpoint with the request
SOAPMessage response = connection.call(request, endpoint);
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.writeTo(out);
String strMsg = new String(out.toByteArray());
System.out.println(strMsg);
}
}
When I run this, it prints strMsg (response from the web service) as follows:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>WSDL</faultcode><faultstring>SOAP-ERROR: Parsing WSDL: Couldn't load from '/www/example.wsdl' : failed to load external entity "/www/example.wsdl"
</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>
I am guessing I have authenticated myself but there was a problem with the web service not mine. But, I am not entirely sure. This error message doesn't seem that common.
Does this mean the authentication I provided was incorrect or insufficient? Or do I have to provide the SSL certificate because the web service uses SSL? If yes, Is there a tutorial on how to use SSL certificates with SAAJ?
The problem was that I was using "https://example.com/xxx.php" as the endpoint not "https://example.com/xxx.php?wsdl". That's why it couldn't load any wsdl file.

How to remove namespace from a tag but leave its prefix?

I am able to generate a SOAP message but I do not know
add prefix only to soapMessage tag (should not have namespace)
SOAPConnectionFactory soapConnectionFactory =
SOAPConnectionFactory.newInstance();
SOAPConnection connection =
soapConnectionFactory.createConnection();
SOAPFactory soapFactory =
SOAPFactory.newInstance();
MessageFactory factory =
MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPMessage message = factory.createMessage();
SOAPHeader header = message.getSOAPHeader();
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPBody body = soapEnvelope.getBody();
soapEnvelope.removeNamespaceDeclaration(soapEnvelope.getPrefix());
soapEnvelope.setPrefix("soap");
body.setPrefix("soap");
header.removeNamespaceDeclaration(header.getPrefix());
header.setPrefix("soap");
soapEnvelope.addNamespaceDeclaration("v9", "URL TO SERVER");
Name bodyName;
bodyName = soapFactory.createName("SearchHotels");
SOAPBodyElement getList = body.addBodyElement(bodyName);
getList.setPrefix("v9");
Name childName = soapFactory.createName("SoapMessage", "v9", "URL TO SERVER");
SOAPElement HotelListRequest = getList.addChildElement(childName);
HotelListRequest.addChildElement("Hotel", "v9").addTextNode("Hilton");
My SOAP Message
...
<v9:SoapMessage xmlns:els="URL TO SERVER">
...
What I expect
...
<v9:SoapMessage>
...
Update :
If I use the following it runs into following error
SOAPElement HotelListRequest = getList.addChildElement("v9:SoapMessage");
org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a
way which is incorrect with regard to namespaces.
To add the namespace prefix to all the tags you have to redeclare the desired prefix (and eventually the namespace) on every inserted child, otherwise it will inherit the namespace (implicitely) from the parent element.
Try for instance:
SOAPBodyElement getList = body.addBodyElement(bodyName, "v9", "http://URL TO SERVER");
or
soapBody.addChildElement("SomeElement", "v9", "http://URL TO SERVER");
or
soapBody.addChildElement("v9:SomeElement");
Sometimes you may have to use a QName object instead of just a String or a Name.
It pretty much depends on the SOAP-API/Implementation you use, but the principle is the same everywhere: either redeclare (explicit) or inherit (implicit).
In your expected SOAP message there is a difference in case of prefix , seems you are writing client where requesting to a service provider which is developed in .net , this is why you may need to change prefix .
I think you can take concept from bellow code :
MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
SOAPBody soapBody = soapEnvelope.getBody();
soapEnvelope.removeNamespaceDeclaration(soapEnvelope.getPrefix());
soapEnvelope.addNamespaceDeclaration("soap", "http://schemas.xmlsoap.org/soap/envelope/");
soapEnvelope.setPrefix("soap");
soapBody.setPrefix("soap");
soapEnvelope.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
soapEnvelope.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema"); soapMessage.getSOAPHeader().detachNode();
soapMessage.getMimeHeaders().setHeader("SOAPAction", "http://www.example.com/TransactionProcess");
For more detail please visit this REFERENCE LINK

SSLHandshakeException: No subject alternative names present

I am invoking HTTPS SOAP web service through java code. I have already imported self-signed certificate in jre cacerts keystore. Now I am getting :
com.sun.xml.internal.ws.com.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
The hostname of the service URL is not matching that of the CN provided in cert. I read about a workaround of defining a custom Hostname verifier here. But i cant able to make where I should implement the workaround in my code.
public SOAPMessage invokeWS(WSBean bean) throws Exception {
SOAPMessage response=null;
try{
/** Create a service and add at least one port to it. **/
String targetNameSpace = bean.getTargetNameSpace();
String endpointUrl = bean.getEndpointUrl();
QName serviceName = new QName(targetNameSpace, bean.getServiceName());
QName portName = new QName(targetNameSpace, bean.getPortName());
String SOAPAction = bean.getSOAPAction();
HashMap<String, String> map = bean.getParameters();
Service service = Service.create(serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);
/** Create a Dispatch instance from a service. **/
Dispatch dispatch = service.createDispatch(portName, SOAPMessage.class,
Service.Mode.MESSAGE);
// The soapActionUri is set here. otherwise we get a error on .net based
// services.
dispatch.getRequestContext().put(Dispatch.SOAPACTION_USE_PROPERTY,
new Boolean(true));
dispatch.getRequestContext().put(Dispatch.SOAPACTION_URI_PROPERTY,
SOAPAction);
/** Create SOAPMessage request. **/
// compose a request message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
// Create objects for the message parts
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
SOAPElement bodyElement = body.addChildElement(bean.getInputMethod(),
bean.getPrefix(), bean.getTargetNameSpace());
...more code to form soap body goes here
// Print request
message.writeTo(System.out);
// Save the message
message.saveChanges();
response = (SOAPMessage)dispatch.invoke(message);
}
catch (Exception e) {
log.error("Error in invokeSiebelWS :"+e);
}
return response;
}
Please ignore WSBean parameter as the namespaces and other wsdl attributes are coming from this bean. And if this exception can solved with some different workarounds, pls do suggest.
Thanks,Bruno for giving me heads up on Common Name and Subject Alternative Name. As we figured out certificate was generated with CN with DNS name of network and asked for regeneration of new certificate with Subject Alternative Name entry i.e. san=ip:10.0.0.1. which is the actual solution.
But, we managed to find out a workaround with which we can able to run on development phase. Just add a static block in the class from which we are making ssl connection.
static {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session)
{
// ip address of the service URL(like.23.28.244.244)
if (hostname.equals("23.28.244.244"))
return true;
return false;
}
});
}
If you happen to be using Java 8, there is a much slicker way of achieving the same result:
static {
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> hostname.equals("127.0.0.1"));
}
Unlike some browsers, Java follows the HTTPS specification strictly when it comes to the server identity verification (RFC 2818, Section 3.1) and IP addresses.
When using a host name, it's possible to fall back to the Common Name in the Subject DN of the server certificate, instead of using the Subject Alternative Name.
When using an IP address, there must be a Subject Alternative Name entry (of type IP address, not DNS name) in the certificate.
You'll find more details about the specification and how to generate such a certificate in this answer.

Categories