Loading keystore/java/android/security/IKeyChainService.aidl +1 −3 Original line number Original line Diff line number Diff line Loading @@ -25,10 +25,8 @@ import android.os.Bundle; */ */ interface IKeyChainService { interface IKeyChainService { // APIs used by KeyChain // APIs used by KeyChain byte[] getPrivate(String alias, String authToken); byte[] getPrivateKey(String alias, String authToken); byte[] getCertificate(String alias, String authToken); byte[] getCertificate(String alias, String authToken); byte[] getCaCertificate(String alias, String authToken); String findIssuer(in Bundle cert); // APIs used by CertInstaller // APIs used by CertInstaller void installCaCertificate(in byte[] caCertificate); void installCaCertificate(in byte[] caCertificate); Loading keystore/java/android/security/KeyChain.java +67 −262 Original line number Original line Diff line number Diff line Loading @@ -28,24 +28,19 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IBinder; import android.os.Looper; import android.os.Looper; import android.os.RemoteException; import android.os.RemoteException; import android.util.Log; import dalvik.system.CloseGuard; import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.IOException; import java.security.KeyFactory; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PrivateKey; import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec; import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters; import java.util.concurrent.BlockingQueue; import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl; import java.util.concurrent.LinkedBlockingQueue; /** /** * @hide * @hide Loading @@ -59,30 +54,6 @@ public final class KeyChain { */ */ public static final String ACCOUNT_TYPE = "com.android.keychain"; public static final String ACCOUNT_TYPE = "com.android.keychain"; /** * @hide Also used by KeyChainService implementation */ // TODO This non-localized CA string to be removed when CAs moved out of keystore public static final String CA_SUFFIX = " CA"; public static final String KEY_INTENT = "intent"; /** * Intentionally not public to leave open the future possibility * of hardware based keys. Callers should use {@link #toPrivateKey * toPrivateKey} in order to convert a bundle to a {@code * PrivateKey} */ private static final String KEY_PKCS8 = "pkcs8"; /** * Intentionally not public to leave open the future possibility * of hardware based certs. Callers should use {@link * #toCertificate toCertificate} in order to convert a bundle to a * {@code PrivateKey} */ private static final String KEY_X509 = "x509"; /** /** * Returns an {@code Intent} for use with {@link * Returns an {@code Intent} for use with {@link * android.app.Activity#startActivityForResult * android.app.Activity#startActivityForResult Loading @@ -97,99 +68,43 @@ public final class KeyChain { } } /** /** * Returns a new {@code KeyChain} instance. When the caller is * Returns a new {@code KeyChainResult} instance. * done using the {@code KeyChain}, it must be closed with {@link * #close()} or resource leaks will occur. */ */ public static KeyChain getInstance(Context context) throws InterruptedException { public static KeyChainResult get(Context context, String alias) return new KeyChain(context); throws InterruptedException, RemoteException { } private final AccountManager mAccountManager; private final Object mServiceLock = new Object(); private IKeyChainService mService; private boolean mIsBound; private Account mAccount; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mServiceLock) { mService = IKeyChainService.Stub.asInterface(service); mServiceLock.notifyAll(); // Account is created if necessary during binding of the IKeyChainService mAccount = mAccountManager.getAccountsByType(ACCOUNT_TYPE)[0]; } } @Override public void onServiceDisconnected(ComponentName name) { synchronized (mServiceLock) { mService = null; } } }; private final Context mContext; private final CloseGuard mGuard = CloseGuard.get(); private KeyChain(Context context) throws InterruptedException { if (context == null) { if (context == null) { throw new NullPointerException("context == null"); throw new NullPointerException("context == null"); } } mContext = context; if (alias == null) { ensureNotOnMainThread(); throw new NullPointerException("alias == null"); mAccountManager = AccountManager.get(mContext); mIsBound = mContext.bindService(new Intent(IKeyChainService.class.getName()), mServiceConnection, Context.BIND_AUTO_CREATE); if (!mIsBound) { throw new AssertionError(); } synchronized (mServiceLock) { // there is a race between binding on this thread and the // callback on the main thread. wait until binding is done // to be sure we have the mAccount initialized. if (mService == null) { mServiceLock.wait(); } } mGuard.open("close"); } /** * {@code Bundle} will contain {@link #KEY_INTENT} if user needs * to confirm application access to requested key. In the alias * does not exist or there is an error, null is * returned. Otherwise the {@code Bundle} contains information * representing the private key which can be interpreted with * {@link #toPrivateKey toPrivateKey}. * * non-null alias */ public Bundle getPrivate(String alias) { return get(alias, Credentials.USER_PRIVATE_KEY); } } ensureNotOnMainThread(context); public Bundle getCertificate(String alias) { final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1); return get(alias, Credentials.USER_CERTIFICATE); ServiceConnection keyChainServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { try { q.put(IKeyChainService.Stub.asInterface(service)); } catch (InterruptedException e) { throw new AssertionError(e); } } public Bundle getCaCertificate(String alias) { return get(alias, Credentials.CA_CERTIFICATE); } } @Override public void onServiceDisconnected(ComponentName name) {} private Bundle get(String alias, String type) { }; if (alias == null) { boolean isBound = context.bindService(new Intent(IKeyChainService.class.getName()), throw new NullPointerException("alias == null"); keyChainServiceConnection, Context.BIND_AUTO_CREATE); if (!isBound) { throw new AssertionError("could not bind to KeyChainService"); } } ensureNotOnMainThread(); IKeyChainService keyChainService; try { keyChainService = q.take(); String authAlias = (type.equals(Credentials.CA_CERTIFICATE)) ? (alias + CA_SUFFIX) : alias; // Account is created if necessary during binding of the IKeyChainService AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount, AccountManager accountManager = AccountManager.get(context); authAlias, Account account = accountManager.getAccountsByType(ACCOUNT_TYPE)[0]; AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account, alias, false, false, null, null, null); null); Loading @@ -209,48 +124,25 @@ public final class KeyChain { // we don't want this Eclair compatability flag, // we don't want this Eclair compatability flag, // it will prevent onActivityResult from being called // it will prevent onActivityResult from being called intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); result.putParcelable(KEY_INTENT, intent); return new KeyChainResult(intent); return result; } } String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); if (authToken == null) { if (authToken == null) { throw new AssertionError("Invalid authtoken"); throw new AssertionError("Invalid authtoken"); } } byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken); byte[] bytes; byte[] certificateBytes = keyChainService.getCertificate(alias, authToken); try { return new KeyChainResult(toPrivateKey(privateKeyBytes), if (type.equals(Credentials.USER_PRIVATE_KEY)) { toCertificate(certificateBytes)); bytes = mService.getPrivate(alias, authToken); } finally { } else if (type.equals(Credentials.USER_CERTIFICATE)) { context.unbindService(keyChainServiceConnection); bytes = mService.getCertificate(alias, authToken); } else if (type.equals(Credentials.CA_CERTIFICATE)) { bytes = mService.getCaCertificate(alias, authToken); } else { throw new AssertionError(); } } catch (RemoteException e) { throw new AssertionError(e); } if (bytes == null) { throw new AssertionError(); } Bundle result = new Bundle(); if (type.equals(Credentials.USER_PRIVATE_KEY)) { result.putByteArray(KEY_PKCS8, bytes); } else if (type.equals(Credentials.USER_CERTIFICATE)) { result.putByteArray(KEY_X509, bytes); } else if (type.equals(Credentials.CA_CERTIFICATE)) { result.putByteArray(KEY_X509, bytes); } else { throw new AssertionError(); } } return result; } } public static PrivateKey toPrivateKey(Bundle bundle) { private static PrivateKey toPrivateKey(byte[] bytes) { byte[] bytes = bundle.getByteArray(KEY_PKCS8); if (bytes == null) { if (bytes == null) { throw new IllegalArgumentException("not a private key bundle"); throw new IllegalArgumentException("bytes == null"); } } try { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); Loading @@ -262,20 +154,9 @@ public final class KeyChain { } } } } public static Bundle fromPrivateKey(PrivateKey privateKey) { private static X509Certificate toCertificate(byte[] bytes) { Bundle bundle = new Bundle(); String format = privateKey.getFormat(); if (!format.equals("PKCS#8")) { throw new IllegalArgumentException("Unsupported private key format " + format); } bundle.putByteArray(KEY_PKCS8, privateKey.getEncoded()); return bundle; } public static X509Certificate toCertificate(Bundle bundle) { byte[] bytes = bundle.getByteArray(KEY_X509); if (bytes == null) { if (bytes == null) { throw new IllegalArgumentException("not a certificate bundle"); throw new IllegalArgumentException("bytes == null"); } } try { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); Loading @@ -286,87 +167,11 @@ public final class KeyChain { } } } } public static Bundle fromCertificate(Certificate cert) { private static void ensureNotOnMainThread(Context context) { Bundle bundle = new Bundle(); String type = cert.getType(); if (!type.equals("X.509")) { throw new IllegalArgumentException("Unsupported certificate type " + type); } try { bundle.putByteArray(KEY_X509, cert.getEncoded()); } catch (CertificateEncodingException e) { throw new AssertionError(e); } return bundle; } private void ensureNotOnMainThread() { Looper looper = Looper.myLooper(); Looper looper = Looper.myLooper(); if (looper != null && looper == mContext.getMainLooper()) { if (looper != null && looper == context.getMainLooper()) { throw new IllegalStateException( throw new IllegalStateException( "calling this from your main thread can lead to deadlock"); "calling this from your main thread can lead to deadlock"); } } } } public Bundle findIssuer(X509Certificate cert) { if (cert == null) { throw new NullPointerException("cert == null"); } ensureNotOnMainThread(); // check and see if the issuer is already known to the default IndexedPKIXParameters IndexedPKIXParameters index = SSLParametersImpl.getDefaultIndexedPKIXParameters(); try { TrustAnchor anchor = index.findTrustAnchor(cert); if (anchor != null && anchor.getTrustedCert() != null) { X509Certificate ca = anchor.getTrustedCert(); return fromCertificate(ca); } } catch (CertPathValidatorException ignored) { } // otherwise, it might be a user installed CA in the keystore String alias; try { alias = mService.findIssuer(fromCertificate(cert)); } catch (RemoteException e) { throw new AssertionError(e); } if (alias == null) { Log.w(TAG, "Lookup failed for issuer"); return null; } Bundle bundle = get(alias, Credentials.CA_CERTIFICATE); Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null) { // permission still required return bundle; } // add the found CA to the index for next time X509Certificate ca = toCertificate(bundle); index.index(new TrustAnchor(ca, null)); return bundle; } public void close() { if (mIsBound) { mContext.unbindService(mServiceConnection); mIsBound = false; mGuard.close(); } } protected void finalize() throws Throwable { // note we don't close, we just warn. // shouldn't be doing I/O in a finalizer, // which the unbind would cause. try { if (mGuard != null) { mGuard.warnIfOpen(); } } finally { super.finalize(); } } } } keystore/java/android/security/KeyChainResult.java 0 → 100644 +72 −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; import android.content.Intent; 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. * * @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()"); } } } Loading
keystore/java/android/security/IKeyChainService.aidl +1 −3 Original line number Original line Diff line number Diff line Loading @@ -25,10 +25,8 @@ import android.os.Bundle; */ */ interface IKeyChainService { interface IKeyChainService { // APIs used by KeyChain // APIs used by KeyChain byte[] getPrivate(String alias, String authToken); byte[] getPrivateKey(String alias, String authToken); byte[] getCertificate(String alias, String authToken); byte[] getCertificate(String alias, String authToken); byte[] getCaCertificate(String alias, String authToken); String findIssuer(in Bundle cert); // APIs used by CertInstaller // APIs used by CertInstaller void installCaCertificate(in byte[] caCertificate); void installCaCertificate(in byte[] caCertificate); Loading
keystore/java/android/security/KeyChain.java +67 −262 Original line number Original line Diff line number Diff line Loading @@ -28,24 +28,19 @@ import android.os.Bundle; import android.os.IBinder; import android.os.IBinder; import android.os.Looper; import android.os.Looper; import android.os.RemoteException; import android.os.RemoteException; import android.util.Log; import dalvik.system.CloseGuard; import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.IOException; import java.security.KeyFactory; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PrivateKey; import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory; import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec; import org.apache.harmony.xnet.provider.jsse.IndexedPKIXParameters; import java.util.concurrent.BlockingQueue; import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl; import java.util.concurrent.LinkedBlockingQueue; /** /** * @hide * @hide Loading @@ -59,30 +54,6 @@ public final class KeyChain { */ */ public static final String ACCOUNT_TYPE = "com.android.keychain"; public static final String ACCOUNT_TYPE = "com.android.keychain"; /** * @hide Also used by KeyChainService implementation */ // TODO This non-localized CA string to be removed when CAs moved out of keystore public static final String CA_SUFFIX = " CA"; public static final String KEY_INTENT = "intent"; /** * Intentionally not public to leave open the future possibility * of hardware based keys. Callers should use {@link #toPrivateKey * toPrivateKey} in order to convert a bundle to a {@code * PrivateKey} */ private static final String KEY_PKCS8 = "pkcs8"; /** * Intentionally not public to leave open the future possibility * of hardware based certs. Callers should use {@link * #toCertificate toCertificate} in order to convert a bundle to a * {@code PrivateKey} */ private static final String KEY_X509 = "x509"; /** /** * Returns an {@code Intent} for use with {@link * Returns an {@code Intent} for use with {@link * android.app.Activity#startActivityForResult * android.app.Activity#startActivityForResult Loading @@ -97,99 +68,43 @@ public final class KeyChain { } } /** /** * Returns a new {@code KeyChain} instance. When the caller is * Returns a new {@code KeyChainResult} instance. * done using the {@code KeyChain}, it must be closed with {@link * #close()} or resource leaks will occur. */ */ public static KeyChain getInstance(Context context) throws InterruptedException { public static KeyChainResult get(Context context, String alias) return new KeyChain(context); throws InterruptedException, RemoteException { } private final AccountManager mAccountManager; private final Object mServiceLock = new Object(); private IKeyChainService mService; private boolean mIsBound; private Account mAccount; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mServiceLock) { mService = IKeyChainService.Stub.asInterface(service); mServiceLock.notifyAll(); // Account is created if necessary during binding of the IKeyChainService mAccount = mAccountManager.getAccountsByType(ACCOUNT_TYPE)[0]; } } @Override public void onServiceDisconnected(ComponentName name) { synchronized (mServiceLock) { mService = null; } } }; private final Context mContext; private final CloseGuard mGuard = CloseGuard.get(); private KeyChain(Context context) throws InterruptedException { if (context == null) { if (context == null) { throw new NullPointerException("context == null"); throw new NullPointerException("context == null"); } } mContext = context; if (alias == null) { ensureNotOnMainThread(); throw new NullPointerException("alias == null"); mAccountManager = AccountManager.get(mContext); mIsBound = mContext.bindService(new Intent(IKeyChainService.class.getName()), mServiceConnection, Context.BIND_AUTO_CREATE); if (!mIsBound) { throw new AssertionError(); } synchronized (mServiceLock) { // there is a race between binding on this thread and the // callback on the main thread. wait until binding is done // to be sure we have the mAccount initialized. if (mService == null) { mServiceLock.wait(); } } mGuard.open("close"); } /** * {@code Bundle} will contain {@link #KEY_INTENT} if user needs * to confirm application access to requested key. In the alias * does not exist or there is an error, null is * returned. Otherwise the {@code Bundle} contains information * representing the private key which can be interpreted with * {@link #toPrivateKey toPrivateKey}. * * non-null alias */ public Bundle getPrivate(String alias) { return get(alias, Credentials.USER_PRIVATE_KEY); } } ensureNotOnMainThread(context); public Bundle getCertificate(String alias) { final BlockingQueue<IKeyChainService> q = new LinkedBlockingQueue<IKeyChainService>(1); return get(alias, Credentials.USER_CERTIFICATE); ServiceConnection keyChainServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { try { q.put(IKeyChainService.Stub.asInterface(service)); } catch (InterruptedException e) { throw new AssertionError(e); } } public Bundle getCaCertificate(String alias) { return get(alias, Credentials.CA_CERTIFICATE); } } @Override public void onServiceDisconnected(ComponentName name) {} private Bundle get(String alias, String type) { }; if (alias == null) { boolean isBound = context.bindService(new Intent(IKeyChainService.class.getName()), throw new NullPointerException("alias == null"); keyChainServiceConnection, Context.BIND_AUTO_CREATE); if (!isBound) { throw new AssertionError("could not bind to KeyChainService"); } } ensureNotOnMainThread(); IKeyChainService keyChainService; try { keyChainService = q.take(); String authAlias = (type.equals(Credentials.CA_CERTIFICATE)) ? (alias + CA_SUFFIX) : alias; // Account is created if necessary during binding of the IKeyChainService AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount, AccountManager accountManager = AccountManager.get(context); authAlias, Account account = accountManager.getAccountsByType(ACCOUNT_TYPE)[0]; AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account, alias, false, false, null, null, null); null); Loading @@ -209,48 +124,25 @@ public final class KeyChain { // we don't want this Eclair compatability flag, // we don't want this Eclair compatability flag, // it will prevent onActivityResult from being called // it will prevent onActivityResult from being called intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); result.putParcelable(KEY_INTENT, intent); return new KeyChainResult(intent); return result; } } String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); if (authToken == null) { if (authToken == null) { throw new AssertionError("Invalid authtoken"); throw new AssertionError("Invalid authtoken"); } } byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken); byte[] bytes; byte[] certificateBytes = keyChainService.getCertificate(alias, authToken); try { return new KeyChainResult(toPrivateKey(privateKeyBytes), if (type.equals(Credentials.USER_PRIVATE_KEY)) { toCertificate(certificateBytes)); bytes = mService.getPrivate(alias, authToken); } finally { } else if (type.equals(Credentials.USER_CERTIFICATE)) { context.unbindService(keyChainServiceConnection); bytes = mService.getCertificate(alias, authToken); } else if (type.equals(Credentials.CA_CERTIFICATE)) { bytes = mService.getCaCertificate(alias, authToken); } else { throw new AssertionError(); } } catch (RemoteException e) { throw new AssertionError(e); } if (bytes == null) { throw new AssertionError(); } Bundle result = new Bundle(); if (type.equals(Credentials.USER_PRIVATE_KEY)) { result.putByteArray(KEY_PKCS8, bytes); } else if (type.equals(Credentials.USER_CERTIFICATE)) { result.putByteArray(KEY_X509, bytes); } else if (type.equals(Credentials.CA_CERTIFICATE)) { result.putByteArray(KEY_X509, bytes); } else { throw new AssertionError(); } } return result; } } public static PrivateKey toPrivateKey(Bundle bundle) { private static PrivateKey toPrivateKey(byte[] bytes) { byte[] bytes = bundle.getByteArray(KEY_PKCS8); if (bytes == null) { if (bytes == null) { throw new IllegalArgumentException("not a private key bundle"); throw new IllegalArgumentException("bytes == null"); } } try { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); Loading @@ -262,20 +154,9 @@ public final class KeyChain { } } } } public static Bundle fromPrivateKey(PrivateKey privateKey) { private static X509Certificate toCertificate(byte[] bytes) { Bundle bundle = new Bundle(); String format = privateKey.getFormat(); if (!format.equals("PKCS#8")) { throw new IllegalArgumentException("Unsupported private key format " + format); } bundle.putByteArray(KEY_PKCS8, privateKey.getEncoded()); return bundle; } public static X509Certificate toCertificate(Bundle bundle) { byte[] bytes = bundle.getByteArray(KEY_X509); if (bytes == null) { if (bytes == null) { throw new IllegalArgumentException("not a certificate bundle"); throw new IllegalArgumentException("bytes == null"); } } try { try { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); Loading @@ -286,87 +167,11 @@ public final class KeyChain { } } } } public static Bundle fromCertificate(Certificate cert) { private static void ensureNotOnMainThread(Context context) { Bundle bundle = new Bundle(); String type = cert.getType(); if (!type.equals("X.509")) { throw new IllegalArgumentException("Unsupported certificate type " + type); } try { bundle.putByteArray(KEY_X509, cert.getEncoded()); } catch (CertificateEncodingException e) { throw new AssertionError(e); } return bundle; } private void ensureNotOnMainThread() { Looper looper = Looper.myLooper(); Looper looper = Looper.myLooper(); if (looper != null && looper == mContext.getMainLooper()) { if (looper != null && looper == context.getMainLooper()) { throw new IllegalStateException( throw new IllegalStateException( "calling this from your main thread can lead to deadlock"); "calling this from your main thread can lead to deadlock"); } } } } public Bundle findIssuer(X509Certificate cert) { if (cert == null) { throw new NullPointerException("cert == null"); } ensureNotOnMainThread(); // check and see if the issuer is already known to the default IndexedPKIXParameters IndexedPKIXParameters index = SSLParametersImpl.getDefaultIndexedPKIXParameters(); try { TrustAnchor anchor = index.findTrustAnchor(cert); if (anchor != null && anchor.getTrustedCert() != null) { X509Certificate ca = anchor.getTrustedCert(); return fromCertificate(ca); } } catch (CertPathValidatorException ignored) { } // otherwise, it might be a user installed CA in the keystore String alias; try { alias = mService.findIssuer(fromCertificate(cert)); } catch (RemoteException e) { throw new AssertionError(e); } if (alias == null) { Log.w(TAG, "Lookup failed for issuer"); return null; } Bundle bundle = get(alias, Credentials.CA_CERTIFICATE); Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null) { // permission still required return bundle; } // add the found CA to the index for next time X509Certificate ca = toCertificate(bundle); index.index(new TrustAnchor(ca, null)); return bundle; } public void close() { if (mIsBound) { mContext.unbindService(mServiceConnection); mIsBound = false; mGuard.close(); } } protected void finalize() throws Throwable { // note we don't close, we just warn. // shouldn't be doing I/O in a finalizer, // which the unbind would cause. try { if (mGuard != null) { mGuard.warnIfOpen(); } } finally { super.finalize(); } } } }
keystore/java/android/security/KeyChainResult.java 0 → 100644 +72 −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; import android.content.Intent; 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. * * @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()"); } } }