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