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