简体   繁体   English

如何在没有用户交互的情况下以编程方式在 Android 上安装 CA 证书

[英]how to install CA certificate programmatically on Android without user interaction

I'm trying to install certificates without prompting the user.我试图在不提示用户的情况下安装证书。 I know this is not good practice, but that's what PM wants.我知道这不是好的做法,但这正是 PM 想要的。

Using KeyChain.createInstallIntent() , I can get Android to launch the certificate installation dialog by calling startActivity .使用KeyChain.createInstallIntent() ,我可以让 Android 通过调用startActivity来启动证书安装对话框。 However, when I pass the intent to sendBroadcast , nothing happens.但是,当我将意图传递给sendBroadcast时,什么也没有发生。 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.显示一个确认对话框是有意的,因为信任证书会产生严重的后果——Android 可以在没有警告的情况下愉快地打开钓鱼网站,等等。也就是说,ICS/JB 中的对话框非常糟糕——它没有告诉你什么您正在安装的证书以及颁发者,只是它是一个 CA 证书,这很明显。

So, either use the public KeyChain API and use startActivity() to get the confirmation dialog, or pre-provision devices before handling them to users.因此,要么使用公共KeyChain API 并使用startActivity()获取确认对话框,要么在将设备处理给用户之前预先配置设备。

Update: In Android 4.4, DevicePolicyManager has a hidden API ( installCaCert ) that allows you to install certificates silently.更新:在 Android 4.4 中, DevicePolicyManager有一个隐藏的 API ( installCaCert ),允许您静默安装证书。 You need the MANAGE_CA_CERTIFICATES permission, which is signature|system , so still not doable for user-installed apps.您需要MANAGE_CA_CERTIFICATES权限,即signature|system ,因此对于用户安装的应用程序仍然不可行。

Using KeyChain.createInstallIntent() , I can get Android to launch the certificate installation dialog by calling startActivity.使用KeyChain.createInstallIntent() ,我可以让 Android 通过调用 startActivity 来启动证书安装对话框。 However, when I pass the intent to sendBroadcast, nothing happens.但是,当我将意图传递给 sendBroadcast 时,什么也没有发生。

Few if any Intent objects that you would pass to startActivity() would work with sendBroadcast() .您将传递给startActivity()Intent对象很少能与sendBroadcast()一起使用。 They are independent channels of the quasi-message bus that is the Intent system.它们是作为Intent系统的准消息总线的独立通道。

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.对于系统应用程序开发人员,我找到了以下解决方案,注意您必须使用系统用户 ID 运行应用程序并使用系统密钥签署应用程序,否则服务将拒绝您安装证书的尝试。

Step 1 - Create interface第 1 步-创建界面

Create a new package in your project: android.security , then copy IKeyChainService.aidl into this package.在您的项目中创建一个新包: android.security ,然后将IKeyChainService.aidl复制到该包中。

Step 2 - Bind to service and install certificate第 2 步-绑定到服务并安装证书

The Activity gives an example of how to install a CA certificate:该活动给出了如何安装 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();
    }
}

I hope this helps someone - it took me a long time to work it out:)我希望这对某人有帮助——我花了很长时间才弄明白:)

This thread is a bit dated already, nevertheless since I stumbled upon the same issue and couldn't find any "out of the box" solution for Android O or later, I thought I'd share what I came up with and which worked well for me when trying to install Certificates (CA and others) to the Android Trusted credentials "User" store:这个线程已经有点过时了,但是由于我偶然发现了同样的问题并且找不到任何适用于 Android O 或更高版本的“开箱即用”解决方案,我想我会分享我的想法并且效果很好尝试将证书(CA 和其他证书)安装到 Android 可信凭据“用户”存储时对我来说:

// Supply context, e.g. from "Context context = getApplicationContext();"
// String fileName points to the file holding the certificate to be installed. pem/der/pfx tested.
RandomAccessFile file = new RandomAccessFile(fileName, "r");
byte[] certificateBytes = new byte[(int)file.length()];
file.read(certificateBytes);

Class<?> keyChainConnectionClass = Objects.requireNonNull(context.getClassLoader()).loadClass("android.security.KeyChain$KeyChainConnection");
Class<?> iKeyChainServiceClass  = Objects.requireNonNull(context.getClassLoader()).loadClass("android.security.IKeyChainService");

Method keyChainBindMethod = KeyChain.class.getMethod("bind", Context.class);
Method keyChainConnectionGetServiceMethod = keyChainConnectionClass.getMethod("getService");
Object keyChainConnectionObject = keyChainBindMethod.invoke(null, context);
Object iKeyChainServiceObject = keyChainConnectionGetServiceMethod.invoke(keyChainConnectionObject);

Method installCaCertificate = iKeyChainServiceClass.getDeclaredMethod("installCaCertificate", byte[].class);
installCaCertificate.invoke(iKeyChainServiceObject, certificateBytes);

Note that if you want to silently install a certificate this way, your app needs to be a system app, ie it needs to have请注意,如果您想以这种方式静默安装证书,您的应用程序需要是系统应用程序,即它需要有

android:sharedUserId="android.uid.system"

declared in it's manifest.在清单中声明。

Cheers!干杯!

If you have root privilege, you could copy the certs file to /data/misc/user/0/cacerts-added/如果您有 root 权限,您可以将证书文件复制到/data/misc/user/0/cacerts-added/

Based on the @ospider 's answer, i managed to succesfully install the cert like this way:根据@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

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/ )我通过手动安装我想要自动化的证书并查看 Android 如何保存它(使用adb shell ls -l /data/misc/user/0/cacerts-added/ )获得了复制文件的名称( 807e3b02.0


Hope this help.希望这有帮助。

Regards.问候。

Only a system user application can silently install a CA certificate.只有系统用户应用程序可以静默安装 CA 证书。 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.尽管在 Lollipop 上,Google 通过 DevicePolicyManager 引入了静默证书管理 API,但您必须是 Android-for-Work 配置文件所有者或设备所有者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在Android中以编程方式安装CA证书(用于EAP WiFi配置)? - How to programmatically install a CA Certificate (for EAP WiFi configuration) in Android? 如何在 Android 设备上安装可信 CA 证书? - How to install trusted CA certificate on Android device? 如何在没有用户交互的情况下以编程方式卸载Android应用程序? - How to uninstall an Android app programmatically without user interaction? Android将我自制的CA证书识别为用户证书,并且未正确安装 - Android recognizes my self made CA certificate as a user certificate, and does not install it properly 以编程方式启用USB网络共享Android,无需用户交互 - Enable USB Tethering Android Programmatically without user Interaction 以编程方式在Android中无需用户交互即可连接蓝牙设备 - Connect Bluetooth devices without user interaction in Android programmatically 无需用户交互,以编程方式将 android 设备连接到 wifi - Connect android device to wifi programmatically without user interaction 无法在 Android Oreo 上安装 CA 证书 - Unable to install CA certificate on Android Oreo 无法在 Android 11 上安装 CA 证书 - Can't install CA certificate on Android 11 android:如何接受CA证书 - android: how to accept CA certificate
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM