Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f37c8cb4 authored by Eran Messeri's avatar Eran Messeri Committed by Rubin Xu
Browse files

[DO NOT MERGE] Rollup Cert-related changes from R

This change includes the following commits from internal R branch
which are related to certificate management:

0206e76f46 CredentialStorage: Install keys using KeyChain
09ceea53d9 Added functionality to select type of certificate to be installed from the Settings app
3acf3f5433 WiFi certificates installable from Wifi sub-preference
8439fd15f7 Fix strings for certificate installation in Settings

Bug: 161347472
Test: builds & manual testing
Change-Id: Ia59dc4780254fab4f34c2f61b25f3b4e56ed7b77
parent 268371c4
Loading
Loading
Loading
Loading
+20 −4
Original line number Diff line number Diff line
@@ -5887,10 +5887,8 @@
    <!-- Title of preference group for credential storage settings [CHAR LIMIT=30] -->
    <string name="credentials_title">Credential storage</string>
    <!-- Title of preference to install certificates from SD card [CHAR LIMIT=30] -->
    <string name="credentials_install" product="nosdcard">Install from storage</string>
    <!-- Title of preference to install certificates from SD card [CHAR LIMIT=30] -->
    <string name="credentials_install" product="default">Install from SD card</string>
    <!-- Title of preference to install certificates [CHAR LIMIT=30] -->
    <string name="credentials_install">Install a certificate</string>
    <!-- Summary of preference to install certificates from SD card [CHAR LIMIT=NONE] -->
    <string name="credentials_install_summary" product="nosdcard">Install certificates from storage</string>
    <!-- Summary of preference to install certificates from SD card [CHAR LIMIT=NONE] -->
@@ -5929,6 +5927,22 @@
    <string name="credentials_not_erased">Credential storage couldn\u2019t be erased.</string>
    <!-- Title of Usage Access preference item [CHAR LIMIT=30] -->
    <string name="usage_access_title">Apps with usage access</string>
    <!-- Title of CA certificate [CHAR LIMIT=30] -->
    <string name="ca_certificate">CA certificate</string>
    <!-- Title of User certificate [CHAR LIMIT=30] -->
    <string name="user_certificate">VPN &amp; app user certificate</string>
    <!-- Title of Wi-Fi certificate [CHAR LIMIT=30] -->
    <string name="wifi_certificate">Wi\u2011Fi certificate</string>
    <!-- Title of warning shown to the user before they can install a CA certificate [CHAR LIMIT=NONE] -->
    <string name="ca_certificate_warning_title">Your data won\u2019t be private</string>
    <!-- Description of warning shown to the user before they can install a CA certificate [CHAR LIMIT=NONE] -->
    <string name="ca_certificate_warning_description">CA certificates are used by websites, apps, and VPNs for encryption. Only install CA certificates from organizations you trust. \n\n If you install a CA certificate, the certificate owner could access your data, such as passwords or credit card details, from websites you visit or apps you use – even if your data is encrypted.</string>
    <!-- Label for button to not install a certificate [CHAR_LIMIT=50] -->
    <string name="certificate_warning_dont_install">Don\u2019t install</string>
    <!-- Label for button to continue installing a certificate [CHAR_LIMIT=50] -->
    <string name="certificate_warning_install_anyway">Install anyway</string>
    <!-- Toast message that a certificate was not installed -->
    <string name="cert_not_installed">Certificate not installed</string>
    <!-- Sound settings screen, setting check box label -->
    <string name="emergency_tone_title">Emergency dialing signal</string>
@@ -6890,6 +6904,8 @@
    <string name="help_url_security" translatable="false"></string>
    <!-- Help URL, Encryption settings [DO NOT TRANSLATE] -->
    <string name="help_url_encryption" translatable="false"></string>
    <!-- Help URL, Install certificate settings [DO NOT TRANSLATE] -->
    <string name="help_url_install_certificate" translatable="false"></string>
    <!-- Help URL, Tap & pay [DO NOT TRANSLATE] -->
    <string name="help_url_nfc_payment" translatable="false"></string>
    <!-- Help URL, Remote display [DO NOT TRANSLATE] -->
+3 −9
Original line number Diff line number Diff line
@@ -58,17 +58,11 @@
            settings:userRestriction="no_config_credentials" />

        <com.android.settingslib.RestrictedPreference
            android:key="credentials_install"
            android:key="install_certificate"
            android:title="@string/credentials_install"
            android:summary="@string/credentials_install_summary"
            settings:userRestriction="no_config_credentials">

            <intent
                android:action="android.credentials.INSTALL"
                android:targetPackage="com.android.certinstaller"
                android:targetClass="com.android.certinstaller.CertInstallerMain" />

        </com.android.settingslib.RestrictedPreference>
            android:fragment="com.android.settings.security.InstallCertificateFromStorage"
            settings:userRestriction="no_config_credentials" />

        <com.android.settingslib.RestrictedPreference
            android:key="credentials_reset"
+70 −0
Original line number Diff line number Diff line
<!--
  ~ Copyright (C) 2019 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.
  -->

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:title="@string/credentials_install"
    android:key="install_certificate_from_storage">

    <PreferenceCategory
        android:key="certificate_types">

        <Preference
            android:key="install_ca_certificate"
            android:title="@string/ca_certificate">

            <intent
                android:action="android.credentials.INSTALL"
                android:targetPackage="com.android.certinstaller"
                android:targetClass="com.android.certinstaller.CertInstallerMain">
                <!-- Same value as CERTIFICATE_USAGE_CA in keystore/java/android/security/Credentials.java -->
                <extra android:name="certificate_install_usage" android:value="ca"/>
            </intent>

        </Preference>

        <Preference
            android:key="install_user_certificate"
            android:title="@string/user_certificate">

            <intent
                android:action="android.credentials.INSTALL"
                android:targetPackage="com.android.certinstaller"
                android:targetClass="com.android.certinstaller.CertInstallerMain">
                <!-- Same value as CERTIFICATE_USAGE_USER in keystore/java/android/security/Credentials.java -->
                <extra android:name="certificate_install_usage" android:value="user"/>
            </intent>

        </Preference>

        <Preference
            android:key="install_wifi_certificate"
            android:title="@string/wifi_certificate">

            <intent
                android:action="android.credentials.INSTALL"
                android:targetPackage="com.android.certinstaller"
                android:targetClass="com.android.certinstaller.CertInstallerMain">
                <!-- Same value as CERTIFICATE_USAGE_WIFI in keystore/java/android/security/Credentials.java -->
                <extra android:name="certificate_install_usage" android:value="wifi"/>
            </intent>

        </Preference>

    </PreferenceCategory>

</PreferenceScreen>
 No newline at end of file
+6 −4
Original line number Diff line number Diff line
@@ -48,10 +48,12 @@
    <Preference
        android:key="install_credentials"
        android:title="@string/wifi_install_credentials">
        <intent android:action="android.credentials.INSTALL_AS_USER"
        <intent
            android:action="android.credentials.INSTALL"
            android:targetPackage="com.android.certinstaller"
            android:targetClass="com.android.certinstaller.CertInstallerMain">
            <extra android:name="install_as_uid" android:value="1010" />
            <!-- Same value as CERTIFICATE_USAGE_WIFI in keystore/java/android/security/Credentials.java -->
            <extra android:name="certificate_install_usage" android:value="wifi"/>
        </intent>
    </Preference>

+55 −79
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.security.Credentials;
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
import android.security.KeyStore;
@@ -42,18 +43,10 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;

import com.android.internal.widget.LockPatternUtils;
import com.android.org.bouncycastle.asn1.ASN1InputStream;
import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import com.android.settings.R;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.vpn2.VpnUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;

/**
 * CredentialStorage handles resetting and installing keys into KeyStore.
 */
@@ -118,20 +111,6 @@ public final class CredentialStorage extends FragmentActivity {
        }
    }

    private boolean isHardwareBackedKey(byte[] keyData) {
        try {
            final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
            final PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
            final String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
            final String algName = new AlgorithmId(new ObjectIdentifier(algOid)).getName();

            return KeyChain.isBoundKeyAlgorithm(algName);
        } catch (IOException e) {
            Log.e(TAG, "Failed to parse key data");
            return false;
        }
    }

    /**
     * Install credentials if available, otherwise do nothing.
     *
@@ -165,56 +144,18 @@ public final class CredentialStorage extends FragmentActivity {
            return true;
        }

        boolean shouldFinish = true;
        if (bundle.containsKey(Credentials.EXTRA_USER_PRIVATE_KEY_NAME)) {
            final String key = bundle.getString(Credentials.EXTRA_USER_PRIVATE_KEY_NAME);
            final byte[] value = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);

            if (!mKeyStore.importKey(key, value, uid, KeyStore.FLAG_NONE)) {
                Log.e(TAG, "Failed to install " + key + " as uid " + uid);
        String alias = bundle.getString(Credentials.EXTRA_USER_KEY_ALIAS, null);
        if (TextUtils.isEmpty(alias)) {
            Log.e(TAG, "Cannot install key without an alias");
            return true;
        }
            // The key was prepended USER_PRIVATE_KEY by the CredentialHelper. However,
            // KeyChain internally uses the raw alias name and only prepends USER_PRIVATE_KEY
            // to the key name when interfacing with KeyStore.
            // This is generally a symptom of CredentialStorage and CredentialHelper relying
            // on internal implementation details of KeyChain and imitating its functionality
            // rather than delegating to KeyChain for the certificate installation.
            if (uid == Process.SYSTEM_UID || uid == KeyStore.UID_SELF) {
                new MarkKeyAsUserSelectable(
                        key.replaceFirst("^" + Credentials.USER_PRIVATE_KEY, "")).execute();
                shouldFinish = false;
            }
        }

        final int flags = KeyStore.FLAG_NONE;

        if (bundle.containsKey(Credentials.EXTRA_USER_CERTIFICATE_NAME)) {
            final String certName = bundle.getString(Credentials.EXTRA_USER_CERTIFICATE_NAME);
        final byte[] privateKeyData = bundle.getByteArray(Credentials.EXTRA_USER_PRIVATE_KEY_DATA);
        final byte[] certData = bundle.getByteArray(Credentials.EXTRA_USER_CERTIFICATE_DATA);

            if (!mKeyStore.put(certName, certData, uid, flags)) {
                Log.e(TAG, "Failed to install " + certName + " as uid " + uid);
                return shouldFinish;
            }
        }

        if (bundle.containsKey(Credentials.EXTRA_CA_CERTIFICATES_NAME)) {
            final String caListName = bundle.getString(Credentials.EXTRA_CA_CERTIFICATES_NAME);
        final byte[] caListData = bundle.getByteArray(Credentials.EXTRA_CA_CERTIFICATES_DATA);
        new InstallKeyInKeyChain(alias, privateKeyData, certData, caListData, uid).execute();

            if (!mKeyStore.put(caListName, caListData, uid, flags)) {
                Log.e(TAG, "Failed to install " + caListName + " as uid " + uid);
                return shouldFinish;
            }
        }

        // Send the broadcast.
        final Intent broadcast = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
        sendBroadcast(broadcast);

        setResult(RESULT_OK);
        return shouldFinish;
        return false;
    }

    /**
@@ -308,26 +249,45 @@ public final class CredentialStorage extends FragmentActivity {
    }

    /**
     * Background task to mark a given key alias as user-selectable, so that
     * it can be selected by users from the Certificate Selection prompt.
     * Background task to install a certificate into KeyChain or the WiFi Keystore.
     */
    private class MarkKeyAsUserSelectable extends AsyncTask<Void, Void, Boolean> {
    private class InstallKeyInKeyChain extends AsyncTask<Void, Void, Boolean> {
        final String mAlias;
        private final byte[] mKeyData;
        private final byte[] mCertData;
        private final byte[] mCaListData;
        private final int mUid;

        MarkKeyAsUserSelectable(String alias) {
        InstallKeyInKeyChain(String alias, byte[] keyData, byte[] certData, byte[] caListData,
                int uid) {
            mAlias = alias;
            mKeyData = keyData;
            mCertData = certData;
            mCaListData = caListData;
            mUid = uid;
        }

        @Override
        protected Boolean doInBackground(Void... unused) {
            try (KeyChainConnection keyChainConnection = KeyChain.bind(CredentialStorage.this)) {
                keyChainConnection.getService().setUserSelectable(mAlias, true);
                IKeyChainService service = keyChainConnection.getService();
                if (!service.installKeyPair(mKeyData, mCertData, mCaListData, mAlias, mUid)) {
                    Log.w(TAG, String.format("Failed installing key %s", mAlias));
                    return false;
                }

                // If this is not a WiFi key, mark  it as user-selectable, so that it can be
                // selected by users from the Certificate Selection prompt.
                if (mUid == Process.SYSTEM_UID || mUid == KeyStore.UID_SELF) {
                    service.setUserSelectable(mAlias, true);
                }

                return true;
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to mark key " + mAlias + " as user-selectable.");
                Log.w(TAG, String.format("Failed to install key %s to uid %d", mAlias, mUid), e);
                return false;
            } catch (InterruptedException e) {
                Log.w(TAG, "Failed to mark key " + mAlias + " as user-selectable.");
                Log.w(TAG, String.format("Interrupted while installing key %s", mAlias), e);
                Thread.currentThread().interrupt();
                return false;
            }
@@ -335,10 +295,26 @@ public final class CredentialStorage extends FragmentActivity {

        @Override
        protected void onPostExecute(Boolean result) {
            Log.i(TAG, String.format("Marked alias %s as selectable, success? %s",
                        mAlias, result));
            CredentialStorage.this.finish();
            CredentialStorage.this.onKeyInstalled(mAlias, mUid, result);
        }
    }

    private void onKeyInstalled(String alias, int uid, boolean result) {
        if (!result) {
            Log.w(TAG, String.format("Error installing alias %s for uid %d", alias, uid));
            finish();
            return;
        }

        Log.i(TAG, String.format("Successfully installed alias %s to uid %d.",
                alias, uid));

        // Send the broadcast.
        final Intent broadcast = new Intent(KeyChain.ACTION_KEYCHAIN_CHANGED);
        sendBroadcast(broadcast);
        setResult(RESULT_OK);

        finish();
    }

    /**
Loading