I'm using ksoap2-android to make a call to wcf service over SSL. I can get it to work without SSL, but now I want to make the call over SSL, but I've run in to some problems.
I'm using the HttpsTransportSE instead of HttpTransportSE, but I'm getting the error: javax.net.ssl.SSLException: Not trusted server certificate
How can I fix this?
Can I add the server certificate to the Keystore in Android to solve the problem?
private static final String SOAP_ACTION = "http://example.com/Service/GetInformation";
private static final String METHOD_NAME = "GetInformation";
private static final String NAMESPACE = "http://example.com";
private static final String URL = "dev.example.com/Service.svc";
public static Result GetInformation()
{
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
PropertyInfo property = new PropertyInfo();
property.name = "request";
Request request =
new Request("12", "13", "Ben");
userInformationProperty.setValue(request);
userInformationProperty.setType(request.getClass());
request.addProperty(property);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
envelope.addMapping(NAMESPACE, "Request",new Request().getClass());
HttpsTransportSE transport = new HttpsTransportSE(URL, 443, "", 1000);
//HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);
transport.debug = true;
try
{
transport.call(SOAP_ACTION, envelope);
return Result.FromSoapResponse((SoapObject)envelope.getResponse());
}
catch (IOException e)
{
e.printStackTrace();
}
catch (XmlPullParserException e)
{
e.printStackTrace();
}
return null;
}
To complement the answer of Vedran with some source code, sorry I can't comment.
The trustManager:
private static TrustManager[] trustManagers;
public static class _FakeX509TrustManager implements
javax.net.ssl.X509TrustManager {
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return (true);
}
public boolean isServerTrusted(X509Certificate[] chain) {
return (true);
}
public X509Certificate[] getAcceptedIssuers() {
return (_AcceptedIssuers);
}
}
public static void allowAllSSL() {
javax.net.ssl.HttpsURLConnection
.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
javax.net.ssl.SSLContext context = null;
if (trustManagers == null) {
trustManagers = new javax.net.ssl.TrustManager[] { new _FakeX509TrustManager() };
}
try {
context = javax.net.ssl.SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
Log.e("allowAllSSL", e.toString());
} catch (KeyManagementException e) {
Log.e("allowAllSSL", e.toString());
}
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
The call at your method:
allowAllSSL();
HttpsTransportSE httpsTransport = new HttpsTransportSE(Server,443, URL, 1000);
Notes:
Which is constructed as: [https://Server:443/URL]
Well, there is an easier way to do this instead of modifying HttpsServiceConnectionSE. You can install a fake trust manager as described in http://groups.google.com/group/android-developers/browse_thread/thread/1ac2b851e07269ba/c7275f3b28ad8bbc?lnk=gst&q=certificate and then call allowAllSSL() before you do any SSL communication/call to ksoap2. It will register a new default HostnameVerifier and TrustManager. ksoap2, when doing its SSL communication, will use the default ones and it works like a charm.
You can also put some more effort into this, make it (much) safer, and install certificates in an application local trust manager, I guess. I was in a safe network and not afraid of man-in-the-middle-attacks so I just did the first.
I found it necessary to use KeepAliveHttpsTransportSE like this new KeepAliveHttpsTransportSE(host, port, file, timeout);
. The parameters go into a URL object, so e.g. to access a Jira installation it's something like new KeepAliveHttpsTransportSE("host.whatever", 443, "/rpc/soap/jirasoapservice-v2", 1000)
.
Sometimes its handy if you are new to the technology or the web service you like to use to play around with it in a J2SE environment instead of in the emulator or even on the device, but in the J2SE/ME ksoap2 library the (KeepAlive)HttpsTransportSE stuff is missing (I used ksoap2-j2se-full-2.1.2.jar). What you could do is to get the sources for the three classes HttpsTransportSE, KeepAliveHttpsTransportSE, and HttpsServiceConnectionSE from the Android spin-off ksoap2-android and put them into your J2SE project and use them. It worked for me and it became a productivity improvement to get the first steps right with an unknown and quite complex web service.
Works for me KSOAP + Web service WCF with eclipse
private static SoapObject getBody(final SoapSerializationEnvelope soapEnvelope) throws Exception {
if (soapEnvelope.bodyIn == null) {
throw new Exception("soapEnvelope.bodyIn=null");
}
else if (soapEnvelope.bodyIn.getClass() == SoapFault.class) {
throw new ExceptionLogic((SoapFault) soapEnvelope.bodyIn));
}
else {
return (SoapObject) soapEnvelope.bodyIn;
}
}
private static SoapSerializationEnvelope sendRequete(final SoapObject soapReq, final String classMappingName,
final Class<?> classMapping, final int timeOutSpecial) {
final SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.implicitTypes = true;
soapEnvelope.dotNet = true;
if (classMappingName != null) {
soapEnvelope.addMapping(NAMESPACE, classMappingName, classMapping);
}
soapEnvelope.setOutputSoapObject(soapReq);
try {
final HttpTransportSE httpTransport = new HttpTransportSE(Constante.urlWebService, timeOutSpecial);
httpTransport.debug = BuildConfig.DEBUG;
// Prod
if (Constante.urlWebService.startsWith("https://")) {
final List<HeaderProperty> headerList = new ArrayList<HeaderProperty>();
headerList.add(new HeaderProperty("Authorization", "Basic "
+ org.kobjects.base64.Base64.encode((Constante.CERTIFICAT_LOGIN + ":" + Constante.CERTIFICAT_MDP).getBytes())));
FakeX509TrustManager.allowAllSSL();
httpTransport.call(NAMESPACE + "/" + soapReq.getName(), soapEnvelope, headerList);
}
// Test
else {
httpTransport.call(NAMESPACE + "/" + soapReq.getName(), soapEnvelope);
}
return soapEnvelope;
}
catch (final Exception e) {
throw new Exception("Erreur : " + e.getMessage(), e);
}
}
private static class FakeX509TrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(final String hostname, final SSLSession session) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[] { new FakeX509TrustManager() };
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
}
catch (final NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (final KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
}
@Override
public void checkClientTrusted(final X509Certificate[] arg0, final String arg1) throws CertificateException {
}
@Override
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With