diff options
Diffstat (limited to 'src/com/trilead/ssh2/signature')
-rw-r--r-- | src/com/trilead/ssh2/signature/DSAPrivateKey.java | 58 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/DSAPublicKey.java | 45 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/DSASHA1Verify.java | 465 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/DSASignature.java | 31 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/ECDSASHA2Verify.java | 487 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/RSAPrivateKey.java | 43 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/RSAPublicKey.java | 31 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/RSASHA1Verify.java | 465 | ||||
-rw-r--r-- | src/com/trilead/ssh2/signature/RSASignature.java | 27 |
9 files changed, 922 insertions, 730 deletions
diff --git a/src/com/trilead/ssh2/signature/DSAPrivateKey.java b/src/com/trilead/ssh2/signature/DSAPrivateKey.java deleted file mode 100644 index d2a63ae..0000000 --- a/src/com/trilead/ssh2/signature/DSAPrivateKey.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSAPrivateKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSAPrivateKey
-{
- private BigInteger p;
- private BigInteger q;
- private BigInteger g;
- private BigInteger x;
- private BigInteger y;
-
- public DSAPrivateKey(BigInteger p, BigInteger q, BigInteger g,
- BigInteger y, BigInteger x)
- {
- this.p = p;
- this.q = q;
- this.g = g;
- this.y = y;
- this.x = x;
- }
-
- public BigInteger getP()
- {
- return p;
- }
-
- public BigInteger getQ()
- {
- return q;
- }
-
- public BigInteger getG()
- {
- return g;
- }
-
- public BigInteger getY()
- {
- return y;
- }
-
- public BigInteger getX()
- {
- return x;
- }
-
- public DSAPublicKey getPublicKey()
- {
- return new DSAPublicKey(p, q, g, y);
- }
-}
\ No newline at end of file diff --git a/src/com/trilead/ssh2/signature/DSAPublicKey.java b/src/com/trilead/ssh2/signature/DSAPublicKey.java deleted file mode 100644 index f8351ff..0000000 --- a/src/com/trilead/ssh2/signature/DSAPublicKey.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSAPublicKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSAPublicKey
-{
- private BigInteger p;
- private BigInteger q;
- private BigInteger g;
- private BigInteger y;
-
- public DSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y)
- {
- this.p = p;
- this.q = q;
- this.g = g;
- this.y = y;
- }
-
- public BigInteger getP()
- {
- return p;
- }
-
- public BigInteger getQ()
- {
- return q;
- }
-
- public BigInteger getG()
- {
- return g;
- }
-
- public BigInteger getY()
- {
- return y;
- }
-}
\ No newline at end of file diff --git a/src/com/trilead/ssh2/signature/DSASHA1Verify.java b/src/com/trilead/ssh2/signature/DSASHA1Verify.java index c838ebd..6fb6ddb 100644 --- a/src/com/trilead/ssh2/signature/DSASHA1Verify.java +++ b/src/com/trilead/ssh2/signature/DSASHA1Verify.java @@ -1,210 +1,255 @@ -
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * DSASHA1Verify.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $
- */
-public class DSASHA1Verify
-{
- private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
-
- public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException
- {
- TypesReader tr = new TypesReader(key);
-
- String key_format = tr.readString();
-
- if (key_format.equals("ssh-dss") == false)
- throw new IllegalArgumentException("This is not a ssh-dss public key!");
-
- BigInteger p = tr.readMPINT();
- BigInteger q = tr.readMPINT();
- BigInteger g = tr.readMPINT();
- BigInteger y = tr.readMPINT();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in DSA public key!");
-
- return new DSAPublicKey(p, q, g, y);
- }
-
- public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-dss");
- tw.writeMPInt(pk.getP());
- tw.writeMPInt(pk.getQ());
- tw.writeMPInt(pk.getG());
- tw.writeMPInt(pk.getY());
-
- return tw.getBytes();
- }
-
- public static byte[] encodeSSHDSASignature(DSASignature ds)
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-dss");
-
- byte[] r = ds.getR().toByteArray();
- byte[] s = ds.getS().toByteArray();
-
- byte[] a40 = new byte[40];
-
- /* Patch (unsigned) r and s into the target array. */
-
- int r_copylen = (r.length < 20) ? r.length : 20;
- int s_copylen = (s.length < 20) ? s.length : 20;
-
- System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen);
- System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen);
-
- tw.writeString(a40, 0, 40);
-
- return tw.getBytes();
- }
-
- public static DSASignature decodeSSHDSASignature(byte[] sig) throws IOException
- {
- byte[] rsArray = null;
-
- if (sig.length == 40)
- {
- /* OK, another broken SSH server. */
- rsArray = sig;
- }
- else
- {
- /* Hopefully a server obeing the standard... */
- TypesReader tr = new TypesReader(sig);
-
- String sig_format = tr.readString();
-
- if (sig_format.equals("ssh-dss") == false)
- throw new IOException("Peer sent wrong signature format");
-
- rsArray = tr.readByteString();
-
- if (rsArray.length != 40)
- throw new IOException("Peer sent corrupt signature");
-
- if (tr.remain() != 0)
- throw new IOException("Padding in DSA signature!");
- }
-
- /* Remember, s and r are unsigned ints. */
-
- byte[] tmp = new byte[20];
-
- System.arraycopy(rsArray, 0, tmp, 0, 20);
- BigInteger r = new BigInteger(1, tmp);
-
- System.arraycopy(rsArray, 20, tmp, 0, 20);
- BigInteger s = new BigInteger(1, tmp);
-
- if (log.isEnabled())
- {
- log.log(30, "decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s("
- + ((rsArray[20]) & 0xff) + ")");
- }
-
- return new DSASignature(r, s);
- }
-
- public static boolean verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk) throws IOException
- {
- /* Inspired by Bouncycastle's DSASigner class */
-
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- BigInteger m = new BigInteger(1, sha_message);
-
- BigInteger r = ds.getR();
- BigInteger s = ds.getS();
-
- BigInteger g = dpk.getG();
- BigInteger p = dpk.getP();
- BigInteger q = dpk.getQ();
- BigInteger y = dpk.getY();
-
- BigInteger zero = BigInteger.ZERO;
-
- if (log.isEnabled())
- {
- log.log(60, "ssh-dss signature: m: " + m.toString(16));
- log.log(60, "ssh-dss signature: r: " + r.toString(16));
- log.log(60, "ssh-dss signature: s: " + s.toString(16));
- log.log(60, "ssh-dss signature: g: " + g.toString(16));
- log.log(60, "ssh-dss signature: p: " + p.toString(16));
- log.log(60, "ssh-dss signature: q: " + q.toString(16));
- log.log(60, "ssh-dss signature: y: " + y.toString(16));
- }
-
- if (zero.compareTo(r) >= 0 || q.compareTo(r) <= 0)
- {
- log.log(20, "ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0");
- return false;
- }
-
- if (zero.compareTo(s) >= 0 || q.compareTo(s) <= 0)
- {
- log.log(20, "ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0");
- return false;
- }
-
- BigInteger w = s.modInverse(q);
-
- BigInteger u1 = m.multiply(w).mod(q);
- BigInteger u2 = r.multiply(w).mod(q);
-
- u1 = g.modPow(u1, p);
- u2 = y.modPow(u2, p);
-
- BigInteger v = u1.multiply(u2).mod(p).mod(q);
-
- return v.equals(r);
- }
-
- public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd)
- {
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- BigInteger m = new BigInteger(1, sha_message);
- BigInteger k;
- int qBitLength = pk.getQ().bitLength();
-
- do
- {
- k = new BigInteger(qBitLength, rnd);
- }
- while (k.compareTo(pk.getQ()) >= 0);
-
- BigInteger r = pk.getG().modPow(k, pk.getP()).mod(pk.getQ());
-
- k = k.modInverse(pk.getQ()).multiply(m.add((pk).getX().multiply(r)));
-
- BigInteger s = k.mod(pk.getQ());
-
- return new DSASignature(r, s);
- }
-}
+ +package com.trilead.ssh2.signature; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + + +/** + * DSASHA1Verify. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: DSASHA1Verify.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ + */ +public class DSASHA1Verify +{ + private static final Logger log = Logger.getLogger(DSASHA1Verify.class); + + public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException + { + TypesReader tr = new TypesReader(key); + + String key_format = tr.readString(); + + if (key_format.equals("ssh-dss") == false) + throw new IllegalArgumentException("This is not a ssh-dss public key!"); + + BigInteger p = tr.readMPINT(); + BigInteger q = tr.readMPINT(); + BigInteger g = tr.readMPINT(); + BigInteger y = tr.readMPINT(); + + if (tr.remain() != 0) + throw new IOException("Padding in DSA public key!"); + + try { + KeyFactory kf = KeyFactory.getInstance("DSA"); + + KeySpec ks = new DSAPublicKeySpec(y, p, q, g); + return (DSAPublicKey) kf.generatePublic(ks); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (InvalidKeySpecException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } + + public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-dss"); + + DSAParams params = pk.getParams(); + tw.writeMPInt(params.getP()); + tw.writeMPInt(params.getQ()); + tw.writeMPInt(params.getG()); + tw.writeMPInt(pk.getY()); + + return tw.getBytes(); + } + + /** + * Convert from Java's signature ASN.1 encoding to the SSH spec. + * <p> + * Java ASN.1 encoding: + * <pre> + * SEQUENCE ::= { + * r INTEGER, + * s INTEGER + * } + * </pre> + */ + public static byte[] encodeSSHDSASignature(byte[] ds) + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-dss"); + + int len, index; + + index = 3; + len = ds[index++] & 0xff; + byte[] r = new byte[len]; + System.arraycopy(ds, index, r, 0, r.length); + + index = index + len + 1; + len = ds[index++] & 0xff; + byte[] s = new byte[len]; + System.arraycopy(ds, index, s, 0, s.length); + + byte[] a40 = new byte[40]; + + /* Patch (unsigned) r and s into the target array. */ + + int r_copylen = (r.length < 20) ? r.length : 20; + int s_copylen = (s.length < 20) ? s.length : 20; + + System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen); + System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen); + + tw.writeString(a40, 0, 40); + + return tw.getBytes(); + } + + public static byte[] decodeSSHDSASignature(byte[] sig) throws IOException + { + byte[] rsArray = null; + + if (sig.length == 40) + { + /* OK, another broken SSH server. */ + rsArray = sig; + } + else + { + /* Hopefully a server obeying the standard... */ + TypesReader tr = new TypesReader(sig); + + String sig_format = tr.readString(); + if (sig_format.equals("ssh-dss") == false) + throw new IOException("Peer sent wrong signature format"); + + rsArray = tr.readByteString(); + + if (rsArray.length != 40) + throw new IOException("Peer sent corrupt signature"); + + if (tr.remain() != 0) + throw new IOException("Padding in DSA signature!"); + } + + int i = 0; + int j = 0; + byte[] tmp; + + if (rsArray[0] == 0 && rsArray[1] == 0 && rsArray[2] == 0) { + j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000) + | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff); + i += j; + j = ((rsArray[i++] << 24) & 0xff000000) | ((rsArray[i++] << 16) & 0x00ff0000) + | ((rsArray[i++] << 8) & 0x0000ff00) | ((rsArray[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(rsArray, i, tmp, 0, j); + rsArray = tmp; + } + + /* ASN.1 */ + int frst = ((rsArray[0] & 0x80) != 0 ? 1 : 0); + int scnd = ((rsArray[20] & 0x80) != 0 ? 1 : 0); + + /* Calculate output length */ + int length = rsArray.length + 6 + frst + scnd; + tmp = new byte[length]; + + /* DER-encoding to match Java */ + tmp[0] = (byte) 0x30; + + if (rsArray.length != 40) + throw new IOException("Peer sent corrupt signature"); + /* Calculate length */ + tmp[1] = (byte) 0x2c; + tmp[1] += frst; + tmp[1] += scnd; + + /* First item */ + tmp[2] = (byte) 0x02; + + /* First item length */ + tmp[3] = (byte) 0x14; + tmp[3] += frst; + + /* Copy in the data for first item */ + System.arraycopy(rsArray, 0, tmp, 4 + frst, 20); + + /* Second item */ + tmp[4 + tmp[3]] = (byte) 0x02; + + /* Second item length */ + tmp[5 + tmp[3]] = (byte) 0x14; + tmp[5 + tmp[3]] += scnd; + + /* Copy in the data for the second item */ + System.arraycopy(rsArray, 20, tmp, 6 + tmp[3] + scnd, 20); + + /* Swap buffers */ + rsArray = tmp; + + return rsArray; + } + + public static boolean verifySignature(byte[] message, byte[] ds, DSAPublicKey dpk) throws IOException + { + try { + Signature s = Signature.getInstance("SHA1withDSA"); + s.initVerify(dpk); + s.update(message); + return s.verify(ds); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException("No such algorithm"); + ex.initCause(e); + throw ex; + } catch (InvalidKeyException e) { + IOException ex = new IOException("No such algorithm"); + ex.initCause(e); + throw ex; + } catch (SignatureException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } + + public static byte[] generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd) throws IOException + { + try { + Signature s = Signature.getInstance("SHA1withDSA"); + s.initSign(pk); + s.update(message); + return s.sign(); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (InvalidKeyException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (SignatureException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } +} diff --git a/src/com/trilead/ssh2/signature/DSASignature.java b/src/com/trilead/ssh2/signature/DSASignature.java deleted file mode 100644 index eff84cd..0000000 --- a/src/com/trilead/ssh2/signature/DSASignature.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * DSASignature.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: DSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class DSASignature
-{
- private BigInteger r;
- private BigInteger s;
-
- public DSASignature(BigInteger r, BigInteger s)
- {
- this.r = r;
- this.s = s;
- }
-
- public BigInteger getR()
- {
- return r;
- }
-
- public BigInteger getS()
- {
- return s;
- }
-}
diff --git a/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java new file mode 100644 index 0000000..7d8dd3e --- /dev/null +++ b/src/com/trilead/ssh2/signature/ECDSASHA2Verify.java @@ -0,0 +1,487 @@ +/** + * + */ +package com.trilead.ssh2.signature; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Map; +import java.util.TreeMap; + +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + +/** + * @author Kenny Root + * + */ +public class ECDSASHA2Verify { + private static final Logger log = Logger.getLogger(ECDSASHA2Verify.class); + + public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-"; + + private static final String NISTP256 = "nistp256"; + private static final String NISTP256_OID = "1.2.840.10045.3.1.7"; + private static final String NISTP384 = "nistp384"; + private static final String NISTP384_OID = "1.3.132.0.34"; + private static final String NISTP521 = "nistp521"; + private static final String NISTP521_OID = "1.3.132.0.35"; + + private static final Map<String, ECParameterSpec> CURVES = new TreeMap<String, ECParameterSpec>(); + static { + CURVES.put(NISTP256, EllipticCurves.nistp256); + CURVES.put(NISTP384, EllipticCurves.nistp384); + CURVES.put(NISTP521, EllipticCurves.nistp521); + } + + private static final Map<Integer, String> CURVE_SIZES = new TreeMap<Integer, String>(); + static { + CURVE_SIZES.put(256, NISTP256); + CURVE_SIZES.put(384, NISTP384); + CURVE_SIZES.put(521, NISTP521); + } + + private static final Map<String, String> CURVE_OIDS = new TreeMap<String, String>(); + static { + CURVE_OIDS.put(NISTP256_OID, NISTP256); + CURVE_OIDS.put(NISTP384_OID, NISTP256); + CURVE_OIDS.put(NISTP521_OID, NISTP256); + } + + 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); + + String key_format = tr.readString(); + + if (key_format.startsWith(ECDSA_SHA2_PREFIX) == false) + throw new IllegalArgumentException("This is not an ECDSA public key"); + + String curveName = tr.readString(); + byte[] groupBytes = tr.readByteString(); + + if (tr.remain() != 0) + throw new IOException("Padding in ECDSA public key!"); + + if (key_format.equals(ECDSA_SHA2_PREFIX + curveName) == false) { + throw new IOException("Key format is inconsistent with curve name: " + key_format + + " != " + curveName); + } + + ECParameterSpec params = CURVES.get(curveName); + if (params == null) { + throw new IOException("Curve is not supported: " + curveName); + } + + ECPoint group = ECDSASHA2Verify.decodeECPoint(groupBytes, params.getCurve()); + if (group == null) { + throw new IOException("Invalid ECDSA group"); + } + + KeySpec keySpec = new ECPublicKeySpec(group, params); + + try { + KeyFactory kf = KeyFactory.getInstance("EC"); + return (ECPublicKey) kf.generatePublic(keySpec); + } catch (NoSuchAlgorithmException nsae) { + IOException ioe = new IOException("No EC KeyFactory available"); + ioe.initCause(nsae); + throw ioe; + } catch (InvalidKeySpecException ikse) { + IOException ioe = new IOException("No EC KeyFactory available"); + ioe.initCause(ikse); + throw ioe; + } + } + + public static byte[] encodeSSHECDSAPublicKey(ECPublicKey key) throws IOException { + TypesWriter tw = new TypesWriter(); + + String curveName = getCurveName(key.getParams()); + + String keyFormat = ECDSA_SHA2_PREFIX + curveName; + + tw.writeString(keyFormat); + + tw.writeString(curveName); + + byte[] encoded = encodeECPoint(key.getW(), key.getParams().getCurve()); + tw.writeString(encoded, 0, encoded.length); + + return tw.getBytes(); + } + + 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) { + return null; + } + return curveName; + } + + public static int getCurveSize(ECParameterSpec params) { + return params.getCurve().getField().getFieldSize(); + } + + public static ECParameterSpec getCurveForOID(String oid) { + String name = CURVE_OIDS.get(oid); + if (name == null) + return null; + return CURVES.get(name); + } + + public static byte[] decodeSSHECDSASignature(byte[] sig) throws IOException { + byte[] rsArray = null; + + TypesReader tr = new TypesReader(sig); + + String sig_format = tr.readString(); + if (sig_format.startsWith(ECDSA_SHA2_PREFIX) == false) + throw new IOException("Peer sent wrong signature format"); + + String curveName = sig_format.substring(ECDSA_SHA2_PREFIX.length()); + if (CURVES.containsKey(curveName) == false) { + throw new IOException("Unsupported curve: " + curveName); + } + + rsArray = tr.readByteString(); + + if (tr.remain() != 0) + throw new IOException("Padding in ECDSA signature!"); + + byte[] rArray; + byte[] sArray; + { + TypesReader rsReader = new TypesReader(rsArray); + rArray = rsReader.readMPINT().toByteArray(); + sArray = rsReader.readMPINT().toByteArray(); + } + + int first = rArray.length; + int second = sArray.length; + + /* We can't have the high bit set, so add an extra zero at the beginning if so. */ + if ((rArray[0] & 0x80) != 0) { + first++; + } + if ((sArray[0] & 0x80) != 0) { + second++; + } + + /* Calculate total output length */ + ByteArrayOutputStream os = new ByteArrayOutputStream(6 + first + second); + + /* ASN.1 SEQUENCE tag */ + os.write(0x30); + + /* Size of SEQUENCE */ + writeLength(4 + first + second, os); + + /* ASN.1 INTEGER tag */ + os.write(0x02); + + /* "r" INTEGER length */ + writeLength(first, os); + + /* Copy in the "r" INTEGER */ + if (first != rArray.length) { + os.write(0x00); + } + os.write(rArray); + + /* ASN.1 INTEGER tag */ + os.write(0x02); + + /* "s" INTEGER length */ + writeLength(second, os); + + /* Copy in the "s" INTEGER */ + if (second != sArray.length) { + os.write(0x00); + } + os.write(sArray); + + return os.toByteArray(); + } + + private static final void writeLength(int length, OutputStream os) throws IOException { + if (length <= 0x7F) { + os.write(length); + return; + } + + int numOctets = 0; + int lenCopy = length; + while (lenCopy != 0) { + lenCopy >>>= 8; + numOctets++; + } + + os.write(0x80 | numOctets); + + for (int i = (numOctets - 1) * 8; i >= 0; i -= 8) { + os.write((byte) (length >> i)); + } + } + + public static byte[] encodeSSHECDSASignature(byte[] sig, ECParameterSpec params) throws IOException + { + TypesWriter tw = new TypesWriter(); + + String curveName = getCurveName(params); + tw.writeString(ECDSA_SHA2_PREFIX + curveName); + + if ((sig[0] != 0x30) || (sig[1] != sig.length - 2) || (sig[2] != 0x02)) { + throw new IOException("Invalid signature format"); + } + + int rLength = sig[3]; + if ((rLength + 6 > sig.length) || (sig[4 + rLength] != 0x02)) { + throw new IOException("Invalid signature format"); + } + + int sLength = sig[5 + rLength]; + if (6 + rLength + sLength > sig.length) { + throw new IOException("Invalid signature format"); + } + + byte[] rArray = new byte[rLength]; + byte[] sArray = new byte[sLength]; + + System.arraycopy(sig, 4, rArray, 0, rLength); + System.arraycopy(sig, 6 + rLength, sArray, 0, sLength); + + BigInteger r = new BigInteger(1, rArray); + BigInteger s = new BigInteger(1, sArray); + + // Write the <r,s> to its own types writer. + TypesWriter rsWriter = new TypesWriter(); + rsWriter.writeMPInt(r); + rsWriter.writeMPInt(s); + byte[] encoded = rsWriter.getBytes(); + tw.writeString(encoded, 0, encoded.length); + + return tw.getBytes(); + } + + public static byte[] generateSignature(byte[] message, ECPrivateKey pk) throws IOException + { + final String algo = getSignatureAlgorithmForParams(pk.getParams()); + + try { + Signature s = Signature.getInstance(algo); + s.initSign(pk); + s.update(message); + return s.sign(); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (InvalidKeyException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (SignatureException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } + + public static boolean verifySignature(byte[] message, byte[] ds, ECPublicKey dpk) throws IOException + { + final String algo = getSignatureAlgorithmForParams(dpk.getParams()); + + try { + Signature s = Signature.getInstance(algo); + s.initVerify(dpk); + s.update(message); + return s.verify(ds); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException("No such algorithm"); + ex.initCause(e); + throw ex; + } catch (InvalidKeyException e) { + IOException ex = new IOException("No such algorithm"); + ex.initCause(e); + throw ex; + } catch (SignatureException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } + + private static String getSignatureAlgorithmForParams(ECParameterSpec params) { + int size = getCurveSize(params); + if (size <= 256) { + return "SHA256withECDSA"; + } else if (size <= 384) { + return "SHA384withECDSA"; + } else { + return "SHA512withECDSA"; + } + } + + 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 + */ + public static ECPoint decodeECPoint(byte[] M, EllipticCurve curve) { + if (M.length == 0) { + return null; + } + + // M has len 2 ceil(log_2(q)/8) + 1 ? + int elementSize = (curve.getField().getFieldSize() + 7) / 8; + if (M.length != 2 * elementSize + 1) { + return null; + } + + // step 3.2 + if (M[0] != 0x04) { + return null; + } + + // Step 3.3 + byte[] xp = new byte[elementSize]; + System.arraycopy(M, 1, xp, 0, elementSize); + + // Step 3.4 + byte[] yp = new byte[elementSize]; + System.arraycopy(M, 1 + elementSize, yp, 0, elementSize); + + ECPoint P = new ECPoint(new BigInteger(1, xp), new BigInteger(1, yp)); + + // TODO check point 3.5 + + // Step 3.6 + return P; + } + + /** + * Encode EllipticCurvePoint to an OctetString + */ + public static byte[] encodeECPoint(ECPoint group, EllipticCurve curve) + { + // M has len 2 ceil(log_2(q)/8) + 1 ? + int elementSize = (curve.getField().getFieldSize() + 7) / 8; + byte[] M = new byte[2 * elementSize + 1]; + + // Uncompressed format + M[0] = 0x04; + + { + byte[] affineX = removeLeadingZeroes(group.getAffineX().toByteArray()); + System.arraycopy(affineX, 0, M, 1 + elementSize - affineX.length, affineX.length); + } + + { + byte[] affineY = removeLeadingZeroes(group.getAffineY().toByteArray()); + System.arraycopy(affineY, 0, M, 1 + elementSize + elementSize - affineY.length, + 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( + new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)), + new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16), + new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)), + new ECPoint(new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16), + new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)), + new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16), + 1); + + public static ECParameterSpec nistp384 = new ECParameterSpec( + new EllipticCurve( + new ECFieldFp(new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", 16)), + new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", 16), + new BigInteger("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", 16)), + new ECPoint(new BigInteger("AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", 16), + new BigInteger("3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", 16)), + new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", 16), + 1); + + public static ECParameterSpec nistp521 = new ECParameterSpec( + new EllipticCurve( + new ECFieldFp(new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)), + new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16), + new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16)), + new ECPoint(new BigInteger("00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", 16), + new BigInteger("011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", 16)), + new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16), + 1); + } +} diff --git a/src/com/trilead/ssh2/signature/RSAPrivateKey.java b/src/com/trilead/ssh2/signature/RSAPrivateKey.java deleted file mode 100644 index 5d5e606..0000000 --- a/src/com/trilead/ssh2/signature/RSAPrivateKey.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * RSAPrivateKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSAPrivateKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSAPrivateKey
-{
- private BigInteger d;
- private BigInteger e;
- private BigInteger n;
-
- public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
- {
- this.d = d;
- this.e = e;
- this.n = n;
- }
-
- public BigInteger getD()
- {
- return d;
- }
-
- public BigInteger getE()
- {
- return e;
- }
-
- public BigInteger getN()
- {
- return n;
- }
-
- public RSAPublicKey getPublicKey()
- {
- return new RSAPublicKey(e, n);
- }
-}
\ No newline at end of file diff --git a/src/com/trilead/ssh2/signature/RSAPublicKey.java b/src/com/trilead/ssh2/signature/RSAPublicKey.java deleted file mode 100644 index e7e6611..0000000 --- a/src/com/trilead/ssh2/signature/RSAPublicKey.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-/**
- * RSAPublicKey.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSAPublicKey.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSAPublicKey
-{
- BigInteger e;
- BigInteger n;
-
- public RSAPublicKey(BigInteger e, BigInteger n)
- {
- this.e = e;
- this.n = n;
- }
-
- public BigInteger getE()
- {
- return e;
- }
-
- public BigInteger getN()
- {
- return n;
- }
-}
\ No newline at end of file diff --git a/src/com/trilead/ssh2/signature/RSASHA1Verify.java b/src/com/trilead/ssh2/signature/RSASHA1Verify.java index 8a0f07a..3406312 100644 --- a/src/com/trilead/ssh2/signature/RSASHA1Verify.java +++ b/src/com/trilead/ssh2/signature/RSASHA1Verify.java @@ -1,285 +1,180 @@ -
-package com.trilead.ssh2.signature;
-
-import java.io.IOException;
-import java.math.BigInteger;
-
-import com.trilead.ssh2.crypto.SimpleDERReader;
-import com.trilead.ssh2.crypto.digest.SHA1;
-import com.trilead.ssh2.log.Logger;
-import com.trilead.ssh2.packets.TypesReader;
-import com.trilead.ssh2.packets.TypesWriter;
-
-
-/**
- * RSASHA1Verify.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-public class RSASHA1Verify
-{
- private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
-
- public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException
- {
- TypesReader tr = new TypesReader(key);
-
- String key_format = tr.readString();
-
- if (key_format.equals("ssh-rsa") == false)
- throw new IllegalArgumentException("This is not a ssh-rsa public key");
-
- BigInteger e = tr.readMPINT();
- BigInteger n = tr.readMPINT();
-
- if (tr.remain() != 0)
- throw new IOException("Padding in RSA public key!");
-
- return new RSAPublicKey(e, n);
- }
-
- public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-rsa");
- tw.writeMPInt(pk.getE());
- tw.writeMPInt(pk.getN());
-
- return tw.getBytes();
- }
-
- public static RSASignature decodeSSHRSASignature(byte[] sig) throws IOException
- {
- TypesReader tr = new TypesReader(sig);
-
- String sig_format = tr.readString();
-
- if (sig_format.equals("ssh-rsa") == false)
- throw new IOException("Peer sent wrong signature format");
-
- /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
- * containing s (which is an integer, without lengths or padding, unsigned and in
- * network byte order)." See also below.
- */
-
- byte[] s = tr.readByteString();
-
- if (s.length == 0)
- throw new IOException("Error in RSA signature, S is empty.");
-
- if (log.isEnabled())
- {
- log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")");
- }
-
- if (tr.remain() != 0)
- throw new IOException("Padding in RSA signature!");
-
- return new RSASignature(new BigInteger(1, s));
- }
-
- public static byte[] encodeSSHRSASignature(RSASignature sig) throws IOException
- {
- TypesWriter tw = new TypesWriter();
-
- tw.writeString("ssh-rsa");
-
- /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
- * containing s (which is an integer, without lengths or padding, unsigned and in
- * network byte order)."
- */
-
- byte[] s = sig.getS().toByteArray();
-
- /* Remove first zero sign byte, if present */
-
- if ((s.length > 1) && (s[0] == 0x00))
- tw.writeString(s, 1, s.length - 1);
- else
- tw.writeString(s, 0, s.length);
-
- return tw.getBytes();
- }
-
- public static RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException
- {
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- byte[] der_header = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
- 0x04, 0x14 };
-
- int rsa_block_len = (pk.getN().bitLength() + 7) / 8;
-
- int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1;
-
- if (num_pad < 8)
- throw new IOException("Cannot sign with RSA, message too long");
-
- byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad];
-
- sig[0] = 0x01;
-
- for (int i = 0; i < num_pad; i++)
- {
- sig[i + 1] = (byte) 0xff;
- }
-
- sig[num_pad + 1] = 0x00;
-
- System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length);
- System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length);
-
- BigInteger m = new BigInteger(1, sig);
-
- BigInteger s = m.modPow(pk.getD(), pk.getN());
-
- return new RSASignature(s);
- }
-
- public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException
- {
- SHA1 md = new SHA1();
- md.update(message);
- byte[] sha_message = new byte[md.getDigestLength()];
- md.digest(sha_message);
-
- BigInteger n = dpk.getN();
- BigInteger e = dpk.getE();
- BigInteger s = ds.getS();
-
- if (n.compareTo(s) <= 0)
- {
- log.log(20, "ssh-rsa signature: n.compareTo(s) <= 0");
- return false;
- }
-
- int rsa_block_len = (n.bitLength() + 7) / 8;
-
- /* And now the show begins */
-
- if (rsa_block_len < 1)
- {
- log.log(20, "ssh-rsa signature: rsa_block_len < 1");
- return false;
- }
-
- byte[] v = s.modPow(e, n).toByteArray();
-
- int startpos = 0;
-
- if ((v.length > 0) && (v[0] == 0x00))
- startpos++;
-
- if ((v.length - startpos) != (rsa_block_len - 1))
- {
- log.log(20, "ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)");
- return false;
- }
-
- if (v[startpos] != 0x01)
- {
- log.log(20, "ssh-rsa signature: v[startpos] != 0x01");
- return false;
- }
-
- int pos = startpos + 1;
-
- while (true)
- {
- if (pos >= v.length)
- {
- log.log(20, "ssh-rsa signature: pos >= v.length");
- return false;
- }
- if (v[pos] == 0x00)
- break;
- if (v[pos] != (byte) 0xff)
- {
- log.log(20, "ssh-rsa signature: v[pos] != (byte) 0xff");
- return false;
- }
- pos++;
- }
-
- int num_pad = pos - (startpos + 1);
-
- if (num_pad < 8)
- {
- log.log(20, "ssh-rsa signature: num_pad < 8");
- return false;
- }
-
- pos++;
-
- if (pos >= v.length)
- {
- log.log(20, "ssh-rsa signature: pos >= v.length");
- return false;
- }
-
- SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos);
-
- byte[] seq = dr.readSequenceAsByteArray();
-
- if (dr.available() != 0)
- {
- log.log(20, "ssh-rsa signature: dr.available() != 0");
- return false;
- }
-
- dr.resetInput(seq);
-
- /* Read digestAlgorithm */
-
- byte digestAlgorithm[] = dr.readSequenceAsByteArray();
-
- /* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */
-
- if ((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9))
- {
- log.log(20, "ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)");
- return false;
- }
-
- byte[] digestAlgorithm_sha1 = new byte[] { 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00 };
-
- for (int i = 0; i < digestAlgorithm.length; i++)
- {
- if (digestAlgorithm[i] != digestAlgorithm_sha1[i])
- {
- log.log(20, "ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]");
- return false;
- }
- }
-
- byte[] digest = dr.readOctetString();
-
- if (dr.available() != 0)
- {
- log.log(20, "ssh-rsa signature: dr.available() != 0 (II)");
- return false;
- }
-
- if (digest.length != sha_message.length)
- {
- log.log(20, "ssh-rsa signature: digest.length != sha_message.length");
- return false;
- }
-
- for (int i = 0; i < sha_message.length; i++)
- {
- if (sha_message[i] != digest[i])
- {
- log.log(20, "ssh-rsa signature: sha_message[i] != digest[i]");
- return false;
- }
- }
-
- return true;
- }
-}
+ +package com.trilead.ssh2.signature; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.RSAPublicKeySpec; + +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; + + +/** + * RSASHA1Verify. + * + * @author Christian Plattner, plattner@trilead.com + * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ + */ +public class RSASHA1Verify +{ + private static final Logger log = Logger.getLogger(RSASHA1Verify.class); + + public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException + { + TypesReader tr = new TypesReader(key); + + String key_format = tr.readString(); + + if (key_format.equals("ssh-rsa") == false) + throw new IllegalArgumentException("This is not a ssh-rsa public key"); + + BigInteger e = tr.readMPINT(); + BigInteger n = tr.readMPINT(); + + if (tr.remain() != 0) + throw new IOException("Padding in RSA public key!"); + + KeySpec keySpec = new RSAPublicKeySpec(n, e); + + try { + KeyFactory kf = KeyFactory.getInstance("RSA"); + return (RSAPublicKey) kf.generatePublic(keySpec); + } catch (NoSuchAlgorithmException nsae) { + IOException ioe = new IOException("No RSA KeyFactory available"); + ioe.initCause(nsae); + throw ioe; + } catch (InvalidKeySpecException ikse) { + IOException ioe = new IOException("No RSA KeyFactory available"); + ioe.initCause(ikse); + throw ioe; + } + } + + public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-rsa"); + tw.writeMPInt(pk.getPublicExponent()); + tw.writeMPInt(pk.getModulus()); + + return tw.getBytes(); + } + + public static byte[] decodeSSHRSASignature(byte[] sig) throws IOException + { + TypesReader tr = new TypesReader(sig); + + String sig_format = tr.readString(); + + if (sig_format.equals("ssh-rsa") == false) + throw new IOException("Peer sent wrong signature format"); + + /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string + * containing s (which is an integer, without lengths or padding, unsigned and in + * network byte order)." See also below. + */ + + byte[] s = tr.readByteString(); + + if (s.length == 0) + throw new IOException("Error in RSA signature, S is empty."); + + if (log.isEnabled()) + { + log.log(80, "Decoding ssh-rsa signature string (length: " + s.length + ")"); + } + + if (tr.remain() != 0) + throw new IOException("Padding in RSA signature!"); + + if (s[0] == 0 && s[1] == 0 && s[2] == 0) { + int i = 0; + int j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000) + | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff); + i += j; + j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000) + | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff); + byte[] tmp = new byte[j]; + System.arraycopy(s, i, tmp, 0, j); + sig = tmp; + } + + return s; + } + + public static byte[] encodeSSHRSASignature(byte[] s) throws IOException + { + TypesWriter tw = new TypesWriter(); + + tw.writeString("ssh-rsa"); + + /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string + * containing s (which is an integer, without lengths or padding, unsigned and in + * network byte order)." + */ + + /* Remove first zero sign byte, if present */ + + if ((s.length > 1) && (s[0] == 0x00)) + tw.writeString(s, 1, s.length - 1); + else + tw.writeString(s, 0, s.length); + + return tw.getBytes(); + } + + public static byte[] generateSignature(byte[] message, RSAPrivateKey pk) throws IOException + { + try { + Signature s = Signature.getInstance("SHA1withRSA"); + s.initSign(pk); + s.update(message); + return s.sign(); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (InvalidKeyException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (SignatureException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } + + public static boolean verifySignature(byte[] message, byte[] ds, RSAPublicKey dpk) throws IOException + { + try { + Signature s = Signature.getInstance("SHA1withRSA"); + s.initVerify(dpk); + s.update(message); + return s.verify(ds); + } catch (NoSuchAlgorithmException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (InvalidKeyException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } catch (SignatureException e) { + IOException ex = new IOException(); + ex.initCause(e); + throw ex; + } + } +} diff --git a/src/com/trilead/ssh2/signature/RSASignature.java b/src/com/trilead/ssh2/signature/RSASignature.java deleted file mode 100644 index e04e7ee..0000000 --- a/src/com/trilead/ssh2/signature/RSASignature.java +++ /dev/null @@ -1,27 +0,0 @@ -
-package com.trilead.ssh2.signature;
-
-import java.math.BigInteger;
-
-
-/**
- * RSASignature.
- *
- * @author Christian Plattner, plattner@trilead.com
- * @version $Id: RSASignature.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
- */
-
-public class RSASignature
-{
- BigInteger s;
-
- public BigInteger getS()
- {
- return s;
- }
-
- public RSASignature(BigInteger s)
- {
- this.s = s;
- }
-}
\ No newline at end of file |