I'm trying to install certificates without prompting the user. I know this is not good practice, but that's what PM wants.
Using KeyChain.createInstallIntent()
, I can get Android to launch the certificate installation dialog by calling startActivity
. However, when I pass the intent to sendBroadcast
, nothing happens. Maybe the platform doesn't support this for security reasons?
String CERT_FILE = Environment.getExternalStorageDirectory() + "/test/IAT.crt";
Intent intent = KeyChain.createInstallIntent();
try {
FileInputStream certIs = new FileInputStream(CERT_FILE);
byte [] cert = new byte[(int)certFile.length()];
certIs.read(cert);
X509Certificate x509 = X509Certificate.getInstance(cert);
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, x509.getEncoded());
intent.putExtra(KeyChain.EXTRA_NAME, "IAT Cert");
EapActivity.this.startActivityForResult(intent, 0); // this works but shows UI
EapActivity.this.sendBroadcast(intent); // this doesn't install cert
} catch (IOException e) {
You can only install certificates silently if you have system privileges. Showing up a confirmation dialog is intentional, since trusting certificates can have serious consequences -- Android could happily open phishing sites without a warning, etc. That said, the dialog in ICS/JB is pretty bad -- it doesn't tell you what certificate you are installing and who issued it, just that it's a CA certificate, which is kind of obvious.
So, either use the public KeyChain
API and use startActivity()
to get the confirmation dialog, or pre-provision devices before handling them to users.
Update: In Android 4.4, DevicePolicyManager
has a hidden API (installCaCert
) that allows you to install certificates silently. You need the MANAGE_CA_CERTIFICATES
permission, which is signature|system
, so still not doable for user-installed apps.
Using KeyChain.createInstallIntent(), I can get Android to launch the certificate installation dialog by calling startActivity. However, when I pass the intent to sendBroadcast, nothing happens.
Few if any Intent
objects that you would pass to startActivity()
would work with sendBroadcast()
. They are independent channels of the quasi-message bus that is the Intent
system.
For non-system app developers - the simple answer is it can not be done without user interaction.
For System App developers, I found the following solution, NB you must run the app with the system user id and sign the app with the system key or the service will reject your attempts to install the certificate.
Step 1 - Create interface
Create a new package in your project: android.security, then copy IKeyChainService.aidl into this package.
Step 2 - Bind to service and install certificate
The Activity gives an example of how to install a CA certificate:
public class KeyChainTest extends Activity {
private final Object mServiceLock = new Object();
private IKeyChainService mService;
private boolean mIsBoundService =false;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override public void onServiceConnected(ComponentName name,
IBinder service) {
synchronized (mServiceLock) {
mService = IKeyChainService.Stub.asInterface(service);
mServiceLock.notifyAll();
try {
byte[] result = YOUR_CA_CERT_AS_BYTE_ARRAY
//The next line actually installs the certificate
mService.installCaCertificate(result);
} catch (Exception e) {
//EXception handling goes here
}
}
}
@Override public void onServiceDisconnected(ComponentName name) {
synchronized (mServiceLock) {
mService = null;
}
}
};
private void bindService() {
mIsBoundService = bindService(new Intent(IKeyChainService.class.getName()),
mServiceConnection,
Context.BIND_AUTO_CREATE);
}
private void unbindServices() {
if (mIsBoundService) {
unbindService(mServiceConnection);
mIsBoundService = false;
}
}
@Override public void onDestroy () {
unbindServices();
}
@Override
protected void onStart() {
super.onStart();
// Bind to KeyChainService
bindService();
}
}
I hope this helps someone - it took me a long time to work it out :)
If you have root privilege, you could copy the certs file to /data/misc/user/0/cacerts-added/
Only a system user application can silently install a CA certificate. On Lollipop though, Google introduced silent certificate management API through DevicePolicyManager, but you would either have to be Android-for-Work profile owner or device owner.
Based on the @ospider's answer, i managed to succesfully install the cert like this way:
adb shell mkdir -p /data/misc/user/0/cacerts-added
adb push certificate.cer /data/misc/user/0/cacerts-added/807e3b02.0
# Maybe these two lines are not strictly necessary...
adb shell chmod 644 /data/misc/user/0/cacerts-added/807e3b02.0
adb shell chown system:system /data/misc/user/0/cacerts-added/807e3b02.0
I got the name of the copied file (807e3b02.0
) by installing manually the cert i wanted to automate and seeing how Android saved it (whith adb shell ls -l /data/misc/user/0/cacerts-added/
)
Hope this help.
Regards.