diff options
author | Kenny Root <kenny@the-b.org> | 2013-04-12 00:13:42 -0700 |
---|---|---|
committer | Kenny Root <kenny@the-b.org> | 2013-04-12 00:33:11 -0700 |
commit | f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5 (patch) | |
tree | a31841f6d282c0a42c3a63376502a24103f42002 | |
parent | d8e49ec3f297f9ba0991762eb2cddbc00ed634d2 (diff) | |
download | connectbot-f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5.tar.gz connectbot-f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5.tar.bz2 connectbot-f7beb3b827f0eb6d03d8ff3589782a0df14fd2f5.zip |
Add EC pubkey to UI
-rw-r--r-- | res/layout/act_generatepubkey.xml | 6 | ||||
-rw-r--r-- | src/com/trilead/ssh2/auth/AuthenticationManager.java | 6 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/ECDSASHA2Verify.java | 37 | ||||
-rw-r--r-- | src/org/connectbot/GeneratePubkeyActivity.java | 70 | ||||
-rw-r--r-- | src/org/connectbot/bean/PubkeyBean.java | 6 | ||||
-rw-r--r-- | src/org/connectbot/util/PubkeyDatabase.java | 3 | ||||
-rw-r--r-- | src/org/connectbot/util/PubkeyUtils.java | 61 |
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; } |