Support Article
SOAP Service picks up incorrect Transport Layer Security version
SA-52953
Summary
SOAP Service does not pick up the correct Transport Layer Security (TLS) versions.
Despite enabling TLS1.1, the outgoing request does not go with TLS1.1.
SOAP Connect rule used is employee_schedule_lookup.
Error Messages
Connection is unsuccessful
Steps to Reproduce
- Log in to the Pega instance
- Launch the HR Forms Initiator portal
- Open the work object 'F-7867'
- Open Tracer
- Choose 'other person' from the Person Search section
- Search with '00274860'
- Click the 'Fetch work schedule' tab and save Tracer
Root Cause
Rule-Connect-SOAP!ValidateInfrastructure is unable to get SSLSocketFactory from Pega API.
Resolution
Perform the following local-change:Modify Rule-Connect-SOAP!ValidateInfrastructure to get SSLSocketFactory from Pega API ServiceUtils.getSecureSocketFactory().
Add the below code to the 6th step of Rule-Connect-SOAP!ValidateInfrastructure:
if (oLog.isInfoEnabled())
{
oLog.info("Starting SOAP connectivity testing");
}
// completely stopped using the parameter page in this step
// in preparation for the day when we test the live page instead of looking up connector in DB.
// - houlj 2014/09/22
com.pega.pegarules.priv.util.ServiceUtilsPriv svcUtilPriv = pega.getServiceUtilsPriv();
ServiceUtils svcUtil = tools.getServiceUtils();
com.pega.pegarules.pub.pal.PAL pal = tools.getPAL();
String statusVal = null;
String statusMsg = null;
String statusWarn = "";
ClipboardPage statusPage = tools.findPage("TempStatusPage");
ClipboardProperty comps = statusPage.getProperty("pyComponents");
ClipboardProperty steps = statusPage.getProperty("pySteps");
final String STATUSNOTATTEMPTED = "Not attempted";
final String STATUSFAIL = "Fail";
final String STATUSSUCCESS = "Success";
try
{
//Test connectivity to the Server.
if(connectSOAPPage == null || !(connectSOAPPage instanceof ClipboardPage))
{
statusMsg = "Connect page is null.";
oLog.error(statusMsg);
statusPage.putString("pyStatusMsg", statusMsg);
return false;
}
ClipboardPage ConnectPage = (ClipboardPage)connectSOAPPage;
final String connectorType = ConnectPage.getString("pxObjClass");
final String connectorAppliesTo = ConnectPage.getString("pyClassName");
final String connectorName = ConnectPage.getString("pyServiceName");
final String connectorAbsoluteLabel = (new StringBuilder(connectorType).append(".").append(connectorAppliesTo).append(".").append(connectorName)).toString();
final String log = "[" + connectorType + ": " + connectorAppliesTo + '.' + connectorName + "] ";
// set label for results page
statusPage.putString("pyLabel", connectorAbsoluteLabel);
final String wsdlURL = ConnectPage.getString("pyWSDLURL");
final String wsdlData = ConnectPage.getString("pyWSDLData");
final String packageName = ConnectPage.getString("pyAxisPackage");
final String stubClassName = ConnectPage.getString("pyAxisBindingClass");
final String svcURLOrig = ConnectPage.getString("pySOAPURL");
//Bug-1372
final String axisVersion = ConnectPage.getString("pyAxisVersion");
//Bug-1372
String svcURL = svcUtilPriv.getStringIndirect(ConnectPage, "pySOAPURL");
String overrideSvcURLWithThis = tools.getParamValue("EndPointURL");
if (overrideSvcURLWithThis != null && overrideSvcURLWithThis.length() != 0)
{
svcURL = overrideSvcURLWithThis.trim();
}
ClipboardProperty javaClasses = ConnectPage.getProperty("pyGeneratedJavaClasses");
// As of 4.2 SP1, all generated connect rules keep the qualified names of the Axis parameters
// in two hidden page-list properties, to reduce the amount of guesswork needed to make the call.
ClipboardProperty reqSchemaInfo = null;
ClipboardProperty resSchemaInfo = null;
if (ConnectPage.containsKey("pyRequestSchemaInfo") && ConnectPage.containsKey("pyResponseSchemaInfo")) {
reqSchemaInfo = ConnectPage.getProperty("pyRequestSchemaInfo");
resSchemaInfo = ConnectPage.getProperty("pyResponseSchemaInfo");
}
if (oLog.isInfoEnabled())
{
oLog.info("Configuring SOAP HTTP client");
}
comps.add("WSDL_URL", wsdlURL);
comps.add("WSDL_data", wsdlData);
String svcURLForDisplay = svcURL;
if (!(svcURLOrig.equals(svcURL)))
{
svcURLForDisplay += " (" + svcURL + ")";
}
comps.add("Service_URL", svcURLForDisplay);
if ( !(stubClassName == null || stubClassName.trim().length() == 0))
{
comps.add("Axis_binding_class", stubClassName);
}
if (oLog.isInfoEnabled())
oLog.info(log + "Configuring SOAP HTTP client");
// Declare the call object
com.pega.apache.axis.client.Call theCall = null;
// Check if the connect was auto-configured via WSDL
//For Task2413, when content of WSDL is given for auto-generation
////Bug-1372
final boolean axisOneMode = ((wsdlURL.length() > 0 || wsdlData.length() > 0)&& !axisVersion.equals("2"));
if (axisOneMode) {
// create the steps that we'll be performing.
// BUT DO NOT ADD THEM YET. Add them in a finally block at the end.
ClipboardPage pStepInitAxisClient = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepInitAxisClient.putString("pyStep", "Initialize Axis client");
pStepInitAxisClient.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepGetAxisConstructor = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepGetAxisConstructor.putString("pyStep", "Get Axis client constructor");
pStepGetAxisConstructor.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepInstantiateAxis = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepInstantiateAxis.putString("pyStep", "Instantiate Axis client");
pStepInstantiateAxis.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepGetCreateCall = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepGetCreateCall.putString("pyStep", "Get createCall method");
pStepGetCreateCall.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepInvokeCreateCall = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepInvokeCreateCall.putString("pyStep", "Create Call object (invoke createCall method)");
pStepInvokeCreateCall.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepSetEndpoint = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepSetEndpoint.putString("pyStep", "Set endpoint target address to service URL");
pStepSetEndpoint.putString("pyStepStatus", STATUSNOTATTEMPTED);
try
{
if (oLog.isInfoEnabled())
{
oLog.info(log + "Initializing Axis client using binding stub class");
}
WebSvcsParser parser = svcUtil.getWSDLParser();
if (!parser.axisClientIsInitialized(stubClassName)) {
// Yikes!!!
// If we don't do this, there is very little chance that
// Axis will be able to deserialize the response data.
java.util.List classList = new java.util.ArrayList();
java.util.Iterator classIter = javaClasses.iterator();
while (classIter.hasNext()) {
ClipboardProperty classProp = (ClipboardProperty)classIter.next();
classList.add(classProp.toString());
}
if (packageName.length() > 0)
{
parser.setPackageName(packageName);
}
parser.initializeAxisClientClasses(classList);
}
// set step to failed... will update if we get far enough to call it a success.
pStepInitAxisClient.putString("pyStepStatus", STATUSFAIL);
Class stubClass = svcUtil.getDynamicClass(stubClassName, false);
if (stubClass == null) {
parser.generateAxisClientClasses(wsdlURL, wsdlData);
stubClass = svcUtil.getDynamicClass(stubClassName, false);
}
if (stubClass == null) {
statusMsg = "Couldn't initialize Axis client stub.";
oLog.error(log + statusMsg);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
pStepInitAxisClient.putString("pyStepStatus", STATUSSUCCESS);
java.lang.reflect.Constructor stubClassCtor = null;
try {
pStepGetAxisConstructor.putString("pyStepStatus", STATUSFAIL);
stubClassCtor = stubClass.getConstructor(new Class[0]);
pStepGetAxisConstructor.putString("pyStepStatus", STATUSSUCCESS);
} catch (Throwable e)
{
oLog.debug("Exception while get axis constructor", e);
}
if (stubClassCtor == null) {
statusMsg = "Couldn't get constructor for service binding stub class: " + stubClassName;
oLog.error(log + statusMsg);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
Object stubInstance = null;
try {
pStepInstantiateAxis.putString("pyStepStatus", STATUSFAIL);
stubInstance = stubClassCtor.newInstance(new Object[0]);
pStepInstantiateAxis.putString("pyStepStatus", STATUSSUCCESS);
} catch (Throwable e)
{
oLog.debug("Exception while init axis", e);
}
if (stubInstance == null) {
statusMsg = "Couldn't instantiate service binding stub class: " + stubClassName;
oLog.error(log + statusMsg);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
java.lang.reflect.Method createCallMethod = null;
try {
pStepGetCreateCall.putString("pyStepStatus", STATUSFAIL);
createCallMethod = stubInstance.getClass().getMethod("createCall", new Class[0]);
pStepGetCreateCall.putString("pyStepStatus", STATUSSUCCESS);
} catch (Throwable e)
{
oLog.debug("Exception when getting createCall", e);
}
if (createCallMethod == null) {
statusMsg = "Couldn't get handle to createCall method of service binding stub class: " + stubClassName;
oLog.error(log + statusMsg);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
// Invoke the createCall() method.
// This method is normally declared private, but we are using a doctored
// axis.jar that produces a public method instead. This is to take advantage of
// of what createCall does - register serializer/deserializer datatype mappings
// for all generated complex top-level and embedded objects, but not be forced
// to use the generated service methods, which (a) don't currently support SOAP
// headers, (b) have a funky type-signature which we would need to interpret,
// and (c) require the use of holder classes for all INOUT and OUT params.
// Using the Call object gives us greater control over configuring the SOAP request.
try {
pStepInvokeCreateCall.putString("pyStepStatus", STATUSFAIL);
theCall = (com.pega.apache.axis.client.Call) createCallMethod.invoke(stubInstance, new Object[0]);
} catch (Throwable e)
{
oLog.debug("Caught exception while invoking createCall", e);
}
if (theCall == null) {
statusMsg = "Couldn't create Call object using service binding stub class: " + stubClassName + "; ";
oLog.error(log + statusMsg);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
pStepInvokeCreateCall.putString("pyStepStatus", STATUSSUCCESS);
// Set the endpoint target address
pStepSetEndpoint.putString("pyStepStatus", STATUSFAIL);
theCall.setTargetEndpointAddress(svcURL);
pStepSetEndpoint.putString("pyStepStatus", STATUSSUCCESS);
}
finally
{
// add Axis1 related steps to the step list, in the order that they were performed.
if (steps != null)
{
steps.add(pStepInitAxisClient);
steps.add(pStepGetCreateCall);
steps.add(pStepInvokeCreateCall);
steps.add(pStepSetEndpoint);
}
}
} // end if of axis1/legacy mode
else
{
// not doing legacy/axis1 stuff.
if (oLog.isInfoEnabled())
{
oLog.info(log + "Initializing Axis client manually");
}
ClipboardPage pStepCreateCallObject = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepCreateCallObject.putString("pyStep", "Create Axis Call object (using service URL)");
pStepCreateCallObject.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepHTTPConnect = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepHTTPConnect.putString("pyStep", "Make HTTP(S) connection to Service Endpoint");
pStepHTTPConnect.putString("pyStepStatus", STATUSNOTATTEMPTED);
try
{
try
{
// do this axis call stuff inside of the HTTP try/finally, so that we always get status of HTTP connection
if (oLog.isDebugEnabled())
{
oLog.debug("Attempting to create Axis call object with service URL " + svcURL);
}
pStepCreateCallObject.putString("pyStepStatus", STATUSFAIL);
// attempt to make an axis Call object using the URL (if this fails, the client would never work).
theCall = new com.pega.apache.axis.client.Call(svcURL);
pStepCreateCallObject.putString("pyStepStatus", STATUSSUCCESS);
} catch (Throwable e)
{
statusMsg = "Service URL '" + svcURL + "' value is invalid: " + e;
oLog.error(log + statusMsg, e);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
} finally
{
if (steps != null)
{
steps.add(pStepCreateCallObject);
}
}
pStepHTTPConnect.putString("pyStepStatus", STATUSFAIL);
final Boolean usingSSL = svcURL.startsWith("https");
comps.add("SSL_TLS_ENABLED", usingSSL.toString());
//Bug-4451
if(! usingSSL)
{
// no special settings required. Just connect with basic java connection.
pStepHTTPConnect.putString("pyStep", "Make HTTP connection to Service Endpoint");
pStepHTTPConnect.putString("pyStepStatus", STATUSFAIL);
if (oLog.isDebugEnabled())
{
oLog.debug("Attempting to make plain HTTP connection to " + svcURL);
}
java.net.URLConnection urlConnection= new java.net.URL(svcURL).openConnection();
urlConnection.connect();
}
else
{
// need to take SSL context into account, so we need to use a more robust HTTP client that we can configure(Apache)
pStepHTTPConnect.putString("pyStep", "Make HTTPS connection to Service Endpoint");
ClipboardPage pStepCreateGETObject = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepCreateGETObject.putString("pyStep", "Create HTTP GET Object");
pStepCreateGETObject.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepSetupSSL = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepSetupSSL.putString("pyStep", "Set up HTTPS Context");
pStepSetupSSL.putString("pyStepStatus", STATUSNOTATTEMPTED);
ClipboardPage pStepCreateHTTPSClient = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepCreateHTTPSClient.putString("pyStep", "Create HTTPS Client");
pStepCreateHTTPSClient.putString("pyStepStatus", STATUSNOTATTEMPTED);
com.pega.apache.http.HttpRequest httpRequest = null;
try
{
// attempt to make GET object first, rather than waste time setting other things up
try
{
pStepCreateGETObject.putString("pyStepStatus", STATUSFAIL);
httpRequest = new com.pega.apache.http.client.methods.HttpGet(svcURL);
}
catch (Throwable e)
{
statusMsg = "Service URL '" + svcURL + "' value is invalid: " + e;
oLog.error(log + statusMsg, e);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
pStepCreateGETObject.putString("pyStepStatus", STATUSSUCCESS);
pStepSetupSSL.putString("pyStepStatus", STATUSFAIL);
// don't init HTTP client here, because we always need to instantiate with custom SSL stuff.
String securityProfile = ConnectPage.getString("pyWSSProfileName");
final String lowestProtocol = ConnectPage.getString("pySSLProtocolVersion");
String keyStoreName = null;
String trustStoreName = null;
if(securityProfile != null && securityProfile.length() > 0)
{
if (oLog.isDebugEnabled())
{
oLog.debug("Attempting to open Security Profile " + securityProfile);
}
ClipboardPage pStepOpenSecurityProfile = tools.createPage("Embed-ISInfrastructureValidateSteps", "");
pStepOpenSecurityProfile.putString("pyStep", "Open associated Security Profile");
pStepOpenSecurityProfile.putString("pyStepStatus", STATUSFAIL);
ClipboardPage wssProfilePage = null;
try
{
ParameterPage dbOpenParams = new ParameterPage();
dbOpenParams.putString("pxObjClass", "Data-Admin-Security-WSSecurityProfile");
dbOpenParams.putString("pyProfileName",securityProfile );
wssProfilePage = tools.getDatabase().open(dbOpenParams, true);
if (wssProfilePage == null)
{
statusMsg = "Couldn't open Security Profile " + securityProfile;
oLog.error(log + statusMsg);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
return false;
}
pStepOpenSecurityProfile.putString("pyStepStatus", STATUSSUCCESS);
}
finally
{
if (steps != null)
{
steps.add(pStepOpenSecurityProfile);
}
}
trustStoreName = wssProfilePage.getString("pyTruststore");
keyStoreName = wssProfilePage.getString("pyKeystore");
comps.add("Security_Profile", securityProfile);
if (trustStoreName != null && trustStoreName.length() > 0)
{
comps.add("Truststore", trustStoreName);
}
if (keyStoreName != null && keyStoreName.length() > 0)
{
comps.add("Keystore", keyStoreName);
}
}
final String allHosts = tools.getSystemSettings().getDynamic("Pega-IntegrationEngine","https/allowAllHostnames");
final boolean trustAllCerts = Boolean.parseBoolean(allHosts);
com.pega.apache.http.conn.ssl.SSLSocketFactory apacheSocketFactory = null;
javax.net.ssl.SSLSocketFactory javaFactory = null;
try
{
javaFactory = svcUtil.getSecureSocketFactory(trustStoreName, keyStoreName, lowestProtocol);
// javaFactory is instance of pega's custom type
}
catch (Exception e)
{
throw new ConnectorException("SSL configuration: unable to instantiate JSSE socket factory with current inputs", e);
}
// get the supported protocols from the factory
String protSet = "UNKNOWN";
try
{
// get the supported protocols from the factory
javax.net.ssl.SSLSocket tempSocket = (javax.net.ssl.SSLSocket) javaFactory.createSocket();
String[] enabled = tempSocket.getEnabledProtocols();
protSet = java.util.Arrays.toString(enabled);
}
catch (Throwable t)
{
statusVal = "Fail: SSL/TLS settings";
statusMsg = "Secure Socket Factory is unable to create a socket. Check Secure protocol configuration of Connector.";
throw new Exception(statusMsg, t);
}
finally
{
comps.add("SSL_Protocols_enabled", protSet);
}
// Create a new SSLContext that merges custom PRPC keystore/truststore with default JDK ones
apacheSocketFactory = new com.pega.apache.http.conn.ssl.SSLSocketFactory(javaFactory);
if (trustAllCerts)
{
// the apache factory has its own hostname verifier built in, and it should be disabled in "trust all" case.
apacheSocketFactory.setHostnameVerifier(com.pega.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
}
// create the scheme for connection
com.pega.apache.http.conn.scheme.SchemeRegistry sr = new com.pega.apache.http.conn.scheme.SchemeRegistry();
sr.register(new com.pega.apache.http.conn.scheme.Scheme("https", apacheSocketFactory, 443));
pStepSetupSSL.putString("pyStepStatus", STATUSSUCCESS);
// get a generic client to build custom client from.
com.pega.apache.http.impl.client.DefaultHttpClient client = new com.pega.apache.http.impl.client.DefaultHttpClient();
// build the client using the custom HTTPS scheme.
pStepCreateHTTPSClient.putString("pyStepStatus", STATUSFAIL);
com.pega.apache.http.impl.conn.SingleClientConnManager cm = new com.pega.apache.http.impl.conn.SingleClientConnManager(client.getParams(), sr);
client = new com.pega.apache.http.impl.client.DefaultHttpClient(cm, client.getParams());
pStepCreateHTTPSClient.putString("pyStepStatus", STATUSSUCCESS);
// now start to actually connect
pStepHTTPConnect.putString("pyStepStatus", STATUSFAIL);
com.pega.apache.http.HttpResponse httpResponse = client.execute((com.pega.apache.http.client.methods.HttpUriRequest) httpRequest);
} finally
{
if (steps != null)
{
steps.add(pStepCreateGETObject);
steps.add(pStepSetupSSL);
steps.add(pStepCreateHTTPSClient);
}
}
} // end of if HTTPS connection
pStepHTTPConnect.putString("pyStepStatus", STATUSSUCCESS);
} finally
{
// we want HTTP(s) connection to be the last step on the list, after any required steps we ran into in the process.
if (steps != null)
{
steps.add(pStepHTTPConnect);
}
}
}
statusVal = "Good";
statusMsg = "";
statusPage.putString("pyStatusVal", statusVal);
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
} catch(Throwable t) {
statusMsg = "Caught unexpected Throwable during SOAP Connectivity Test: " + t.toString();
statusPage.putString("pyStatusMsg", statusWarn + statusMsg);
}
if (oLog.isInfoEnabled())
{
oLog.info("Finished SOAP connectivity testing");
}
Published September 5, 2018 - Updated October 8, 2020
Have a question? Get answers now.
Visit the Collaboration Center to ask questions, engage in discussions, share ideas, and help others.