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

Commit ba1a667b authored by Brian Carlstrom's avatar Brian Carlstrom
Browse files

Remove need for onActivityResult from KeyChain API

Change-Id: I97bb9db06978f6dc039d22bfee116671d7b3e336
parent f8355d54
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -162,6 +162,7 @@ LOCAL_SRC_FILES += \
	core/java/com/android/internal/view/IInputMethodSession.aidl \
	core/java/com/android/internal/view/IInputMethodSession.aidl \
	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
	core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
	core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
	keystore/java/android/security/IKeyChainAliasResponse.aidl \
	keystore/java/android/security/IKeyChainService.aidl \
	keystore/java/android/security/IKeyChainService.aidl \
	location/java/android/location/ICountryDetector.aidl \
	location/java/android/location/ICountryDetector.aidl \
	location/java/android/location/ICountryListener.aidl \
	location/java/android/location/ICountryListener.aidl \
+26 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2011 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 android.security;

/**
 * Used by the {@code KeyChainActivity} to return alias for {@link KeyStore#chooseAlias}.
 *
 * @hide
 */
interface IKeyChainAliasResponse {

    void alias(String alias);
}
+0 −2
Original line number Original line Diff line number Diff line
@@ -15,8 +15,6 @@
 */
 */
package android.security;
package android.security;


import android.os.Bundle;

/**
/**
 * Caller is required to ensure that {@link KeyStore#unlock
 * Caller is required to ensure that {@link KeyStore#unlock
 * KeyStore.unlock} was successful.
 * KeyStore.unlock} was successful.
+144 −39
Original line number Original line Diff line number Diff line
@@ -17,9 +17,11 @@ package android.security;


import android.accounts.Account;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -56,64 +58,123 @@ public final class KeyChain {
    public static final String ACCOUNT_TYPE = "com.android.keychain";
    public static final String ACCOUNT_TYPE = "com.android.keychain";


    /**
    /**
     * Returns an {@code Intent} for use with {@link
     * @hide Also used by KeyChainActivity implementation
     * android.app.Activity#startActivityForResult
     * startActivityForResult}. The result will be returned via {@link
     * android.app.Activity#onActivityResult onActivityResult} with
     * {@link android.app.Activity#RESULT_OK RESULT_OK} and the alias
     * in the returned {@code Intent}'s extra data with key {@link
     * android.content.Intent#EXTRA_TEXT Intent.EXTRA_TEXT}.
     */
     */
    public static Intent chooseAlias() {
    public static final String EXTRA_RESPONSE = "response";
        return new Intent("com.android.keychain.CHOOSER");
    }


    /**
    /**
     * Returns a new {@code KeyChainResult} instance.
     * Launches an {@code Activity} for the user to select the alias
     * for a private key and certificate pair for authentication. The
     * selected alias or null will be returned via the
     * IKeyChainAliasResponse callback.
     */
     */
    public static KeyChainResult get(Context context, String alias)
    public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) {
            throws InterruptedException, RemoteException {
        if (activity == null) {
            throw new NullPointerException("activity == null");
        }
        if (response == null) {
            throw new NullPointerException("response == null");
        }
        Intent intent = new Intent("com.android.keychain.CHOOSER");
        intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response));
        activity.startActivity(intent);
    }

    private static class AliasResponse extends IKeyChainAliasResponse.Stub {
        private final Activity activity;
        private final KeyChainAliasResponse keyChainAliasResponse;
        private AliasResponse(Activity activity, KeyChainAliasResponse keyChainAliasResponse) {
            this.activity = activity;
            this.keyChainAliasResponse = keyChainAliasResponse;
        }
        @Override public void alias(String alias) {
            if (alias == null) {
            if (alias == null) {
            throw new NullPointerException("alias == null");
                keyChainAliasResponse.alias(null);
                return;
            }
            }
        KeyChainConnection keyChainConnection = bind(context);
            AccountManager accountManager = AccountManager.get(activity);
        try {
            accountManager.getAuthToken(getAccount(activity),
            // Account is created if necessary during binding of the IKeyChainService
            AccountManager accountManager = AccountManager.get(context);
            Account account = accountManager.getAccountsByType(ACCOUNT_TYPE)[0];
            AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account,
                                        alias,
                                        alias,
                                                                              false,
                                        null,
                                        null,
                                        activity,
                                        new AliasAccountManagerCallback(keyChainAliasResponse,
                                                                        alias),
                                        null);
                                        null);
        }
    }

    private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> {
        private final KeyChainAliasResponse keyChainAliasResponse;
        private final String alias;
        private AliasAccountManagerCallback(KeyChainAliasResponse keyChainAliasResponse,
                                            String alias) {
            this.keyChainAliasResponse = keyChainAliasResponse;
            this.alias = alias;
        }
        @Override public void run(AccountManagerFuture<Bundle> future) {
            Bundle bundle;
            Bundle bundle;
            try {
            try {
                bundle = future.getResult();
                bundle = future.getResult();
            } catch (OperationCanceledException e) {
            } catch (OperationCanceledException e) {
                throw new AssertionError(e);
                keyChainAliasResponse.alias(null);
                return;
            } catch (IOException e) {
            } catch (IOException e) {
                throw new AssertionError(e);
                keyChainAliasResponse.alias(null);
                return;
            } catch (AuthenticatorException e) {
            } catch (AuthenticatorException e) {
                throw new AssertionError(e);
                keyChainAliasResponse.alias(null);
                return;
            }
            String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
            if (authToken != null) {
                keyChainAliasResponse.alias(alias);
            } else {
                keyChainAliasResponse.alias(null);
            }
        }
        }
            Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
            if (intent != null) {
                Bundle result = new Bundle();
                // we don't want this Eclair compatability flag,
                // it will prevent onActivityResult from being called
                intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
                return new KeyChainResult(intent);
    }
    }


            String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
    /**
     * Returns the {@code PrivateKey} for the requested alias, or null
     * if no there is no result.
     */
    public static PrivateKey getPrivateKey(Context context, String alias)
            throws InterruptedException, RemoteException {
        if (alias == null) {
            throw new NullPointerException("alias == null");
        }
        KeyChainConnection keyChainConnection = bind(context);
        try {
            String authToken = authToken(context, alias);
            if (authToken == null) {
            if (authToken == null) {
                throw new AssertionError("Invalid authtoken");
                return null;
            }
            }
            IKeyChainService keyChainService = keyChainConnection.getService();
            IKeyChainService keyChainService = keyChainConnection.getService();
            byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken);
            byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken);
            return toPrivateKey(privateKeyBytes);
        } finally {
            keyChainConnection.close();
        }
    }

    /**
     * Returns the {@code X509Certificate} chain for the requested
     * alias, or null if no there is no result.
     */
    public static X509Certificate[] getCertificateChain(Context context, String alias)
            throws InterruptedException, RemoteException {
        if (alias == null) {
            throw new NullPointerException("alias == null");
        }
        KeyChainConnection keyChainConnection = bind(context);
        try {
            String authToken = authToken(context, alias);
            if (authToken == null) {
                return null;
            }
            IKeyChainService keyChainService = keyChainConnection.getService();
            byte[] certificateBytes = keyChainService.getCertificate(alias, authToken);
            byte[] certificateBytes = keyChainService.getCertificate(alias, authToken);
            return new KeyChainResult(toPrivateKey(privateKeyBytes),
            return new X509Certificate[] { toCertificate(certificateBytes) };
                                      toCertificate(certificateBytes));
        } finally {
        } finally {
            keyChainConnection.close();
            keyChainConnection.close();
        }
        }
@@ -146,6 +207,50 @@ public final class KeyChain {
        }
        }
    }
    }


    private static String authToken(Context context, String alias) {
        AccountManager accountManager = AccountManager.get(context);
        AccountManagerFuture<Bundle> future = accountManager.getAuthToken(getAccount(context),
                                                                          alias,
                                                                          false,
                                                                          null,
                                                                          null);
        Bundle bundle;
        try {
            bundle = future.getResult();
        } catch (OperationCanceledException e) {
            throw new AssertionError(e);
        } catch (IOException e) {
            // KeyChainAccountAuthenticator doesn't do I/O
            throw new AssertionError(e);
        } catch (AuthenticatorException e) {
            throw new AssertionError(e);
        }
        Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
        if (intent != null) {
            return null;
        }
        String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
        if (authToken == null) {
            throw new AssertionError("Invalid authtoken");
        }
        return authToken;
    }

    private static Account getAccount(Context context) {
        AccountManager accountManager = AccountManager.get(context);
        Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
        if (accounts.length == 0) {
            try {
                // Account is created if necessary during binding of the IKeyChainService
                bind(context).close();
            } catch (InterruptedException e) {
                throw new AssertionError(e);
            }
            accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
        }
        return accounts[0];
    }

    /**
    /**
     * @hide for reuse by CertInstaller and Settings.
     * @hide for reuse by CertInstaller and Settings.
     * @see KeyChain#bind
     * @see KeyChain#bind
+35 −0
Original line number Original line Diff line number Diff line
@@ -20,53 +20,16 @@ import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.security.cert.X509Certificate;


/**
/**
 * The KeyChainResult is the complex result value from {@link
 * The KeyChainAliasResponse is the callback for {@link
 * KeyChain#get}. The caller should first inspect {@link #getIntent}
 * KeyChain#chooseAlias}.
 * to determine if the user needs to grant the application access to
 * the protected contents. If {@code getIntent} returns null, access
 * has been granted and the methods {@link #getPrivateKey} and {@link
 * #getCertificate} can be used to access the credentials.
 *
 *
 * @hide
 * @hide
 */
 */
public final class KeyChainResult {
public interface KeyChainAliasResponse {

    private final Intent intent;
    private final PrivateKey privateKey;
    private final X509Certificate certificate;

    KeyChainResult(Intent intent) {
        this(intent, null, null);
    }

    KeyChainResult(PrivateKey privateKey, X509Certificate certificate) {
        this(null, privateKey, certificate);
    }

    private KeyChainResult(Intent intent, PrivateKey privateKey, X509Certificate certificate) {
        this.intent = intent;
        this.privateKey = privateKey;
        this.certificate = certificate;
    }

    public Intent getIntent() {
        return intent;
    }

    public PrivateKey getPrivateKey() {
        checkIntent();
        return privateKey;
    }

    public X509Certificate getCertificate() {
        checkIntent();
        return certificate;
    }

    private void checkIntent() {
        if (intent != null) {
            throw new IllegalStateException("non-null Intent, check getIntent()");
        }
    }


    /**
     * Called with the alias of the certificate chosen by the user, or
     * null if no value was chosen.
     */
    public void alias(String alias);
}
}
Loading