Loading Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -162,6 +162,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodSession.aidl \ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ keystore/java/android/security/IKeyChainAliasResponse.aidl \ keystore/java/android/security/IKeyChainService.aidl \ location/java/android/location/ICountryDetector.aidl \ location/java/android/location/ICountryListener.aidl \ Loading keystore/java/android/security/IKeyChainAliasResponse.aidl 0 → 100644 +26 −0 Original line number 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); } keystore/java/android/security/IKeyChainService.aidl +0 −2 Original line number Diff line number Diff line Loading @@ -15,8 +15,6 @@ */ package android.security; import android.os.Bundle; /** * Caller is required to ensure that {@link KeyStore#unlock * KeyStore.unlock} was successful. Loading keystore/java/android/security/KeyChain.java +144 −39 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package android.security; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading Loading @@ -56,64 +58,123 @@ public final class KeyChain { public static final String ACCOUNT_TYPE = "com.android.keychain"; /** * Returns an {@code Intent} for use with {@link * 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}. * @hide Also used by KeyChainActivity implementation */ public static Intent chooseAlias() { return new Intent("com.android.keychain.CHOOSER"); } public static final String EXTRA_RESPONSE = "response"; /** * 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) throws InterruptedException, RemoteException { public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) { 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) { throw new NullPointerException("alias == null"); keyChainAliasResponse.alias(null); return; } KeyChainConnection keyChainConnection = bind(context); try { // 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, AccountManager accountManager = AccountManager.get(activity); accountManager.getAuthToken(getAccount(activity), alias, false, null, activity, new AliasAccountManagerCallback(keyChainAliasResponse, alias), 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; try { bundle = future.getResult(); } catch (OperationCanceledException e) { throw new AssertionError(e); keyChainAliasResponse.alias(null); return; } catch (IOException e) { throw new AssertionError(e); keyChainAliasResponse.alias(null); return; } 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) { throw new AssertionError("Invalid authtoken"); return null; } IKeyChainService keyChainService = keyChainConnection.getService(); 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); return new KeyChainResult(toPrivateKey(privateKeyBytes), toCertificate(certificateBytes)); return new X509Certificate[] { toCertificate(certificateBytes) }; } finally { keyChainConnection.close(); } Loading Loading @@ -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. * @see KeyChain#bind Loading keystore/java/android/security/KeyChainResult.java→keystore/java/android/security/KeyChainAliasResponse.java +35 −0 Original line number Diff line number Diff line Loading @@ -20,53 +20,16 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; /** * The KeyChainResult is the complex result value from {@link * KeyChain#get}. The caller should first inspect {@link #getIntent} * 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. * The KeyChainAliasResponse is the callback for {@link * KeyChain#chooseAlias}. * * @hide */ public final class KeyChainResult { 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()"); } } public interface KeyChainAliasResponse { /** * Called with the alias of the certificate chosen by the user, or * null if no value was chosen. */ public void alias(String alias); } Loading
Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -162,6 +162,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodSession.aidl \ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ keystore/java/android/security/IKeyChainAliasResponse.aidl \ keystore/java/android/security/IKeyChainService.aidl \ location/java/android/location/ICountryDetector.aidl \ location/java/android/location/ICountryListener.aidl \ Loading
keystore/java/android/security/IKeyChainAliasResponse.aidl 0 → 100644 +26 −0 Original line number 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); }
keystore/java/android/security/IKeyChainService.aidl +0 −2 Original line number Diff line number Diff line Loading @@ -15,8 +15,6 @@ */ package android.security; import android.os.Bundle; /** * Caller is required to ensure that {@link KeyStore#unlock * KeyStore.unlock} was successful. Loading
keystore/java/android/security/KeyChain.java +144 −39 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package android.security; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerCallback; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; Loading Loading @@ -56,64 +58,123 @@ public final class KeyChain { public static final String ACCOUNT_TYPE = "com.android.keychain"; /** * Returns an {@code Intent} for use with {@link * 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}. * @hide Also used by KeyChainActivity implementation */ public static Intent chooseAlias() { return new Intent("com.android.keychain.CHOOSER"); } public static final String EXTRA_RESPONSE = "response"; /** * 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) throws InterruptedException, RemoteException { public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) { 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) { throw new NullPointerException("alias == null"); keyChainAliasResponse.alias(null); return; } KeyChainConnection keyChainConnection = bind(context); try { // 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, AccountManager accountManager = AccountManager.get(activity); accountManager.getAuthToken(getAccount(activity), alias, false, null, activity, new AliasAccountManagerCallback(keyChainAliasResponse, alias), 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; try { bundle = future.getResult(); } catch (OperationCanceledException e) { throw new AssertionError(e); keyChainAliasResponse.alias(null); return; } catch (IOException e) { throw new AssertionError(e); keyChainAliasResponse.alias(null); return; } 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) { throw new AssertionError("Invalid authtoken"); return null; } IKeyChainService keyChainService = keyChainConnection.getService(); 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); return new KeyChainResult(toPrivateKey(privateKeyBytes), toCertificate(certificateBytes)); return new X509Certificate[] { toCertificate(certificateBytes) }; } finally { keyChainConnection.close(); } Loading Loading @@ -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. * @see KeyChain#bind Loading
keystore/java/android/security/KeyChainResult.java→keystore/java/android/security/KeyChainAliasResponse.java +35 −0 Original line number Diff line number Diff line Loading @@ -20,53 +20,16 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; /** * The KeyChainResult is the complex result value from {@link * KeyChain#get}. The caller should first inspect {@link #getIntent} * 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. * The KeyChainAliasResponse is the callback for {@link * KeyChain#chooseAlias}. * * @hide */ public final class KeyChainResult { 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()"); } } public interface KeyChainAliasResponse { /** * Called with the alias of the certificate chosen by the user, or * null if no value was chosen. */ public void alias(String alias); }