• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.certinstaller;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.RemoteException;
23 import android.security.Credentials;
24 import android.security.KeyChain;
25 import android.security.IKeyChainService;
26 import android.text.Html;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import com.android.org.bouncycastle.asn1.ASN1InputStream;
30 import com.android.org.bouncycastle.asn1.ASN1Sequence;
31 import com.android.org.bouncycastle.asn1.DEROctetString;
32 import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
33 import java.io.ByteArrayInputStream;
34 import java.io.IOException;
35 import java.security.KeyFactory;
36 import java.security.KeyStore.PasswordProtection;
37 import java.security.KeyStore.PrivateKeyEntry;
38 import java.security.KeyStore;
39 import java.security.NoSuchAlgorithmException;
40 import java.security.PrivateKey;
41 import java.security.cert.Certificate;
42 import java.security.cert.CertificateEncodingException;
43 import java.security.cert.CertificateException;
44 import java.security.cert.CertificateFactory;
45 import java.security.cert.X509Certificate;
46 import java.security.spec.InvalidKeySpecException;
47 import java.security.spec.PKCS8EncodedKeySpec;
48 import java.util.ArrayList;
49 import java.util.Enumeration;
50 import java.util.HashMap;
51 import java.util.List;
52 
53 /**
54  * A helper class for accessing the raw data in the intent extra and handling
55  * certificates.
56  */
57 class CredentialHelper {
58     private static final String DATA_KEY = "data";
59     private static final String CERTS_KEY = "crts";
60 
61     private static final String TAG = "CredentialHelper";
62 
63     // keep raw data from intent's extra
64     private HashMap<String, byte[]> mBundle = new HashMap<String, byte[]>();
65 
66     private String mName = "";
67     private PrivateKey mUserKey;
68     private X509Certificate mUserCert;
69     private List<X509Certificate> mCaCerts = new ArrayList<X509Certificate>();
70 
CredentialHelper()71     CredentialHelper() {
72     }
73 
CredentialHelper(Intent intent)74     CredentialHelper(Intent intent) {
75         Bundle bundle = intent.getExtras();
76         if (bundle == null) {
77             return;
78         }
79 
80         String name = bundle.getString(KeyChain.EXTRA_NAME);
81         bundle.remove(KeyChain.EXTRA_NAME);
82         if (name != null) {
83             mName = name;
84         }
85 
86         Log.d(TAG, "# extras: " + bundle.size());
87         for (String key : bundle.keySet()) {
88             byte[] bytes = bundle.getByteArray(key);
89             Log.d(TAG, "   " + key + ": " + ((bytes == null) ? -1 : bytes.length));
90             mBundle.put(key, bytes);
91         }
92         parseCert(getData(KeyChain.EXTRA_CERTIFICATE));
93     }
94 
onSaveStates(Bundle outStates)95     synchronized void onSaveStates(Bundle outStates) {
96         try {
97             outStates.putSerializable(DATA_KEY, mBundle);
98             outStates.putString(KeyChain.EXTRA_NAME, mName);
99             if (mUserKey != null) {
100                 outStates.putByteArray(Credentials.USER_PRIVATE_KEY,
101                         mUserKey.getEncoded());
102             }
103             ArrayList<byte[]> certs = new ArrayList<byte[]>(mCaCerts.size() + 1);
104             if (mUserCert != null) {
105                 certs.add(mUserCert.getEncoded());
106             }
107             for (X509Certificate cert : mCaCerts) {
108                 certs.add(cert.getEncoded());
109             }
110             outStates.putByteArray(CERTS_KEY, Util.toBytes(certs));
111         } catch (CertificateEncodingException e) {
112             throw new AssertionError(e);
113         }
114     }
115 
onRestoreStates(Bundle savedStates)116     void onRestoreStates(Bundle savedStates) {
117         mBundle = (HashMap) savedStates.getSerializable(DATA_KEY);
118         mName = savedStates.getString(KeyChain.EXTRA_NAME);
119         byte[] bytes = savedStates.getByteArray(Credentials.USER_PRIVATE_KEY);
120         if (bytes != null) {
121             setPrivateKey(bytes);
122         }
123 
124         ArrayList<byte[]> certs = Util.fromBytes(savedStates.getByteArray(CERTS_KEY));
125         for (byte[] cert : certs) {
126             parseCert(cert);
127         }
128     }
129 
getUserCertificate()130     X509Certificate getUserCertificate() {
131         return mUserCert;
132     }
133 
parseCert(byte[] bytes)134     private void parseCert(byte[] bytes) {
135         if (bytes == null) {
136             return;
137         }
138 
139         try {
140             CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
141             X509Certificate cert = (X509Certificate)
142                     certFactory.generateCertificate(
143                             new ByteArrayInputStream(bytes));
144             if (isCa(cert)) {
145                 Log.d(TAG, "got a CA cert");
146                 mCaCerts.add(cert);
147             } else {
148                 Log.d(TAG, "got a user cert");
149                 mUserCert = cert;
150             }
151         } catch (CertificateException e) {
152             Log.w(TAG, "parseCert(): " + e);
153         }
154     }
155 
isCa(X509Certificate cert)156     private boolean isCa(X509Certificate cert) {
157         try {
158             // TODO: add a test about this
159             byte[] asn1EncodedBytes = cert.getExtensionValue("2.5.29.19");
160             if (asn1EncodedBytes == null) {
161                 return false;
162             }
163             DEROctetString derOctetString = (DEROctetString)
164                     new ASN1InputStream(asn1EncodedBytes).readObject();
165             byte[] octets = derOctetString.getOctets();
166             ASN1Sequence sequence = (ASN1Sequence)
167                     new ASN1InputStream(octets).readObject();
168             return BasicConstraints.getInstance(sequence).isCA();
169         } catch (IOException e) {
170             return false;
171         }
172     }
173 
hasPkcs12KeyStore()174     boolean hasPkcs12KeyStore() {
175         return mBundle.containsKey(KeyChain.EXTRA_PKCS12);
176     }
177 
hasKeyPair()178     boolean hasKeyPair() {
179         return mBundle.containsKey(Credentials.EXTRA_PUBLIC_KEY)
180                 && mBundle.containsKey(Credentials.EXTRA_PRIVATE_KEY);
181     }
182 
hasUserCertificate()183     boolean hasUserCertificate() {
184         return (mUserCert != null);
185     }
186 
hasCaCerts()187     boolean hasCaCerts() {
188         return !mCaCerts.isEmpty();
189     }
190 
hasAnyForSystemInstall()191     boolean hasAnyForSystemInstall() {
192         return (mUserKey != null) || hasUserCertificate() || hasCaCerts();
193     }
194 
setPrivateKey(byte[] bytes)195     void setPrivateKey(byte[] bytes) {
196         try {
197             KeyFactory keyFactory = KeyFactory.getInstance("RSA");
198             mUserKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
199         } catch (NoSuchAlgorithmException e) {
200             throw new AssertionError(e);
201         } catch (InvalidKeySpecException e) {
202             throw new AssertionError(e);
203         }
204     }
205 
containsAnyRawData()206     boolean containsAnyRawData() {
207         return !mBundle.isEmpty();
208     }
209 
getData(String key)210     byte[] getData(String key) {
211         return mBundle.get(key);
212     }
213 
putPkcs12Data(byte[] data)214     void putPkcs12Data(byte[] data) {
215         mBundle.put(KeyChain.EXTRA_PKCS12, data);
216     }
217 
getDescription(Context context)218     CharSequence getDescription(Context context) {
219         // TODO: create more descriptive string
220         StringBuilder sb = new StringBuilder();
221         String newline = "<br>";
222         if (mUserKey != null) {
223             sb.append(context.getString(R.string.one_userkey)).append(newline);
224         }
225         if (mUserCert != null) {
226             sb.append(context.getString(R.string.one_usercrt)).append(newline);
227         }
228         int n = mCaCerts.size();
229         if (n > 0) {
230             if (n == 1) {
231                 sb.append(context.getString(R.string.one_cacrt));
232             } else {
233                 sb.append(context.getString(R.string.n_cacrts, n));
234             }
235         }
236         return Html.fromHtml(sb.toString());
237     }
238 
setName(String name)239     void setName(String name) {
240         mName = name;
241     }
242 
getName()243     String getName() {
244         return mName;
245     }
246 
createSystemInstallIntent()247     Intent createSystemInstallIntent() {
248         Intent intent = new Intent("com.android.credentials.INSTALL");
249         // To prevent the private key from being sniffed, we explicitly spell
250         // out the intent receiver class.
251         intent.setClassName("com.android.settings", "com.android.settings.CredentialStorage");
252         try {
253             if (mUserKey != null) {
254                 intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_NAME,
255                         Credentials.USER_PRIVATE_KEY + mName);
256                 intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_DATA,
257                         mUserKey.getEncoded());
258             }
259             if (mUserCert != null) {
260                 intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_NAME,
261                         Credentials.USER_CERTIFICATE + mName);
262                 intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_DATA,
263                         Credentials.convertToPem(mUserCert));
264             }
265             if (!mCaCerts.isEmpty()) {
266                 intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_NAME,
267                         Credentials.CA_CERTIFICATE + mName);
268                 X509Certificate[] caCerts
269                         = mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
270                 intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_DATA,
271                         Credentials.convertToPem(caCerts));
272             }
273             return intent;
274         } catch (IOException e) {
275             throw new AssertionError(e);
276         } catch (CertificateEncodingException e) {
277             throw new AssertionError(e);
278         }
279     }
280 
installCaCertsToKeyChain(IKeyChainService keyChainService)281     boolean installCaCertsToKeyChain(IKeyChainService keyChainService) {
282         for (X509Certificate caCert : mCaCerts) {
283             byte[] bytes = null;
284             try {
285                 bytes = caCert.getEncoded();
286             } catch (CertificateEncodingException e) {
287                 throw new AssertionError(e);
288             }
289             if (bytes != null) {
290                 try {
291                     keyChainService.installCaCertificate(bytes);
292                 } catch (RemoteException e) {
293                     Log.w(TAG, "installCaCertsToKeyChain(): " + e);
294                     return false;
295                 }
296             }
297         }
298         return true;
299     }
300 
extractPkcs12(String password)301     boolean extractPkcs12(String password) {
302         try {
303             return extractPkcs12Internal(password);
304         } catch (Exception e) {
305             Log.w(TAG, "extractPkcs12(): " + e, e);
306             return false;
307         }
308     }
309 
extractPkcs12Internal(String password)310     private boolean extractPkcs12Internal(String password)
311             throws Exception {
312         // TODO: add test about this
313         java.security.KeyStore keystore = java.security.KeyStore.getInstance("PKCS12");
314         PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray());
315         keystore.load(new ByteArrayInputStream(getData(KeyChain.EXTRA_PKCS12)),
316                       passwordProtection.getPassword());
317 
318         Enumeration<String> aliases = keystore.aliases();
319         if (!aliases.hasMoreElements()) {
320             return false;
321         }
322 
323         while (aliases.hasMoreElements()) {
324             String alias = aliases.nextElement();
325             KeyStore.Entry entry = keystore.getEntry(alias, passwordProtection);
326             Log.d(TAG, "extracted alias = " + alias + ", entry=" + entry.getClass());
327 
328             if (entry instanceof PrivateKeyEntry) {
329                 if (TextUtils.isEmpty(mName)) {
330                     mName = alias;
331                 }
332                 return installFrom((PrivateKeyEntry) entry);
333             }
334         }
335         return true;
336     }
337 
installFrom(PrivateKeyEntry entry)338     private synchronized boolean installFrom(PrivateKeyEntry entry) {
339         mUserKey = entry.getPrivateKey();
340         mUserCert = (X509Certificate) entry.getCertificate();
341 
342         Certificate[] certs = entry.getCertificateChain();
343         Log.d(TAG, "# certs extracted = " + certs.length);
344         mCaCerts = new ArrayList<X509Certificate>(certs.length);
345         for (Certificate c : certs) {
346             X509Certificate cert = (X509Certificate) c;
347             if (isCa(cert)) {
348                 mCaCerts.add(cert);
349             }
350         }
351         Log.d(TAG, "# ca certs extracted = " + mCaCerts.size());
352 
353         return true;
354     }
355 }
356