diff options
Diffstat (limited to 'keystore')
5 files changed, 111 insertions, 67 deletions
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index b93424d..af05578 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -104,8 +104,6 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato /* EC */ private static final int EC_DEFAULT_KEY_SIZE = 256; - private static final int EC_MIN_KEY_SIZE = 192; - private static final int EC_MAX_KEY_SIZE = 521; /* RSA */ private static final int RSA_DEFAULT_KEY_SIZE = 2048; @@ -115,16 +113,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE = new HashMap<String, Integer>(); private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>(); + private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>(); static { - // Aliases for NIST P-192 - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-192", 192); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp192r1", 192); - SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime192v1", 192); - // Aliases for NIST P-224 SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224); SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224); + // Aliases for NIST P-256 SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256); SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256); @@ -140,6 +135,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet()); Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES); + + SUPPORTED_EC_NIST_CURVE_SIZES.addAll( + new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values())); + Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES); } private final int mOriginalKeymasterAlgorithm; @@ -598,9 +597,9 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato throws InvalidAlgorithmParameterException { switch (keymasterAlgorithm) { case KeymasterDefs.KM_ALGORITHM_EC: - if (keySize < EC_MIN_KEY_SIZE || keySize > EC_MAX_KEY_SIZE) { - throw new InvalidAlgorithmParameterException("EC key size must be >= " - + EC_MIN_KEY_SIZE + " and <= " + EC_MAX_KEY_SIZE); + if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) { + throw new InvalidAlgorithmParameterException("Unsupported EC key size: " + + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES); } break; case KeymasterDefs.KM_ALGORITHM_RSA: @@ -624,7 +623,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato int keySizeBits, KeyGenParameterSpec spec) { // Constraints: - // 1. Key must be authorized for signing. + // 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 @@ -636,6 +635,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato // Key not authorized for signing return null; } + if (spec.isUserAuthenticationRequired()) { + // Key not authorized for use without user authentication + return null; + } if (!spec.isDigestsSpecified()) { // Key not authorized for any digests -- can't sign return null; diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 4c0631f..8d4bfcd 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -70,6 +70,8 @@ import javax.security.auth.x500.X500Principal; * <p>NOTE: The key material of the generated symmetric and private keys is not accessible. The key * material of the public keys is accessible. * + * <p>Instances of this class are immutable. + * * <p><h3>Example: Asymmetric key pair</h3> * The following example illustrates how to generate an EC key pair in the Android KeyStore system * under alias {@code key1} authorized to be used only for signing using SHA-256, SHA-384, @@ -79,11 +81,12 @@ import javax.security.auth.x500.X500Principal; * KeyProperties.KEY_ALGORITHM_EC, * "AndroidKeyStore"); * keyPairGenerator.initialize( - * new KeyGenParameterSpec.Builder("key1", + * new KeyGenParameterSpec.Builder( + * "key1", * KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY) - * .setDigests(KeyProperties.DIGEST_SHA256 - * | KeyProperties.DIGEST_SHA384 - * | KeyProperties.DIGEST_SHA512) + * .setDigests(KeyProperties.DIGEST_SHA256, + * KeyProperties.DIGEST_SHA384, + * KeyProperties.DIGEST_SHA512) * // Only permit this key to be used if the user authenticated * // within the last five minutes. * .setUserAuthenticationRequired(true) @@ -100,16 +103,17 @@ import javax.security.auth.x500.X500Principal; * * <p><h3>Example: Symmetric key</h3> * The following example illustrates how to generate an AES key in the Android KeyStore system under - * alias {@code key2} authorized to be used only for encryption/decryption in CTR mode. + * alias {@code key2} authorized to be used only for encryption/decryption in CBC mode with PKCS#7 + * padding. * <pre> {@code * KeyGenerator keyGenerator = KeyGenerator.getInstance( - * KeyProperties.KEY_ALGORITHM_HMAC_SHA256, + * KeyProperties.KEY_ALGORITHM_AES, * "AndroidKeyStore"); * keyGenerator.initialize( * new KeyGenParameterSpec.Builder("key2", * KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) - * .setBlockModes(KeyProperties.BLOCK_MODE_CTR) - * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + * .setBlockModes(KeyProperties.BLOCK_MODE_CBC) + * .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) * .build()); * SecretKey key = keyGenerator.generateKey(); * @@ -169,10 +173,6 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { int userAuthenticationValidityDurationSeconds) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); - } else if ((userAuthenticationValidityDurationSeconds < 0) - && (userAuthenticationValidityDurationSeconds != -1)) { - throw new IllegalArgumentException( - "userAuthenticationValidityDurationSeconds must not be negative"); } if (certificateSubject == null) { @@ -197,11 +197,11 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { mSpec = spec; mCertificateSubject = certificateSubject; mCertificateSerialNumber = certificateSerialNumber; - mCertificateNotBefore = certificateNotBefore; - mCertificateNotAfter = certificateNotAfter; - mKeyValidityStart = keyValidityStart; - mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; - mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; + mCertificateNotBefore = Utils.cloneIfNotNull(certificateNotBefore); + mCertificateNotAfter = Utils.cloneIfNotNull(certificateNotAfter); + mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); + mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); + mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); mPurposes = purposes; mDigests = ArrayUtils.cloneIfNotEmpty(digests); mEncryptionPaddings = @@ -217,6 +217,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { * Returns the alias that will be used in the {@code java.security.KeyStore} * in conjunction with the {@code AndroidKeyStore}. */ + @NonNull public String getKeystoreAlias() { return mKeystoreAlias; } @@ -231,10 +232,10 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { } /** - * Returns the {@link AlgorithmParameterSpec} that will be used for creation - * of the key pair. + * Returns the key algorithm-specific {@link AlgorithmParameterSpec} that will be used for + * creation of the key or {@code null} if algorithm-specific defaults should be used. */ - @NonNull + @Nullable public AlgorithmParameterSpec getAlgorithmParameterSpec() { return mSpec; } @@ -263,7 +264,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Date getCertificateNotBefore() { - return mCertificateNotBefore; + return Utils.cloneIfNotNull(mCertificateNotBefore); } /** @@ -272,7 +273,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Date getCertificateNotAfter() { - return mCertificateNotAfter; + return Utils.cloneIfNotNull(mCertificateNotAfter); } /** @@ -281,7 +282,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @Nullable public Date getKeyValidityStart() { - return mKeyValidityStart; + return Utils.cloneIfNotNull(mKeyValidityStart); } /** @@ -290,7 +291,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @Nullable public Date getKeyValidityForConsumptionEnd() { - return mKeyValidityForConsumptionEnd; + return Utils.cloneIfNotNull(mKeyValidityForConsumptionEnd); } /** @@ -299,7 +300,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @Nullable public Date getKeyValidityForOriginationEnd() { - return mKeyValidityForOriginationEnd; + return Utils.cloneIfNotNull(mKeyValidityForOriginationEnd); } /** @@ -411,7 +412,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { * restricted. * * @return duration in seconds or {@code -1} if authentication is required for every use of the - * key. + * key. * * @see #isUserAuthenticationRequired() */ @@ -447,7 +448,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { * Creates a new instance of the {@code Builder}. * * @param keystoreAlias alias of the entry in which the generated key will appear in - * Android KeyStore. + * Android KeyStore. Must not be empty. * @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be * used. Attempts to use the key for any other purpose will be rejected. * @@ -462,6 +463,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { public Builder(@NonNull String keystoreAlias, @KeyProperties.PurposeEnum int purposes) { if (keystoreAlias == null) { throw new NullPointerException("keystoreAlias == null"); + } else if (keystoreAlias.isEmpty()) { + throw new IllegalArgumentException("keystoreAlias must not be empty"); } mKeystoreAlias = keystoreAlias; mPurposes = purposes; @@ -488,7 +491,11 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { /** * Sets the algorithm-specific key generation parameters. For example, for RSA keys this may - * be an instance of {@link java.security.spec.RSAKeyGenParameterSpec}. + * be an instance of {@link java.security.spec.RSAKeyGenParameterSpec} whereas for EC keys + * this may be an instance of {@link java.security.spec.ECGenParameterSpec}. + * + * <p>These key generation parameters must match other explicitly set parameters (if any), + * such as key size. */ public Builder setAlgorithmParameterSpec(@NonNull AlgorithmParameterSpec spec) { if (spec == null) { @@ -537,7 +544,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { if (date == null) { throw new NullPointerException("date == null"); } - mCertificateNotBefore = date; + mCertificateNotBefore = Utils.cloneIfNotNull(date); return this; } @@ -552,7 +559,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { if (date == null) { throw new NullPointerException("date == null"); } - mCertificateNotAfter = date; + mCertificateNotAfter = Utils.cloneIfNotNull(date); return this; } @@ -565,7 +572,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Builder setKeyValidityStart(Date startDate) { - mKeyValidityStart = startDate; + mKeyValidityStart = Utils.cloneIfNotNull(startDate); return this; } @@ -594,7 +601,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Builder setKeyValidityForOriginationEnd(Date endDate) { - mKeyValidityForOriginationEnd = endDate; + mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(endDate); return this; } @@ -608,7 +615,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Builder setKeyValidityForConsumptionEnd(Date endDate) { - mKeyValidityForConsumptionEnd = endDate; + mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(endDate); return this; } @@ -768,14 +775,15 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { @NonNull public Builder setUserAuthenticationValidityDurationSeconds( @IntRange(from = -1) int seconds) { + if (seconds < -1) { + throw new IllegalArgumentException("seconds must be -1 or larger"); + } mUserAuthenticationValidityDurationSeconds = seconds; return this; } /** * Builds an instance of {@code KeyGenParameterSpec}. - * - * @throws IllegalArgumentException if a required field is missing */ @NonNull public KeyGenParameterSpec build() { diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java index e4f921e..03b4100 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -33,6 +33,8 @@ import javax.crypto.SecretKey; * is authorized for (e.g., only in {@code CBC} mode, or signing only), whether the key should be * encrypted at rest, the key's and validity start and end dates. * + * <p>Instances of this class are immutable. + * * <p><h3>Example: Symmetric Key</h3> * The following example illustrates how to obtain a {@code KeyInfo} describing the provided Android * Keystore {@link SecretKey}. @@ -102,9 +104,9 @@ public class KeyInfo implements KeySpec { mInsideSecureHardware = insideSecureHardware; mOrigin = origin; mKeySize = keySize; - mKeyValidityStart = keyValidityStart; - mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; - mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; + mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); + mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); + mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); mPurposes = purposes; mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); @@ -155,7 +157,7 @@ public class KeyInfo implements KeySpec { */ @Nullable public Date getKeyValidityStart() { - return mKeyValidityStart; + return Utils.cloneIfNotNull(mKeyValidityStart); } /** @@ -165,7 +167,7 @@ public class KeyInfo implements KeySpec { */ @Nullable public Date getKeyValidityForConsumptionEnd() { - return mKeyValidityForConsumptionEnd; + return Utils.cloneIfNotNull(mKeyValidityForConsumptionEnd); } /** @@ -175,7 +177,7 @@ public class KeyInfo implements KeySpec { */ @Nullable public Date getKeyValidityForOriginationEnd() { - return mKeyValidityForOriginationEnd; + return Utils.cloneIfNotNull(mKeyValidityForOriginationEnd); } /** diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 432fc12..1e0611c 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -47,6 +47,8 @@ import javax.crypto.Cipher; * * <p>NOTE: The key material of keys stored in the Android KeyStore is not accessible. * + * <p>Instances of this class are immutable. + * * <p><h3>Example: Symmetric Key</h3> * The following example illustrates how to import an AES key into the Android KeyStore under alias * {@code key1} authorized to be used only for encryption/decryption in CBC mode with PKCS#7 @@ -122,15 +124,9 @@ public final class KeyProtection implements ProtectionParameter { boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds) { - if ((userAuthenticationValidityDurationSeconds < 0) - && (userAuthenticationValidityDurationSeconds != -1)) { - throw new IllegalArgumentException( - "userAuthenticationValidityDurationSeconds must not be negative"); - } - - mKeyValidityStart = keyValidityStart; - mKeyValidityForOriginationEnd = keyValidityForOriginationEnd; - mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd; + mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); + mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); + mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); mPurposes = purposes; mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings)); @@ -150,7 +146,7 @@ public final class KeyProtection implements ProtectionParameter { */ @Nullable public Date getKeyValidityStart() { - return mKeyValidityStart; + return Utils.cloneIfNotNull(mKeyValidityStart); } /** @@ -160,7 +156,7 @@ public final class KeyProtection implements ProtectionParameter { */ @Nullable public Date getKeyValidityForConsumptionEnd() { - return mKeyValidityForConsumptionEnd; + return Utils.cloneIfNotNull(mKeyValidityForConsumptionEnd); } /** @@ -170,7 +166,7 @@ public final class KeyProtection implements ProtectionParameter { */ @Nullable public Date getKeyValidityForOriginationEnd() { - return mKeyValidityForOriginationEnd; + return Utils.cloneIfNotNull(mKeyValidityForOriginationEnd); } /** @@ -320,7 +316,7 @@ public final class KeyProtection implements ProtectionParameter { */ @NonNull public Builder setKeyValidityStart(Date startDate) { - mKeyValidityStart = startDate; + mKeyValidityStart = Utils.cloneIfNotNull(startDate); return this; } @@ -349,7 +345,7 @@ public final class KeyProtection implements ProtectionParameter { */ @NonNull public Builder setKeyValidityForOriginationEnd(Date endDate) { - mKeyValidityForOriginationEnd = endDate; + mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(endDate); return this; } @@ -363,7 +359,7 @@ public final class KeyProtection implements ProtectionParameter { */ @NonNull public Builder setKeyValidityForConsumptionEnd(Date endDate) { - mKeyValidityForConsumptionEnd = endDate; + mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(endDate); return this; } @@ -517,6 +513,9 @@ public final class KeyProtection implements ProtectionParameter { @NonNull public Builder setUserAuthenticationValidityDurationSeconds( @IntRange(from = -1) int seconds) { + if (seconds < -1) { + throw new IllegalArgumentException("seconds must be -1 or larger"); + } mUserAuthenticationValidityDurationSeconds = seconds; return this; } diff --git a/keystore/java/android/security/keystore/Utils.java b/keystore/java/android/security/keystore/Utils.java new file mode 100644 index 0000000..9bec682 --- /dev/null +++ b/keystore/java/android/security/keystore/Utils.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.keystore; + +import java.util.Date; + +/** + * Assorted utility methods. + * + * @hide + */ +abstract class Utils { + private Utils() {} + + static Date cloneIfNotNull(Date value) { + return (value != null) ? (Date) value.clone() : null; + } +} |