2 * Copyright (C) 2014 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 package org.strongswan.android.ui;
18 import android.annotation.TargetApi;
19 import android.app.Activity;
20 import android.app.Dialog;
21 import android.content.ActivityNotFoundException;
22 import android.content.DialogInterface;
23 import android.content.Intent;
24 import android.net.Uri;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.support.v4.app.FragmentTransaction;
28 import android.support.v7.app.AlertDialog;
29 import android.support.v7.app.AppCompatActivity;
30 import android.support.v7.app.AppCompatDialogFragment;
31 import android.widget.Toast;
33 import org.strongswan.android.R;
34 import org.strongswan.android.data.VpnProfileDataSource;
35 import org.strongswan.android.logic.TrustedCertificateManager;
37 import java.io.FileNotFoundException;
38 import java.io.InputStream;
39 import java.security.KeyStore;
40 import java.security.cert.CertificateException;
41 import java.security.cert.CertificateFactory;
42 import java.security.cert.X509Certificate;
44 public class TrustedCertificateImportActivity extends AppCompatActivity
46 private static final int OPEN_DOCUMENT = 0;
47 private static final String DIALOG_TAG = "Dialog";
48 private Uri mCertificateUri;
50 @TargetApi(Build.VERSION_CODES.KITKAT)
52 public void onCreate(Bundle savedInstanceState)
54 super.onCreate(savedInstanceState);
56 if (savedInstanceState != null)
57 { /* do nothing when we are restoring */
61 Intent intent = getIntent();
62 String action = intent.getAction();
63 if (Intent.ACTION_VIEW.equals(action))
65 importCertificate(intent.getData());
67 else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
69 Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
70 openIntent.setType("*/*");
73 startActivityForResult(openIntent, OPEN_DOCUMENT);
75 catch (ActivityNotFoundException e)
76 { /* some devices are unable to browse for files */
84 protected void onActivityResult(int requestCode, int resultCode, Intent data)
86 super.onActivityResult(requestCode, resultCode, data);
90 if (resultCode == Activity.RESULT_OK && data != null)
92 mCertificateUri = data.getData();
101 protected void onPostResume()
103 super.onPostResume();
104 if (mCertificateUri != null)
106 importCertificate(mCertificateUri);
107 mCertificateUri = null;
112 * Import the file pointed to by the given URI as a certificate.
116 private void importCertificate(Uri uri)
118 X509Certificate certificate = parseCertificate(uri);
119 if (certificate == null)
121 Toast.makeText(this, R.string.cert_import_failed, Toast.LENGTH_LONG).show();
125 /* Ask the user whether to import the certificate. This is particularly
126 * necessary because the import activity can be triggered by any app on
127 * the system. Also, if our app is the only one that is registered to
128 * open certificate files by MIME type the user would have no idea really
129 * where the file was imported just by reading the Toast we display. */
130 ConfirmImportDialog dialog = new ConfirmImportDialog();
131 Bundle args = new Bundle();
132 args.putSerializable(VpnProfileDataSource.KEY_CERTIFICATE, certificate);
133 dialog.setArguments(args);
134 FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
135 ft.add(dialog, DIALOG_TAG);
140 * Load the file from the given URI and try to parse it as X.509 certificate.
143 * @return certificate or null
145 private X509Certificate parseCertificate(Uri uri)
147 X509Certificate certificate = null;
150 CertificateFactory factory = CertificateFactory.getInstance("X.509");
151 InputStream in = getContentResolver().openInputStream(uri);
152 certificate = (X509Certificate)factory.generateCertificate(in);
153 /* we don't check whether it's actually a CA certificate or not */
155 catch (CertificateException e)
159 catch (FileNotFoundException e)
168 * Try to store the given certificate in the KeyStore.
171 * @return whether it was successfully stored
173 private boolean storeCertificate(X509Certificate certificate)
177 KeyStore store = KeyStore.getInstance("LocalCertificateStore");
178 store.load(null, null);
179 store.setCertificateEntry(null, certificate);
180 TrustedCertificateManager.getInstance().reset();
191 * Class that displays a confirmation dialog when a certificate should get
192 * imported. If the user confirms the import we try to store it.
194 public static class ConfirmImportDialog extends AppCompatDialogFragment
197 public Dialog onCreateDialog(Bundle savedInstanceState)
199 final X509Certificate certificate;
201 certificate = (X509Certificate)getArguments().getSerializable(VpnProfileDataSource.KEY_CERTIFICATE);
203 return new AlertDialog.Builder(getActivity())
204 .setIcon(R.drawable.ic_launcher)
205 .setTitle(R.string.import_certificate)
206 .setMessage(certificate.getSubjectDN().toString())
207 .setPositiveButton(R.string.import_certificate, new DialogInterface.OnClickListener()
210 public void onClick(DialogInterface dialog, int whichButton)
212 TrustedCertificateImportActivity activity = (TrustedCertificateImportActivity)getActivity();
213 if (activity.storeCertificate(certificate))
215 Toast.makeText(getActivity(), R.string.cert_imported_successfully, Toast.LENGTH_LONG).show();
216 getActivity().setResult(Activity.RESULT_OK);
220 Toast.makeText(getActivity(), R.string.cert_import_failed, Toast.LENGTH_LONG).show();
222 getActivity().finish();
225 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
228 public void onClick(DialogInterface dialog, int which)
230 getActivity().finish();
236 public void onCancel(DialogInterface dialog)
238 getActivity().finish();