diff options
| author | 2018-04-02 23:13:25 +0000 | |
|---|---|---|
| committer | 2018-04-02 23:13:25 +0000 | |
| commit | 1a2743029adabca71c8c8c36ca3a9f68a776d080 (patch) | |
| tree | 5d9c394110a860a32e5fc24fa3d5aaa6b8029478 | |
| parent | 8700c434837f5ca834b35cb0d690d744e8bf1eea (diff) | |
| parent | 50228a647183c9315312f8f36ba849f8b1e6d3d0 (diff) | |
Merge "Revise secure key import API after review" into pi-dev
am: 50228a6471
Change-Id: I4fe07806b781d337f2df40f34fb75662187f4360
9 files changed, 157 insertions, 12 deletions
diff --git a/api/current.txt b/api/current.txt index bea1b2a1dc34..47ce87d0d03b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -38729,7 +38729,18 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean); } + public class SecureKeyImportUnavailableException extends java.security.ProviderException { + ctor public SecureKeyImportUnavailableException(); + ctor public SecureKeyImportUnavailableException(java.lang.String); + ctor public SecureKeyImportUnavailableException(java.lang.String, java.lang.Throwable); + ctor public SecureKeyImportUnavailableException(java.lang.Throwable); + } + public class StrongBoxUnavailableException extends java.security.ProviderException { + ctor public StrongBoxUnavailableException(); + ctor public StrongBoxUnavailableException(java.lang.String); + ctor public StrongBoxUnavailableException(java.lang.String, java.lang.Throwable); + ctor public StrongBoxUnavailableException(java.lang.Throwable); } public class UserNotAuthenticatedException extends java.security.InvalidKeyException { diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java index e4cf84afcb8f..cc805605bdf8 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java @@ -93,15 +93,17 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider { putSymmetricCipherImpl("AES/CTR/NoPadding", PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding"); - putSymmetricCipherImpl("DESede/CBC/NoPadding", + if ("true".equals(System.getProperty("supports3DES"))) { + putSymmetricCipherImpl("DESede/CBC/NoPadding", PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding"); - putSymmetricCipherImpl("DESede/CBC/PKCS7Padding", + putSymmetricCipherImpl("DESede/CBC/PKCS7Padding", PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding"); - putSymmetricCipherImpl("DESede/ECB/NoPadding", + putSymmetricCipherImpl("DESede/ECB/NoPadding", PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding"); - putSymmetricCipherImpl("DESede/ECB/PKCS7Padding", + putSymmetricCipherImpl("DESede/ECB/PKCS7Padding", PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding"); + } putSymmetricCipherImpl("AES/GCM/NoPadding", PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding"); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java index d68a33de2c61..5fc742afeaeb 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -475,6 +475,12 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato success = true; return keyPair; + } catch (ProviderException e) { + if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) { + throw new SecureKeyImportUnavailableException(e); + } else { + throw e; + } } finally { if (!success) { Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 2c45d43da3f1..9b7695daf5ff 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -67,6 +67,8 @@ public class AndroidKeyStoreProvider extends Provider { public AndroidKeyStoreProvider() { super(PROVIDER_NAME, 1.0, "Android KeyStore security provider"); + boolean supports3DES = "true".equals(System.getProperty("supports3DES")); + // java.security.KeyStore put("KeyStore.AndroidKeyStore", PACKAGE_NAME + ".AndroidKeyStoreSpi"); @@ -80,16 +82,21 @@ public class AndroidKeyStoreProvider extends Provider { // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); - put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1"); put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224"); put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256"); put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384"); put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512"); + if (supports3DES) { + put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede"); + } + // java.security.SecretKeyFactory putSecretKeyFactoryImpl("AES"); - putSecretKeyFactoryImpl("DESede"); + if (supports3DES) { + putSecretKeyFactoryImpl("DESede"); + } putSecretKeyFactoryImpl("HmacSHA1"); putSecretKeyFactoryImpl("HmacSHA224"); putSecretKeyFactoryImpl("HmacSHA256"); diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java index fc86ca0443b0..3e9853c455e5 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java @@ -26,6 +26,7 @@ import android.security.keymaster.KeymasterArguments; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; +import android.security.keystore.SecureKeyImportUnavailableException; import android.security.keystore.WrappedKeyEntry; import android.util.Log; @@ -755,7 +756,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { 0, // FIXME fingerprint id? mUid, new KeyCharacteristics()); - if (errorCode != KeyStore.NO_ERROR) { + if (errorCode == KeymasterDefs.KM_ERROR_UNIMPLEMENTED) { + throw new SecureKeyImportUnavailableException("Could not import wrapped key"); + } else if (errorCode != KeyStore.NO_ERROR) { throw new KeyStoreException("Failed to import wrapped key. Keystore error code: " + errorCode); } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index f54b6decc5c4..f12a659039ee 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -689,7 +689,11 @@ public abstract class KeyProperties { */ public static final int ORIGIN_UNKNOWN = 1 << 2; - /** Key was imported into the AndroidKeyStore in an encrypted wrapper */ + /** + * Key was imported into the AndroidKeyStore in an encrypted wrapper. Unlike imported keys, + * securely imported keys can be imported without appearing as plaintext in the device's host + * memory. + */ public static final int ORIGIN_SECURELY_IMPORTED = 1 << 3; diff --git a/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java b/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java new file mode 100644 index 000000000000..d1cc572bdaf7 --- /dev/null +++ b/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 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 android.security.KeyStore; +import android.security.KeyStoreException; + +import java.security.ProviderException; + +/** + * Indicates that the Keystore does not support securely importing wrapped keys. + */ +public class SecureKeyImportUnavailableException extends ProviderException { + + public SecureKeyImportUnavailableException() { + super(); + } + + public SecureKeyImportUnavailableException(String message) { + super(message, new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, + "Secure Key Import not available")); + } + + public SecureKeyImportUnavailableException(String message, Throwable cause) { + super(message, cause); + } + + public SecureKeyImportUnavailableException(Throwable cause) { + super(cause); + } +} + diff --git a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java index 66a77ed1a0de..6c7e9a9521e0 100644 --- a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java +++ b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java @@ -27,13 +27,22 @@ import java.security.ProviderException; */ public class StrongBoxUnavailableException extends ProviderException { - /** - * @hide - */ + public StrongBoxUnavailableException() { + super(); + } + public StrongBoxUnavailableException(String message) { super(message, new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, "No StrongBox available") ); } + + public StrongBoxUnavailableException(String message, Throwable cause) { + super(message, cause); + } + + public StrongBoxUnavailableException(Throwable cause) { + super(cause); + } } diff --git a/keystore/java/android/security/keystore/WrappedKeyEntry.java b/keystore/java/android/security/keystore/WrappedKeyEntry.java index a8f4afe7a201..7155686b1385 100644 --- a/keystore/java/android/security/keystore/WrappedKeyEntry.java +++ b/keystore/java/android/security/keystore/WrappedKeyEntry.java @@ -21,7 +21,55 @@ import java.security.KeyStore.Entry; import java.security.spec.AlgorithmParameterSpec; /** - * An {@link Entry} that holds a wrapped key. + * An {@link Entry} that holds a wrapped key. Wrapped keys contain encrypted key data and + * description information that can be used to securely import key material into a hardware-backed + * Keystore. + * + * <p> + * The wrapped key is in DER-encoded ASN.1 format, specified by the following schema: + * </p> + * + * <pre> + * KeyDescription ::= SEQUENCE( + * keyFormat INTEGER, # Values from KeyFormat enum. + * keyParams AuthorizationList, + * ) + * + * SecureKeyWrapper ::= SEQUENCE( + * version INTEGER, # Contains value 0 + * encryptedTransportKey OCTET_STRING, + * initializationVector OCTET_STRING, + * keyDescription KeyDescription, + * encryptedKey OCTET_STRING, + * tag OCTET_STRING + * ) + * </pre> + * <ul> + * <li>keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext + * key material. + * </li> + * <li>keyParams is the characteristics of the key to be imported (as with generateKey or + * importKey). If the secure import is successful, these characteristics must be + * associated with the key exactly as if the key material had been insecurely imported + * with importKey. See <a href="https://developer.android.com/training/articles/security-key-attestation.html#certificate_schema">Key Attestation</a> for the AuthorizationList format. + * </li> + * <li>encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted + * in RSA-OAEP mode (SHA-256 digest, SHA-1 MGF1 digest) with the wrapping key specified by + * wrappingKeyBlob. + * </li> + * <li>keyDescription is a KeyDescription, above. + * </li> + * <li>encryptedKey is the key material of the key to be imported, in format keyFormat, and + * encrypted with encryptedEphemeralKey in AES-GCM mode, with the DER-encoded + * representation of keyDescription provided as additional authenticated data. + * </li> + * <li>tag is the tag produced by the AES-GCM encryption of encryptedKey. + * </li> + *</ul> + * + * <p> + * Imported wrapped keys will have KeymasterDefs.KM_ORIGIN_SECURELY_IMPORTED + * </p> */ public class WrappedKeyEntry implements Entry { @@ -30,6 +78,14 @@ public class WrappedKeyEntry implements Entry { private final String mTransformation; private final AlgorithmParameterSpec mAlgorithmParameterSpec; + /** + * Constructs a {@link WrappedKeyEntry} with a binary wrapped key. + * + * @param wrappedKeyBytes ASN.1 DER encoded wrapped key + * @param wrappingKeyAlias identifies the private key that can unwrap the wrapped key + * @param transformation used to unwrap the key. ex: "RSA/ECB/OAEPPadding" + * @param algorithmParameterSpec spec for the private key used to unwrap the wrapped key + */ public WrappedKeyEntry(byte[] wrappedKeyBytes, String wrappingKeyAlias, String transformation, AlgorithmParameterSpec algorithmParameterSpec) { mWrappedKeyBytes = wrappedKeyBytes; @@ -54,3 +110,4 @@ public class WrappedKeyEntry implements Entry { return mAlgorithmParameterSpec; } } + |