1340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang/* 2340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Copyright (C) 2013 The Android Open Source Project 3340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * 4340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * you may not use this file except in compliance with the License. 6340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * You may obtain a copy of the License at 7340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * 8340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * http://www.apache.org/licenses/LICENSE-2.0 9340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * 10340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Unless required by applicable law or agreed to in writing, software 11340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * distributed under the License is distributed on an "AS IS" BASIS, 12340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * See the License for the specific language governing permissions and 14340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * limitations under the License. 15340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang */ 16340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 17340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangpackage com.android.settings.vpn2; 18340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 19340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport android.os.Environment; 20340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport android.security.Credentials; 21340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport android.security.KeyStore; 22340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport android.util.Log; 23340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 24340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport com.android.internal.net.VpnProfile; 25340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport com.android.org.bouncycastle.asn1.ASN1InputStream; 26340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport com.android.org.bouncycastle.asn1.ASN1Sequence; 27340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport com.android.org.bouncycastle.asn1.DEROctetString; 28340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport com.android.org.bouncycastle.asn1.x509.BasicConstraints; 29340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 30340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport junit.framework.Assert; 31340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 32340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport libcore.io.Streams; 33340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 34340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.io.ByteArrayInputStream; 35340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.io.File; 36340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.io.FileInputStream; 37340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.io.IOException; 38340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.io.InputStream; 39340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.nio.charset.StandardCharsets; 40340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.KeyStoreException; 41340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.NoSuchAlgorithmException; 42340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.KeyStore.PasswordProtection; 43340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.KeyStore.PrivateKeyEntry; 44340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.PrivateKey; 45340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.UnrecoverableEntryException; 46340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.cert.Certificate; 47340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.cert.CertificateEncodingException; 48340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.cert.CertificateException; 49340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.security.cert.X509Certificate; 50340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.util.ArrayList; 51340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.util.Collections; 52340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.util.Enumeration; 53340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangimport java.util.List; 54340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 55340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang/** 56340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Certificate installer helper to extract information from a provided file 57340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * and install certificates to keystore. 58340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang */ 59340bda7154194d64a719fb5c86a702a4e5773be0Xia Wangpublic class CertInstallerHelper { 60340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private static final String TAG = "CertInstallerHelper"; 61340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang /* Define a password to unlock keystore after it is reset */ 62340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private static final String CERT_STORE_PASSWORD = "password"; 63340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private final int mUid = KeyStore.UID_SELF; 64340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private PrivateKey mUserKey; // private key 65340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private X509Certificate mUserCert; // user certificate 66340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private List<X509Certificate> mCaCerts = new ArrayList<X509Certificate>(); 67340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private KeyStore mKeyStore = KeyStore.getInstance(); 68340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 69340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang /** 70340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Unlock keystore and set password 71340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang */ 72340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang public CertInstallerHelper() { 73340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang mKeyStore.reset(); 747236f2abbabefc08e68226677f51fe2117c8c0a5Chad Brubaker mKeyStore.onUserPasswordChanged(CERT_STORE_PASSWORD); 75340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 76340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 77340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private void extractCertificate(String certFile, String password) { 78340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang InputStream in = null; 79340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang final byte[] raw; 80340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang java.security.KeyStore keystore = null; 81340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang try { 82340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang // Read .p12 file from SDCARD and extract with password 83340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang in = new FileInputStream(new File( 84340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Environment.getExternalStorageDirectory(), certFile)); 85340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang raw = Streams.readFully(in); 86340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 87340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang keystore = java.security.KeyStore.getInstance("PKCS12"); 88340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray()); 89340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang keystore.load(new ByteArrayInputStream(raw), passwordProtection.getPassword()); 90340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 91340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang // Install certificates and private keys 92340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Enumeration<String> aliases = keystore.aliases(); 93340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (!aliases.hasMoreElements()) { 94340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Assert.fail("key store failed to put in keychain"); 95340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 96340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang ArrayList<String> aliasesList = Collections.list(aliases); 97340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang // The keystore is initialized for each test case, there will 98340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang // be only one alias in the keystore 99340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Assert.assertEquals(1, aliasesList.size()); 100340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang String alias = aliasesList.get(0); 101340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang java.security.KeyStore.Entry entry = keystore.getEntry(alias, passwordProtection); 102340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.d(TAG, "extracted alias = " + alias + ", entry=" + entry.getClass()); 103340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 104340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (entry instanceof PrivateKeyEntry) { 105340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Assert.assertTrue(installFrom((PrivateKeyEntry) entry)); 106340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 107340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (IOException e) { 108340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Assert.fail("Failed to read certficate: " + e); 109340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (KeyStoreException e) { 110340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "failed to extract certificate" + e); 111340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (NoSuchAlgorithmException e) { 112340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "failed to extract certificate" + e); 113340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (CertificateException e) { 114340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "failed to extract certificate" + e); 115340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (UnrecoverableEntryException e) { 116340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "failed to extract certificate" + e); 117340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 118340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang finally { 119340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (in != null) { 120340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang try { 121340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang in.close(); 122340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (IOException e) { 123340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "close FileInputStream error: " + e); 124340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 125340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 126340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 127340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 128340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 129340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang /** 130340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Extract private keys, user certificates and ca certificates 131340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang */ 132340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private synchronized boolean installFrom(PrivateKeyEntry entry) { 133340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang mUserKey = entry.getPrivateKey(); 134340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang mUserCert = (X509Certificate) entry.getCertificate(); 135340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 136340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Certificate[] certs = entry.getCertificateChain(); 137340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.d(TAG, "# certs extracted = " + certs.length); 138340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang mCaCerts = new ArrayList<X509Certificate>(certs.length); 139340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang for (Certificate c : certs) { 140340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang X509Certificate cert = (X509Certificate) c; 141340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (isCa(cert)) { 142340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang mCaCerts.add(cert); 143340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 144340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 145340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.d(TAG, "# ca certs extracted = " + mCaCerts.size()); 146340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return true; 147340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 148340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 149340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang private boolean isCa(X509Certificate cert) { 150340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang try { 151340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang byte[] asn1EncodedBytes = cert.getExtensionValue("2.5.29.19"); 152340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (asn1EncodedBytes == null) { 153340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return false; 154340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 155340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang DEROctetString derOctetString = (DEROctetString) 156340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang new ASN1InputStream(asn1EncodedBytes).readObject(); 157340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang byte[] octets = derOctetString.getOctets(); 158340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang ASN1Sequence sequence = (ASN1Sequence) 159340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang new ASN1InputStream(octets).readObject(); 160340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return BasicConstraints.getInstance(sequence).isCA(); 161340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (IOException e) { 162340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return false; 163340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 164340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 165340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 166340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang /** 167340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * Extract certificate from the given file, and install it to keystore 168340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * @param name certificate name 169340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * @param certFile .p12 file which includes certificates 170340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang * @param password password to extract the .p12 file 171340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang */ 172340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang public void installCertificate(VpnProfile profile, String certFile, String password) { 173340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang // extract private keys, certificates from the provided file 174340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang extractCertificate(certFile, password); 175340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang // install certificate to the keystore 176340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang int flags = KeyStore.FLAG_ENCRYPTED; 177340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang try { 178340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (mUserKey != null) { 179340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.v(TAG, "has private key"); 180340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang String key = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert; 181340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang byte[] value = mUserKey.getEncoded(); 182340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 183340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (!mKeyStore.importKey(key, value, mUid, flags)) { 184340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "Failed to install " + key + " as user " + mUid); 185340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return; 186340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 187340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.v(TAG, "install " + key + " as user " + mUid + " is successful"); 188340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 189340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 190340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (mUserCert != null) { 191340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang String certName = Credentials.USER_CERTIFICATE + profile.ipsecUserCert; 192340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang byte[] certData = Credentials.convertToPem(mUserCert); 193340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 194340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (!mKeyStore.put(certName, certData, mUid, flags)) { 195340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "Failed to install " + certName + " as user " + mUid); 196340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return; 197340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 198340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.v(TAG, "install " + certName + " as user" + mUid + " is successful."); 199340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 200340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 201340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (!mCaCerts.isEmpty()) { 202340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang String caListName = Credentials.CA_CERTIFICATE + profile.ipsecCaCert; 203340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang X509Certificate[] caCerts = mCaCerts.toArray(new X509Certificate[mCaCerts.size()]); 204340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang byte[] caListData = Credentials.convertToPem(caCerts); 205340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 206340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang if (!mKeyStore.put(caListName, caListData, mUid, flags)) { 207340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "Failed to install " + caListName + " as user " + mUid); 208340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return; 209340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 210340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.v(TAG, " install " + caListName + " as user " + mUid + " is successful"); 211340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 212340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (CertificateEncodingException e) { 213340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "Exception while convert certificates to pem " + e); 214340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang throw new AssertionError(e); 215340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } catch (IOException e) { 216340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang Log.e(TAG, "IOException while convert to pem: " + e); 217340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 218340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 219340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang 220340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang public int getUid() { 221340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang return mUid; 222340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang } 223340bda7154194d64a719fb5c86a702a4e5773be0Xia Wang} 224