From 25d2270704e246d897596c991a36233cdc620790 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 25 Jun 2015 09:04:37 -0700 Subject: Support RSA encrypt using private key and PKCS#1 paddding. Some apps such as OpenVPN Connect for some reason generate PKCS#1-padded RSA signatures using Cipher (initialized in Cipher.ENCRYPT_MODE with private key!) instead of using Signature. Unfortunately, RI supports this strange behavior and previous releases of Android Keystore did the same. So, we have to continue supporting this craziness. Bug: 22083023 Change-Id: Ife3950d0d4ceb4e44e08014635312c1252878b69 --- .../keystore/AndroidKeyStoreCipherSpiBase.java | 15 +++++ .../keystore/AndroidKeyStoreRSACipherSpi.java | 69 ++++++++++++++++------ 2 files changed, 66 insertions(+), 18 deletions(-) (limited to 'keystore') diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java index 50f3ed4..38cacd0 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java @@ -731,6 +731,21 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor return mMainDataStreamer.getProducedOutputSizeBytes(); } + static String opmodeToString(int opmode) { + switch (opmode) { + case Cipher.ENCRYPT_MODE: + return "ENCRYPT_MODE"; + case Cipher.DECRYPT_MODE: + return "DECRYPT_MODE"; + case Cipher.WRAP_MODE: + return "WRAP_MODE"; + case Cipher.UNWRAP_MODE: + return "UNWRAP_MODE"; + default: + return String.valueOf(opmode); + } + } + // The methods below need to be implemented by subclasses. /** diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java index 8e58195..94ed8b4 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java @@ -60,9 +60,10 @@ 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. + protected boolean adjustConfigForEncryptingWithPrivateKey() { + // RSA encryption with no padding using private key is a way to implement raw RSA + // signatures which JCA does not expose via Signature. We thus have to support this. + setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); return true; } @@ -198,6 +199,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } @Override + protected boolean adjustConfigForEncryptingWithPrivateKey() { + // RSA encryption with PCKS#1 padding using private key is a way to implement RSA + // signatures with PKCS#1 padding. We have to support this for legacy reasons. + setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN); + setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); + return true; + } + + @Override protected void initAlgorithmSpecificParameters() throws InvalidKeyException {} @Override @@ -425,6 +435,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } private final int mKeymasterPadding; + private int mKeymasterPaddingOverride; private int mModulusSizeBytes = -1; @@ -458,20 +469,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase // Permitted break; case Cipher.ENCRYPT_MODE: - if (!isEncryptingUsingPrivateKeyPermitted()) { + case Cipher.WRAP_MODE: + if (!adjustConfigForEncryptingWithPrivateKey()) { throw new InvalidKeyException( - "RSA private keys cannot be used with Cipher.ENCRYPT_MODE" + "RSA private keys cannot be used with " + opmodeToString(opmode) + + " and padding " + + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding) + ". 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); @@ -485,12 +491,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase break; 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."); + throw new InvalidKeyException( + "RSA public keys cannot be used with " + opmodeToString(opmode) + + " and padding " + + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding) + + ". Only RSA private keys supported for this opmode."); // break; default: throw new InvalidKeyException( - "RSA public keys cannot be used with opmode: " + opmode); + "RSA public keys cannot be used with " + opmodeToString(opmode)); } } @@ -511,13 +520,22 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase setKey(keystoreKey); } - protected boolean isEncryptingUsingPrivateKeyPermitted() { + /** + * Adjusts the configuration of this cipher for encrypting using the private key. + * + *

The default implementation does nothing and refuses to adjust the configuration. + * + * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting + * using private key is not permitted for this cipher. + */ + protected boolean adjustConfigForEncryptingWithPrivateKey() { return false; } @Override protected final void resetAll() { mModulusSizeBytes = -1; + mKeymasterPaddingOverride = -1; super.resetAll(); } @@ -530,7 +548,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase protected void addAlgorithmSpecificParametersToBegin( @NonNull KeymasterArguments keymasterArgs) { keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA); - keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding); + int keymasterPadding = getKeymasterPaddingOverride(); + if (keymasterPadding == -1) { + keymasterPadding = mKeymasterPadding; + } + keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding); int purposeOverride = getKeymasterPurposeOverride(); if ((purposeOverride != -1) && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN) @@ -568,4 +590,15 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } return mModulusSizeBytes; } + + /** + * Overrides the default padding of the crypto operation. + */ + protected final void setKeymasterPaddingOverride(int keymasterPadding) { + mKeymasterPaddingOverride = keymasterPadding; + } + + protected final int getKeymasterPaddingOverride() { + return mKeymasterPaddingOverride; + } } -- cgit v1.1