From: Tobias Brunner Date: Tue, 28 Aug 2012 12:09:18 +0000 (+0200) Subject: android: Allow configuration of a user certificate X-Git-Tag: 5.0.1~152^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=f46da851ab51a1b6ef6b9817f49a866fb25e1c09;p=thirdparty%2Fstrongswan.git android: Allow configuration of a user certificate --- diff --git a/src/frontends/android/res/layout/profile_detail_view.xml b/src/frontends/android/res/layout/profile_detail_view.xml index 2a52ae032d..39c94348b8 100644 --- a/src/frontends/android/res/layout/profile_detail_view.xml +++ b/src/frontends/android/res/layout/profile_detail_view.xml @@ -62,7 +62,7 @@ android:id="@+id/vpn_type" android:layout_width="match_parent" android:layout_height="wrap_content" - android:spinnerMode="dialog" + android:spinnerMode="dropdown" android:entries="@array/vpn_types" /> + + + + + + + + IKEv2 EAP (Benutzername/Passwort) + IKEv2 Zertifikat \ No newline at end of file diff --git a/src/frontends/android/res/values-de/strings.xml b/src/frontends/android/res/values-de/strings.xml index 9e415ab8b3..a04da7208f 100644 --- a/src/frontends/android/res/values-de/strings.xml +++ b/src/frontends/android/res/values-de/strings.xml @@ -25,6 +25,7 @@ Suchen VPN nicht unterstützt Ihr Gerät unterstützt keine VPN Anwendungen.\nBitte kontaktieren Sie den Hersteller. + Laden… Log @@ -53,6 +54,9 @@ Benutzername: Passwort: (anfordern wenn benötigt) + Benutzer-Zertifikat: + Benutzer-Zertifikat auswählen + Wählen Sie ein bestimmtes Benutzer-Zertifikat CA-Zertifikat: Automatisch wählen CA-Zertifikat auswählen diff --git a/src/frontends/android/res/values-pl/arrays.xml b/src/frontends/android/res/values-pl/arrays.xml index ed14934e07..3e1af5f82f 100644 --- a/src/frontends/android/res/values-pl/arrays.xml +++ b/src/frontends/android/res/values-pl/arrays.xml @@ -17,5 +17,6 @@ IKEv2 EAP (użytkownik/hasło) + IKEv2 certyfikat \ No newline at end of file diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml index 4a474e88b3..54f4259aef 100644 --- a/src/frontends/android/res/values-pl/strings.xml +++ b/src/frontends/android/res/values-pl/strings.xml @@ -27,6 +27,7 @@ Szukaj Nie obsługiwany VPN Urządzenie nie obsługuje aplikacji VPN.\nProszę skontaktować się z producentem. + Wczytywanie… Log @@ -54,7 +55,10 @@ Typ: Użytkownik: Hasło: - (w razie potrzebz zapromptuj) + (w razie potrzeby zapromptuj) + Certyfikat użytkownika: + Wybierz certyfikat użytkownika + >Wybierz określony certyfikat użytkownika Certyfikat CA: Wybierz automatycznie Wybierz certyfikat CA diff --git a/src/frontends/android/res/values/arrays.xml b/src/frontends/android/res/values/arrays.xml index 62164f7c23..21576f22ce 100644 --- a/src/frontends/android/res/values/arrays.xml +++ b/src/frontends/android/res/values/arrays.xml @@ -17,5 +17,6 @@ IKEv2 EAP (Username/Password) + IKEv2 Certificate \ No newline at end of file diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml index 29cd6430bb..3e4b746fd5 100644 --- a/src/frontends/android/res/values/strings.xml +++ b/src/frontends/android/res/values/strings.xml @@ -25,6 +25,7 @@ Search VPN not supported Your device does not support VPN applications.\nPlease contact the manufacturer. + Loading… Log @@ -53,6 +54,9 @@ Username: Password: (prompt when needed) + User certificate: + Select user certificate + Select a specific user certificate CA certificate: Select automatically Select CA certificate diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java index 6cdf97b4b0..91e521cf44 100644 --- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java +++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java @@ -28,9 +28,14 @@ import org.strongswan.android.logic.TrustedCertificateManager; import android.app.Activity; import android.app.AlertDialog; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.os.AsyncTask; import android.os.Bundle; +import android.security.KeyChain; +import android.security.KeyChainAliasCallback; +import android.security.KeyChainException; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -54,6 +59,8 @@ public class VpnProfileDetailActivity extends Activity private VpnProfileDataSource mDataSource; private Long mId; private TrustedCertificateEntry mCertEntry; + private String mUserCertLoading; + private TrustedCertificateEntry mUserCertEntry; private VpnType mVpnType = VpnType.IKEV2_EAP; private VpnProfile mProfile; private EditText mName; @@ -62,6 +69,8 @@ public class VpnProfileDetailActivity extends Activity private ViewGroup mUsernamePassword; private EditText mUsername; private EditText mPassword; + private ViewGroup mUserCertificate; + private TwoLineListItem mSelectUserCert; private CheckBox mCheckAuto; private TwoLineListItem mSelectCert; @@ -86,6 +95,9 @@ public class VpnProfileDetailActivity extends Activity mUsername = (EditText)findViewById(R.id.username); mPassword = (EditText)findViewById(R.id.password); + mUserCertificate = (ViewGroup)findViewById(R.id.user_certificate_group); + mSelectUserCert = (TwoLineListItem)findViewById(R.id.select_user_certificate); + mCheckAuto = (CheckBox)findViewById(R.id.ca_auto); mSelectCert = (TwoLineListItem)findViewById(R.id.select_certificate); @@ -94,17 +106,19 @@ public class VpnProfileDetailActivity extends Activity public void onItemSelected(AdapterView parent, View view, int position, long id) { mVpnType = VpnType.values()[position]; - updateClientCredentialView(); + updateCredentialView(); } @Override public void onNothingSelected(AdapterView parent) { /* should not happen */ mVpnType = VpnType.IKEV2_EAP; - updateClientCredentialView(); + updateCredentialView(); } }); + mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener()); + mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) @@ -131,7 +145,7 @@ public class VpnProfileDetailActivity extends Activity loadProfileData(savedInstanceState); - updateClientCredentialView(); + updateCredentialView(); updateCertificateSelector(); } @@ -150,6 +164,10 @@ public class VpnProfileDetailActivity extends Activity { outState.putLong(VpnProfileDataSource.KEY_ID, mId); } + if (mUserCertEntry != null) + { + outState.putString(VpnProfileDataSource.KEY_USER_CERTIFICATE, mUserCertEntry.getAlias()); + } if (mCertEntry != null) { outState.putString(VpnProfileDataSource.KEY_CERTIFICATE, mCertEntry.getAlias()); @@ -201,11 +219,32 @@ public class VpnProfileDetailActivity extends Activity } /** - * Update the UI to enter client credentials depending on the type of VPN currently selected + * Update the UI to enter credentials depending on the type of VPN currently selected */ - private void updateClientCredentialView() + private void updateCredentialView() { mUsernamePassword.setVisibility(mVpnType.getRequiresUsernamePassword() ? View.VISIBLE : View.GONE); + mUserCertificate.setVisibility(mVpnType.getRequiresCertificate() ? View.VISIBLE : View.GONE); + + if (mVpnType.getRequiresCertificate()) + { + if (mUserCertLoading != null) + { + mSelectUserCert.getText1().setText(mUserCertLoading); + mSelectUserCert.getText2().setText(R.string.loading); + } + else if (mUserCertEntry != null) + { /* clear any errors and set the new data */ + mSelectUserCert.getText1().setError(null); + mSelectUserCert.getText1().setText(mUserCertEntry.getAlias()); + mSelectUserCert.getText2().setText(mUserCertEntry.getCertificate().getSubjectDN().toString()); + } + else + { + mSelectUserCert.getText1().setText(R.string.profile_user_select_certificate_label); + mSelectUserCert.getText2().setText(R.string.profile_user_select_certificate); + } + } } /** @@ -300,6 +339,11 @@ public class VpnProfileDetailActivity extends Activity valid = false; } } + if (mVpnType.getRequiresCertificate() && mUserCertEntry == null) + { /* let's show an error icon */ + mSelectUserCert.getText1().setError(""); + valid = false; + } if (!mCheckAuto.isChecked() && mCertEntry == null) { showCertificateAlert(); @@ -326,6 +370,10 @@ public class VpnProfileDetailActivity extends Activity password = password.isEmpty() ? null : password; mProfile.setPassword(password); } + if (mVpnType.getRequiresCertificate()) + { + mProfile.setUserCertificateAlias(mUserCertEntry.getAlias()); + } String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias(); mProfile.setCertificateAlias(certAlias); } @@ -337,7 +385,7 @@ public class VpnProfileDetailActivity extends Activity */ private void loadProfileData(Bundle savedInstanceState) { - String alias = null; + String useralias = null, alias = null; getActionBar().setTitle(R.string.add_profile); if (mId != null && mId != 0) @@ -350,6 +398,7 @@ public class VpnProfileDetailActivity extends Activity mVpnType = mProfile.getVpnType(); mUsername.setText(mProfile.getUsername()); mPassword.setText(mProfile.getPassword()); + useralias = mProfile.getUserCertificateAlias(); alias = mProfile.getCertificateAlias(); getActionBar().setTitle(mProfile.getName()); } @@ -363,7 +412,16 @@ public class VpnProfileDetailActivity extends Activity mSelectVpnType.setSelection(mVpnType.ordinal()); - /* check if the user selected a certificate previously */ + /* check if the user selected a user certificate previously */ + useralias = savedInstanceState == null ? useralias: savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE); + if (useralias != null) + { + UserCertificateLoader loader = new UserCertificateLoader(this, useralias); + mUserCertLoading = useralias; + loader.execute(); + } + + /* check if the user selected a CA certificate previously */ alias = savedInstanceState == null ? alias : savedInstanceState.getString(VpnProfileDataSource.KEY_CERTIFICATE); mCheckAuto.setChecked(alias == null); if (alias != null) @@ -380,4 +438,102 @@ public class VpnProfileDetailActivity extends Activity } } } + + private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback + { + @Override + public void onClick(View v) + { + String useralias = mUserCertEntry != null ? mUserCertEntry.getAlias() : null; + KeyChain.choosePrivateKeyAlias(VpnProfileDetailActivity.this, this, new String[] { "RSA" }, null, null, -1, useralias); + } + + @Override + public void alias(final String alias) + { + if (alias != null) + { /* otherwise the dialog was canceled, the request denied */ + try + { + final X509Certificate[] chain = KeyChain.getCertificateChain(VpnProfileDetailActivity.this, alias); + /* alias() is not called from our main thread */ + runOnUiThread(new Runnable() { + @Override + public void run() + { + if (chain != null && chain.length > 0) + { + mUserCertEntry = new TrustedCertificateEntry(alias, chain[0]); + } + updateCredentialView(); + } + }); + } + catch (KeyChainException e) + { + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + } + + /** + * Load the selected user certificate asynchronously. This cannot be done + * from the main thread as getCertificateChain() calls back to our main + * thread to bind to the KeyChain service resulting in a deadlock. + */ + private class UserCertificateLoader extends AsyncTask + { + private final Context mContext; + private final String mAlias; + + public UserCertificateLoader(Context context, String alias) + { + mContext = context; + mAlias = alias; + } + + @Override + protected X509Certificate doInBackground(Void... params) + { + X509Certificate[] chain = null; + try + { + chain = KeyChain.getCertificateChain(mContext, mAlias); + } + catch (KeyChainException e) + { + e.printStackTrace(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + if (chain != null && chain.length > 0) + { + return chain[0]; + } + return null; + } + + @Override + protected void onPostExecute(X509Certificate result) + { + if (result != null) + { + mUserCertEntry = new TrustedCertificateEntry(mAlias, result); + } + else + { /* previously selected certificate is not here anymore */ + mSelectUserCert.getText1().setError(""); + mUserCertEntry = null; + } + mUserCertLoading = null; + updateCredentialView(); + } + } }