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