From 1b937eebdd38a7bea9b86831370a78bcded4d63c Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Tue, 16 Jun 2015 09:51:20 -0700 Subject: Harden and clean up KeyGenParameterSpec. This ensures that instances of this class are immutable and cleans up some flawed logic discovered when writing up CTS tests. Bug: 18088752 Change-Id: I7a27dbfc3445599bccabbd26e39535ce988a2ab2 --- .../security/keystore/KeyGenParameterSpec.java | 75 ++++++++++++---------- keystore/java/android/security/keystore/Utils.java | 32 +++++++++ 2 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 keystore/java/android/security/keystore/Utils.java (limited to 'keystore') diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 4c0631f..c7f7c9a 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; *

NOTE: The key material of the generated symmetric and private keys is not accessible. The key * material of the public keys is accessible. * + *

Instances of this class are immutable. + * *

Example: Asymmetric key pair

* 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,15 +103,15 @@ import javax.security.auth.x500.X500Principal; * *

Example: Symmetric key

* 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. *
 {@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)
+ *                 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
  *                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
  *                 .build());
  * SecretKey key = keyGenerator.generateKey();
@@ -169,10 +172,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 +196,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 +216,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 +231,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 +263,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
      */
     @NonNull
     public Date getCertificateNotBefore() {
-        return mCertificateNotBefore;
+        return Utils.cloneIfNotNull(mCertificateNotBefore);
     }
 
     /**
@@ -272,7 +272,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
      */
     @NonNull
     public Date getCertificateNotAfter() {
-        return mCertificateNotAfter;
+        return Utils.cloneIfNotNull(mCertificateNotAfter);
     }
 
     /**
@@ -281,7 +281,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
      */
     @Nullable
     public Date getKeyValidityStart() {
-        return mKeyValidityStart;
+        return Utils.cloneIfNotNull(mKeyValidityStart);
     }
 
     /**
@@ -290,7 +290,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
      */
     @Nullable
     public Date getKeyValidityForConsumptionEnd() {
-        return mKeyValidityForConsumptionEnd;
+        return Utils.cloneIfNotNull(mKeyValidityForConsumptionEnd);
     }
 
     /**
@@ -299,7 +299,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
      */
     @Nullable
     public Date getKeyValidityForOriginationEnd() {
-        return mKeyValidityForOriginationEnd;
+        return Utils.cloneIfNotNull(mKeyValidityForOriginationEnd);
     }
 
     /**
@@ -411,7 +411,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 +447,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 +462,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 +490,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}.
+         *
+         * 

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 +543,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 +558,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 +571,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Builder setKeyValidityStart(Date startDate) { - mKeyValidityStart = startDate; + mKeyValidityStart = Utils.cloneIfNotNull(startDate); return this; } @@ -594,7 +600,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Builder setKeyValidityForOriginationEnd(Date endDate) { - mKeyValidityForOriginationEnd = endDate; + mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(endDate); return this; } @@ -608,7 +614,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec { */ @NonNull public Builder setKeyValidityForConsumptionEnd(Date endDate) { - mKeyValidityForConsumptionEnd = endDate; + mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(endDate); return this; } @@ -768,14 +774,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/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; + } +} -- cgit v1.1