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