aboutsummaryrefslogtreecommitdiffstats
path: root/sshlib/src/main/java/com/trilead/ssh2/crypto/dh
diff options
context:
space:
mode:
Diffstat (limited to 'sshlib/src/main/java/com/trilead/ssh2/crypto/dh')
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java132
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java113
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java106
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java93
4 files changed, 444 insertions, 0 deletions
diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
new file mode 100644
index 0000000..3acde25
--- /dev/null
+++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhExchange.java
@@ -0,0 +1,132 @@
+/**
+ *
+ */
+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.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;
+
+/**
+ * @author kenny
+ *
+ */
+public class DhExchange extends GenericDhExchange {
+
+ /* Given by the standard */
+
+ 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 */
+
+ private DHPrivateKey clientPrivate;
+ private DHPublicKey clientPublic;
+
+ /* Server public */
+
+ 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);
+ }
+ }
+
+ @Override
+ public byte[] getE() {
+ if (clientPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
+
+ return clientPublic.getY().toByteArray();
+ }
+
+ @Override
+ protected byte[] getServerE() {
+ if (serverPublic == null)
+ throw new IllegalStateException("DhExchange not initialized!");
+
+ return serverPublic.getY().toByteArray();
+ }
+
+ @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);
+ }
+
+ sharedSecret = new BigInteger(ka.generateSecret());
+ }
+
+ @Override
+ public String getHashAlgo() {
+ return "SHA1";
+ }
+}
diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
new file mode 100644
index 0000000..a888950
--- /dev/null
+++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/DhGroupExchange.java
@@ -0,0 +1,113 @@
+
+package com.trilead.ssh2.crypto.dh;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import com.trilead.ssh2.DHGexParameters;
+import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
+
+
+/**
+ * DhGroupExchange.
+ *
+ * @author Christian Plattner, plattner@trilead.com
+ * @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
+ */
+public class DhGroupExchange
+{
+ /* Given by the standard */
+
+ private BigInteger p;
+ private BigInteger g;
+
+ /* Client public and private */
+
+ private BigInteger e;
+ private BigInteger x;
+
+ /* Server public */
+
+ private BigInteger f;
+
+ /* Shared secret */
+
+ private BigInteger k;
+
+ public DhGroupExchange(BigInteger p, BigInteger g)
+ {
+ this.p = p;
+ this.g = g;
+ }
+
+ public void init(SecureRandom rnd)
+ {
+ k = null;
+
+ x = new BigInteger(p.bitLength() - 1, rnd);
+ e = g.modPow(x, p);
+ }
+
+ /**
+ * @return Returns the e.
+ */
+ public BigInteger getE()
+ {
+ if (e == null)
+ throw new IllegalStateException("Not initialized!");
+
+ return e;
+ }
+
+ /**
+ * @return Returns the shared secret k.
+ */
+ public BigInteger getK()
+ {
+ if (k == null)
+ throw new IllegalStateException("Shared secret not yet known, need f first!");
+
+ return k;
+ }
+
+ /**
+ * Sets f and calculates the shared secret.
+ */
+ public void setF(BigInteger f)
+ {
+ if (e == null)
+ throw new IllegalStateException("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(String hashAlgo, byte[] clientversion, byte[] serverversion,
+ byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
+ {
+ HashForSSH2Types hash = new HashForSSH2Types(hashAlgo);
+
+ hash.updateByteString(clientversion);
+ hash.updateByteString(serverversion);
+ hash.updateByteString(clientKexPayload);
+ hash.updateByteString(serverKexPayload);
+ hash.updateByteString(hostKey);
+ if (para.getMin_group_len() > 0)
+ hash.updateUINT32(para.getMin_group_len());
+ hash.updateUINT32(para.getPref_group_len());
+ if (para.getMax_group_len() > 0)
+ hash.updateUINT32(para.getMax_group_len());
+ hash.updateBigInt(p);
+ hash.updateBigInt(g);
+ hash.updateBigInt(e);
+ hash.updateBigInt(f);
+ hash.updateBigInt(k);
+
+ return hash.getDigest();
+ }
+}
diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/EcDhExchange.java
new file mode 100644
index 0000000..870a3b4
--- /dev/null
+++ b/sshlib/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(1, ka.generateSecret());
+ }
+
+ @Override
+ public String getHashAlgo() {
+ return ECDSASHA2Verify.getDigestAlgorithmForParams(clientPublic.getParams());
+ }
+}
diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
new file mode 100644
index 0000000..039ff75
--- /dev/null
+++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/dh/GenericDhExchange.java
@@ -0,0 +1,93 @@
+
+package com.trilead.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+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 = new HashForSSH2Types(getHashAlgo());
+
+ 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();
+}