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