diff options
author | Rubin Xu <rubinxu@google.com> | 2016-03-23 12:13:22 +0000 |
---|---|---|
committer | Rubin Xu <rubinxu@google.com> | 2016-03-30 11:57:58 +0100 |
commit | b43659170824dd8d753d9249fe6ccfd37c6221ae (patch) | |
tree | c9b45209e1828431ae5cbe3043fb6b522db65a24 /keystore/java/android/security/KeyChain.java | |
parent | 6b69b122025631290380f4350f7bd0074bad10dd (diff) |
Add DevicePolicyManager API to install a client cert chain.
When installing a keypair the caller will have the option to specify a
certificate chain which will later be returned to whoever requests access
to the keypair via KeyChain.
Bug: 18239590
Change-Id: Id21ef026e31537db38d891cb9b712dd4fe7159c7
Diffstat (limited to 'keystore/java/android/security/KeyChain.java')
-rw-r--r-- | keystore/java/android/security/KeyChain.java | 54 |
1 files changed, 48 insertions, 6 deletions
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 0886487f70cf..cce58c2096f3 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -42,6 +42,8 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.concurrent.BlockingQueue; @@ -389,7 +391,12 @@ public final class KeyChain { /** * Returns the {@code X509Certificate} chain for the requested - * alias, or null if no there is no result. + * alias, or null if there is no result. + * <p> + * <strong>Note:</strong> If a certificate chain was explicitly specified when the alias was + * installed, this method will return that chain. If only the client certificate was specified + * at the installation time, this method will try to build a certificate chain using all + * available trust anchors (preinstalled and user-added). * * <p> This method may block while waiting for a connection to another process, and must never * be called from the main thread. @@ -413,11 +420,31 @@ public final class KeyChain { if (certificateBytes == null) { return null; } - - TrustedCertificateStore store = new TrustedCertificateStore(); - List<X509Certificate> chain = store - .getCertificateChain(toCertificate(certificateBytes)); - return chain.toArray(new X509Certificate[chain.size()]); + X509Certificate leafCert = toCertificate(certificateBytes); + final byte[] certChainBytes = keyChainService.getCaCertificates(alias); + // If the keypair is installed with a certificate chain by either + // DevicePolicyManager.installKeyPair or CertInstaller, return that chain. + if (certChainBytes != null && certChainBytes.length != 0) { + Collection<X509Certificate> chain = toCertificates(certChainBytes); + ArrayList<X509Certificate> fullChain = new ArrayList<>(chain.size() + 1); + fullChain.add(leafCert); + fullChain.addAll(chain); + return fullChain.toArray(new X509Certificate[fullChain.size()]); + } else { + // If there isn't a certificate chain, either due to a pre-existing keypair + // installed before N, or no chain is explicitly installed under the new logic, + // fall back to old behavior of constructing the chain from trusted credentials. + // + // This logic exists to maintain old behaviour for already installed keypair, at + // the cost of potentially returning extra certificate chain for new clients who + // explicitly installed only the client certificate without a chain. The latter + // case is actually no different from pre-N behaviour of getCertificateChain(), + // in that sense this change introduces no regression. Besides the returned chain + // is still valid so the consumer of the chain should have no problem verifying it. + TrustedCertificateStore store = new TrustedCertificateStore(); + List<X509Certificate> chain = store.getCertificateChain(leafCert); + return chain.toArray(new X509Certificate[chain.size()]); + } } catch (CertificateException e) { throw new KeyChainException(e); } catch (RemoteException e) { @@ -486,6 +513,21 @@ public final class KeyChain { } } + /** @hide */ + @NonNull + public static Collection<X509Certificate> toCertificates(@NonNull byte[] bytes) { + if (bytes == null) { + throw new IllegalArgumentException("bytes == null"); + } + try { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + return (Collection<X509Certificate>) certFactory.generateCertificates( + new ByteArrayInputStream(bytes)); + } catch (CertificateException e) { + throw new AssertionError(e); + } + } + /** * @hide for reuse by CertInstaller and Settings. * @see KeyChain#bind |