Fix SSL certificate selection for exchange.

- Since the Email and Exchange processes do not share UID, we need to
ensure the call to the KeyChain to request the certificate happens in
the Exchange process
- Misc UI fixes so it's not so ugly

Bug: 5117682
Change-Id: If80698850902e0178eb0998493b4cf4b89e2a15c
This commit is contained in:
Ben Komalo 2011-08-05 11:28:52 -07:00
parent 6be8cceadd
commit 994343b14b
4 changed files with 113 additions and 36 deletions

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.emailcommon.utility;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
/**
* A headless Activity which simply calls into the framework {@link KeyChain} service to select
* a certificate to use for establishing secure connections in the Email app.
*/
public class CertificateRequestor extends Activity implements KeyChainAliasCallback {
public static final String ACTION_REQUEST_CERT = "com.android.emailcommon.REQUEST_CERT";
public static final String EXTRA_HOST = "CertificateRequestor.host";
public static final String EXTRA_PORT = "CertificateRequestor.port";
public static final String RESULT_ALIAS = "CertificateRequestor.alias";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = getIntent();
String host = i.getStringExtra(EXTRA_HOST);
int port = i.getIntExtra(EXTRA_PORT, -1);
KeyChain.choosePrivateKeyAlias(
this, this,
null /* keytypes */, null /* issuers */,
host, port,
null /* alias */);
}
/**
* Callback for the certificate request. Does not happen on the UI thread.
*/
@Override
public void alias(String alias) {
if (alias == null) {
setResult(RESULT_CANCELED);
} else {
Intent data = new Intent();
data.putExtra(RESULT_ALIAS, alias);
setResult(RESULT_OK, data);
}
finish();
}
}

View File

@ -20,10 +20,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:layout_marginBottom="8dip"
android:layout_marginLeft="32dip"
android:layout_marginRight="32dip"
android:layout_margin="8dip"
android:gravity="center_vertical"
android:orientation="horizontal" >
<TextView
@ -31,9 +29,12 @@
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:visibility="gone" />
<Button
android:id="@+id/select_button"
style="@style/accountSetupButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/account_setup_exchange_use_certificate" />

View File

@ -18,6 +18,8 @@ package com.android.email.activity.setup;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.Editable;
@ -38,10 +40,12 @@ import com.android.email.activity.UiUtilities;
import com.android.email.provider.AccountBackupRestore;
import com.android.email.service.EmailServiceUtils;
import com.android.email.view.CertificateSelector;
import com.android.email.view.CertificateSelector.HostCallback;
import com.android.emailcommon.Device;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.utility.CertificateRequestor;
import com.android.emailcommon.utility.Utility;
import java.io.IOException;
@ -53,8 +57,9 @@ import java.io.IOException;
* (for editing existing accounts).
*/
public class AccountSetupExchangeFragment extends AccountServerBaseFragment
implements OnCheckedChangeListener {
implements OnCheckedChangeListener, HostCallback {
private static final int CERTIFICATE_REQUEST = 0;
private final static String STATE_KEY_CREDENTIAL = "AccountSetupExchangeFragment.credential";
private final static String STATE_KEY_LOADED = "AccountSetupExchangeFragment.loaded";
@ -147,7 +152,7 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
Log.d(Logging.LOG_TAG, "AccountSetupExchangeFragment onActivityCreated");
}
super.onActivityCreated(savedInstanceState);
mClientCertificateSelector.setActivity(getActivity());
mClientCertificateSelector.setHostActivity(this);
}
/**
@ -414,4 +419,21 @@ public class AccountSetupExchangeFragment extends AccountServerBaseFragment
SetupData.CHECK_INCOMING);
}
@Override
public void onCertificateRequested() {
Intent intent = new Intent(CertificateRequestor.ACTION_REQUEST_CERT);
intent.setData(Uri.parse("eas://com.android.emailcommon/certrequest"));
startActivityForResult(intent, CERTIFICATE_REQUEST);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
if (certAlias != null) {
mClientCertificateSelector.setCertificate(certAlias);
}
}
}
}

View File

@ -17,16 +17,10 @@
package com.android.email.view;
import com.android.email.R;
import com.android.email.activity.UiUtilities;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
@ -34,22 +28,26 @@ import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.email.R;
import com.android.email.activity.UiUtilities;
/**
* A simple view that can be used to select a certificate from the system {@link KeyChain}.
*
* Host activities must register themselves view {@link #setActivity} for this selector to work,
* since it requires firing system {@link Intent}s.
* Host activities must register themselves view {@link #setHostActivity} for this selector to work.
*/
public class CertificateSelector extends LinearLayout implements
OnClickListener, KeyChainAliasCallback {
public class CertificateSelector extends LinearLayout implements OnClickListener {
/** Button to select or remove the certificate. */
private Button mSelectButton;
private TextView mAliasText;
/** The host activity. */
private Activity mActivity;
private HostCallback mHost;
public interface HostCallback {
void onCertificateRequested();
}
public CertificateSelector(Context context) {
super(context);
@ -61,8 +59,11 @@ public class CertificateSelector extends LinearLayout implements
super(context, attrs, defStyle);
}
public void setActivity(Activity activity) {
mActivity = activity;
public void setHostActivity(HostCallback host) {
mHost = host;
}
public void setDelegate(String uri) {
}
@Override
@ -98,31 +99,16 @@ public class CertificateSelector extends LinearLayout implements
@Override
public void onClick(View target) {
if (target == mSelectButton && mActivity != null) {
if (target == mSelectButton && mHost != null) {
if (hasCertificate()) {
// Handle the click on the button when it says "Remove"
setCertificate(null);
} else {
// We don't restrict the chooser for certificate types since 95% of the time the
// user will probably only have one certificate installed and it'll be the right
// "type". Just let them fail and select a different one if it doesn't match.
KeyChain.choosePrivateKeyAlias(
mActivity, this,
null /* keytypes */, null /* issuers */,
null /* host */, -1 /* port */,
null /* alias */);
mHost.onCertificateRequested();
}
}
}
// KeyChainAliasCallback
@Override
public void alias(String alias) {
if (alias != null) {
setCertificate(alias);
}
}
@Override
protected void onRestoreInstanceState(Parcelable parcel) {
SavedState savedState = (SavedState) parcel;