diff options
Diffstat (limited to 'sshlib/src/main/java/com/trilead/ssh2')
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); |