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