aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/AuthAgentCallback.java6
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/Connection.java27
-rw-r--r--sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java75
3 files changed, 85 insertions, 23 deletions
diff --git a/sshlib/src/main/java/com/trilead/ssh2/AuthAgentCallback.java b/sshlib/src/main/java/com/trilead/ssh2/AuthAgentCallback.java
index 96f2d07..17dcbb4 100644
--- a/sshlib/src/main/java/com/trilead/ssh2/AuthAgentCallback.java
+++ b/sshlib/src/main/java/com/trilead/ssh2/AuthAgentCallback.java
@@ -17,9 +17,9 @@ public interface AuthAgentCallback {
Map<String,byte[]> retrieveIdentities();
/**
- * @param pair A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
- * containing a DSA or RSA private key of
- * the user in Trilead object format.
+ * @param pair A <code>RSAPrivateKey</code>, <code>ECPrivateKey</code>, or
+ * <code>DSAPrivateKey</code> containing a DSA, EC, or RSA private
+ * and corresponding <code>PublicKey</code>.
* @param comment comment associated with this key
* @param confirmUse whether to prompt before using this key
* @param lifetime lifetime in seconds for key to be remembered
diff --git a/sshlib/src/main/java/com/trilead/ssh2/Connection.java b/sshlib/src/main/java/com/trilead/ssh2/Connection.java
index efb84b8..a957b2a 100644
--- a/sshlib/src/main/java/com/trilead/ssh2/Connection.java
+++ b/sshlib/src/main/java/com/trilead/ssh2/Connection.java
@@ -289,7 +289,7 @@ public class Connection
* <code>getRemainingAuthMethods</code> method to get a list of the
* remaining possible methods).
*
- * @param user
+ * @param user the username to attempt to log in as
* @return if the connection is now authenticated.
* @throws IOException
*/
@@ -321,8 +321,8 @@ public class Connection
/**
* After a successful connect, one has to authenticate oneself. The
* authentication method "publickey" works by signing a challenge sent by
- * the server. The signature is either DSA or RSA based - it just depends on
- * the type of private key you specify, either a DSA or RSA private key in
+ * the server. The signature is either DSA, EC, or RSA based - it just depends
+ * on the type of private key you specify, either a DSA or RSA private key in
* PEM format. And yes, this is may seem to be a little confusing, the
* method is called "publickey" in the SSH-2 protocol specification, however
* since we need to generate a signature, you actually have to supply a
@@ -330,7 +330,7 @@ public class Connection
* <p>
* The private key contained in the PEM file may also be encrypted
* ("Proc-Type: 4,ENCRYPTED"). The library supports DES-CBC and DES-EDE3-CBC
- * encryption, as well as the more exotic PEM encrpytions AES-128-CBC,
+ * encryption, as well as the more exotic PEM encryption AES-128-CBC,
* AES-192-CBC and AES-256-CBC.
* <p>
* If the authentication phase is complete, <code>true</code> will be
@@ -391,9 +391,9 @@ public class Connection
/**
* After a successful connect, one has to authenticate oneself. The
* authentication method "publickey" works by signing a challenge sent by
- * the server. The signature is either DSA or RSA based - it just depends on
- * the type of private key you specify, either a DSA or RSA private key in
- * PEM format. And yes, this is may seem to be a little confusing, the
+ * the server. The signature is either DSA, EC, or RSA based - it just depends
+ * on the type of private key you specify, either a DSA, EC, or RSA private key
+ * in PEM format. And yes, this is may seem to be a little confusing, the
* method is called "publickey" in the SSH-2 protocol specification, however
* since we need to generate a signature, you actually have to supply a
* private key =).
@@ -408,9 +408,9 @@ public class Connection
* @param user
* A <code>String</code> holding the username.
* @param pair
- * A <code>RSAPrivateKey</code> or <code>DSAPrivateKey</code>
- * containing a DSA or RSA private key of
- * the user in Trilead object format.
+ * A <code>KeyPair</code> containing a <code>RSAPrivateKey</code>,
+ * <code>DSAPrivateKey</code>, or <code>ECPrivateKey</code> and
+ * corresponding PublicKey.
*
* @return whether the connection is now authenticated.
* @throws IOException
@@ -442,7 +442,7 @@ public class Connection
}
/**
* A convenience wrapper function which reads in a private key (PEM format,
- * either DSA or RSA) and then calls
+ * either DSA, EC, or RSA) and then calls
* <code>authenticateWithPublicKey(String, char[], String)</code>.
* <p>
* NOTE PUTTY USERS: Event though your key file may start with
@@ -455,8 +455,9 @@ public class Connection
* A <code>String</code> holding the username.
* @param pemFile
* A <code>File</code> object pointing to a file containing a
- * DSA or RSA private key of the user in OpenSSH key format (PEM,
- * you can't miss the "-----BEGIN DSA PRIVATE KEY-----" or
+ * DSA, EC, or RSA private key of the user in OpenSSH key format
+ * (PEM, you can't miss the "-----BEGIN DSA PRIVATE KEY-----",
+ * "-----BEGIN EC PRIVATE KEY-----", or
* "-----BEGIN RSA PRIVATE KEY-----" tag).
* @param password
* If the PEM file is encrypted then you must specify the
diff --git a/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
index 281a59d..f628ae7 100644
--- a/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
+++ b/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
@@ -267,6 +267,24 @@ public class ECDSASHA2Verify {
}
}
+ private static final int readLength(byte[] sig, int offset, int numOctets) throws IOException {
+ if (numOctets > 4 || numOctets <= 0) {
+ throw new IOException("Cannot decode DER length");
+ }
+
+ long length = 0L;
+ for (int i = 0; i < numOctets; i++) {
+ length <<= 8;
+ length |= sig[offset++];
+ }
+
+ if (length > 0xFFFFFFL || length < 0L) {
+ throw new IOException("Invalid DER length");
+ }
+
+ return (int) length;
+ }
+
public static byte[] encodeSSHECDSASignature(byte[] sig, ECParameterSpec params) throws IOException
{
TypesWriter tw = new TypesWriter();
@@ -274,25 +292,68 @@ public class ECDSASHA2Verify {
String curveName = getCurveName(params);
tw.writeString(ECDSA_SHA2_PREFIX + curveName);
- if ((sig[0] != 0x30) || (sig[1] != sig.length - 2) || (sig[2] != 0x02)) {
+ /*
+ * This is a signature in ASN.1 DER format. It should look like:
+ * 0x30 <len>
+ * 0x02 <len> <data[len]>
+ * 0x02 <len> <data[len]>
+ */
+
+ if (sig[0] != 0x30) {
throw new IOException("Invalid signature format");
}
- int rLength = sig[3];
- if ((rLength + 6 > sig.length) || (sig[4 + rLength] != 0x02)) {
+ final int seqHeaderLength;
+ final int seqLength;
+ if ((sig[1] & 0x80) != 0) {
+ int seqHeaderOctets = sig[1] & 0x7F;
+ seqHeaderLength = seqHeaderOctets + 1;
+ seqLength = readLength(sig, 2, seqHeaderOctets);
+ } else {
+ seqHeaderLength = 1;
+ seqLength = sig[1];
+ }
+
+ if ((seqLength == 0) || (1 + seqHeaderLength + seqLength != sig.length) || (sig[1 + seqHeaderLength] != 0x02)) {
+ throw new IOException("Invalid signature format");
+ }
+
+ final int rHeaderLength;
+ final int rLength;
+ if ((sig[1 + seqHeaderLength + 1] & 0x80) != 0) {
+ int rHeaderOctets = sig[seqHeaderLength + 2] & 0x7F;
+ rHeaderLength = rHeaderOctets + 1;
+ rLength = readLength(sig, seqHeaderLength + 3, rHeaderOctets);
+ } else {
+ rHeaderLength = 1;
+ rLength = sig[seqHeaderLength + 2];
+ }
+
+ if ((rLength == 0) || (rLength > seqLength - (rHeaderLength + 1 + 1 + 1)) ||
+ sig[1 + seqHeaderLength + 1 + rHeaderLength + rLength] != 0x02) {
throw new IOException("Invalid signature format");
}
- int sLength = sig[5 + rLength];
- if (6 + rLength + sLength > sig.length) {
+ final int sHeaderLength;
+ final int sLength;
+ if ((sig[1 + seqHeaderLength + 1 + rHeaderLength + rLength + 1] & 0x80) != 0) {
+ int sHeaderOctets = sig[1 + seqHeaderLength + 1 + rHeaderLength + rLength + 1] & 0x7F;
+ sHeaderLength = sHeaderOctets + 1;
+ sLength = readLength(sig, 4 + rHeaderLength + rLength, sHeaderOctets);
+ } else {
+ sHeaderLength = 1;
+ sLength = sig[1 + seqHeaderLength + 1 + rHeaderLength + rLength + 1];
+ }
+
+ if ((sLength == 0) || 2 + rHeaderLength + rLength + sHeaderLength + sLength > seqLength) {
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);
+ System.arraycopy(sig, 1 + seqHeaderLength + 1, rArray, 0, rLength);
+ System.arraycopy(sig, 1 + seqHeaderLength + 1 + rLength + 1 + sHeaderLength, sArray, 0, sLength);
BigInteger r = new BigInteger(1, rArray);
BigInteger s = new BigInteger(1, sArray);