views:

160

answers:

5

I'm using HttpClient on Android to connect to https://someUrl.com/somePath. The problem is that the site's certificate is for *.someUrl.com, not someUrl.com, so I get an SSLException. Lame on the part of the site, yes, but unless I can get it fixed, I'm stuck. Is there a way I can get HttpClient to relax and accept the certificate?

A: 

http://stackoverflow.com/questions/2012497/accepting-a-certificate-for-https-on-android

Nate Zaugg
HttpClient docs are pretty useless on this. They all refer to HttpClient#getHostConfiguration which is not present in the Android HttpClient (see http://developer.android.com/reference/org/apache/http/client/HttpClient.html).
noah
The accepted answer on the previous link points to the Apache Http Client 3 documentation. It looks like Android is based on version 4: http://hc.apache.org/httpcomponents-client-4.0.1/index.html
Bruno
A: 

If it wants *.someUrl.com, then it seems like you could just give it www.someUrl.com/somePath instead of someUrl.com/somePath.

Matt
Nope. www.someUrl.com redirects to someUrl.com
noah
I suppose that's useless, then. Do you really need https? I would guess so, but that would make life a lot easier.
Matt
+1  A: 

The BouncyCastle on Android is too old and it doesn't recognize wildcard certificate.

You can write your own X509TrustManager to check for wildcard.

or you can disable certificate check altogether if you can accept the risk. See this question,

http://stackoverflow.com/questions/1217141/self-signed-ssl-acceptance-android

ZZ Coder
Actually ZZ, if the OP is correct about the name of the site and the structure of the certificate, then the error message is correct: **.someUrl.com* is supposed to match www.someUrl.com and anything.someUrl.com, but *not* someUrl.com or this.that.someUrl.com.
GregS
A: 

This is my (edited) solution:

class MyVerifier extends AbstractVerifier {

    private final X509HostnameVerifier delegate;

    public MyVerifier(final X509HostnameVerifier delegate) {
        this.delegate = delegate;
    }

    @Override
    public void verify(String host, String[] cns, String[] subjectAlts)
                throws SSLException {
        boolean ok = false;
        try {
            delegate.verify(host, cns, subjectAlts);
        } catch (SSLException e) {
            for (String cn : cns) {
                if (cn.startsWith("*.")) {
                    try {
                          delegate.verify(host, new String[] { 
                                cn.substring(2) }, subjectAlts);
                          ok = true;
                    } catch (Exception e1) { }
                }
            }
            if(!ok) throw e;
        }
    }
}


public DefaultHttpClient getTolerantClient() {
    DefaultHttpClient client = new DefaultHttpClient();
    SSLSocketFactory sslSocketFactory = (SSLSocketFactory) client
            .getConnectionManager().getSchemeRegistry().getScheme("https")
            .getSocketFactory();
    final X509HostnameVerifier delegate = sslSocketFactory.getHostnameVerifier();
    if(!(delegate instanceof MyVerifier)) {
        sslSocketFactory.setHostnameVerifier(new MyVerifier(delegate));
    }
    return client;
}

It has the advantage of not changing the default behavior unless there is a wildcard domain, and in that case it revalidates as though the 2 part domain (e.g., someUrl.com) were part of the certificate, otherwise the original exception is rethrown. That means truly invalid certs will still fail.

noah
I tried using the above, but sometimes get stack overflow exceptions - the verify() method never stops recursing. Out of a few hundred thousand installs, it happens about 80 times a week. Have you seen any issues with this?