aboutsummaryrefslogtreecommitdiffstats
path: root/src/org/connectbot/GeneratePubkeyActivity.java
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2008-10-29 05:01:19 +0000
committerKenny Root <kenny@the-b.org>2008-10-29 05:01:19 +0000
commitdc59e640790d70a4542b37e3718dbccbd6be0d08 (patch)
tree22e0b3112753ddafe4831f41575d37e7689f4d43 /src/org/connectbot/GeneratePubkeyActivity.java
parent540c693d2c0fdd10426468de6b66982d02a3ec91 (diff)
downloadconnectbot-dc59e640790d70a4542b37e3718dbccbd6be0d08.tar.gz
connectbot-dc59e640790d70a4542b37e3718dbccbd6be0d08.tar.bz2
connectbot-dc59e640790d70a4542b37e3718dbccbd6be0d08.zip
* First pass at publickey authentication.
* RSA and DSA keys can be generated (not imported yet). * RSA and DSA keys can be copied to the clipboard and deleted. * Encrypted keys are not tried right now, only unencrypted. * Restore Marcus's name (Jeffrey, fix your editor!) * Fix a typo in the EULA.
Diffstat (limited to 'src/org/connectbot/GeneratePubkeyActivity.java')
-rw-r--r--src/org/connectbot/GeneratePubkeyActivity.java284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/org/connectbot/GeneratePubkeyActivity.java b/src/org/connectbot/GeneratePubkeyActivity.java
new file mode 100644
index 0000000..d33ffb3
--- /dev/null
+++ b/src/org/connectbot/GeneratePubkeyActivity.java
@@ -0,0 +1,284 @@
+/*
+ ConnectBot: simple, powerful, open-source SSH client for Android
+ Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package org.connectbot;
+
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.connectbot.util.EntropyDialog;
+import org.connectbot.util.EntropyView;
+import org.connectbot.util.PubkeyDatabase;
+import org.connectbot.util.OnEntropyGatheredListener;
+import org.connectbot.util.PubkeyUtils;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.RadioGroup;
+import android.widget.SeekBar;
+import android.widget.RadioGroup.OnCheckedChangeListener;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener {
+ public final static String TAG = GeneratePubkeyActivity.class.toString();
+
+ final static String KEY_TYPE_RSA = "RSA",
+ KEY_TYPE_DSA = "DSA";
+
+ final static int DEFAULT_BITS = 1024;
+
+ protected LayoutInflater inflater = null;
+
+ private EditText nickname;
+ private RadioGroup keyTypeGroup;
+ private SeekBar bitsSlider;
+ private EditText bitsText;
+ private CheckBox unlockAtStartup;
+ private Button save;
+ private Dialog entropyDialog;
+ private ProgressDialog progress;
+
+ private EditText password1, password2;
+
+ private String keyType = KEY_TYPE_RSA;
+ private int minBits = 768;
+ private int bits = DEFAULT_BITS;
+
+ private byte[] entropy;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.act_generatepubkey);
+
+ nickname = (EditText) findViewById(R.id.nickname);
+
+ keyTypeGroup = (RadioGroup) findViewById(R.id.key_type);
+
+ bitsText = (EditText) findViewById(R.id.bits);
+ bitsSlider = (SeekBar) findViewById(R.id.bits_slider);
+
+ password1 = (EditText) findViewById(R.id.password1);
+ password2 = (EditText) findViewById(R.id.password2);
+
+ unlockAtStartup = (CheckBox) findViewById(R.id.unlock_at_startup);
+
+ save = (Button) findViewById(R.id.save);
+
+ inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ nickname.addTextChangedListener(textChecker);
+ password1.addTextChangedListener(textChecker);
+ password2.addTextChangedListener(textChecker);
+
+ keyTypeGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ if (checkedId == R.id.rsa) {
+ minBits = 768;
+
+ bitsSlider.setEnabled(true);
+ bitsSlider.setProgress(DEFAULT_BITS - minBits);
+
+ bitsText.setText(String.valueOf(DEFAULT_BITS));
+ bitsText.setEnabled(true);
+
+ keyType = KEY_TYPE_RSA;
+ } else if (checkedId == R.id.dsa) {
+ // DSA keys can only be 1024 bits
+
+ bitsSlider.setEnabled(false);
+ bitsSlider.setProgress(DEFAULT_BITS - minBits);
+
+ bitsText.setText(String.valueOf(DEFAULT_BITS));
+ bitsText.setEnabled(false);
+
+ keyType = KEY_TYPE_DSA;
+ }
+ }
+ });
+
+ bitsSlider.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+
+ public void onProgressChanged(SeekBar seekBar, int progress,
+ boolean fromTouch) {
+ // Stay evenly divisible by 8 because it looks nicer to have
+ // 2048 than 2043 bits.
+
+ int leftover = progress % 8;
+
+ if (leftover > 0)
+ progress += 8 - leftover;
+
+ bits = minBits + progress;
+ bitsText.setText(String.valueOf(bits));
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // We don't care about the start.
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // We don't care about the stop.
+ }
+ });
+
+ bitsText.setOnFocusChangeListener(new OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ try {
+ bits = Integer.parseInt(bitsText.getText().toString());
+ } catch (NumberFormatException nfe) {
+ bits = DEFAULT_BITS;
+ bitsText.setText(String.valueOf(bits));
+ }
+ }
+ }
+ });
+
+ save.setOnClickListener(new OnClickListener() {
+ public void onClick(View view) {
+ GeneratePubkeyActivity.this.save.setEnabled(false);
+
+ GeneratePubkeyActivity.this.startEntropyGather();
+ }
+ });
+
+ }
+
+ private void checkEntries() {
+ boolean allowSave = true;
+
+ if (!password1.getText().toString().equals(password2.getText().toString()))
+ allowSave = false;
+
+ if (nickname.getText().length() == 0)
+ allowSave = false;
+
+ save.setEnabled(allowSave);
+ }
+
+ private void startEntropyGather() {
+ final View entropyView = inflater.inflate(R.layout.dia_gatherentropy, null, false);
+ ((EntropyView)entropyView.findViewById(R.id.entropy)).addOnEntropyGatheredListener(GeneratePubkeyActivity.this);
+ entropyDialog = new EntropyDialog(GeneratePubkeyActivity.this, entropyView);
+ entropyDialog.show();
+ }
+
+ public void onEntropyGathered(byte[] entropy) {
+ this.entropy = entropy;
+
+ Log.d(TAG, "entropy gathered; attemping to generate key...");
+ startKeyGen();
+ }
+
+ private void startKeyGen() {
+ progress = new ProgressDialog(GeneratePubkeyActivity.this);
+ progress.setMessage(GeneratePubkeyActivity.this.getResources().getText(R.string.pubkey_generating));
+ progress.setIndeterminate(true);
+ progress.setCancelable(false);
+ progress.show();
+
+ new Thread(mKeyGen).start();
+ }
+
+ private Handler handler = new Handler() {
+ public void handleMessage(Message msg) {
+ progress.dismiss();
+ GeneratePubkeyActivity.this.finish();
+ }
+ };
+
+ final private Runnable mKeyGen = new Runnable() {
+ public void run() {
+ try {
+ boolean encrypted = false;
+
+ SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+
+ random.setSeed(entropy);
+
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(keyType);
+
+ keyPairGen.initialize(bits, random);
+
+ KeyPair pair = keyPairGen.generateKeyPair();
+ PrivateKey priv = pair.getPrivate();
+ PublicKey pub = pair.getPublic();
+
+ String secret = password1.getText().toString();
+ if (secret.length() > 0)
+ encrypted = true;
+
+ Log.d(TAG, "private: " + PubkeyUtils.formatKey(priv));
+ Log.d(TAG, "public: " + PubkeyUtils.formatKey(pub));
+
+ PubkeyDatabase hostdb = new PubkeyDatabase(GeneratePubkeyActivity.this);
+ hostdb.createPubkey(null,
+ nickname.getText().toString(),
+ keyType,
+ PubkeyUtils.getEncodedPrivate(priv, secret),
+ PubkeyUtils.getEncodedPublic(pub),
+ encrypted,
+ unlockAtStartup.isChecked());
+ } catch (Exception e) {
+ Log.e(TAG, "Could not generate key pair");
+
+ e.printStackTrace();
+ }
+
+ handler.sendEmptyMessage(0);
+ }
+
+ };
+
+ final private TextWatcher textChecker = new TextWatcher() {
+ public void afterTextChanged(Editable s) {}
+
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {}
+
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ checkEntries();
+ }
+ };
+}