Как программно установить сертификат CA на Android без взаимодействия с пользователем
Я пытаюсь установить сертификаты, не запрашивая пользователя. Я знаю, что это не хорошая практика, а то, что хочет PM.
Используя KeyChain.createInstallIntent()
, я могу заставить Android запустить диалог установки сертификата, вызвав startActivity
. Однако, когда я передаю намерение sendBroadcast
, ничего не происходит. Может быть, платформа не поддерживает это по соображениям безопасности?
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) {
Ответы
Ответ 1
Вы можете устанавливать сертификаты только при наличии системных привилегий. Отображение диалогового окна подтверждения является преднамеренным, так как доверенные сертификаты могут иметь серьезные последствия - Android может с радостью открывать фишинговые сайты без предупреждения и т.д. Тем не менее, диалог в ICS/JB довольно плох - он не говорит вам, что сертификат, который вы устанавливаете, и кто его выпустил, просто это сертификат CA, который является очевидным.
Итак, используйте общедоступный API KeyChain
и используйте startActivity()
, чтобы получить диалоговое окно подтверждения или предустановить устройства, прежде чем обращаться с ними для пользователей.
Обновление. В Android 4.4 у DevicePolicyManager
есть скрытый API (installCaCert
), который позволяет устанавливать сертификаты молча. Вам нужно разрешение MANAGE_CA_CERTIFICATES
, которое signature|system
, поэтому все еще не выполнимо для установленных пользователем приложений.
Ответ 2
Используя KeyChain.createInstallIntent(), я могу заставить Android запустить диалог установки сертификата, вызвав startActivity. Однако, когда я передаю намерение sendBroadcast, ничего не происходит.
Мало, если любые объекты Intent
, которые вы передадите в startActivity()
, будут работать с sendBroadcast()
. Они являются независимыми каналами шины квази-сообщений, которая является системой Intent
.
Ответ 3
Для разработчиков, не относящихся к системным приложениям, простой ответ заключается в том, что он не может быть выполнен без взаимодействия с пользователем.
Для разработчиков системного приложения я нашел следующее решение: NB вы должны запустить приложение с идентификатором пользователя системы и подписать приложение с помощью системного ключа или служба отклонит ваши попытки установить сертификат.
Шаг 1 - создать интерфейс
Создайте новый пакет в своем проекте: android.security, затем скопируйте IKeyChainService.aidl в этот пакет.
Шаг 2 - привязка к сервису и установке сертификата
В действии приведен пример установки сертификата CA:
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();
}
}
Я надеюсь, что это кому-то поможет - мне потребовалось много времени, чтобы решить эту проблему:)
Ответ 4
Если у вас есть привилегия root, вы можете скопировать файл сертификатов в /data/misc/user/0/cacerts-added/
Ответ 5
Только системное пользовательское приложение может тихо установить сертификат ЦС. Тем не менее, на Lollipop компания Google внедрила API-интерфейс управления сертификатами через DevicePolicyManager, но вы должны либо быть владельцем профиля Android-for-Work, либо владельцем устройства.
Ответ 6
Основываясь на ответе @ospider, мне удалось успешно установить сертификат следующим образом:
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
Я получил имя скопированного файла (807e3b02.0
), установив вручную сертификат, который я хотел автоматизировать, и увидев, как Android его сохранил (с помощью adb shell ls -l/data/misc/user/0/cacerts-added/
)
Надеюсь, это поможет.
С уважением.
Ответ 7
Основываясь на этом и всех связанных ресурсах (отличных!), Я пробовал 2 подхода для установки сертификата CA в систему Android CA Store. Я работаю над Android Pie.
- API Keystore, использующие setCertificateEntry (я запускаю системное приложение), но это молча завершается ошибкой... мой сертификат не виден в учетных данных доверенных (или пользователей).
- Создание моего приложения владельцем устройства, а затем использование DevicePolicyManager api installCaCert() Это работает, но показывает мой сертификат в разделе "Пользователь" вместо "Системные сертификаты".
Есть идеи, что я могу упустить?