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