diff options
author | Alex Klyubin <klyubin@google.com> | 2015-06-04 13:04:43 -0700 |
---|---|---|
committer | Alex Klyubin <klyubin@google.com> | 2015-06-04 13:19:50 -0700 |
commit | 97a27a73e6c5f5800303596ceebf314d90429d35 (patch) | |
tree | dd19b573980a3f1862a6b5c15cefbbf40551c298 /keystore | |
parent | cb9400aa23b81f12d8af198187fc799d6bcf164a (diff) | |
download | frameworks_base-97a27a73e6c5f5800303596ceebf314d90429d35.zip frameworks_base-97a27a73e6c5f5800303596ceebf314d90429d35.tar.gz frameworks_base-97a27a73e6c5f5800303596ceebf314d90429d35.tar.bz2 |
Export KeyFactory backed by Android Keystore.
The KeyFactory can be used to obtain information (KeyInfo) about
Android Keystore private keys.
Bug: 18088752
Change-Id: Ied1a69928f391537de6765cef7dc7d7241cf62bb
Diffstat (limited to 'keystore')
3 files changed, 133 insertions, 17 deletions
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java new file mode 100644 index 0000000..20db41b --- /dev/null +++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java @@ -0,0 +1,91 @@ +/* + * 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 android.security.Credentials; +import android.security.KeyStore; + +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactorySpi; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; + +/** + * {@link KeyFactorySpi} backed by Android KeyStore. + * + * @hide + */ +public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi { + + private final KeyStore mKeyStore = KeyStore.getInstance(); + + @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)) { + 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()); + } + String keyAliasInKeystore = ((AndroidKeyStoreKey) 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; + } + + @Override + protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException { + throw new UnsupportedOperationException( + "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with" + + " " + KeyGenParameterSpec.class.getName()); + } + + @Override + protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException { + throw new UnsupportedOperationException( + "To generate a key pair in Android KeyStore, use KeyPairGenerator initialized with" + + " " + KeyGenParameterSpec.class.getName()); + } + + @Override + protected Key engineTranslateKey(Key arg0) throws InvalidKeyException { + throw new UnsupportedOperationException( + "To import a key into Android KeyStore, use KeyStore.setEntry with " + + KeyProtection.class.getName()); + } +} diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java index 649a515..1cd49d7 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -52,6 +52,10 @@ public class AndroidKeyStoreProvider extends Provider { put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); + // java.security.KeyFactory + putKeyFactoryImpl("EC"); + putKeyFactoryImpl("RSA"); + // javax.crypto.KeyGenerator put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1"); @@ -100,6 +104,10 @@ public class AndroidKeyStoreProvider extends Provider { put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi"); } + private void putKeyFactoryImpl(String algorithm) { + put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi"); + } + /** * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto * primitive. diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java index 455f170..8b00821 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java @@ -21,9 +21,8 @@ import android.security.KeyStore; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterDefs; -import libcore.util.EmptyArray; - import java.security.InvalidKeyException; +import java.security.ProviderException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.ArrayList; @@ -35,7 +34,7 @@ import javax.crypto.SecretKeyFactorySpi; import javax.crypto.spec.SecretKeySpec; /** - * {@link SecretKeyFactorySpi} backed by Android KeyStore. + * {@link SecretKeyFactorySpi} backed by Android Keystore. * * @hide */ @@ -60,7 +59,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { if (!KeyInfo.class.equals(keySpecClass)) { throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); } - String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias(); + String keyAliasInKeystore = ((AndroidKeyStoreKey) key).getAlias(); String entryAlias; if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) { entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length()); @@ -68,11 +67,15 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); } + return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore); + } + + static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore) { KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); int errorCode = - mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics); + keyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics); if (errorCode != KeyStore.NO_ERROR) { - throw new InvalidKeySpecException("Failed to obtain information about key." + throw new ProviderException("Failed to obtain information about key." + " Keystore error: " + errorCode); } @@ -81,6 +84,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { int keySize; @KeyProperties.PurposeEnum int purposes; String[] encryptionPaddings; + String[] signaturePaddings; @KeyProperties.DigestEnum String[] digests; @KeyProperties.BlockModeEnum String[] blockModes; int keymasterSwEnforcedUserAuthenticators; @@ -95,29 +99,40 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { origin = KeyProperties.Origin.fromKeymaster( keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1)); } else { - throw new InvalidKeySpecException("Key origin not available"); + throw new ProviderException("Key origin not available"); } Integer keySizeInteger = keyCharacteristics.getInteger(KeymasterDefs.KM_TAG_KEY_SIZE); if (keySizeInteger == null) { - throw new InvalidKeySpecException("Key size not available"); + throw new ProviderException("Key size not available"); } keySize = keySizeInteger; purposes = KeyProperties.Purpose.allFromKeymaster( keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PURPOSE)); List<String> encryptionPaddingsList = new ArrayList<String>(); + List<String> signaturePaddingsList = new ArrayList<String>(); + // Keymaster stores both types of paddings in the same array -- we split it into two. for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) { - @KeyProperties.EncryptionPaddingEnum String jcaPadding; try { - jcaPadding = KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); + @KeyProperties.EncryptionPaddingEnum String jcaPadding = + KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding); + encryptionPaddingsList.add(jcaPadding); } catch (IllegalArgumentException e) { - throw new InvalidKeySpecException( - "Unsupported encryption padding: " + keymasterPadding); + try { + @KeyProperties.SignaturePaddingEnum String padding = + KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding); + signaturePaddingsList.add(padding); + } catch (IllegalArgumentException e2) { + throw new ProviderException( + "Unsupported encryption padding: " + keymasterPadding); + } } - encryptionPaddingsList.add(jcaPadding); + } encryptionPaddings = encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]); + signaturePaddings = + signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]); digests = KeyProperties.Digest.allFromKeymaster( keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST)); @@ -128,7 +143,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { keymasterHwEnforcedUserAuthenticators = keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0); } catch (IllegalArgumentException e) { - throw new InvalidKeySpecException("Unsupported key characteristic", e); + throw new ProviderException("Unsupported key characteristic", e); } Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME); @@ -164,7 +179,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { keyValidityForConsumptionEnd, purposes, encryptionPaddings, - EmptyArray.STRING, // no signature paddings -- this is symmetric crypto + signaturePaddings, digests, blockModes, userAuthenticationRequired, @@ -175,12 +190,14 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { @Override protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException { throw new UnsupportedOperationException( - "Key import into Android KeyStore is not supported"); + "To generate secret key in Android KeyStore, use KeyGenerator initialized with " + + KeyGenParameterSpec.class.getName()); } @Override protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException { throw new UnsupportedOperationException( - "Key import into Android KeyStore is not supported"); + "To import a secret key into Android KeyStore, use KeyStore.setEntry with " + + KeyProtection.class.getName()); } } |