aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2013-04-10 18:43:35 -0700
committerKenny Root <kenny@the-b.org>2013-04-10 20:14:43 -0700
commit8e00d2fc37bc277a50c495938cc1ec7ab32aef66 (patch)
treeb821012175ee2a4afb5faa5d16632be4f609301d
parent1ad1f57886747362abb2e6f7eb91a221369eed35 (diff)
downloadsshlib-8e00d2fc37bc277a50c495938cc1ec7ab32aef66.tar.gz
sshlib-8e00d2fc37bc277a50c495938cc1ec7ab32aef66.tar.bz2
sshlib-8e00d2fc37bc277a50c495938cc1ec7ab32aef66.zip
Add ECDH support
Add support for the ECDH methods required by RFC 5656 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521
-rw-r--r--lib/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java11
-rw-r--r--lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java223
-rw-r--r--lib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java106
-rw-r--r--lib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java100
-rw-r--r--lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java10
-rw-r--r--lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java10
-rw-r--r--lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java5
-rw-r--r--lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java34
-rw-r--r--lib/src/main/java/com/trilead/ssh2/transport/KexManager.java31
-rw-r--r--lib/src/main/java/com/trilead/ssh2/transport/KexState.java5
10 files changed, 381 insertions, 154 deletions
diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java b/lib/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java
index 5dfb55e..035717d 100644
--- a/lib/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java
+++ b/lib/src/main/java/com/trilead/ssh2/crypto/KeyMaterial.java
@@ -3,6 +3,8 @@ package com.trilead.ssh2.crypto;
import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
@@ -66,13 +68,18 @@ public class KeyMaterial
return res;
}
- public static KeyMaterial create(String hashType, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
+ public static KeyMaterial create(String hashAlgo, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
throws IllegalArgumentException
{
KeyMaterial km = new KeyMaterial();
- HashForSSH2Types sh = new HashForSSH2Types(hashType);
+ HashForSSH2Types sh;
+ try {
+ sh = new HashForSSH2Types(MessageDigest.getInstance(hashAlgo));
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException(e);
+ }
km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java b/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
index 5622a72..d5bb112 100644
--- a/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
+++ b/lib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
@@ -1,147 +1,132 @@
-
+/**
+ *
+ */
package com.trilead.ssh2.crypto.dh;
-import java.io.UnsupportedEncodingException;
+import java.io.IOException;
import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
-import com.trilead.ssh2.log.Logger;
-
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.KeyAgreement;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPublicKeySpec;
/**
- * DhExchange.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ * @author kenny
+ *
*/
-public class DhExchange
-{
- private static final Logger log = Logger.getLogger(DhExchange.class);
+public class DhExchange extends GenericDhExchange {
/* Given by the standard */
- static final BigInteger p1, p14;
- static final BigInteger g;
-
- BigInteger p;
+ private static final BigInteger P1 = new BigInteger(
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ + "FFFFFFFFFFFFFFFF", 16);
+
+ private static final BigInteger P14 = new BigInteger(
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
+ + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"
+ + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16);
+
+ private static final BigInteger G = BigInteger.valueOf(2);
/* Client public and private */
- BigInteger e;
- BigInteger x;
+ private DHPrivateKey clientPrivate;
+ private DHPublicKey clientPublic;
/* Server public */
- BigInteger f;
-
- /* Shared secret */
-
- BigInteger k;
-
- static
- {
- final String p1_string = "17976931348623159077083915679378745319786029604875"
- + "60117064444236841971802161585193689478337958649255415021805654859805036464"
- + "40548199239100050792877003355816639229553136239076508735759914822574862575"
- + "00742530207744771258955095793777842444242661733472762929938766870920560605"
- + "0270810842907692932019128194467627007";
-
- final String p14_string = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129"
- + "024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0"
- + "A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB"
- + "6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A"
- + "163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208"
- + "552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36C"
- + "E3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171"
- + "83995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF";
-
- p1 = new BigInteger(p1_string);
- p14 = new BigInteger(p14_string, 16);
- g = new BigInteger("2");
- }
-
- public DhExchange()
- {
+ private DHPublicKey serverPublic;
+
+ @Override
+ public void init(String name) throws IOException {
+ final DHParameterSpec spec;
+ if ("diffie-hellman-group1-sha1".equals(name)) {
+ spec = new DHParameterSpec(P1, G);
+ } else if ("diffie-hellman-group14-sha1".equals(name)) {
+ spec = new DHParameterSpec(P14, G);
+ } else {
+ throw new IllegalArgumentException("Unknown DH group " + name);
+ }
+
+ try {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH");
+ kpg.initialize(spec);
+ KeyPair pair = kpg.generateKeyPair();
+ clientPrivate = (DHPrivateKey) pair.getPrivate();
+ clientPublic = (DHPublicKey) pair.getPublic();
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No DH keypair generator").initCause(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw (IOException) new IOException("Invalid DH parameters").initCause(e);
+ }
}
- public void init(int group, SecureRandom rnd)
- {
- k = null;
+ @Override
+ public byte[] getE() {
+ if (clientPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
- if (group == 1)
- p = p1;
- else if (group == 14)
- p = p14;
- else
- throw new IllegalArgumentException("Unknown DH group " + group);
-
- x = new BigInteger(p.bitLength() - 1, rnd);
-
- e = g.modPow(x, p);
- }
-
- /**
- * @return Returns the e.
- * @throws IllegalStateException
- */
- public BigInteger getE()
- {
- if (e == null)
- throw new IllegalStateException("DhDsaExchange not initialized!");
-
- return e;
+ return clientPublic.getY().toByteArray();
}
- /**
- * @return Returns the shared secret k.
- * @throws IllegalStateException
- */
- public BigInteger getK()
- {
- if (k == null)
- throw new IllegalStateException("Shared secret not yet known, need f first!");
+ @Override
+ protected byte[] getServerE() {
+ if (serverPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
- return k;
+ return serverPublic.getY().toByteArray();
}
- /**
- * @param f
- */
- public void setF(BigInteger f)
- {
- if (e == null)
- throw new IllegalStateException("DhDsaExchange not initialized!");
-
- BigInteger zero = BigInteger.valueOf(0);
-
- if (zero.compareTo(f) >= 0 || p.compareTo(f) <= 0)
- throw new IllegalArgumentException("Invalid f specified!");
-
- this.f = f;
- this.k = f.modPow(x, p);
- }
-
- public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
- byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
- {
- HashForSSH2Types hash = new HashForSSH2Types("SHA1");
-
- if (log.isEnabled())
- {
- log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'");
- log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'");
+ @Override
+ public void setF(byte[] f) throws IOException {
+ if (clientPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
+
+ final KeyAgreement ka;
+ try {
+ KeyFactory kf = KeyFactory.getInstance("DH");
+ DHParameterSpec params = clientPublic.getParams();
+ this.serverPublic = (DHPublicKey) kf.generatePublic(new DHPublicKeySpec(
+ new BigInteger(f), params.getP(), params.getG()));
+
+ ka = KeyAgreement.getInstance("DH");
+ ka.init(clientPrivate);
+ ka.doPhase(serverPublic, true);
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No DH key agreement method").initCause(e);
+ } catch (InvalidKeyException e) {
+ throw (IOException) new IOException("Invalid DH key").initCause(e);
+ } catch (InvalidKeySpecException e) {
+ throw (IOException) new IOException("Invalid DH key").initCause(e);
}
- hash.updateByteString(clientversion);
- hash.updateByteString(serverversion);
- hash.updateByteString(clientKexPayload);
- hash.updateByteString(serverKexPayload);
- hash.updateByteString(hostKey);
- hash.updateBigInt(e);
- hash.updateBigInt(f);
- hash.updateBigInt(k);
+ sharedSecret = new BigInteger(ka.generateSecret());
+ }
- return hash.getDigest();
+ @Override
+ public String getHashAlgo() {
+ return "SHA1";
}
}
diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java b/lib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java
new file mode 100644
index 0000000..43d31ad
--- /dev/null
+++ b/lib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java
@@ -0,0 +1,106 @@
+/**
+ *
+ */
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.crypto.KeyAgreement;
+
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
+
+/**
+ * @author kenny
+ *
+ */
+public class EcDhExchange extends GenericDhExchange {
+ private ECPrivateKey clientPrivate;
+ private ECPublicKey clientPublic;
+ private ECPublicKey serverPublic;
+
+ @Override
+ public void init(String name) throws IOException {
+ final ECParameterSpec spec;
+
+ if ("ecdh-sha2-nistp256".equals(name)) {
+ spec = ECDSASHA2Verify.EllipticCurves.nistp256;
+ } else if ("ecdh-sha2-nistp384".equals(name)) {
+ spec = ECDSASHA2Verify.EllipticCurves.nistp384;
+ } else if ("ecdh-sha2-nistp521".equals(name)) {
+ spec = ECDSASHA2Verify.EllipticCurves.nistp521;
+ } else {
+ throw new IllegalArgumentException("Unknown EC curve " + name);
+ }
+
+ KeyPairGenerator kpg;
+ try {
+ kpg = KeyPairGenerator.getInstance("EC");
+ kpg.initialize(spec);
+ KeyPair pair = kpg.generateKeyPair();
+ clientPrivate = (ECPrivateKey) pair.getPrivate();
+ clientPublic = (ECPublicKey) pair.getPublic();
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No DH keypair generator").initCause(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw (IOException) new IOException("Invalid DH parameters").initCause(e);
+ }
+ }
+
+ @Override
+ public byte[] getE() {
+ return ECDSASHA2Verify.encodeECPoint(clientPublic.getW(), clientPublic.getParams()
+ .getCurve());
+ }
+
+ @Override
+ protected byte[] getServerE() {
+ return ECDSASHA2Verify.encodeECPoint(serverPublic.getW(), serverPublic.getParams()
+ .getCurve());
+ }
+
+ @Override
+ public void setF(byte[] f) throws IOException {
+
+ if (clientPublic == null)
+ throw new IllegalStateException("DhDsaExchange not initialized!");
+
+ final KeyAgreement ka;
+ try {
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ ECParameterSpec params = clientPublic.getParams();
+ ECPoint serverPoint = ECDSASHA2Verify.decodeECPoint(f, params.getCurve());
+ this.serverPublic = (ECPublicKey) kf.generatePublic(new ECPublicKeySpec(serverPoint,
+ params));
+
+ ka = KeyAgreement.getInstance("ECDH");
+ ka.init(clientPrivate);
+ ka.doPhase(serverPublic, true);
+ } catch (NoSuchAlgorithmException e) {
+ throw (IOException) new IOException("No ECDH key agreement method").initCause(e);
+ } catch (InvalidKeyException e) {
+ throw (IOException) new IOException("Invalid ECDH key").initCause(e);
+ } catch (InvalidKeySpecException e) {
+ throw (IOException) new IOException("Invalid ECDH key").initCause(e);
+ }
+
+ sharedSecret = new BigInteger(ka.generateSecret());
+ }
+
+ @Override
+ public String getHashAlgo() {
+ return ECDSASHA2Verify.getDigestAlgorithmForParams(clientPublic.getParams());
+ }
+}
diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java b/lib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
new file mode 100644
index 0000000..d65490a
--- /dev/null
+++ b/lib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
@@ -0,0 +1,100 @@
+
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
+import com.trilead.ssh2.log.Logger;
+
+
+/**
+ * DhExchange.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DhExchange.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
+ */
+public abstract class GenericDhExchange
+{
+ private static final Logger log = Logger.getLogger(GenericDhExchange.class);
+
+ /* Shared secret */
+
+ BigInteger sharedSecret;
+
+ protected GenericDhExchange()
+ {
+ }
+
+ public static GenericDhExchange getInstance(String algo) {
+ if (algo.startsWith("ecdh-sha2-")) {
+ return new EcDhExchange();
+ } else {
+ return new DhExchange();
+ }
+ }
+
+ public abstract void init(String name) throws IOException;
+
+ /**
+ * @return Returns the e (public value)
+ * @throws IllegalStateException
+ */
+ public abstract byte[] getE();
+
+ /**
+ * @return Returns the server's e (public value)
+ * @throws IllegalStateException
+ */
+ protected abstract byte[] getServerE();
+
+ /**
+ * @return Returns the shared secret k.
+ * @throws IllegalStateException
+ */
+ public BigInteger getK()
+ {
+ if (sharedSecret == null)
+ throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+ return sharedSecret;
+ }
+
+ /**
+ * @param f
+ */
+ public abstract void setF(byte[] f) throws IOException;
+
+ public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+ byte[] serverKexPayload, byte[] hostKey) throws UnsupportedEncodingException
+ {
+ HashForSSH2Types hash;
+ try {
+ hash = new HashForSSH2Types(MessageDigest.getInstance(getHashAlgo()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new UnsupportedOperationException(e);
+ }
+
+ if (log.isEnabled())
+ {
+ log.log(90, "Client: '" + new String(clientversion, "ISO-8859-1") + "'");
+ log.log(90, "Server: '" + new String(serverversion, "ISO-8859-1") + "'");
+ }
+
+ hash.updateByteString(clientversion);
+ hash.updateByteString(serverversion);
+ hash.updateByteString(clientKexPayload);
+ hash.updateByteString(serverKexPayload);
+ hash.updateByteString(hostKey);
+ hash.updateByteString(getE());
+ hash.updateByteString(getServerE());
+ hash.updateBigInt(sharedSecret);
+
+ return hash.getDigest();
+ }
+
+ public abstract String getHashAlgo();
+}
diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java
index 26e14f6..9201936 100644
--- a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java
+++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHInit.java
@@ -1,7 +1,5 @@
package com.trilead.ssh2.packets;
-import java.math.BigInteger;
-
/**
* PacketKexDHInit.
*
@@ -12,11 +10,11 @@ public class PacketKexDHInit
{
byte[] payload;
- BigInteger e;
+ byte[] publicKey;
- public PacketKexDHInit(BigInteger e)
+ public PacketKexDHInit(byte[] publicKey)
{
- this.e = e;
+ this.publicKey = publicKey;
}
public byte[] getPayload()
@@ -25,7 +23,7 @@ public class PacketKexDHInit
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
- tw.writeMPInt(e);
+ tw.writeString(publicKey, 0, publicKey.length);
payload = tw.getBytes();
}
return payload;
diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java
index 0803ff9..461f262 100644
--- a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java
+++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexDHReply.java
@@ -2,8 +2,6 @@ package com.trilead.ssh2.packets;
import java.io.IOException;
-import java.math.BigInteger;
-
/**
* PacketKexDHReply.
*
@@ -15,7 +13,7 @@ public class PacketKexDHReply
byte[] payload;
byte[] hostKey;
- BigInteger f;
+ byte[] publicKey;
byte[] signature;
public PacketKexDHReply(byte payload[], int off, int len) throws IOException
@@ -32,15 +30,15 @@ public class PacketKexDHReply
+ packet_type + ")");
hostKey = tr.readByteString();
- f = tr.readMPINT();
+ publicKey = tr.readByteString();
signature = tr.readByteString();
if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
}
- public BigInteger getF()
+ public byte[] getF()
{
- return f;
+ return publicKey;
}
public byte[] getHostKey()
diff --git a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java
index 7da5067..2a961c2 100644
--- a/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java
+++ b/lib/src/main/java/com/trilead/ssh2/packets/PacketKexInit.java
@@ -4,7 +4,6 @@ package com.trilead.ssh2.packets;
import java.io.IOException;
import java.security.SecureRandom;
-import com.trilead.ssh2.compression.CompressionFactory;
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.transport.KexParameters;
@@ -21,10 +20,10 @@ public class PacketKexInit
KexParameters kp = new KexParameters();
- public PacketKexInit(CryptoWishList cwl, SecureRandom rnd)
+ public PacketKexInit(CryptoWishList cwl)
{
kp.cookie = new byte[16];
- rnd.nextBytes(kp.cookie);
+ new SecureRandom().nextBytes(kp.cookie);
kp.kex_algorithms = cwl.kexAlgorithms;
kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms;
diff --git a/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
index 1876bea..97bda5f 100644
--- a/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
+++ b/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
@@ -322,6 +322,17 @@ public class ECDSASHA2Verify {
}
}
+ public static String getDigestAlgorithmForParams(ECParameterSpec params) {
+ int size = getCurveSize(params);
+ if (size <= 256) {
+ return "SHA256";
+ } else if (size <= 384) {
+ return "SHA384";
+ } else {
+ return "SHA512";
+ }
+ }
+
/**
* Decode an OctetString to EllipticCurvePoint according to SECG 2.3.4
*/
@@ -370,18 +381,33 @@ public class ECDSASHA2Verify {
M[0] = 0x04;
{
- byte[] affineX = group.getAffineX().toByteArray();
- System.arraycopy(affineX, 0, M, 1, elementSize - affineX.length);
+ byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray());
+ System.arraycopy(affineX, 0, M, 1, affineX.length);
}
{
- byte[] affineY = group.getAffineY().toByteArray();
- System.arraycopy(affineY, 0, M, 1 + elementSize, elementSize - affineY.length);
+ byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray());
+ System.arraycopy(affineY, 0, M, 1 + elementSize, affineY.length);
}
return M;
}
+ private static byte[] removeLeadingZeroes(byte[] input) {
+ if (input[0] != 0x00) {
+ return input;
+ }
+
+ int pos = 1;
+ while (pos < input.length - 1 && input[pos] == 0x00) {
+ pos++;
+ }
+
+ byte[] output = new byte[input.length - pos];
+ System.arraycopy(input, pos, output, 0, output.length);
+ return output;
+ }
+
public static class EllipticCurves {
public static ECParameterSpec nistp256 = new ECParameterSpec(
new EllipticCurve(
diff --git a/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java b/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java
index a74a224..04043e4 100644
--- a/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java
+++ b/lib/src/main/java/com/trilead/ssh2/transport/KexManager.java
@@ -18,8 +18,8 @@ import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.crypto.KeyMaterial;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
-import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
+import com.trilead.ssh2.crypto.dh.GenericDhExchange;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.log.Logger;
import com.trilead.ssh2.packets.PacketKexDHInit;
@@ -58,6 +58,9 @@ public class KexManager
private static final Set<String> KEX_ALGS = new TreeSet<String>();
static {
+ KEX_ALGS.add("ecdh-sha2-nistp256");
+ KEX_ALGS.add("ecdh-sha2-nistp384");
+ KEX_ALGS.add("ecdh-sha2-nistp521");
KEX_ALGS.add("diffie-hellman-group-exchange-sha1");
KEX_ALGS.add("diffie-hellman-group14-sha1");
KEX_ALGS.add("diffie-hellman-group1-sha1");
@@ -261,7 +264,7 @@ public class KexManager
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
- PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList, rnd);
+ PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList);
kxs.localKEX = kp;
tm.sendKexMessage(kp.getPayload());
}
@@ -279,7 +282,7 @@ public class KexManager
int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
- km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
+ km = KeyMaterial.create(kxs.hashAlgo, kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
}
catch (IllegalArgumentException e)
@@ -424,7 +427,7 @@ public class KexManager
*/
kxs = new KexState();
kxs.dhgexParameters = nextKEXdhgexParameters;
- kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
+ kip = new PacketKexInit(nextKEXcryptoWishList);
kxs.localKEX = kip;
tm.sendKexMessage(kip.getPayload());
}
@@ -459,19 +462,20 @@ public class KexManager
PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
tm.sendKexMessage(dhgexreq.getPayload());
}
+ kxs.hashAlgo = "SHA1";
kxs.state = 1;
return;
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
- || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
- {
- kxs.dhx = new DhExchange();
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp256")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp384")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) {
+ kxs.dhx = GenericDhExchange.getInstance(kxs.np.kex_algo);
- if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1"))
- kxs.dhx.init(1, rnd);
- else
- kxs.dhx.init(14, rnd);
+ kxs.dhx.init(kxs.np.kex_algo);
+ kxs.hashAlgo = kxs.dhx.getHashAlgo();
PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
tm.sendKexMessage(kp.getPayload());
@@ -600,7 +604,10 @@ public class KexManager
}
if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
- || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1"))
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp256")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp384")
+ || kxs.np.kex_algo.equals("ecdh-sha2-nistp521"))
{
if (kxs.state == 1)
{
diff --git a/lib/src/main/java/com/trilead/ssh2/transport/KexState.java b/lib/src/main/java/com/trilead/ssh2/transport/KexState.java
index dabf450..d9f1004 100644
--- a/lib/src/main/java/com/trilead/ssh2/transport/KexState.java
+++ b/lib/src/main/java/com/trilead/ssh2/transport/KexState.java
@@ -4,8 +4,8 @@ package com.trilead.ssh2.transport;
import java.math.BigInteger;
import com.trilead.ssh2.DHGexParameters;
-import com.trilead.ssh2.crypto.dh.DhExchange;
import com.trilead.ssh2.crypto.dh.DhGroupExchange;
+import com.trilead.ssh2.crypto.dh.GenericDhExchange;
import com.trilead.ssh2.packets.PacketKexInit;
/**
@@ -26,7 +26,8 @@ public class KexState
public byte[] hostkey;
- public DhExchange dhx;
+ public String hashAlgo;
+ public GenericDhExchange dhx;
public DhGroupExchange dhgx;
public DHGexParameters dhgexParameters;
}