set svn:ignore for bin directory and .project/.classpath

asymmetric file encryption working, some more cleanup, introduced an application directory to encrypt to by default
This commit is contained in:
Thialfihar 2010-04-19 13:56:43 +00:00
parent bd4463774e
commit 26cf672d67
11 changed files with 223 additions and 73 deletions

View File

@ -183,6 +183,14 @@ public class Apg {
return; return;
} }
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Constants.path.app_dir);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
}
}
loadKeyRings(context, Id.type.public_key); loadKeyRings(context, Id.type.public_key);
loadKeyRings(context, Id.type.secret_key); loadKeyRings(context, Id.type.secret_key);

View File

@ -25,7 +25,6 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.util.Log;
public class BaseActivity extends Activity public class BaseActivity extends Activity
implements Runnable, ProgressDialogUpdater, implements Runnable, ProgressDialogUpdater,
@ -142,6 +141,21 @@ public class BaseActivity extends Activity
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
long newId = bundle.getLong("selectedKeyId");
if (getSecretKeyId() != newId) {
Apg.setPassPhrase(null);
}
setSecretKeyId(newId);
} else {
setSecretKeyId(0);
Apg.setPassPhrase(null);
}
break;
}
default: { default: {
break; break;
} }
@ -208,7 +222,6 @@ public class BaseActivity extends Activity
} }
public void passPhraseCallback(String passPhrase) { public void passPhraseCallback(String passPhrase) {
Log.e("oink", "setting pass phrase to " + passPhrase);
Apg.setPassPhrase(passPhrase); Apg.setPassPhrase(passPhrase);
} }

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* 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 org.thialfihar.android.apg;
import android.os.Environment;
public final class Constants {
public static final class path {
public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG";
}
public static final class pref {
public static final String has_seen_change_log = "seenChangeLogDialog" + Apg.VERSION;
}
}

View File

@ -36,7 +36,6 @@ import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.text.InputType; import android.text.InputType;
import android.text.method.PasswordTransformationMethod; import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -116,7 +115,6 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
} }
public boolean havePassPhrase() { public boolean havePassPhrase() {
Log.e("oink", "password is " + Apg.getPassPhrase());
return (Apg.getPassPhrase() != null && !Apg.getPassPhrase().equals("")) || return (Apg.getPassPhrase() != null && !Apg.getPassPhrase().equals("")) ||
(mNewPassPhrase != null && mNewPassPhrase.equals("")); (mNewPassPhrase != null && mNewPassPhrase.equals(""));
} }

View File

@ -16,10 +16,13 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.ByteArrayOutputStream; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
@ -32,11 +35,14 @@ import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey; import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing; import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.openintents.intents.FileManager; import org.openintents.intents.FileManager;
import org.thialfihar.android.apg.Apg.GeneralException;
import android.app.Dialog;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Message; import android.os.Message;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
@ -51,44 +57,49 @@ import android.widget.Toast;
import android.widget.TabHost.TabSpec; import android.widget.TabHost.TabSpec;
public class EncryptFileActivity extends BaseActivity { public class EncryptFileActivity extends BaseActivity {
private final String TAB_ASYMMETRIC = "TAB_ASYMMETRIC";
private final String TAB_SYMMETRIC = "TAB_SYMMETRIC";
private TabHost mTabHost = null;
private EditText mFilename = null; private EditText mFilename = null;
private ImageButton mBrowse = null; private ImageButton mBrowse = null;
private CheckBox mSign = null; private CheckBox mSign = null;
private TextView mMainUserId = null; private TextView mMainUserId = null;
private TextView mMainUserIdRest = null; private TextView mMainUserIdRest = null;
private ListView mList; private ListView mPublicKeyList = null;
private Button mEncryptButton = null; private Button mEncryptButton = null;
private long mEncryptionKeyIds[] = null; private long mEncryptionKeyIds[] = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.encrypt_file); setContentView(R.layout.encrypt_file);
TabHost tabHost = (TabHost) findViewById(R.id.tab_host); mTabHost = (TabHost) findViewById(R.id.tab_host);
tabHost.setup(); mTabHost.setup();
TabSpec ts1 = tabHost.newTabSpec("TAB_ASYMMETRIC"); TabSpec ts1 = mTabHost.newTabSpec(TAB_ASYMMETRIC);
ts1.setIndicator(getString(R.string.tab_asymmetric), ts1.setIndicator(getString(R.string.tab_asymmetric),
getResources().getDrawable(R.drawable.key)); getResources().getDrawable(R.drawable.key));
ts1.setContent(R.id.tab_asymmetric); ts1.setContent(R.id.tab_asymmetric);
tabHost.addTab(ts1); mTabHost.addTab(ts1);
TabSpec ts2 = tabHost.newTabSpec("TAB_SYMMETRIC"); TabSpec ts2 = mTabHost.newTabSpec(TAB_SYMMETRIC);
ts2.setIndicator(getString(R.string.tab_symmetric), ts2.setIndicator(getString(R.string.tab_symmetric),
getResources().getDrawable(R.drawable.encrypted)); getResources().getDrawable(R.drawable.encrypted));
ts2.setContent(R.id.tab_symmetric); ts2.setContent(R.id.tab_symmetric);
tabHost.addTab(ts2); mTabHost.addTab(ts2);
tabHost.setCurrentTab(0); mTabHost.setCurrentTab(0);
Vector<PGPPublicKeyRing> keyRings = Vector<PGPPublicKeyRing> keyRings =
(Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone(); (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
Collections.sort(keyRings, new Apg.PublicKeySorter()); Collections.sort(keyRings, new Apg.PublicKeySorter());
mList = (ListView) findViewById(R.id.public_key_list); mPublicKeyList = (ListView) findViewById(R.id.public_key_list);
mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings)); mPublicKeyList.setAdapter(new SelectPublicKeyListAdapter(mPublicKeyList, keyRings));
mFilename = (EditText) findViewById(R.id.filename); mFilename = (EditText) findViewById(R.id.filename);
mBrowse = (ImageButton) findViewById(R.id.btn_browse); mBrowse = (ImageButton) findViewById(R.id.btn_browse);
@ -180,17 +191,60 @@ public class EncryptFileActivity extends BaseActivity {
} }
private void encryptClicked() { private void encryptClicked() {
String currentFilename = mFilename.getText().toString();
if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
mInputFilename = mFilename.getText().toString();
File file = new File(mInputFilename);
mOutputFilename = Constants.path.app_dir + "/" + file.getName() + ".gpg";
}
if (mInputFilename.equals("")) {
Toast.makeText(this, "Select a file first.", Toast.LENGTH_SHORT).show();
return;
}
if (mTabHost.getCurrentTabTag().equals(TAB_ASYMMETRIC)) {
Vector<Long> vector = new Vector<Long>();
for (int i = 0; i < mPublicKeyList.getCount(); ++i) {
if (mPublicKeyList.isItemChecked(i)) {
vector.add(mPublicKeyList.getItemIdAtPosition(i));
}
}
if (vector.size() > 0) {
mEncryptionKeyIds = new long[vector.size()];
for (int i = 0; i < vector.size(); ++i) {
mEncryptionKeyIds[i] = vector.get(i);
}
} else {
mEncryptionKeyIds = null;
}
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
if (getSecretKeyId() == 0 && !encryptIt) {
Toast.makeText(this, "Select a signature key or encryption keys.",
Toast.LENGTH_SHORT).show();
return;
}
if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) { if (getSecretKeyId() != 0 && Apg.getPassPhrase() == null) {
showDialog(Id.dialog.pass_phrase); showDialog(Id.dialog.pass_phrase);
} else { return;
encryptStart();
} }
} else {
}
askForOutputFilename();
} }
@Override @Override
public void passPhraseCallback(String passPhrase) { public void passPhraseCallback(String passPhrase) {
super.passPhraseCallback(passPhrase); super.passPhraseCallback(passPhrase);
encryptStart(); askForOutputFilename();
}
private void askForOutputFilename() {
showDialog(Id.dialog.output_filename);
} }
private void encryptStart() { private void encryptStart() {
@ -205,19 +259,35 @@ public class EncryptFileActivity extends BaseActivity {
Message msg = new Message(); Message msg = new Message();
try { try {
InputStream in = new FileInputStream(mFilename.getText().toString()); if (mInputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
ByteArrayOutputStream out = new ByteArrayOutputStream(); mOutputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new GeneralException("external storage not ready");
}
}
if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) { InputStream in = new FileInputStream(mInputFilename);
OutputStream out = new FileOutputStream(mOutputFilename);
if (mTabHost.getCurrentTabTag().equals(TAB_ASYMMETRIC)) {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
if (encryptIt) {
Apg.encrypt(in, out, true, mEncryptionKeyIds, getSecretKeyId(), Apg.encrypt(in, out, true, mEncryptionKeyIds, getSecretKeyId(),
Apg.getPassPhrase(), this); Apg.getPassPhrase(), this);
data.putString("message", new String(out.toByteArray()));
} else { } else {
Apg.signText(in, out, getSecretKeyId(), Apg.signText(in, out, getSecretKeyId(),
Apg.getPassPhrase(), HashAlgorithmTags.SHA256, this); Apg.getPassPhrase(), HashAlgorithmTags.SHA256, this);
data.putString("message", new String(out.toByteArray()));
} }
} catch (IOException e) { } else {
}
out.close();
} catch (FileNotFoundException e) {
error = "file not found: " + e.getMessage();
}
catch (IOException e) {
error = e.getMessage(); error = e.getMessage();
} catch (PGPException e) { } catch (PGPException e) {
error = e.getMessage(); error = e.getMessage();
@ -235,12 +305,50 @@ public class EncryptFileActivity extends BaseActivity {
if (error != null) { if (error != null) {
data.putString("error", error); data.putString("error", error);
// delete the file if an error occurred
File file = new File(mOutputFilename);
file.delete();
} }
msg.setData(data); msg.setData(data);
sendMessage(msg); sendMessage(msg);
} }
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case Id.dialog.output_filename: {
return FileDialog.build(this, "Encrypt to file",
"Please specify which file to encrypt to.\n" +
"WARNING! File will be overwritten if it exists.",
mOutputFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.output_filename);
mOutputFilename = filename;
encryptStart();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.output_filename);
}
},
getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save),
Id.request.output_filename);
}
default: {
break;
}
}
return super.onCreateDialog(id);
}
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
@ -261,20 +369,21 @@ public class EncryptFileActivity extends BaseActivity {
return; return;
} }
case Id.request.secret_keys: { case Id.request.output_filename: {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK && data != null) {
Bundle bundle = data.getExtras(); String filename = data.getDataString();
long newId = bundle.getLong("selectedKeyId"); if (filename != null) {
if (getSecretKeyId() != newId) { // Get rid of URI prefix:
Apg.setPassPhrase(null); if (filename.startsWith("file://")) {
filename = filename.substring(7);
} }
setSecretKeyId(newId); // replace %20 and so on
} else { filename = Uri.decode(filename);
setSecretKeyId(0);
Apg.setPassPhrase(null); FileDialog.setFilename(filename);
} }
updateView(); }
break; return;
} }
default: { default: {

View File

@ -178,6 +178,9 @@ public class EncryptMessageActivity extends BaseActivity {
try { try {
boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0; boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
if (getSecretKeyId() == 0 && !encryptIt) {
throw new Apg.GeneralException("no signature key or encryption key selected");
}
String message = mMessage.getText().toString(); String message = mMessage.getText().toString();
if (!encryptIt) { if (!encryptIt) {
@ -198,12 +201,11 @@ public class EncryptMessageActivity extends BaseActivity {
if (encryptIt) { if (encryptIt) {
Apg.encrypt(in, out, true, mEncryptionKeyIds, getSecretKeyId(), Apg.encrypt(in, out, true, mEncryptionKeyIds, getSecretKeyId(),
Apg.getPassPhrase(), this); Apg.getPassPhrase(), this);
data.putString("message", new String(out.toByteArray()));
} else { } else {
Apg.signText(in, out, getSecretKeyId(), Apg.signText(in, out, getSecretKeyId(),
Apg.getPassPhrase(), HashAlgorithmTags.SHA256, this); Apg.getPassPhrase(), HashAlgorithmTags.SHA256, this);
data.putString("message", new String(out.toByteArray()));
} }
data.putString("message", new String(out.toByteArray()));
} catch (IOException e) { } catch (IOException e) {
error = e.getMessage(); error = e.getMessage();
} catch (PGPException e) { } catch (PGPException e) {
@ -288,22 +290,6 @@ public class EncryptMessageActivity extends BaseActivity {
break; break;
} }
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
long newId = bundle.getLong("selectedKeyId");
if (getSecretKeyId() != newId) {
Apg.setPassPhrase(null);
}
setSecretKeyId(newId);
} else {
setSecretKeyId(0);
Apg.setPassPhrase(null);
}
updateView();
break;
}
default: { default: {
break; break;
} }

View File

@ -37,6 +37,7 @@ public class FileDialog {
private static Activity mActivity; private static Activity mActivity;
private static String mFileManagerTitle; private static String mFileManagerTitle;
private static String mFileManagerButton; private static String mFileManagerButton;
private static int mRequestCode;
public static interface OnClickListener { public static interface OnClickListener {
public void onCancelClick(); public void onCancelClick();
@ -45,7 +46,8 @@ public class FileDialog {
public static AlertDialog build(Activity activity, String title, String message, public static AlertDialog build(Activity activity, String title, String message,
String defaultFile, OnClickListener onClickListener, String defaultFile, OnClickListener onClickListener,
String fileManagerTitle, String fileManagerButton) { String fileManagerTitle, String fileManagerButton,
int requestCode) {
LayoutInflater inflater = LayoutInflater inflater =
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity); AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@ -67,6 +69,7 @@ public class FileDialog {
}); });
mFileManagerTitle = fileManagerTitle; mFileManagerTitle = fileManagerTitle;
mFileManagerButton = fileManagerButton; mFileManagerButton = fileManagerButton;
mRequestCode = requestCode;
alert.setView(view); alert.setView(view);
@ -107,7 +110,7 @@ public class FileDialog {
intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton); intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
try { try {
mActivity.startActivityForResult(intent, Id.request.filename); mActivity.startActivityForResult(intent, mRequestCode);
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
// No compatible file manager was found. // No compatible file manager was found.
Toast.makeText(mActivity, R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show(); Toast.makeText(mActivity, R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show();

View File

@ -48,6 +48,7 @@ public final class Id {
public static final int public_keys = 0x21070001; public static final int public_keys = 0x21070001;
public static final int secret_keys = 0x21070002; public static final int secret_keys = 0x21070002;
public static final int filename = 0x21070003; public static final int filename = 0x21070003;
public static final int output_filename = 0x21070004;
} }
public static final class dialog { public static final class dialog {
@ -67,6 +68,7 @@ public final class Id {
public static final int new_account = 0x2107000e; public static final int new_account = 0x2107000e;
public static final int about = 0x2107000f; public static final int about = 0x2107000f;
public static final int change_log = 0x21070010; public static final int change_log = 0x21070010;
public static final int output_filename = 0x21070011;
} }
public static final class task { public static final class task {

View File

@ -52,9 +52,6 @@ import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
private static String PREF_SEEN_CHANGE_LOG = "seenChangeLogDialog" + Apg.VERSION;
private ListView mAccounts = null; private ListView mAccounts = null;
@Override @Override
@ -116,7 +113,7 @@ public class MainActivity extends BaseActivity {
registerForContextMenu(mAccounts); registerForContextMenu(mAccounts);
SharedPreferences prefs = getPreferences(MODE_PRIVATE); SharedPreferences prefs = getPreferences(MODE_PRIVATE);
if (!prefs.getBoolean(PREF_SEEN_CHANGE_LOG, false)) { if (!prefs.getBoolean(Constants.pref.has_seen_change_log, false)) {
showDialog(Id.dialog.change_log); showDialog(Id.dialog.change_log);
} }
} }
@ -227,6 +224,7 @@ public class MainActivity extends BaseActivity {
new SpannableString("Read the warnings!\n\n" + new SpannableString("Read the warnings!\n\n" +
"Changes:\n" + "Changes:\n" +
" * OI File Manager support\n" + " * OI File Manager support\n" +
" * file encryption\n" +
"\n" + "\n" +
"WARNING: be careful editing your existing keys, as they " + "WARNING: be careful editing your existing keys, as they " +
"WILL be stripped of certificates right now.\n" + "WILL be stripped of certificates right now.\n" +
@ -253,7 +251,7 @@ public class MainActivity extends BaseActivity {
MainActivity.this.removeDialog(Id.dialog.change_log); MainActivity.this.removeDialog(Id.dialog.change_log);
SharedPreferences prefs = getPreferences(MODE_PRIVATE); SharedPreferences prefs = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit(); SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(PREF_SEEN_CHANGE_LOG, true); editor.putBoolean(Constants.pref.has_seen_change_log, true);
editor.commit(); editor.commit();
} }
}); });

View File

@ -192,7 +192,8 @@ public class PublicKeyListActivity extends BaseActivity {
} }
}, },
getString(R.string.filemanager_title_open), getString(R.string.filemanager_title_open),
getString(R.string.filemanager_btn_open)); getString(R.string.filemanager_btn_open),
Id.request.filename);
} }
case Id.dialog.export_key: { case Id.dialog.export_key: {
@ -228,7 +229,8 @@ public class PublicKeyListActivity extends BaseActivity {
} }
}, },
getString(R.string.filemanager_title_save), getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save)); getString(R.string.filemanager_btn_save),
Id.request.filename);
} }
default: { default: {

View File

@ -217,7 +217,8 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
} }
}, },
getString(R.string.filemanager_title_open), getString(R.string.filemanager_title_open),
getString(R.string.filemanager_btn_open)); getString(R.string.filemanager_btn_open),
Id.request.filename);
} }
case Id.dialog.export_key: { case Id.dialog.export_key: {
@ -254,7 +255,8 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
} }
}, },
getString(R.string.filemanager_title_save), getString(R.string.filemanager_title_save),
getString(R.string.filemanager_btn_save)); getString(R.string.filemanager_btn_save),
Id.request.filename);
} }
case Id.dialog.pass_phrase: { case Id.dialog.pass_phrase: {