diff options
author | Alex Klyubin <klyubin@google.com> | 2015-06-08 18:10:05 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-06-08 18:10:09 +0000 |
commit | 2c500236f4892b62b4df140f7e61f219a07371e0 (patch) | |
tree | f407cd0c036e6acd4fe986f63adcbc648b5b0fb8 | |
parent | 5b688e848c05d8ce0903348b3644184df3d5711a (diff) | |
parent | 5552c89fa9cb5ac72edbbcb5a71ef14a07f5ea11 (diff) | |
download | frameworks_base-2c500236f4892b62b4df140f7e61f219a07371e0.zip frameworks_base-2c500236f4892b62b4df140f7e61f219a07371e0.tar.gz frameworks_base-2c500236f4892b62b4df140f7e61f219a07371e0.tar.bz2 |
Merge "RSA encrypt with private key in Android Keystore." into mnc-dev
-rw-r--r-- | keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java | 28 | ||||
-rw-r--r-- | keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java | 69 |
2 files changed, 85 insertions, 12 deletions
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java index fd9bdb8..0e8d03e 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java @@ -56,6 +56,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after // doFinal finishes. private boolean mEncrypting; + private int mKeymasterPurposeOverride = -1; private AndroidKeyStoreKey mKey; private SecureRandom mRng; @@ -165,6 +166,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor mKeyStore.abort(operationToken); } mEncrypting = false; + mKeymasterPurposeOverride = -1; mKey = null; mRng = null; mOperationToken = null; @@ -210,9 +212,16 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( mRng, getAdditionalEntropyAmountForBegin()); + int purpose; + if (mKeymasterPurposeOverride != -1) { + purpose = mKeymasterPurposeOverride; + } else { + purpose = mEncrypting + ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT; + } OperationResult opResult = mKeyStore.begin( mKey.getAlias(), - mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT, + purpose, true, // permit aborting this operation if keystore runs out of resources keymasterInputArgs, additionalEntropy); @@ -346,11 +355,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor } catch (KeyStoreException e) { switch (e.getErrorCode()) { case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH: - throw new IllegalBlockSizeException(); + throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT: - throw new BadPaddingException(); + throw (BadPaddingException) new BadPaddingException().initCause(e); case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: - throw new AEADBadTagException(); + throw (AEADBadTagException) new AEADBadTagException().initCause(e); default: throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); } @@ -437,6 +446,17 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor } /** + * Overrides the default purpose/type of the crypto operation. + */ + protected final void setKeymasterPurposeOverride(int keymasterPurpose) { + mKeymasterPurposeOverride = keymasterPurpose; + } + + protected final int getKeymasterPurposeOverride() { + return mKeymasterPurposeOverride; + } + + /** * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this * cipher is initialized for decryption. */ diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java index f70c323..5643caf 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java @@ -60,6 +60,13 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } @Override + protected boolean isEncryptingUsingPrivateKeyPermitted() { + // RSA encryption with no padding using private key is is a way to implement raw RSA + // signatures. We have to support this. + return true; + } + + @Override protected void initAlgorithmSpecificParameters() throws InvalidKeyException {} @Override @@ -152,8 +159,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase paddedInput.length - bufferedInput.length, bufferedInput.length); } else { - // No need to pad input - paddedInput = bufferedInput; + // RI throws BadPaddingException in this scenario. INVALID_ARGUMENT below will + // be translated into BadPaddingException. + throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT, + "Message size (" + bufferedInput.length + " bytes) must be smaller than" + + " modulus (" + mModulusSizeBytes + " bytes)"); } return mDelegate.doFinal(paddedInput, 0, paddedInput.length); } @@ -412,14 +422,46 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } if (keystoreKey instanceof PrivateKey) { - if ((opmode != Cipher.DECRYPT_MODE) && (opmode != Cipher.UNWRAP_MODE)) { - throw new InvalidKeyException("Private key cannot be used with opmode: " + opmode - + ". Only DECRYPT_MODE and UNWRAP_MODE supported"); + // Private key + switch (opmode) { + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + // Permitted + break; + case Cipher.ENCRYPT_MODE: + if (!isEncryptingUsingPrivateKeyPermitted()) { + throw new InvalidKeyException( + "RSA private keys cannot be used with Cipher.ENCRYPT_MODE" + + ". Only RSA public keys supported for this mode"); + } + // JCA doesn't provide a way to generate raw RSA signatures (with arbitrary + // padding). Thus, encrypting with private key is used instead. + setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); + break; + case Cipher.WRAP_MODE: + throw new InvalidKeyException( + "RSA private keys cannot be used with Cipher.WRAP_MODE" + + ". Only RSA public keys supported for this mode"); + // break; + default: + throw new InvalidKeyException( + "RSA private keys cannot be used with opmode: " + opmode); } } else { - if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.WRAP_MODE)) { - throw new InvalidKeyException("Public key cannot be used with opmode: " + opmode - + ". Only ENCRYPT_MODE and WRAP_MODE supported"); + // Public key + switch (opmode) { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + // Permitted + return; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + throw new InvalidKeyException("RSA public keys cannot be used with opmode: " + + opmode + ". Only RSA private keys supported for this opmode."); + // break; + default: + throw new InvalidKeyException( + "RSA public keys cannot be used with opmode: " + opmode); } } @@ -438,6 +480,10 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase setKey(keystoreKey); } + protected boolean isEncryptingUsingPrivateKeyPermitted() { + return false; + } + @Override protected final void resetAll() { mModulusSizeBytes = -1; @@ -454,6 +500,13 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase @NonNull KeymasterArguments keymasterArgs) { keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); + int purposeOverride = getKeymasterPurposeOverride(); + if ((purposeOverride != -1) + && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN) + || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) { + // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE. + keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE); + } } @Override |