A number of Android users, myself included, have posted forum complaints that they cannot read or send mail, or do other internet activities such as XMPP/Jabber, that involve SSL/TLS to a server whose X.509 host certificate is signed by a trust vendor or other origin of trust that is not in Android's certificate storage area. Here is the procedure to add your preferred X.509 root certificate, or to delete a certificate which you object to on political grounds.
If a reader has gotten to this page he/she probably is fully aware of the issues, but they should be repeated: you should only believe in a root certificate if you have obtained it from a trusted source, e.g. from the hand of a trusted system administrator or over an internal LAN on which the enemy is not likely to be operating, and if you trust the person who can wield its secret key to only certify hosts or persons whose hat color matches yours.
These instructions are for Android 1.5
Cupcake, but are likely
to be fairly stable on back or future versions.
To add (or delete) a root certificate:
Make sure that the Java package on your development machine includes
keytool; look for
/usr/bin/keytool. My distro (OpenSuSE 11.1)
provides java-1_6_0-sun-1.6.0.u13 which provides this program symlinked through
Make sure OpenSSL is installed. Look for the command
Obtain and install the Android SDK (software development kit). It is not necessary to have a functioning Eclipse setup for this, but you will be using adb.
Check out a copy of the sources. See the
Get Source page for
how to do this. For these examples full path names in the source code
are shown beginning with
~/android; modify to match where you put the sources. In many
cases files and directories can be adjusted and may possibly change in future
versions of Android.
Change directory to
Download the BouncyCastle provider JAR file from http://www.bouncycastle.org/latest_releases.html. This JAR file is not included with Android, though the contents are in the distro; nonetheless I was not able to make the procedure work using the unpacked native source code. I planted the JAR file in the current directory, purely for convenience.
In this directory is a subdirectory called
contains authority root certificates. You need to add your own root
certificate to the directory, or delete the politically poisonous one. The
certificates are in PEM
format: view the file and look for
-----BEGIN CERTIFICATE-----, a lot of
base64 encoded stuff, down to
-----END CERTIFICATE-----. A text
description may come before or after the boundary lines; it will be ignored.
The certificate files are named after their subject hashes. The naming is
unimportant, but you should import only one copy of each certificate; the
typical subject hash links used in OpenSSL will confuse the script and would
have to be excluded one way or another, if present.
To display a certificate:
openssl x509 -in cacerts/e4a7f639.0 -noout -text
To determine the subject hash (append .0 in the likely case of no duplicates):
openssl x509 -in example-com.pem -noout -subject_hash
You will find
./cacerts.bks which is the pre-built
certificate store. It will end up as
/system/etc/security/cacerts.bks and is bit-for-bit identical to
this file (before hacking). The scripts assume that this file may be removed
at the start, being created anew by keytool.
You will also find
./certimport.sh which is a wrapper that
calls keytool. However, the needed JAR
file is not present and some of the other pathnames and options do not seem to
be functional. Likely this is the script that one of the real
developers used, at some time in the past, to create
./cacerts.bks. We're going to need to update this script.
The attached file
./certimportJ cacerts.bks cacerts
The key item in this script is that it calls keytool with the following
parameters for each certificate. Bash syntax is used to pipe the output of
openssl into keytool. Each parameter is shown on a separate line to make it
more readable. In the filenames I am assuming that the script is executed in
-alias cert$((COUNTER++)) \
-file <(openssl x509 -in $cert -outform DER) \
-keystore ./cacerts.bks \
-storetype BKS \
-storepass SillyNoKeysHere \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
Notes on the parameters:
$certrepresents the filename of the PEM format certificate.
Now you have a keystore file that includes your root certificate. Here is how to get it onto your phone.
adb push ./cacerts.bks /sdcard/cacerts.bks
/systempartition in read-write mode. It should be sufficient to do (on the host system)
, but I'm getting a permission problem, so here is how to do it by hand.
cat /proc/mounts; look for
(in the unlikely event of future alterations to the partitioning, adjust the following mount command to match what's actually in
/dev/block/mtdblock3 /system yaffs2 ro 0 0
mount -o remount,rw -t yaffs2 /dev/block/mtdblock3 /system
cat /proc/mountsagain and make sure
/systemnow has the
ls -l; you should see
cacerts.bks, mode 644, owner root:root. If you're not too familiar with UNIX, the mode code will be shown as -rw-r--r--.
mv cacerts.bks cacerts.bks.orig; you're saving the original keystore in case of problems. Of course, skip this step if you've already saved the original version.
cp(copy) command? Use whichever of these command lines is going to work for you, to bring in the new keystore from where you left it on the SD card. Note the dot representing the current directory, as the destination of
cp /sdcard/cacerts.bks .
dd if=/sdcard/cacerts.bks of=./cacerts.bks
chmod 644 cacerts.bks; the file's mode comes out wrong, most likely because the shell's umask is not set, and you need to fix it. Afterward do
and compare to the result seen previously.
/systemread-only by repeating the above remount steps interchanging
rw, but you're just going to reboot your phone, so don't bother.
exittwice) and disconnect the USB cable.
Now you should be able to connect to your SSL/TLS servers without being hassled about certificates issued by an untrusted origin of trust.
This issue has been posted on Android's bug system as issue 3237. It has been merged into an earlier similar thread, Android bug 1016.