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

Commit 4f9f8537 authored by Alex Klyubin's avatar Alex Klyubin Committed by Android Git Automerger
Browse files

am c28e5ae1: am e1cf7583: Merge "Don\'t offer crypto ops for public keys of...

am c28e5ae1: am e1cf7583: Merge "Don\'t offer crypto ops for public keys of trusted cert entries." into mnc-dev

* commit 'c28e5ae1':
  Don't offer crypto ops for public keys of trusted cert entries.
parents f337641f c28e5ae1
Loading
Loading
Loading
Loading
+51 −8
Original line number Diff line number Diff line
@@ -140,21 +140,64 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
            throw new NullPointerException("alias == null");
        }

        byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
        if (certificate != null) {
            return wrapIntoKeyStoreCertificate(
                    Credentials.USER_PRIVATE_KEY + alias, toCertificate(certificate));
        byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
        if (encodedCert != null) {
            return getCertificateForPrivateKeyEntry(alias, encodedCert);
        }

        certificate = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
        if (certificate != null) {
            return wrapIntoKeyStoreCertificate(
                    Credentials.USER_PRIVATE_KEY + alias, toCertificate(certificate));
        encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
        if (encodedCert != null) {
            return getCertificateForTrustedCertificateEntry(encodedCert);
        }

        // This entry/alias does not contain a certificate.
        return null;
    }

    private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
        // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
        // there's no need to wrap this certificate as opposed to the certificate associated with
        // a private key entry.
        return toCertificate(encodedCert);
    }

    private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
        // All crypto algorithms offered by Android Keystore for its private keys must also
        // be offered for the corresponding public keys stored in the Android Keystore. The
        // complication is that the underlying keystore service operates only on full key pairs,
        // rather than just public keys or private keys. As a result, Android Keystore-backed
        // crypto can only be offered for public keys for which keystore contains the
        // corresponding private key. This is not the case for certificate-only entries (e.g.,
        // trusted certificates).
        //
        // getCertificate().getPublicKey() is the only way to obtain the public key
        // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
        // that the returned public key points to the underlying key pair / private key
        // when available.

        X509Certificate cert = toCertificate(encodedCert);
        if (cert == null) {
            // Failed to parse the certificate.
            return null;
        }

        String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
        if (mKeyStore.contains(privateKeyAlias)) {
            // As expected, keystore contains the private key corresponding to this public key. Wrap
            // the certificate so that its getPublicKey method returns an Android Keystore
            // PublicKey. This key will delegate crypto operations involving this public key to
            // Android Keystore when higher-priority providers do not offer these crypto
            // operations for this key.
            return wrapIntoKeyStoreCertificate(privateKeyAlias, cert);
        } else {
            // This KeyStore entry/alias is supposed to contain the private key corresponding to
            // the public key in this certificate, but it does not for some reason. It's probably a
            // bug. Let other providers handle crypto operations involving the public key returned
            // by this certificate's getPublicKey.
            return cert;
        }
    }

    /**
     * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
     * returned by the certificate contains information about the alias of the private key in