Tuesday, January 22, 2013

Android HttpsUrlConnection, self-signed certificate and hostname verifier

Today, an Android hint. Note for myself for future :)

So, what we have:

  1. Server
    REST (JAX-RS+akka), published to Tomcat. Tomcat serves app via HTTPS having a self-signed certificate (official tomcat docs).
    No CA, no chain - just a single certificate done in a really usual way.
    Tomcat is published on a box accessible only via IP, no DNS here.
    The actual server is https://46.4.224.49 just in case someone wants to take a look @cert.
  2. Client
    Android, trying to talk to the server above in JSON. Should use HTTPS for both traffic encryption and identity confirmation.
Generally, implementation seems pretty straightforward - get the public certificate, and make Android https facilities to trust it. 

I decided to use HttpsUrlConnection rather than HttpClient as the documentation suggest the former was only preferred for small operations in Android versions prior to 3. Trick is that Android is not shipped with the JKS keystore implementation which seems to belong to Oracle (Sun before).

After some googling I combined 3 articles to make a good working example:
  1. http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
    use first 2 steps, how to obtain public certificate off the server, pack it to the BKS keystore and some clues on how to setup BouncyCastle
    (hint - as of early 2013 we need version bcprov-jdk15on-146.jar, though newer is there)
    Added correct line to jre/lib/security/java.security to support another one provider, and dropped the jar into jre/lib/ext. This is only needed for keytool to understand new format.
  2. http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html
    Feed HttpsUrlConnection with the SslSocketFactory relying on that keystore. Keystore is put into the res/raw and loaded via context.getResources().openRawResource(int id)
And this is where the trouble started. The test code was producing error. After getting the certificate in the error has changed from 'Not trusted certificate' to 'Can not verify hostname'. Which is progress. 

The very major of the solutions on the Internet were just allowing all the hostname, and even deeper, accepting all the certificates -- which was inacceptable due to security reason.

The certificate had an IP address in CN field. After some research, it was clear that certificate is there, it is trusted, however hostname check fails. That's when DefaultHostnameVerifier was spotted (yes i know javadoc is old, but debugger shows this very class, which is hidden in library-no source). However, old javadoc says clearly that all the checks are declined!

Surely we can not have this check passed as nothing is allowed. 

The issue is resolved easily with setting conn.setHostnameVerifier(new BrowserCompatHostnameVerifier()) - this triggers HttpsUrlConnection to the same mode as the browser (passing *.domain CNs, and IP addresses, etc), which is perfectly OK for our case. 
 

Thursday, April 12, 2012

Site of the company I Work For

Happy to announce that we have finally gone public - http://www.logicify.com

Software service to your service :)

Thursday, December 22, 2011

MySQL failover talk

Hi! Today I had a talk at local IT user group regarding the MySQL fault-tolerance cluster. No original research neither anything specific - just a bit of experience which may help you setup the same easier ;).


Alternatively you can look at them locally here:


Wicket 1.4 modal window not hiding contents in hierarchy on close! :o

Looks like plenty of the posts are for myself not to forget about 'specific' issues found in the frameworks during the development. Well, wicket 1.4.(18?) this time.

The problem
So, we have a modal window. There is a form (call it form2) in this modal window. For the last, modal window itself is within a form (call it form1). Hierarchy looks like form1 <- modal window <- form2.

Both form1 and form2 have validators for their components, and both do have submitting components.

When the MW is shown and visible, everything works just fine according to the Nested form rules of the wicket. However, as you close the modal window by any means, the form2 will still be validated on the submit of form1! However, as it is _not_ visible on client (modal window is closed) we can not show to user what's wrong, they actually don't have access to the components failing.

Wicket modal windows do close by means of Ajax. E.g. when the actual DOM element is hidden by clicking cross, or outside modal window, or close button, wicket issues an ajax-call to server to notify it that window is no more visible.

This call is handled by WindowClose behavior, the subclass of AbstractAjaxDefaultBehavior.For greater detail you may take a look at it in the ModalWindow class. What this behavior does in the original implementation:
  1. Reset shown flag to false, so window will be opened again when next time show(target) is invoked.
  2. In case of the modal window showing pages, not components, do cleanup of the pagemap.
  3. Invoke WindowCloseListener instance (if any has been set to this ModalWindow).
That's it. It means that closing modal window does not hide the components inside it from the wicket hierarchy, and they are treated as visible however they are not really!

The solution
General solution for me was to hide content on the modal window close action.
2 approaches:
  1. Local.
    Just implement the windowClosedCallback, pass it to the instance of modal window, and in it, do hide your content on the close action.
    Bad things are that if you are developing a reusable component, you may want clients of this component to use windowClosedCallback for their own stuff.

  2. General.
    Subclass a ModalWindow. Say, MyModalWindow extends ModalWindow

  3. Override newWindowClosedBehavior() of it

  4. In this method, create your own ajax behavior doing same thing as wicket does, plus hide component if it is there. Pass it on to the return. Voila, we are done.
Now the components will be really hidden, and you won't be getting nested forms edgecase validation issues (or whatever else hiding there - I don't really know).

That's the code snippet:

//... inside our ModalWindow subclass, MyModalWindow
private class MyModalWindowClosedBehavior extends AbstractDefaultAjaxBehavior
implements
IWindowClosedBehavior {
private static final long serialVersionUID = 1L;

@Override
protected void respond(AjaxRequestTarget target) {
respondOnWindowClosed(target); // that'd be a call to the wicket's MW behavior.
if (getContent() != null) {
getContent().setVisible(false);
}
}

@Override
public CharSequence getCallbackScript() {
return super.getCallbackScript();
}
}

@Override
protected IWindowClosedBehavior newWindowClosedBehavior() {
return new PoseidonWindowClosedBehavior();
}

Tuesday, December 20, 2011

Inform user about Ajax failed (no network) in Wicket

The problem

It is well known to the internet. If you have an Ajax application you have to somehow inform user in case Ajax fails. And in the modern world Ajax may fail very easily - WiFi switched off, bad smartphone connection et al.

Also we have a Wicket which does hide all the Ajax work behind the scenes - you drop components to the page, they do communicate via Ajax.

The (possible) solution
At least we employed the one for us.

1. Create the localized message key in the property file. We are using XML properties, so it'd look like Ajax failed! Reload please

2. Create a function in one of the javascript files you always load. You may do it inline, but the function in the file makes it easier to change. My guess it should survive absence of the Wicket too.

AlertError here is the generic error function showing jQueryUI custom dialog.


function tryRegisterWicketAjaxOnFailure(message) {
if (!Wicket || !Wicket.Ajax) {
return;
}
Wicket.Ajax.registerFailureHandler(function() {
alertError(message, 0, 'center', true);
});
}


3. As our tryRegister takes a message parameter we need to pass it from the Wicket. So, in your BasePage.java or whatever code just add


add(new HeaderContributor(new IHeaderContributor() {
public void renderHead(IHeaderResponse response) {
response.renderOnDomReadyJavascript(
String.format("tryRegisterWicketAjaxOnFailure('%s')",
getString("ajaxCommunicationFailedMessage")));
}
}));
4. Just enjoy it. You may put 'refresh' button, or a spinner at this dialog which would constantly check if the network is back. It's up to your creativity and demand.