diff options
Diffstat (limited to 'keystore')
-rw-r--r-- | keystore/java/android/security/IKeyChainAliasCallback.aidl (renamed from keystore/java/android/security/IKeyChainAliasResponse.aidl) | 2 | ||||
-rw-r--r-- | keystore/java/android/security/KeyChain.java | 124 | ||||
-rw-r--r-- | keystore/java/android/security/KeyChainAliasCallback.java (renamed from keystore/java/android/security/KeyChainAliasResponse.java) | 8 | ||||
-rw-r--r-- | keystore/java/android/security/KeyChainException.java | 67 |
4 files changed, 186 insertions, 15 deletions
diff --git a/keystore/java/android/security/IKeyChainAliasResponse.aidl b/keystore/java/android/security/IKeyChainAliasCallback.aidl index e042001..1ea9521 100644 --- a/keystore/java/android/security/IKeyChainAliasResponse.aidl +++ b/keystore/java/android/security/IKeyChainAliasCallback.aidl @@ -20,7 +20,7 @@ package android.security; * * @hide */ -interface IKeyChainAliasResponse { +interface IKeyChainAliasCallback { void alias(String alias); } diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index ba784ed..39d65be 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -36,6 +36,7 @@ import java.io.IOException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; +import java.security.Principal; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; @@ -47,8 +48,38 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** - * @hide + * The {@code KeyChain} class provides access to private keys and + * their corresponding certificate chains in credential storage. + * + * <p>Applications accessing the {@code KeyChain} normally go through + * these steps: + * + * <ol> + * + * <li>Receive a callback from an {@link javax.net.ssl.X509KeyManager + * X509KeyManager} that a private key is requested. + * + * <li>Call {@link #choosePrivateKeyAlias + * choosePrivateKeyAlias} to allow the user to select from a + * list of currently available private keys and corresponding + * certificate chains. The chosen alias will be returned by the + * callback {@link KeyChainAliasCallback#alias}, or null if no private + * key is available or the user cancels the request. + * + * <li>Call {@link #getPrivateKey} and {@link #getCertificateChain} to + * retrieve the credentials to return to the corresponding {@link + * javax.net.ssl.X509KeyManager} callbacks. + * + * </ol> + * + * <p>An application may remember the value of a selected alias to + * avoid prompting the user with {@link #choosePrivateKeyAlias + * choosePrivateKeyAlias} on subsequent connections. If the alias is + * no longer valid, null will be returned on lookups using that value + * + * @hide to be unhidden as part of KeyChain API */ +// TODO reference intent for credential installation when public public final class KeyChain { private static final String TAG = "KeyChain"; @@ -67,9 +98,58 @@ public final class KeyChain { * 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. + * KeyChainAliasCallback callback. + * + * <p>{@code keyTypes} and {@code issuers} may be used to + * highlight suggested choices to the user, although to cope with + * sometimes erroneous values provided by servers, the user may be + * able to override these suggestions. + * + * <p>{@code host} and {@code port} may be used to give the user + * more context about the server requesting the credentials. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#USE_CREDENTIALS}. + * + * @param activity The {@link Activity} context to use for + * launching the new sub-Activity to prompt the user to select + * a private key; used only to call startActivity(); must not + * be null. + * @param response Callback to invoke when the request completes; + * must not be null + * @param keyTypes The acceptable types of asymmetric keys such as + * "RSA" or "DSA", or a null array. + * @param issuers The acceptable certificate issuers for the + * certificate matching the private key, or null. + * @param host The host name of the server requesting the + * certificate, or null if unavailable. + * @param port The port number of the server requesting the + * certificate, or -1 if unavailable. */ - public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) { + public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response, + String[] keyTypes, Principal[] issuers, + String host, int port) { + /* + * TODO currently keyTypes, issuers, host, and port are + * unused. They are meant to follow the semantics and purpose + * of X509KeyManager method arguments. + * + * keyTypes would allow the list to be filtered and typically + * will be set correctly by the server. In practice today, + * most all users will want only RSA, rarely DSA, and usually + * only a small number of certs will be available. + * + * issuers is typically not useful. Some servers historically + * will send the entire list of public CAs known to the + * server. Others will send none. If this is used, if there + * are no matches after applying the constraint, it should be + * ignored. + * + * host and port may be shown to the user if available, but it + * should be clear that they are not validated values, perhaps + * shown along with requesting application identity to clarify + * the source of the request. + */ if (activity == null) { throw new NullPointerException("activity == null"); } @@ -81,10 +161,10 @@ public final class KeyChain { activity.startActivity(intent); } - private static class AliasResponse extends IKeyChainAliasResponse.Stub { + private static class AliasResponse extends IKeyChainAliasCallback.Stub { private final Activity activity; - private final KeyChainAliasResponse keyChainAliasResponse; - private AliasResponse(Activity activity, KeyChainAliasResponse keyChainAliasResponse) { + private final KeyChainAliasCallback keyChainAliasResponse; + private AliasResponse(Activity activity, KeyChainAliasCallback keyChainAliasResponse) { this.activity = activity; this.keyChainAliasResponse = keyChainAliasResponse; } @@ -105,9 +185,9 @@ public final class KeyChain { } private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> { - private final KeyChainAliasResponse keyChainAliasResponse; + private final KeyChainAliasCallback keyChainAliasResponse; private final String alias; - private AliasAccountManagerCallback(KeyChainAliasResponse keyChainAliasResponse, + private AliasAccountManagerCallback(KeyChainAliasCallback keyChainAliasResponse, String alias) { this.keyChainAliasResponse = keyChainAliasResponse; this.alias = alias; @@ -138,9 +218,16 @@ public final class KeyChain { /** * Returns the {@code PrivateKey} for the requested alias, or null * if no there is no result. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#USE_CREDENTIALS}. + * + * @param alias The alias of the desired private key, typically + * returned via {@link KeyChainAliasCallback#alias}. + * @throws KeyChainException if the alias was valid but there was some problem accessing it. */ public static PrivateKey getPrivateKey(Context context, String alias) - throws InterruptedException, RemoteException { + throws KeyChainException, InterruptedException { if (alias == null) { throw new NullPointerException("alias == null"); } @@ -153,6 +240,11 @@ public final class KeyChain { IKeyChainService keyChainService = keyChainConnection.getService(); byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken); return toPrivateKey(privateKeyBytes); + } catch (RemoteException e) { + throw new KeyChainException(e); + } catch (RuntimeException e) { + // only certain RuntimeExceptions can be propagated across the IKeyChainService call + throw new KeyChainException(e); } finally { keyChainConnection.close(); } @@ -161,9 +253,16 @@ public final class KeyChain { /** * Returns the {@code X509Certificate} chain for the requested * alias, or null if no there is no result. + * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#USE_CREDENTIALS}. + * + * @param alias The alias of the desired certificate chain, typically + * returned via {@link KeyChainAliasCallback#alias}. + * @throws KeyChainException if the alias was valid but there was some problem accessing it. */ public static X509Certificate[] getCertificateChain(Context context, String alias) - throws InterruptedException, RemoteException { + throws KeyChainException, InterruptedException { if (alias == null) { throw new NullPointerException("alias == null"); } @@ -176,6 +275,11 @@ public final class KeyChain { IKeyChainService keyChainService = keyChainConnection.getService(); byte[] certificateBytes = keyChainService.getCertificate(alias, authToken); return new X509Certificate[] { toCertificate(certificateBytes) }; + } catch (RemoteException e) { + throw new KeyChainException(e); + } catch (RuntimeException e) { + // only certain RuntimeExceptions can be propagated across the IKeyChainService call + throw new KeyChainException(e); } finally { keyChainConnection.close(); } diff --git a/keystore/java/android/security/KeyChainAliasResponse.java b/keystore/java/android/security/KeyChainAliasCallback.java index bcca123..daa348b 100644 --- a/keystore/java/android/security/KeyChainAliasResponse.java +++ b/keystore/java/android/security/KeyChainAliasCallback.java @@ -20,12 +20,12 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; /** - * The KeyChainAliasResponse is the callback for {@link - * KeyChain#chooseAlias}. + * The KeyChainAliasCallback is the callback for {@link + * KeyChain#choosePrivateKeyAlias}. * - * @hide + * @hide to be unhidden as part of KeyChain API */ -public interface KeyChainAliasResponse { +public interface KeyChainAliasCallback { /** * Called with the alias of the certificate chosen by the user, or diff --git a/keystore/java/android/security/KeyChainException.java b/keystore/java/android/security/KeyChainException.java new file mode 100644 index 0000000..3953f58 --- /dev/null +++ b/keystore/java/android/security/KeyChainException.java @@ -0,0 +1,67 @@ +/* + * 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; + +/** + * Thrown on problems accessing the {@link KeyChain}. + * + * @hide to be unhidden as part of KeyChain API + */ +public class KeyChainException extends Exception { + + /** + * Constructs a new {@code KeyChainException} that includes the + * current stack trace. + */ + public KeyChainException() { + } + + /** + * Constructs a new {@code KeyChainException} with the current stack + * trace and the specified detail message. + * + * @param detailMessage + * the detail message for this exception. + */ + public KeyChainException(String detailMessage) { + super(detailMessage); + } + + /** + * Constructs a new {@code KeyChainException} with the current stack + * trace, the specified detail message and the specified cause. + * + * @param message + * the detail message for this exception. + * @param cause + * the cause of this exception, may be {@code null}. + */ + public KeyChainException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new {@code KeyChainException} with the current stack + * trace and the specified cause. + * + * @param cause + * the cause of this exception, may be {@code null}. + */ + public KeyChainException(Throwable cause) { + super((cause == null ? null : cause.toString()), cause); + } +} |