1 /* 2 * Copyright (C) 2009 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; 18 19 import android.content.ActivityNotFoundException; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.util.Log; 23 24 import com.android.org.bouncycastle.util.io.pem.PemObject; 25 import com.android.org.bouncycastle.util.io.pem.PemReader; 26 import com.android.org.bouncycastle.util.io.pem.PemWriter; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.io.IOException; 31 import java.io.InputStreamReader; 32 import java.io.OutputStreamWriter; 33 import java.io.Reader; 34 import java.io.Writer; 35 import java.nio.charset.StandardCharsets; 36 import java.security.KeyPair; 37 import java.security.cert.Certificate; 38 import java.security.cert.CertificateEncodingException; 39 import java.security.cert.CertificateException; 40 import java.security.cert.CertificateFactory; 41 import java.security.cert.X509Certificate; 42 import java.util.ArrayList; 43 import java.util.List; 44 45 /** 46 * {@hide} 47 */ 48 public class Credentials { 49 private static final String LOGTAG = "Credentials"; 50 51 public static final String INSTALL_ACTION = "android.credentials.INSTALL"; 52 53 public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER"; 54 55 public static final String UNLOCK_ACTION = "com.android.credentials.UNLOCK"; 56 57 /** Key prefix for CA certificates. */ 58 public static final String CA_CERTIFICATE = "CACERT_"; 59 60 /** Key prefix for user certificates. */ 61 public static final String USER_CERTIFICATE = "USRCERT_"; 62 63 /** Key prefix for user private and secret keys. */ 64 public static final String USER_PRIVATE_KEY = "USRPKEY_"; 65 66 /** Key prefix for user secret keys. 67 * @deprecated use {@code USER_PRIVATE_KEY} for this category instead. 68 */ 69 public static final String USER_SECRET_KEY = "USRSKEY_"; 70 71 /** Key prefix for VPN. */ 72 public static final String VPN = "VPN_"; 73 74 /** Key prefix for WIFI. */ 75 public static final String WIFI = "WIFI_"; 76 77 /** Key containing suffix of lockdown VPN profile. */ 78 public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN"; 79 80 /** Data type for public keys. */ 81 public static final String EXTRA_PUBLIC_KEY = "KEY"; 82 83 /** Data type for private keys. */ 84 public static final String EXTRA_PRIVATE_KEY = "PKEY"; 85 86 // historically used by Android 87 public static final String EXTENSION_CRT = ".crt"; 88 public static final String EXTENSION_P12 = ".p12"; 89 // commonly used on Windows 90 public static final String EXTENSION_CER = ".cer"; 91 public static final String EXTENSION_PFX = ".pfx"; 92 93 /** 94 * Intent extra: install the certificate bundle as this UID instead of 95 * system. 96 */ 97 public static final String EXTRA_INSTALL_AS_UID = "install_as_uid"; 98 99 /** 100 * Intent extra: name for the user's private key. 101 */ 102 public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name"; 103 104 /** 105 * Intent extra: data for the user's private key in PEM-encoded PKCS#8. 106 */ 107 public static final String EXTRA_USER_PRIVATE_KEY_DATA = "user_private_key_data"; 108 109 /** 110 * Intent extra: name for the user's certificate. 111 */ 112 public static final String EXTRA_USER_CERTIFICATE_NAME = "user_certificate_name"; 113 114 /** 115 * Intent extra: data for the user's certificate in PEM-encoded X.509. 116 */ 117 public static final String EXTRA_USER_CERTIFICATE_DATA = "user_certificate_data"; 118 119 /** 120 * Intent extra: name for CA certificate chain 121 */ 122 public static final String EXTRA_CA_CERTIFICATES_NAME = "ca_certificates_name"; 123 124 /** 125 * Intent extra: data for CA certificate chain in PEM-encoded X.509. 126 */ 127 public static final String EXTRA_CA_CERTIFICATES_DATA = "ca_certificates_data"; 128 129 /** 130 * Convert objects to a PEM format which is used for 131 * CA_CERTIFICATE and USER_CERTIFICATE entries. 132 */ convertToPem(Certificate... objects)133 public static byte[] convertToPem(Certificate... objects) 134 throws IOException, CertificateEncodingException { 135 ByteArrayOutputStream bao = new ByteArrayOutputStream(); 136 Writer writer = new OutputStreamWriter(bao, StandardCharsets.US_ASCII); 137 PemWriter pw = new PemWriter(writer); 138 for (Certificate o : objects) { 139 pw.writeObject(new PemObject("CERTIFICATE", o.getEncoded())); 140 } 141 pw.close(); 142 return bao.toByteArray(); 143 } 144 /** 145 * Convert objects from PEM format, which is used for 146 * CA_CERTIFICATE and USER_CERTIFICATE entries. 147 */ convertFromPem(byte[] bytes)148 public static List<X509Certificate> convertFromPem(byte[] bytes) 149 throws IOException, CertificateException { 150 ByteArrayInputStream bai = new ByteArrayInputStream(bytes); 151 Reader reader = new InputStreamReader(bai, StandardCharsets.US_ASCII); 152 PemReader pr = new PemReader(reader); 153 154 try { 155 CertificateFactory cf = CertificateFactory.getInstance("X509"); 156 157 List<X509Certificate> result = new ArrayList<X509Certificate>(); 158 PemObject o; 159 while ((o = pr.readPemObject()) != null) { 160 if (o.getType().equals("CERTIFICATE")) { 161 Certificate c = cf.generateCertificate(new ByteArrayInputStream(o.getContent())); 162 result.add((X509Certificate) c); 163 } else { 164 throw new IllegalArgumentException("Unknown type " + o.getType()); 165 } 166 } 167 return result; 168 } finally { 169 pr.close(); 170 } 171 } 172 173 private static Credentials singleton; 174 getInstance()175 public static Credentials getInstance() { 176 if (singleton == null) { 177 singleton = new Credentials(); 178 } 179 return singleton; 180 } 181 unlock(Context context)182 public void unlock(Context context) { 183 try { 184 Intent intent = new Intent(UNLOCK_ACTION); 185 context.startActivity(intent); 186 } catch (ActivityNotFoundException e) { 187 Log.w(LOGTAG, e.toString()); 188 } 189 } 190 install(Context context)191 public void install(Context context) { 192 try { 193 Intent intent = KeyChain.createInstallIntent(); 194 context.startActivity(intent); 195 } catch (ActivityNotFoundException e) { 196 Log.w(LOGTAG, e.toString()); 197 } 198 } 199 install(Context context, KeyPair pair)200 public void install(Context context, KeyPair pair) { 201 try { 202 Intent intent = KeyChain.createInstallIntent(); 203 intent.putExtra(EXTRA_PRIVATE_KEY, pair.getPrivate().getEncoded()); 204 intent.putExtra(EXTRA_PUBLIC_KEY, pair.getPublic().getEncoded()); 205 context.startActivity(intent); 206 } catch (ActivityNotFoundException e) { 207 Log.w(LOGTAG, e.toString()); 208 } 209 } 210 install(Context context, String type, byte[] value)211 public void install(Context context, String type, byte[] value) { 212 try { 213 Intent intent = KeyChain.createInstallIntent(); 214 intent.putExtra(type, value); 215 context.startActivity(intent); 216 } catch (ActivityNotFoundException e) { 217 Log.w(LOGTAG, e.toString()); 218 } 219 } 220 221 /** 222 * Delete all types (private key, user certificate, CA certificate) for a 223 * particular {@code alias}. All three can exist for any given alias. 224 * Returns {@code true} if the alias no longer contains any types. 225 */ deleteAllTypesForAlias(KeyStore keystore, String alias)226 public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias) { 227 return deleteAllTypesForAlias(keystore, alias, KeyStore.UID_SELF); 228 } 229 230 /** 231 * Delete all types (private key, user certificate, CA certificate) for a 232 * particular {@code alias}. All three can exist for any given alias. 233 * Returns {@code true} if the alias no longer contains any types. 234 */ deleteAllTypesForAlias(KeyStore keystore, String alias, int uid)235 public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias, int uid) { 236 /* 237 * Make sure every type is deleted. There can be all three types, so 238 * don't use a conditional here. 239 */ 240 return deleteUserKeyTypeForAlias(keystore, alias, uid) 241 & deleteCertificateTypesForAlias(keystore, alias, uid); 242 } 243 244 /** 245 * Delete certificate types (user certificate, CA certificate) for a 246 * particular {@code alias}. Both can exist for any given alias. 247 * Returns {@code true} if the alias no longer contains either type. 248 */ deleteCertificateTypesForAlias(KeyStore keystore, String alias)249 public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias) { 250 return deleteCertificateTypesForAlias(keystore, alias, KeyStore.UID_SELF); 251 } 252 253 /** 254 * Delete certificate types (user certificate, CA certificate) for a 255 * particular {@code alias}. Both can exist for any given alias. 256 * Returns {@code true} if the alias no longer contains either type. 257 */ deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid)258 public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid) { 259 /* 260 * Make sure every certificate type is deleted. There can be two types, 261 * so don't use a conditional here. 262 */ 263 return keystore.delete(Credentials.USER_CERTIFICATE + alias, uid) 264 & keystore.delete(Credentials.CA_CERTIFICATE + alias, uid); 265 } 266 267 /** 268 * Delete user key for a particular {@code alias}. 269 * Returns {@code true} if the entry no longer exists. 270 */ deleteUserKeyTypeForAlias(KeyStore keystore, String alias)271 public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias) { 272 return deleteUserKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF); 273 } 274 275 /** 276 * Delete user key for a particular {@code alias}. 277 * Returns {@code true} if the entry no longer exists. 278 */ deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid)279 public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) { 280 return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) || 281 keystore.delete(Credentials.USER_SECRET_KEY + alias, uid); 282 } 283 284 /** 285 * Delete legacy prefixed entry for a particular {@code alias} 286 * Returns {@code true} if the entry no longer exists. 287 */ deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid)288 public static boolean deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid) { 289 return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid); 290 } 291 } 292