android: Use Storage Access Framework to import certificates
authorTobias Brunner <tobias@strongswan.org>
Sat, 31 May 2014 14:49:01 +0000 (16:49 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 22 Jul 2014 08:41:51 +0000 (10:41 +0200)
Thanks to the SAF, introduced with Android 4.4, browsing and opening
files on the system is very easy to implement.

On older systems the menu option is removed.

src/frontends/android/res/menu/certificates.xml
src/frontends/android/src/org/strongswan/android/ui/TrustedCertificateImportActivity.java
src/frontends/android/src/org/strongswan/android/ui/TrustedCertificatesActivity.java

index c735e0c..6066cab 100644 (file)
 <menu xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <item
+        android:id="@+id/menu_import_certificate"
+        android:title="@string/import_certificate"
+        android:showAsAction="withText" />
+
+    <item
         android:id="@+id/menu_reload_certs"
         android:title="@string/reload_trusted_certs"
         android:showAsAction="withText" />
index 663c414..f8a9438 100644 (file)
@@ -23,13 +23,27 @@ import java.security.cert.X509Certificate;
 import org.strongswan.android.R;
 import org.strongswan.android.logic.TrustedCertificateManager;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.widget.Toast;
 
 public class TrustedCertificateImportActivity extends Activity
 {
+       private static final int OPEN_DOCUMENT = 0;
+
+       /* same as those listed in the manifest */
+       private static final String[] ACCEPTED_MIME_TYPES = {
+                                                                                                                "application/x-x509-ca-cert",
+                                                                                                                "application/x-x509-server-cert",
+                                                                                                                "application/x-pem-file",
+                                                                                                                "application/pkix-cert"
+       };
+
+       @TargetApi(Build.VERSION_CODES.KITKAT)
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
@@ -39,24 +53,63 @@ public class TrustedCertificateImportActivity extends Activity
                String action = intent.getAction();
                if (Intent.ACTION_VIEW.equals(action))
                {
-                       try
-                       {
-                               CertificateFactory factory = CertificateFactory.getInstance("X.509");
-                               InputStream in = getContentResolver().openInputStream(intent.getData());
-                               X509Certificate certificate = (X509Certificate)factory.generateCertificate(in);
-                               /* we don't check whether it's actually a CA certificate or not */
-                               KeyStore store = KeyStore.getInstance("LocalCertificateStore");
-                               store.load(null, null);
-                               store.setCertificateEntry(null, certificate);
-                               TrustedCertificateManager.getInstance().reset();
-                               Toast.makeText(this, R.string.cert_imported_successfully, Toast.LENGTH_LONG).show();
-                       }
-                       catch (Exception e)
-                       {
-                               Toast.makeText(this, R.string.cert_import_failed, Toast.LENGTH_LONG).show();
-                               e.printStackTrace();
-                       }
+                       importCertificate(intent.getData());
+               }
+               else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+               {
+                       Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+                       openIntent.setType("*/*");
+                       openIntent.putExtra(Intent.EXTRA_MIME_TYPES, ACCEPTED_MIME_TYPES);
+                       startActivityForResult(openIntent, OPEN_DOCUMENT);
+                       return;
                }
                finish();
        }
+
+       @Override
+       protected void onActivityResult(int requestCode, int resultCode, Intent data)
+       {
+               switch (requestCode)
+               {
+                       case OPEN_DOCUMENT:
+                               if (resultCode == Activity.RESULT_OK && data != null)
+                               {
+                                       if (importCertificate(data.getData()))
+                                       {
+                                               setResult(Activity.RESULT_OK);
+                                       }
+                               }
+                               finish();
+                               return;
+               }
+               super.onActivityResult(requestCode, resultCode, data);
+       }
+
+       /**
+        * Try to import the file pointed to by the given URI as a certificate.
+        * @param uri
+        * @return whether the import was successful
+        */
+       private boolean importCertificate(Uri uri)
+       {
+               try
+               {
+                       CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                       InputStream in = getContentResolver().openInputStream(uri);
+                       X509Certificate certificate = (X509Certificate)factory.generateCertificate(in);
+                       /* we don't check whether it's actually a CA certificate or not */
+                       KeyStore store = KeyStore.getInstance("LocalCertificateStore");
+                       store.load(null, null);
+                       store.setCertificateEntry(null, certificate);
+                       TrustedCertificateManager.getInstance().reset();
+                       Toast.makeText(this, R.string.cert_imported_successfully, Toast.LENGTH_LONG).show();
+                       return true;
+               }
+               catch (Exception e)
+               {
+                       Toast.makeText(this, R.string.cert_import_failed, Toast.LENGTH_LONG).show();
+                       e.printStackTrace();
+               }
+               return false;
+       }
 }
index 1211ef5..663950c 100644 (file)
@@ -30,6 +30,7 @@ import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentTransaction;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -38,6 +39,7 @@ public class TrustedCertificatesActivity extends Activity implements TrustedCert
 {
        public static final String SELECT_CERTIFICATE = "org.strongswan.android.action.SELECT_CERTIFICATE";
        private static final String DIALOG_TAG = "Dialog";
+       private static final int IMPORT_CERTIFICATE = 0;
        private boolean mSelect;
 
        @Override
@@ -92,6 +94,16 @@ public class TrustedCertificatesActivity extends Activity implements TrustedCert
        }
 
        @Override
+       public boolean onPrepareOptionsMenu(Menu menu)
+       {
+               if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+               {
+                       menu.removeItem(R.id.menu_import_certificate);
+               }
+               return true;
+       }
+
+       @Override
        public boolean onOptionsItemSelected(MenuItem item)
        {
                switch (item.getItemId())
@@ -102,11 +114,30 @@ public class TrustedCertificatesActivity extends Activity implements TrustedCert
                        case R.id.menu_reload_certs:
                                reloadCertificates();
                                return true;
+                       case R.id.menu_import_certificate:
+                               Intent intent = new Intent(this, TrustedCertificateImportActivity.class);
+                               startActivityForResult(intent, IMPORT_CERTIFICATE);
+                               return true;
                }
                return super.onOptionsItemSelected(item);
        }
 
        @Override
+       protected void onActivityResult(int requestCode, int resultCode, Intent data)
+       {
+               switch (requestCode)
+               {
+                       case IMPORT_CERTIFICATE:
+                               if (resultCode == Activity.RESULT_OK)
+                               {
+                                       reloadCertificates();
+                               }
+                               return;
+               }
+               super.onActivityResult(requestCode, resultCode, data);
+       }
+
+       @Override
        public void onTrustedCertificateSelected(TrustedCertificateEntry selected)
        {
                if (mSelect)