summaryrefslogtreecommitdiffstats
path: root/keystore
diff options
context:
space:
mode:
authorAlex Klyubin <klyubin@google.com>2015-06-04 14:45:54 -0700
committerAlex Klyubin <klyubin@google.com>2015-06-04 17:11:28 -0700
commit5552c89fa9cb5ac72edbbcb5a71ef14a07f5ea11 (patch)
treeff25ceae092d5727eeb17b891410e8460baf49f1 /keystore
parent3a4656e8874a7bfa11ff8c68dfca87b7f399ff36 (diff)
downloadframeworks_base-5552c89fa9cb5ac72edbbcb5a71ef14a07f5ea11.zip
frameworks_base-5552c89fa9cb5ac72edbbcb5a71ef14a07f5ea11.tar.gz
frameworks_base-5552c89fa9cb5ac72edbbcb5a71ef14a07f5ea11.tar.bz2
RSA encrypt with private key in Android Keystore.
This adds support for RSA encryption using private key and no padding. This mode of operation is needed because JCA does not offer an RSA Signature primitive that does not apply padding. Bug: 18088752 Bug: 20912868 Change-Id: I0b481b4c19916f601aa270fada5eabfb12987e8d
Diffstat (limited to 'keystore')
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java28
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java69
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