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