1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.security.keystore; 18 19 import android.security.Credentials; 20 import android.security.KeyStore; 21 22 import java.security.InvalidKeyException; 23 import java.security.Key; 24 import java.security.KeyFactorySpi; 25 import java.security.PrivateKey; 26 import java.security.PublicKey; 27 import java.security.spec.ECPublicKeySpec; 28 import java.security.spec.InvalidKeySpecException; 29 import java.security.spec.KeySpec; 30 import java.security.spec.PKCS8EncodedKeySpec; 31 import java.security.spec.RSAPublicKeySpec; 32 import java.security.spec.X509EncodedKeySpec; 33 34 /** 35 * {@link KeyFactorySpi} backed by Android KeyStore. 36 * 37 * @hide 38 */ 39 public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi { 40 41 private final KeyStore mKeyStore = KeyStore.getInstance(); 42 43 @Override engineGetKeySpec(Key key, Class<T> keySpecClass)44 protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass) 45 throws InvalidKeySpecException { 46 if (key == null) { 47 throw new InvalidKeySpecException("key == null"); 48 } else if ((!(key instanceof AndroidKeyStorePrivateKey)) 49 && (!(key instanceof AndroidKeyStorePublicKey))) { 50 throw new InvalidKeySpecException( 51 "Unsupported key type: " + key.getClass().getName() 52 + ". This KeyFactory supports only Android Keystore asymmetric keys"); 53 } 54 55 // key is an Android Keystore private or public key 56 57 if (keySpecClass == null) { 58 throw new InvalidKeySpecException("keySpecClass == null"); 59 } else if (KeyInfo.class.equals(keySpecClass)) { 60 if (!(key instanceof AndroidKeyStorePrivateKey)) { 61 throw new InvalidKeySpecException( 62 "Unsupported key type: " + key.getClass().getName() 63 + ". KeyInfo can be obtained only for Android Keystore private keys"); 64 } 65 AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key; 66 String keyAliasInKeystore = keystorePrivateKey.getAlias(); 67 String entryAlias; 68 if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) { 69 entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length()); 70 } else { 71 throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore); 72 } 73 @SuppressWarnings("unchecked") 74 T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo( 75 mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid()); 76 return result; 77 } else if (X509EncodedKeySpec.class.equals(keySpecClass)) { 78 if (!(key instanceof AndroidKeyStorePublicKey)) { 79 throw new InvalidKeySpecException( 80 "Unsupported key type: " + key.getClass().getName() 81 + ". X509EncodedKeySpec can be obtained only for Android Keystore public" 82 + " keys"); 83 } 84 @SuppressWarnings("unchecked") 85 T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded()); 86 return result; 87 } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) { 88 if (key instanceof AndroidKeyStorePrivateKey) { 89 throw new InvalidKeySpecException( 90 "Key material export of Android Keystore private keys is not supported"); 91 } else { 92 throw new InvalidKeySpecException( 93 "Cannot export key material of public key in PKCS#8 format." 94 + " Only X.509 format (X509EncodedKeySpec) supported for public keys."); 95 } 96 } else if (RSAPublicKeySpec.class.equals(keySpecClass)) { 97 if (key instanceof AndroidKeyStoreRSAPublicKey) { 98 AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key; 99 @SuppressWarnings("unchecked") 100 T result = 101 (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent()); 102 return result; 103 } else { 104 throw new InvalidKeySpecException( 105 "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " " 106 + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") 107 + " key"); 108 } 109 } else if (ECPublicKeySpec.class.equals(keySpecClass)) { 110 if (key instanceof AndroidKeyStoreECPublicKey) { 111 AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key; 112 @SuppressWarnings("unchecked") 113 T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams()); 114 return result; 115 } else { 116 throw new InvalidKeySpecException( 117 "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " " 118 + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public") 119 + " key"); 120 } 121 } else { 122 throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName()); 123 } 124 } 125 126 @Override engineGeneratePrivate(KeySpec spec)127 protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException { 128 throw new InvalidKeySpecException( 129 "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with" 130 + " " + KeyGenParameterSpec.class.getName()); 131 } 132 133 @Override engineGeneratePublic(KeySpec spec)134 protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException { 135 throw new InvalidKeySpecException( 136 "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with" 137 + " " + KeyGenParameterSpec.class.getName()); 138 } 139 140 @Override engineTranslateKey(Key key)141 protected Key engineTranslateKey(Key key) throws InvalidKeyException { 142 if (key == null) { 143 throw new InvalidKeyException("key == null"); 144 } else if ((!(key instanceof AndroidKeyStorePrivateKey)) 145 && (!(key instanceof AndroidKeyStorePublicKey))) { 146 throw new InvalidKeyException( 147 "To import a key into Android Keystore, use KeyStore.setEntry"); 148 } 149 return key; 150 } 151 } 152