NOTE: I ended up dropping support for Android 2.2 entirely, due to other problems... this issue remains a mistery though.
I have been experiencing a SSL exception in Android 2.2 on a Huawei U8150.
java.io.IOException: Error parsing the certificates: Ok
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.nativeinit(Native Method)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.init(OpenSSLSocketImpl.java:125)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.<init>(OpenSSLSocketImpl.java:241)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImplWrapper.<init>(OpenSSLSocketImplWrapper.java:35)
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketFactoryImpl.createSocket(OpenSSLSocketFactoryImpl.java:83)
at org.jivesoftware.smack.tcp.XMPPTCPConnection.proceedTLSReceived(XMPPTCPConnection.java:739)
The code I'm using is fairly simple: https://github.com/kontalk/androidclient/blob/master/app/src/main/java/org/kontalk/client/KontalkConnection.java
The code above just sets a client certificate and a private key for authentication. The code works correctly on Android 2.3 and later.
I've inspected Apache Harmony code to understand what's hunder the hood, and it seems to fail when parsing the certificate chain: http://www.netmite.com/android/mydroid/dalvik/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
In function init()
:
ByteArrayOutputStream certificateOS = new ByteArrayOutputStream();
PEMWriter certificateWriter = new PEMWriter(new OutputStreamWriter(certificateOS));
for (int i = 0; i < certificates.length; i++) {
certificateWriter.writeObject(certificates[i]);
}
certificateWriter.close();
It just uses a PEMWriter
to create a concatenated chain in PEM format to pass it on to OpenSSL (via nativeinit
function).
The certificate generated by my app is genuine, tested with openssl x509
. I have to say it has a custom extension, but I repeat that any version newer than 2.2 just work.
I've uploaded the certificate here: http://paste.debian.net/209823/
UPDATE
I've been trying to reproduce on my desktop all the steps that Android does to reach the problematic point, that is (marked with CHECK the ones I've tried successfully):
- store certificate into keystore
- load certificate from keystore (in DER format) - CHECK
- convert certificate to PEM format - CHECK
- parse PEM certificate by OpenSSL - CHECK
For step 3 I'm using Bouncy Castle 1.49 with PEMWriter, just like Android does.
For step 4 I've created a small C utility linked against OpenSSL 0.9.8m (version installed in Android 2.2) that does the same function calls to initialize a SSL context and load a certificate from a file.
Now what I have left is:
- something wrong in keystore handling (I doubt that, although Android is known to have a crippled/modified version of Bouncy Castle)
- Android Java/Dalvik bug (unlikely, but still possible)
- exclude Android 2.2 completely (I'd rather not do it, especially because the issue could also be in other more recent versions that I haven't tried yet)
Code is posted here: https://gist.github.com/daniele-athome/47f458d64aefed6e6c11