aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2013-04-12 00:13:42 -0700
committerKenny Root <kenny@the-b.org>2013-04-12 00:33:11 -0700
commitf7beb3b827f0eb6d03d8ff3589782a0df14fd2f5 (patch)
treea31841f6d282c0a42c3a63376502a24103f42002
parentd8e49ec3f297f9ba0991762eb2cddbc00ed634d2 (diff)
downloadconnectbot-f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5.tar.gz
connectbot-f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5.tar.bz2
connectbot-f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5.zip
Add EC pubkey to UI
-rw-r--r--res/layout/act_generatepubkey.xml6
-rw-r--r--src/com/trilead/ssh2/auth/AuthenticationManager.java6
-rw-r--r--src/com/trilead/ssh2/signature/ECDSASHA2Verify.java37
-rw-r--r--src/org/connectbot/GeneratePubkeyActivity.java70
-rw-r--r--src/org/connectbot/bean/PubkeyBean.java6
-rw-r--r--src/org/connectbot/util/PubkeyDatabase.java3
-rw-r--r--src/org/connectbot/util/PubkeyUtils.java61
7 files changed, 155 insertions, 34 deletions
diff --git a/res/layout/act_generatepubkey.xml b/res/layout/act_generatepubkey.xml
index 324c125..4aebf84 100644
--- a/res/layout/act_generatepubkey.xml
+++ b/res/layout/act_generatepubkey.xml
@@ -74,6 +74,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DSA" />
+
+ <RadioButton
+ android:id="@+id/ec"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="EC" />
</RadioGroup>
</TableRow>
diff --git a/src/com/trilead/ssh2/auth/AuthenticationManager.java b/src/com/trilead/ssh2/auth/AuthenticationManager.java
index 7a1305b..e551495 100644
--- a/src/com/trilead/ssh2/auth/AuthenticationManager.java
+++ b/src/com/trilead/ssh2/auth/AuthenticationManager.java
@@ -246,6 +246,8 @@ public class AuthenticationManager implements MessageHandler
else if (key instanceof ECPrivateKey)
{
ECPrivateKey pk = (ECPrivateKey) key;
+ final String algo = ECDSASHA2Verify.ECDSA_SHA2_PREFIX
+ + ECDSASHA2Verify.getCurveName(pk.getParams());
byte[] pk_enc = ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic());
@@ -259,7 +261,7 @@ public class AuthenticationManager implements MessageHandler
tw.writeString("ssh-connection");
tw.writeString("publickey");
tw.writeBoolean(true);
- tw.writeString("ecdsa-sha2-nistp256");
+ tw.writeString(algo);
tw.writeString(pk_enc, 0, pk_enc.length);
}
@@ -270,7 +272,7 @@ public class AuthenticationManager implements MessageHandler
byte[] ec_sig_enc = ECDSASHA2Verify.encodeSSHECDSASignature(ds, pk.getParams());
PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
- "ecdsa-sha2-nistp256", pk_enc, ec_sig_enc);
+ algo, pk_enc, ec_sig_enc);
tm.sendMessage(ua.getPayload());
}
diff --git a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
index 4f3bae2..7b4f6af 100644
--- a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
+++ b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java
@@ -55,6 +55,23 @@ public class ECDSASHA2Verify {
CURVE_SIZES.put(521, NISTP521);
}
+ public static int[] getCurveSizes() {
+ int[] keys = new int[CURVE_SIZES.size()];
+ int i = 0;
+ for (Integer n : CURVE_SIZES.keySet().toArray(new Integer[keys.length])) {
+ keys[i++] = n;
+ }
+ return keys;
+ }
+
+ public static ECParameterSpec getCurveForSize(int size) {
+ final String name = CURVE_SIZES.get(size);
+ if (name == null) {
+ return null;
+ }
+ return CURVES.get(name);
+ }
+
public static ECPublicKey decodeSSHECDSAPublicKey(byte[] key) throws IOException
{
TypesReader tr = new TypesReader(key);
@@ -112,21 +129,30 @@ public class ECDSASHA2Verify {
tw.writeString(curveName);
- tw.writeBytes(encodeECPoint(key.getW(), key.getParams().getCurve()));
+ byte[] encoded = encodeECPoint(key.getW(), key.getParams().getCurve());
+ tw.writeString(encoded, 0, encoded.length);
return tw.getBytes();
}
- private static String getCurveName(ECParameterSpec params) throws IOException {
+ public static String getCurveName(ECParameterSpec params) throws IOException {
int fieldSize = getCurveSize(params);
+ final String curveName = getCurveName(fieldSize);
+ if (curveName == null) {
+ throw new IOException("invalid curve size " + fieldSize);
+ }
+ return curveName;
+ }
+
+ public static String getCurveName(int fieldSize) {
String curveName = CURVE_SIZES.get(fieldSize);
if (curveName == null) {
- throw new IOException("Unsupported curve field size: " + fieldSize);
+ return null;
}
return curveName;
}
- private static int getCurveSize(ECParameterSpec params) {
+ public static int getCurveSize(ECParameterSpec params) {
return params.getCurve().getField().getFieldSize();
}
@@ -258,7 +284,8 @@ public class ECDSASHA2Verify {
TypesWriter rsWriter = new TypesWriter();
rsWriter.writeMPInt(r);
rsWriter.writeMPInt(s);
- tw.writeBytes(rsWriter.getBytes());
+ byte[] encoded = rsWriter.getBytes();
+ tw.writeString(encoded, 0, encoded.length);
return tw.getBytes();
}
diff --git a/src/org/connectbot/GeneratePubkeyActivity.java b/src/org/connectbot/GeneratePubkeyActivity.java
index 089e485..aa004cf 100644
--- a/src/org/connectbot/GeneratePubkeyActivity.java
+++ b/src/org/connectbot/GeneratePubkeyActivity.java
@@ -33,7 +33,6 @@ 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;
@@ -48,15 +47,26 @@ 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;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+
public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener {
- public final static String TAG = "ConnectBot.GeneratePubkeyActivity";
+ /**
+ *
+ */
+ private static final int RSA_MINIMUM_BITS = 768;
+
+ public final static String TAG = "ConnectBot.GeneratePubkeyActivity";
final static int DEFAULT_BITS = 1024;
+ final static int[] ECDSA_SIZES = ECDSASHA2Verify.getCurveSizes();
+
+ final static int ECDSA_DEFAULT_BITS = ECDSA_SIZES[0];
+
private LayoutInflater inflater = null;
private EditText nickname;
@@ -109,7 +119,7 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == R.id.rsa) {
- minBits = 768;
+ minBits = RSA_MINIMUM_BITS;
bitsSlider.setEnabled(true);
bitsSlider.setProgress(DEFAULT_BITS - minBits);
@@ -128,6 +138,16 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
bitsText.setEnabled(false);
keyType = PubkeyDatabase.KEY_TYPE_DSA;
+ } else if (checkedId == R.id.ec) {
+ minBits = ECDSA_DEFAULT_BITS;
+
+ bitsSlider.setEnabled(true);
+ bitsSlider.setProgress(ECDSA_DEFAULT_BITS - minBits);
+
+ bitsText.setText(String.valueOf(ECDSA_DEFAULT_BITS));
+ bitsText.setEnabled(true);
+
+ keyType = PubkeyDatabase.KEY_TYPE_EC;
}
}
});
@@ -136,16 +156,16 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
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;
- int ourProgress = progress;
-
- if (leftover > 0)
- ourProgress += 8 - leftover;
+ if (PubkeyDatabase.KEY_TYPE_EC.equals(keyType)) {
+ bits = getClosestFieldSize(progress + minBits);
+ seekBar.setProgress(bits - minBits);
+ } else {
+ // Stay evenly divisible by 8 because it looks nicer to have
+ // 2048 than 2043 bits.
+ final int ourProgress = progress - (progress % 8);
+ bits = minBits + ourProgress;
+ }
- bits = minBits + ourProgress;
bitsText.setText(String.valueOf(bits));
}
@@ -161,14 +181,18 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
bitsText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
+ final boolean isEc = PubkeyDatabase.KEY_TYPE_EC.equals(keyType);
try {
bits = Integer.parseInt(bitsText.getText().toString());
if (bits < minBits) {
bits = minBits;
bitsText.setText(String.valueOf(bits));
}
+ if (isEc) {
+ bits = getClosestFieldSize(bits);
+ }
} catch (NumberFormatException nfe) {
- bits = DEFAULT_BITS;
+ bits = isEc ? ECDSA_DEFAULT_BITS : DEFAULT_BITS;
bitsText.setText(String.valueOf(bits));
}
@@ -250,8 +274,10 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
try {
boolean encrypted = false;
- SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
+ SecureRandom random = new SecureRandom();
+ // Work around JVM bug
+ random.nextInt();
random.setSeed(entropy);
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(keyType);
@@ -315,4 +341,18 @@ public class GeneratePubkeyActivity extends Activity implements OnEntropyGathere
return numSetBits;
}
+
+ private int getClosestFieldSize(int bits) {
+ int outBits = ECDSA_DEFAULT_BITS;
+ int distance = Math.abs(bits - ECDSA_DEFAULT_BITS);
+
+ for (int i = 1; i < ECDSA_SIZES.length; i++) {
+ int thisDistance = Math.abs(bits - ECDSA_SIZES[i]);
+ if (thisDistance < distance) {
+ distance = thisDistance;
+ outBits = ECDSA_SIZES[i];
+ }
+ }
+ return outBits;
+ }
}
diff --git a/src/org/connectbot/bean/PubkeyBean.java b/src/org/connectbot/bean/PubkeyBean.java
index 790c6cc..d7213f0 100644
--- a/src/org/connectbot/bean/PubkeyBean.java
+++ b/src/org/connectbot/bean/PubkeyBean.java
@@ -22,6 +22,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
@@ -190,6 +191,11 @@ public class PubkeyBean extends AbstractBean {
sb.append("-bit");
} else if (publicKey instanceof DSAPublicKey) {
sb.append("DSA 1024-bit");
+ } else if (publicKey instanceof ECPublicKey) {
+ int bits = ((ECPublicKey) publicKey).getParams().getCurve().getField().getFieldSize();
+ sb.append("EC ");
+ sb.append(bits);
+ sb.append("-bit");
} else {
sb.append("Unknown Key Type");
}
diff --git a/src/org/connectbot/util/PubkeyDatabase.java b/src/org/connectbot/util/PubkeyDatabase.java
index 30712ce..a8993cb 100644
--- a/src/org/connectbot/util/PubkeyDatabase.java
+++ b/src/org/connectbot/util/PubkeyDatabase.java
@@ -52,7 +52,8 @@ public class PubkeyDatabase extends RobustSQLiteOpenHelper {
public final static String KEY_TYPE_RSA = "RSA",
KEY_TYPE_DSA = "DSA",
- KEY_TYPE_IMPORTED = "IMPORTED";
+ KEY_TYPE_IMPORTED = "IMPORTED",
+ KEY_TYPE_EC = "EC";
private Context context;
diff --git a/src/org/connectbot/util/PubkeyUtils.java b/src/org/connectbot/util/PubkeyUtils.java
index 57326b6..c214fc7 100644
--- a/src/org/connectbot/util/PubkeyUtils.java
+++ b/src/org/connectbot/util/PubkeyUtils.java
@@ -6,7 +6,7 @@
* 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
+ * 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,
@@ -33,10 +33,16 @@ import java.security.SecureRandom;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECField;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
@@ -55,10 +61,14 @@ import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+
import android.util.Log;
import com.trilead.ssh2.crypto.Base64;
import com.trilead.ssh2.signature.DSASHA1Verify;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
import com.trilead.ssh2.signature.RSASHA1Verify;
public class PubkeyUtils {
@@ -173,19 +183,41 @@ public class PubkeyUtils {
pub = kf.generatePublic(pubKeySpec);
} catch (ClassCastException e) {
- kf = KeyFactory.getInstance(PubkeyDatabase.KEY_TYPE_DSA);
- priv = kf.generatePrivate(privKeySpec);
+ try {
+ kf = KeyFactory.getInstance(PubkeyDatabase.KEY_TYPE_DSA);
+ priv = kf.generatePrivate(privKeySpec);
- DSAParams params = ((DSAPrivateKey) priv).getParams();
+ DSAParams params = ((DSAPrivateKey) priv).getParams();
- // Calculate public key Y
- BigInteger y = params.getG().modPow(((DSAPrivateKey) priv).getX(),
- params.getP());
+ // Calculate public key Y
+ BigInteger y = params.getG().modPow(((DSAPrivateKey) priv).getX(),
+ params.getP());
- pubKeySpec = new DSAPublicKeySpec(y, params.getP(), params.getQ(),
- params.getG());
+ pubKeySpec = new DSAPublicKeySpec(y, params.getP(), params.getQ(),
+ params.getG());
- pub = kf.generatePublic(pubKeySpec);
+ pub = kf.generatePublic(pubKeySpec);
+ } catch (ClassCastException e2) {
+ kf = KeyFactory.getInstance(PubkeyDatabase.KEY_TYPE_EC);
+ priv = kf.generatePrivate(privKeySpec);
+
+ ECParameterSpec params = ((ECPrivateKey) priv).getParams();
+
+ // Calculate public key Y
+ EllipticCurve curve = params.getCurve();
+ ECField field = curve.getField();
+ ECCurve bcCurve = new ECCurve.Fp(((ECFieldFp)field).getP(), curve.getA(), curve.getB());
+ java.security.spec.ECPoint generator = params.getGenerator();
+ ECPoint bcGenerator = bcCurve.createPoint(generator.getAffineX(), generator.getAffineY(), false);
+ ECPoint w = bcGenerator.multiply(((ECPrivateKey) priv).getS());
+
+ pubKeySpec = new ECPublicKeySpec(
+ new java.security.spec.ECPoint(w.getX().toBigInteger(),
+ w.getY().toBigInteger()),
+ params);
+
+ pub = kf.generatePublic(pubKeySpec);
+ }
}
return new KeyPair(pub, priv);
@@ -208,6 +240,11 @@ public class PubkeyUtils {
String data = "ssh-dss ";
data += String.valueOf(Base64.encode(DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pk)));
return data + " " + nickname;
+ } else if (pk instanceof ECPublicKey) {
+ ECPublicKey ecPub = (ECPublicKey) pk;
+ String keyType = ECDSASHA2Verify.getCurveName(ecPub.getParams().getCurve().getField().getFieldSize());
+ String keyData = String.valueOf(Base64.encode(ECDSASHA2Verify.encodeSSHECDSAPublicKey(ecPub)));
+ return ECDSASHA2Verify.ECDSA_SHA2_PREFIX + keyType + " " + keyData + " " + nickname;
}
throw new InvalidKeyException("Unknown key type");
@@ -228,6 +265,8 @@ public class PubkeyUtils {
return RSASHA1Verify.encodeSSHRSAPublicKey((RSAPublicKey) pair.getPublic());
} else if (pubKey instanceof DSAPublicKey) {
return DSASHA1Verify.encodeSSHDSAPublicKey((DSAPublicKey) pair.getPublic());
+ } else if (pubKey instanceof ECPublicKey) {
+ return ECDSASHA2Verify.encodeSSHECDSAPublicKey((ECPublicKey) pair.getPublic());
} else {
return null;
}