如何在Android上以编程方式安装CA证书而无需用户交互

如何在Android上以编程方式安装CA证书而无需用户交互,android,android-intent,x509certificate,android-wifi,Android,Android Intent,X509certificate,Android Wifi,我正在尝试在不提示用户的情况下安装证书。我知道这不是好的做法,但这正是首相想要的 使用,我可以通过调用startActivity让Android启动证书安装对话框。但是,当我将意图传递给sendBroadcast时,什么也没有发生。可能是因为安全原因平台不支持这一点 String CERT_FILE = Environment.getExternalStorageDirectory() + "/test/IAT.crt"; Intent intent = KeyChain.createInsta

我正在尝试在不提示用户的情况下安装证书。我知道这不是好的做法,但这正是首相想要的

使用,我可以通过调用
startActivity
让Android启动证书安装对话框。但是,当我将意图传递给
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) {
使用,我可以通过调用startActivity让Android启动证书安装对话框。然而,当我传递发送广播的意图时,什么也没有发生


传递给
startActivity()
Intent
对象很少能与
sendBroadcast()
一起使用。它们是作为
Intent
系统的准消息总线的独立通道。

只有在具有系统权限的情况下,才能以静默方式安装证书。显示确认对话框是有意的,因为信任证书可能会产生严重后果--Android可以在没有警告的情况下愉快地打开钓鱼网站,等等。也就是说,ICS/JB中的对话框非常糟糕--它不会告诉你正在安装什么证书以及谁颁发了证书,只是它是CA证书,这是很明显的

因此,可以使用public
KeyChain
API并使用
startActivity()
来获取确认对话框,或者在将设备处理给用户之前预先配置设备


更新:在Android 4.4中,
DevicePolicyManager
有一个隐藏的API(
installCaCert
),允许您以静默方式安装证书。您需要
MANAGE\u CA\u CERTIFICATES
权限,即
signature\system
,因此对于用户安装的应用程序仍然不可行

对于非系统应用程序开发人员-简单的答案是,没有用户交互就无法完成

对于系统应用程序开发人员,我发现了以下解决方案,请注意,您必须使用系统用户id运行应用程序,并使用系统密钥对应用程序进行签名,否则服务将拒绝您安装证书的尝试

步骤1-创建界面

在项目中创建一个新包:android.security,然后复制到此包中

步骤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();
    }
}

我希望这对某人有所帮助—我花了很长时间才解决:)

只有系统用户应用程序才能静默地安装CA证书。不过在棒棒糖上,谷歌通过DevicePolicyManager引入了静默证书管理API,但你必须是工作配置文件所有者或设备所有者的Android。

如果你有root权限,你可以根据用户的回答将证书文件复制到
/data/misc/user/0/cacerts added/
,我成功地通过以下方式安装了证书:

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


希望这有帮助


问候。

这个帖子已经有点过时了,但是因为我偶然发现了同样的问题,并且找不到任何适用于Android O或更高版本的“开箱即用”解决方案,所以我想分享一下我的想法,在尝试将证书(CA和其他证书)安装到Android可信凭据“用户”商店时,它对我来说非常有效:

在舱单上声明


干杯

没有接收器监听该
意图
——只是系统中的一个活动,并且有充分的理由——允许任何恶意随机应用程序以静默方式安装根CA将是一个可怕的安全漏洞。它还迫使您使用数字PIN码或锁屏解锁模式来保护您的设备,这有点烦人,但在用户安全方面是可以理解的。我猜他们希望确保安装证书的人也是设备的所有者。但是,如果没有密码/PIN,您可以将其设置为任何您想要的。然后,密码/PIN用于派生主密钥以加密私钥。证书也会使用它进行加密,这并不是绝对必要的。
installCaCert
已在中可见,并且设备管理员显然可以使用。@RupertRawnsley AFAIK,它仅在您的设备管理员也是设备所有者(“超级管理员”)的情况下才有效。设备所有者是我的新所有者。通过NFC访问系统-可能出现什么问题?;-)在应用程序共享UID之前,这是不可能的<代码>最终字符串actualPackage=getPackageManager().getNameForUid(getCallingUid());如果(!expectedPackage.equals(actualPackage)){抛出新的IllegalStateException(actualPackage);}验证调用方。你能解释一下在没有共享UID的情况下你是如何管理的吗?@Pankaj-我们在系统应用程序的Android清单的sharedUserId属性中使用了sysytem UID,也许这就是为什么我不需要像你那样通过编程来实现它的原因。
E AndroidRuntime:java.lang.NoSuchMethodError:No interface method installCaCertificate([B]Ljava/lang/String;在类Landroid/se-security/IKeyChainService;或其超类中(在/system/framework/framework.jar中出现“android.security.IKeyChainService”声明)
通过查看
DevicePolicyManager
IKeyChainService
通过
KeychainConnection=KeyChain.bind>获得(context);keychainConnection.getService()
。对于系统应用程序开发人员和不想与
DevicePolicyManager
为伍的人,我们可以通过反射获得
IKeyChainService
。无论如何,我尝试并成功安装了第三方cacert,但安全设置屏幕显示“tr”
// 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);
android:sharedUserId="android.uid.system"