• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.googlecode.android_scripting.facade;
18 
19 import android.os.Environment;
20 import android.util.Log;
21 
22 import com.android.internal.net.VpnProfile;
23 import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
24 import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
25 import com.android.internal.org.bouncycastle.asn1.DEROctetString;
26 import com.android.internal.org.bouncycastle.asn1.x509.BasicConstraints;
27 
28 import libcore.io.Streams;
29 
30 import junit.framework.Assert;
31 
32 import java.io.ByteArrayInputStream;
33 import java.io.File;
34 import java.io.FileInputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.security.KeyStore;
38 import java.security.KeyStore.PasswordProtection;
39 import java.security.KeyStore.PrivateKeyEntry;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.PrivateKey;
43 import java.security.UnrecoverableEntryException;
44 import java.security.cert.Certificate;
45 import java.security.cert.CertificateException;
46 import java.security.cert.X509Certificate;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.Enumeration;
50 import java.util.List;
51 
52 /**
53  * Certificate installer helper to extract information from a provided file
54  * and install certificates to keystore.
55  */
56 public class CertInstallerHelper {
57     private static final String TAG = "CertInstallerHelper";
58     /* Define a password to unlock keystore after it is reset */
59     private PrivateKey mUserKey;  // private key
60     private X509Certificate mUserCert;  // user certificate
61     private List<X509Certificate> mCaCerts = new ArrayList<X509Certificate>();
62     private final KeyStore mKeyStore;
63 
64     /**
65      * Delete all key for caller.
66      */
CertInstallerHelper()67     public CertInstallerHelper() {
68         try {
69             mKeyStore = KeyStore.getInstance("AndroidKeyStore");
70             mKeyStore.load(null);
71             final Enumeration<String> aliases = mKeyStore.aliases();
72             while (aliases.hasMoreElements()) {
73                 mKeyStore.deleteEntry(aliases.nextElement());
74             }
75         } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException
76                 | IOException e) {
77             Log.e(TAG, "Failed to open and cleanup Keystore.", e);
78             throw new RuntimeException("Failed to open and cleanup Keystore.", e);
79         }
80     }
81 
extractCertificate(String certFile, String password)82     private void extractCertificate(String certFile, String password) {
83         InputStream in = null;
84         final byte[] raw;
85         java.security.KeyStore keystore = null;
86         try {
87             // Read .p12 file from SDCARD and extract with password
88             in = new FileInputStream(new File(
89                     Environment.getExternalStorageDirectory(), certFile));
90             raw = Streams.readFully(in);
91 
92             keystore = java.security.KeyStore.getInstance("PKCS12");
93             PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray());
94             keystore.load(new ByteArrayInputStream(raw), passwordProtection.getPassword());
95 
96             // Install certificates and private keys
97             Enumeration<String> aliases = keystore.aliases();
98             if (!aliases.hasMoreElements()) {
99                 Assert.fail("key store failed to put in keychain");
100             }
101             ArrayList<String> aliasesList = Collections.list(aliases);
102             // The keystore is initialized for each test case, there will
103             // be only one alias in the keystore
104             Assert.assertEquals(1, aliasesList.size());
105             String alias = aliasesList.get(0);
106             java.security.KeyStore.Entry entry = keystore.getEntry(alias, passwordProtection);
107             Log.d(TAG, "extracted alias = " + alias + ", entry=" + entry.getClass());
108 
109             if (entry instanceof PrivateKeyEntry) {
110                 Assert.assertTrue(installFrom((PrivateKeyEntry) entry));
111             }
112         } catch (IOException e) {
113             Assert.fail("Failed to read certficate: " + e);
114         } catch (KeyStoreException e) {
115             Log.e(TAG, "failed to extract certificate" + e);
116         } catch (NoSuchAlgorithmException e) {
117             Log.e(TAG, "failed to extract certificate" + e);
118         } catch (CertificateException e) {
119             Log.e(TAG, "failed to extract certificate" + e);
120         } catch (UnrecoverableEntryException e) {
121             Log.e(TAG, "failed to extract certificate" + e);
122         }
123         finally {
124             if (in != null) {
125                 try {
126                     in.close();
127                 } catch (IOException e) {
128                     Log.e(TAG, "close FileInputStream error: " + e);
129                 }
130             }
131         }
132     }
133 
134     /**
135      * Extract private keys, user certificates and ca certificates
136      */
installFrom(PrivateKeyEntry entry)137     private synchronized boolean installFrom(PrivateKeyEntry entry) {
138         mUserKey = entry.getPrivateKey();
139         mUserCert = (X509Certificate) entry.getCertificate();
140 
141         Certificate[] certs = entry.getCertificateChain();
142         Log.d(TAG, "# certs extracted = " + certs.length);
143         mCaCerts = new ArrayList<X509Certificate>(certs.length);
144         for (Certificate c : certs) {
145             X509Certificate cert = (X509Certificate) c;
146             if (isCa(cert)) {
147                 mCaCerts.add(cert);
148             }
149         }
150         Log.d(TAG, "# ca certs extracted = " + mCaCerts.size());
151         return true;
152     }
153 
isCa(X509Certificate cert)154     private boolean isCa(X509Certificate cert) {
155         try {
156             byte[] asn1EncodedBytes = cert.getExtensionValue("2.5.29.19");
157             if (asn1EncodedBytes == null) {
158                 return false;
159             }
160             DEROctetString derOctetString = (DEROctetString)
161                     new ASN1InputStream(asn1EncodedBytes).readObject();
162             byte[] octets = derOctetString.getOctets();
163             ASN1Sequence sequence = (ASN1Sequence)
164                     new ASN1InputStream(octets).readObject();
165             return BasicConstraints.getInstance(sequence).isCA();
166         } catch (IOException e) {
167             return false;
168         }
169     }
170 
171     /**
172      * Extract certificate from the given file, and install it to keystore
173      * @param profile VpnProfile
174      * @param certFile .p12 file which includes certificates
175      * @param password password to extract the .p12 file
176      */
installCertificate(VpnProfile profile, String certFile, String password)177     public void installCertificate(VpnProfile profile, String certFile, String password) {
178         // extract private keys, certificates from the provided file
179         extractCertificate(certFile, password);
180         // install certificate to the keystore
181         try {
182             boolean caInstalledWithUserKey = false;
183 
184             if (mUserKey != null) {
185                 Log.v(TAG, "has private key");
186                 if (mUserCert == null) {
187                     throw new AssertionError("Must have user cert if user key is installed.");
188                 }
189                 final List<Certificate> certChain = new ArrayList<Certificate>();
190                 certChain.add(mUserCert);
191                 if (profile.ipsecUserCert.equals(profile.ipsecCaCert)) {
192                     // If the CA certs should be installed under the same alias they have to be
193                     // added to the end of the certificate chain.
194                     certChain.addAll(mCaCerts);
195                     // Make a note that we have installed the CA cert chain along with the
196                     // user key and cert.
197                     caInstalledWithUserKey = true;
198                 }
199                 mKeyStore.setKeyEntry(profile.ipsecUserCert, mUserKey, null,
200                         certChain.toArray(new Certificate[0]));
201                 Log.v(TAG, "install " + profile.ipsecUserCert + " is successful");
202             }
203 
204             if (!(mCaCerts.isEmpty() || caInstalledWithUserKey)) {
205                 if (mCaCerts.size() != 1) {
206                     throw new AssertionError("Trusted certificate cannot be a cert chain.");
207                 }
208                 mKeyStore.setCertificateEntry(profile.ipsecCaCert, mCaCerts.get(0));
209                 Log.v(TAG, " install " + profile.ipsecCaCert + " is successful");
210             }
211         } catch (KeyStoreException e) {
212             Log.e(TAG, "Exception trying to import certificates. " + e);
213             throw new AssertionError(e);
214         }
215     }
216 }
217