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