blob: b14822b30d6f35ca967f6aa8dffde74cab98d454 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.certinstaller;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Base64;
import android.util.Base64InputStream;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.List;
/**
* Delegated certificate installer app that responds to specific intents and executes various DPM
* certificate manipulation APIs. The following APIs are exercised:
* {@link DevicePolicyManager#installCaCert},
* {@link DevicePolicyManager#uninstallCaCert},
* {@link DevicePolicyManager#hasCaCertInstalled},
* {@link DevicePolicyManager#getInstalledCaCerts},
* {@link DevicePolicyManager#installKeyPair}.
*/
public class CertInstallerActivity extends Activity {
private static final String TAG = "DelegatedCertInstaller";
// exercises {@link DevicePolicyManager#installCaCert} and
// {@link DevicePolicyManager#hasCaCertInstalled},
private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert";
// exercises {@link DevicePolicyManager#uninstallCaCert} and
// {@link DevicePolicyManager#hasCaCertInstalled},
private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert";
// exercises {@link DevicePolicyManager#getInstalledCaCerts},
private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert";
// exercises {@link DevicePolicyManager#installKeyPair},
private static final String ACTION_INSTALL_KEYPAIR =
"com.android.cts.certinstaller.install_keypair";
private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
private static final String EXTRA_CERT_DATA = "extra_cert_data";
private static final String EXTRA_KEY_DATA = "extra_key_data";
private static final String EXTRA_KEY_ALIAS = "extra_key_alias";
private static final String EXTRA_RESULT_VALUE = "extra_result_value";
private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent == null) {
finish();
return;
}
String action = intent.getAction();
DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
Context.DEVICE_POLICY_SERVICE);
byte[] certBuffer;
if (ACTION_INSTALL_CERT.equals(action)) {
try {
certBuffer = intent.getByteArrayExtra(EXTRA_CERT_DATA);
// Verify cert is not currently installed.
if (dpm.hasCaCertInstalled(null, certBuffer)) {
throw new RuntimeException("Cert already on device?");
}
if (!dpm.installCaCert(null, certBuffer)) {
throw new RuntimeException("installCaCert returned false.");
}
if (!dpm.hasCaCertInstalled(null, certBuffer)) {
throw new RuntimeException("Cannot find cert after installation.");
}
sendResult(true, null);
} catch (Exception e) {
Log.e(TAG, "Exception raised duing ACTION_INSTALL_CERT", e);
sendResult(false, e);
}
} else if (ACTION_REMOVE_CERT.equals(action)) {
try {
certBuffer = intent.getByteArrayExtra(EXTRA_CERT_DATA);
if (!dpm.hasCaCertInstalled(null, certBuffer)) {
throw new RuntimeException("Trying to uninstall a non-existent cert.");
}
dpm.uninstallCaCert(null, certBuffer);
sendResult(!dpm.hasCaCertInstalled(null, certBuffer), null);
} catch (Exception e) {
Log.e(TAG, "Exception raised duing ACTION_REMOVE_CERT", e);
sendResult(false, e);
}
} else if (ACTION_VERIFY_CERT.equals(action)) {
try {
certBuffer = intent.getByteArrayExtra(EXTRA_CERT_DATA);
sendResult(containsCertificate(dpm.getInstalledCaCerts(null), certBuffer), null);
} catch (Exception e) {
Log.e(TAG, "Exception raised duing ACTION_VERIFY_CERT", e);
sendResult(false, e);
}
} else if (ACTION_INSTALL_KEYPAIR.equals(action)) {
String alias = intent.getStringExtra(EXTRA_KEY_ALIAS);
String key = intent.getStringExtra(EXTRA_KEY_DATA);
String cert = intent.getStringExtra(EXTRA_CERT_DATA);
try {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
Base64.decode(key, Base64.DEFAULT));
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privatekey = kf.generatePrivate(keySpec);
Certificate certificate = CertificateFactory.getInstance("X.509")
.generateCertificate(
new Base64InputStream(new ByteArrayInputStream(cert.getBytes()),
Base64.DEFAULT));
// Unfortunately there is no programmatically way to check if the given private key
// is indeed in the key store as a unprivileged app. So we just rely on
// installKeyPair() returning true as the success criteria of this test. Separate
// CTS keychain tests will make sure the API's behaviour is correct.
// Note: installKeyPair() will silently return false if there is no lockscreen
// password, however the test setup should have set one already.
sendResult(dpm.installKeyPair(null, privatekey, certificate, alias), null);
} catch (Exception e) {
Log.e(TAG, "Exception raised duing ACTION_INSTALL_KEYPAIR", e);
sendResult(false, e);
}
}
finish();
}
private void sendResult(boolean succeed, Exception e) {
Intent intent = new Intent();
intent.setAction(ACTION_CERT_OPERATION_DONE);
intent.putExtra(EXTRA_RESULT_VALUE, succeed);
if (e != null) {
intent.putExtra(EXTRA_RESULT_EXCEPTION, e);
}
sendBroadcast(intent);
}
private boolean containsCertificate(List<byte[]> certificates, byte[] toMatch)
throws CertificateException {
Certificate certificateToMatch = readCertificate(toMatch);
for (byte[] certBuffer : certificates) {
Certificate cert = readCertificate(certBuffer);
if (certificateToMatch.equals(cert)) {
return true;
}
}
return false;
}
private Certificate readCertificate(byte[] certBuffer) throws CertificateException {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return certFactory.generateCertificate(new ByteArrayInputStream(certBuffer));
}
}