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 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      */
convertToPem(Certificate... objects)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      */
convertFromPem(byte[] bytes)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 
getInstance()173     public static Credentials getInstance() {
174         if (singleton == null) {
175             singleton = new Credentials();
176         }
177         return singleton;
178     }
179 
unlock(Context context)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 
install(Context context)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 
install(Context context, KeyPair pair)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 
install(Context context, String type, byte[] value)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      */
deleteAllTypesForAlias(KeyStore keystore, String alias)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      */
deleteAllTypesForAlias(KeyStore keystore, String alias, int uid)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      */
deleteCertificateTypesForAlias(KeyStore keystore, String alias)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      */
deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid)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      */
deletePrivateKeyTypeForAlias(KeyStore keystore, String alias)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      */
deletePrivateKeyTypeForAlias(KeyStore keystore, String alias, int uid)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      */
deleteSecretKeyTypeForAlias(KeyStore keystore, String alias)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      */
deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid)294     public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
295         return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
296     }
297 }
298