diff options
Diffstat (limited to 'keystore')
5 files changed, 96 insertions, 39 deletions
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java index fc53451..83131ed 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java @@ -446,7 +446,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor inputLen = input.length; src.get(input); } - super.engineUpdateAAD(input, inputOffset, inputLen); + engineUpdateAAD(input, inputOffset, inputLen); } @Override diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java index 20db41b..250bad7 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java @@ -24,9 +24,12 @@ import java.security.Key; import java.security.KeyFactorySpi; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.security.spec.X509EncodedKeySpec; /** * {@link KeyFactorySpi} backed by Android KeyStore. @@ -40,32 +43,83 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi { @Override protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass) throws InvalidKeySpecException { - if (keySpecClass == null) { - throw new InvalidKeySpecException("keySpecClass == null"); - } - if (!(key instanceof AndroidKeyStorePrivateKey)) { - throw new InvalidKeySpecException("Only Android KeyStore private keys supported: " + - ((key != null) ? key.getClass().getName() : "null")); - } - if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpecClass)) { + if (key == null) { + throw new InvalidKeySpecException("key == null"); + } else if ((!(key instanceof AndroidKeyStorePrivateKey)) + && (!(key instanceof AndroidKeyStorePublicKey))) { throw new InvalidKeySpecException( - "Key material export of Android KeyStore keys is not supported"); - } - if (!KeyInfo.class.equals(keySpecClass)) { - throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); + "Unsupported key type: " + key.getClass().getName() + + ". This KeyFactory supports only Android Keystore asymmetric keys"); } - String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias(); - String entryAlias; - if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) { - entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length()); + + // key is an Android Keystore private or public key + + if (keySpecClass == null) { + throw new InvalidKeySpecException("keySpecClass == null"); + } else if (KeyInfo.class.equals(keySpecClass)) { + if (!(key instanceof AndroidKeyStorePrivateKey)) { + throw new InvalidKeySpecException( + "Unsupported key type: " + key.getClass().getName() + + ". KeyInfo can be obtained only for Android Keystore private keys"); + } + String keyAliasInKeystore = ((AndroidKeyStorePrivateKey) key).getAlias(); + String entryAlias; + if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) { + entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length()); + } else { + throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); + } + @SuppressWarnings("unchecked") + T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo( + mKeyStore, entryAlias, keyAliasInKeystore); + return result; + } else if (X509EncodedKeySpec.class.equals(keySpecClass)) { + if (!(key instanceof AndroidKeyStorePublicKey)) { + throw new InvalidKeySpecException( + "Unsupported key type: " + key.getClass().getName() + + ". X509EncodedKeySpec can be obtained only for Android Keystore public" + + " keys"); + } + @SuppressWarnings("unchecked") + T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded()); + return result; + } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) { + if (key instanceof AndroidKeyStorePrivateKey) { + throw new InvalidKeySpecException( + "Key material export of Android Keystore private keys is not supported"); + } else { + throw new InvalidKeySpecException( + "Cannot export key material of public key in PKCS#8 format." + + " Only X.509 format (X509EncodedKeySpec) supported for public keys."); + } + } else if (RSAPublicKeySpec.class.equals(keySpecClass)) { + if (key instanceof AndroidKeyStoreRSAPublicKey) { + AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key; + @SuppressWarnings("unchecked") + T result = + (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent()); + return result; + } else { + throw new InvalidKeySpecException( + "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " " + + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") + + " key"); + } + } else if (ECPublicKeySpec.class.equals(keySpecClass)) { + if (key instanceof AndroidKeyStoreECPublicKey) { + AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key; + @SuppressWarnings("unchecked") + T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams()); + return result; + } else { + throw new InvalidKeySpecException( + "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " " + + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") + + " key"); + } } else { - throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); + throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); } - - @SuppressWarnings("unchecked") - T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo( - mKeyStore, entryAlias, keyAliasInKeystore); - return result; } @Override diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index 2055cdb..f7ff07f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -624,9 +624,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato // Constraints: // 1. Key must be authorized for signing without user authentication. // 2. Signature digest must be one of key's authorized digests. - // 3. For RSA keys, the digest output size must not exceed modulus size minus space needed - // for RSA PKCS#1 signature padding (about 29 bytes: minimum 10 bytes of padding + 15--19 - // bytes overhead for encoding digest OID and digest value in DER). + // 3. For RSA keys, the digest output size must not exceed modulus size minus space overhead + // of RSA PKCS#1 signature padding scheme (about 30 bytes). // 4. For EC keys, the there is no point in using a digest whose output size is longer than // key/field size because the digest will be truncated to that size. @@ -727,10 +726,12 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato spec.getDigests(), AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests()); - // The amount of space available for the digest is less than modulus size because - // padding must be at least 10 bytes long, and then there's also the 15--19 - // bytes overhead for encoding digest OID and digest value in DER. - int maxDigestOutputSizeBits = keySizeBits - 29 * 8; + // The amount of space available for the digest is less than modulus size by about + // 30 bytes because padding must be at least 11 bytes long (00 || 01 || PS || 00, + // where PS must be at least 8 bytes long), and then there's also the 15--19 bytes + // overhead (depending the on chosen digest) for encoding digest OID and digest + // value in DER. + int maxDigestOutputSizeBits = keySizeBits - 30 * 8; int bestKeymasterDigest = -1; int bestDigestOutputSizeBits = -1; for (int keymasterDigest : availableKeymasterDigests) { diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 1732db9..3d23399 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -634,11 +634,12 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { /** * Sets the set of digests algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which - * the key can be used when signing/verifying. Attempts to use the key with any other digest - * algorithm will be rejected. + * the key can be used. Attempts to use the key with any other digest algorithm will be + * rejected. * - * <p>This must be specified for keys which are used for signing/verification. For HMAC - * keys, the set of digests defaults to the digest associated with the key algorithm (e.g., + * <p>This must be specified for signing/verification keys and RSA encryption/decryption + * keys used with RSA OAEP padding scheme because these operations involve a digest. For + * HMAC keys, the default is the digest associated with the key algorithm (e.g., * {@code SHA-256} for key algorithm {@code HmacSHA256}). * * <p>For private keys used for TLS/SSL client or server authentication it is usually diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index b7a2a0b..5b4b3e7 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -417,12 +417,13 @@ public final class KeyProtection implements ProtectionParameter { /** * Sets the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384}) with which the - * key can be used when signing/verifying or generating MACs. Attempts to use the key with - * any other digest algorithm will be rejected. + * key can be used. Attempts to use the key with any other digest algorithm will be + * rejected. * - * <p>For HMAC keys, the default is the digest algorithm specified in - * {@link Key#getAlgorithm()}. For asymmetric signing keys the set of digest algorithms - * must be specified. + * <p>This must be specified for signing/verification keys and RSA encryption/decryption + * keys used with RSA OAEP padding scheme because these operations involve a digest. For + * HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()} (e.g., + * {@code SHA-256} for key algorithm {@code HmacSHA256}). * * <p>For private keys used for TLS/SSL client or server authentication it is usually * necessary to authorize the use of no digest ({@link KeyProperties#DIGEST_NONE}). This is |