diff options
Diffstat (limited to 'keystore')
3 files changed, 68 insertions, 9 deletions
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 3058bd3..459514d 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -287,6 +287,22 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()); mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster( spec.getEncryptionPaddings()); + if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) + && (spec.isRandomizedEncryptionRequired())) { + for (int keymasterPadding : mKeymasterEncryptionPaddings) { + if (!KeymasterUtils + .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( + keymasterPadding)) { + throw new InvalidAlgorithmParameterException( + "Randomized encryption (IND-CPA) required but may be violated" + + " by padding scheme: " + + KeyProperties.EncryptionPadding.fromKeymaster( + keymasterPadding) + + ". See " + KeyGenParameterSpec.class.getName() + + " documentation."); + } + } + } mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster( spec.getSignaturePaddings()); if (spec.isDigestsSpecified()) { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index de483f4..dc8f1e3 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -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 diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 0006601..3cd3f2a 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -74,7 +74,7 @@ public abstract class KeymasterUtils { return true; default: throw new IllegalArgumentException( - "Unsupported encryption padding scheme: " + keymasterPadding); + "Unsupported asymmetric encryption padding scheme: " + keymasterPadding); } } |