diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 8e023621..77cdc4f7 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -34,6 +34,12 @@ jobs: check-latest: true - name: Build with Maven run: mvn -B -DskipTests=true package --file pom.xml + - uses: actions/upload-artifact@v3 + with: + name: java-${{ matrix.java }}-jars + path: | + **/target/*.jar + if: always() - name: Set up test JDK ${{ matrix.java }} uses: actions/setup-java@v3 with: @@ -49,4 +55,5 @@ jobs: **/target/surefire-reports/ **/target/failsafe-reports/ **/target/site/jacoco/ + **/target/site/jacoco-it/ if: always() diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index dc3affce..ca5ab4ba 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -14,5 +14,5 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/ChangeLog.md b/ChangeLog.md index 43a3ffb1..245b5387 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,23 @@ +* [0.2.7](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.7) + * Fix exception logging in Log4j2Logger. + * [#265](https://github.com/mwiede/jsch/issues/265) change buffer_margin computation to be dynamic based upon the MAC to allow connections that advertise small maximum packet sizes. + * [#266](https://github.com/mwiede/jsch/issues/266) fix PuTTY key parsing to work with unix line endings. + * Add support for ECDSA & EdDSA type PuTTY keys. + * [#71](https://github.com/mwiede/jsch/issues/71) add support for PuTTY version 3 format keys. + * Encrypted PuTTY version 3 format keys requires [Bouncy Castle](https://www.bouncycastle.org/java.html) (bcprov-jdk18on). + * Eliminate KeyPairDeferred and instead change handling of OpenSSH V1 type keys to be more like other KeyPair types. + * Be more vigilant about clearing private key data. + * Improve PKCS8 key handling and add support for PKCS5 2.1 encryption. + * Add support for ECDSA type PKCS8 keys. + * Add support for SCrypt type KDF for PKCS8 keys. + * PKCS8 keys using SCrypt requires [Bouncy Castle](https://www.bouncycastle.org/java.html) (bcprov-jdk18on). + * Add support for EdDSA type PKCS8 keys. + * EdDSA type PKCS8 keys requires [Bouncy Castle](https://www.bouncycastle.org/java.html) (bcprov-jdk18on). + * Attempt to authenticate using other signature algorithms supported by the same public key. + * Allow this behavior to be disabled via `try_additional_pubkey_algorithms` config option. + * Some servers incorrectly respond with `SSH_MSG_USERAUTH_PK_OK` to an initial auth query that they don't actually support for RSA keys. + * Add a new config option `enable_pubkey_auth_query` to allow skipping auth queries and proceed directly to attempting full `SSH_MSG_USERAUTH_REQUEST`'s. + * Add a new config option `enable_auth_none` to control whether an initial auth request for the method `none` is sent to detect all supported auth methods available on the server. * [0.2.6](https://github.com/mwiede/jsch/releases/tag/jsch-0.2.6) * Include host alias instead of the real host in messages and exceptions by @ShadelessFox in https://github.com/mwiede/jsch/pull/257 * Fix missing keySize set when loading V1 RSA keys by @Alex-Vol-Amz in https://github.com/mwiede/jsch/pull/258 diff --git a/pom.xml b/pom.xml index b2a9b915..44e699c7 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ true 2.6.1 5.12.1 + 2.19.0 @@ -118,7 +119,7 @@ org.apache.logging.log4j log4j-api - 2.19.0 + ${log4j.version} true @@ -163,33 +164,28 @@ test - ch.qos.logback - logback-classic - 1.3.4 + com.google.guava + guava + 31.1-jre test - org.scala-lang - scala-library - 2.13.6 + org.apache.logging.log4j + log4j-core + ${log4j.version} test - org.scalatest - scalatest_2.13 - 3.2.13 + org.apache.logging.log4j + log4j-core + ${log4j.version} + test-jar test - - - org.scala-lang - scala-library - - - co.helmethair - scalatest-junit-runner - 0.2.0 + com.github.valfirst + slf4j-test + 2.8.1 test @@ -282,7 +278,7 @@ true true - -Xlint:all + -Xlint:all,-processing -Werror @@ -373,40 +369,12 @@ - - net.alchim31.maven - scala-maven-plugin - 4.8.0 - - all - false - - -deprecation - -feature - -unchecked - -Xfatal-warnings - - - - - - testCompile - - - - org.apache.maven.plugins maven-surefire-plugin 2.22.2 false - - - java.util.logging.config.file - ${project.basedir}/src/test/resources/logging.properties - - @@ -415,12 +383,6 @@ 2.22.2 false - - - java.util.logging.config.file - ${project.basedir}/src/test/resources/logging.properties - - @@ -594,12 +556,24 @@ prepare-agent + + default-prepare-agent-integration + + prepare-agent-integration + + default-report report + + default-report-integration + + report-integration + + @@ -630,7 +604,7 @@ com.google.errorprone error_prone_core - 2.15.0 + 2.17.0 @@ -639,4 +613,4 @@ - \ No newline at end of file + diff --git a/src/main/java/com/jcraft/jsch/Argon2.java b/src/main/java/com/jcraft/jsch/Argon2.java new file mode 100644 index 00000000..be0c6876 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/Argon2.java @@ -0,0 +1,40 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface Argon2 extends KDF{ + public static final int ARGON2D = 0; + public static final int ARGON2I = 1; + public static final int ARGON2ID = 2; + public static final int V10 = 0x10; + public static final int V13 = 0x13; + + void init(byte[] salt, int iteration, int type, byte[] additional, byte[] secret, int memory, int parallelism, int version) throws Exception; +} diff --git a/src/main/java/com/jcraft/jsch/BCrypt.java b/src/main/java/com/jcraft/jsch/BCrypt.java new file mode 100644 index 00000000..e3e6e51b --- /dev/null +++ b/src/main/java/com/jcraft/jsch/BCrypt.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface BCrypt extends KDF{ + void init(byte[] salt, int iteration) throws Exception; +} diff --git a/src/main/java/com/jcraft/jsch/Buffer.java b/src/main/java/com/jcraft/jsch/Buffer.java index 0f75f7b7..f6f4e067 100644 --- a/src/main/java/com/jcraft/jsch/Buffer.java +++ b/src/main/java/com/jcraft/jsch/Buffer.java @@ -212,8 +212,13 @@ byte getCommand(){ return buffer[5]; } + // Hardcode this since we can't use dynamic Session value + private static final int buffer_margin = 32 + // maximum padding length + 64 + // maximum mac length + 32; // margin for deflater; deflater may inflate data + void checkFreeSize(int n){ - int size = index+n+Session.buffer_margin; + int size = index+n+buffer_margin; if(buffer.length0){ - int _l=l; - if(l>_bufl-(14+dataLen)-Session.buffer_margin){ - _l=_bufl-(14+dataLen)-Session.buffer_margin; - } - - if(_l<=0){ - flush(); - continue; + try{ + while(l>0){ + int _l=l; + int buffer_margin=getSession().getBufferMargin(); + if(l>_bufl-(14+dataLen)-buffer_margin){ + _l=_bufl-(14+dataLen)-buffer_margin; + } + + if(_l<=0){ + flush(); + continue; + } + + System.arraycopy(buf, s, _buf, 14+dataLen, _l); + dataLen+=_l; + s+=_l; + l-=_l; } - - System.arraycopy(buf, s, _buf, 14+dataLen, _l); - dataLen+=_l; - s+=_l; - l-=_l; + } + catch(JSchException e){ + throw new IOException(e.toString(), e); } } diff --git a/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java b/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java index 7ace8095..0751adf0 100644 --- a/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java +++ b/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java @@ -47,7 +47,7 @@ protected Packet genChannelOpenPacket() { Buffer buf = new Buffer(50 + socketPath.length() + - Session.buffer_margin); + session.getBufferMargin()); Packet packet = new Packet(buf); packet.reset(); buf.putByte((byte) SSH_MSG_CHANNEL_OPEN); diff --git a/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java b/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java index f4c127be..5be68d8a 100644 --- a/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java +++ b/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java @@ -104,7 +104,7 @@ void run(){ i=io.in.read(buf.buffer, 14, buf.buffer.length-14 - -Session.buffer_margin + -_session.getBufferMargin() ); if(i<=0){ eof(); @@ -154,7 +154,7 @@ public void setOutputStream(OutputStream out){ protected Packet genChannelOpenPacket(){ Buffer buf = new Buffer(50 + // 6 + 4*8 + 12 host.length() + originator_IP_address.length() + - Session.buffer_margin); + session.getBufferMargin()); Packet packet = new Packet(buf); // byte SSH_MSG_CHANNEL_OPEN(90) // string channel type // diff --git a/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java b/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java index 0210e059..c3115e47 100644 --- a/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java +++ b/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java @@ -96,14 +96,14 @@ public void run(){ Packet packet=new Packet(buf); int i=0; try{ - Session _session = getSession(); + Session _session=getSession(); while(thread!=null && io!=null && io.in!=null){ i=io.in.read(buf.buffer, 14, buf.buffer.length-14 - -Session.buffer_margin + -_session.getBufferMargin() ); if(i<=0){ eof(); diff --git a/src/main/java/com/jcraft/jsch/ChannelSession.java b/src/main/java/com/jcraft/jsch/ChannelSession.java index 7e45ead8..046b436c 100644 --- a/src/main/java/com/jcraft/jsch/ChannelSession.java +++ b/src/main/java/com/jcraft/jsch/ChannelSession.java @@ -241,6 +241,7 @@ void run(){ Packet packet=new Packet(buf); int i=-1; try{ + Session _session=getSession(); while(isConnected() && thread!=null && io!=null && @@ -248,7 +249,7 @@ void run(){ i=io.in.read(buf.buffer, 14, buf.buffer.length-14 - -Session.buffer_margin + -_session.getBufferMargin() ); if(i==0)continue; if(i==-1){ @@ -262,7 +263,7 @@ void run(){ buf.putInt(recipient); buf.putInt(i); buf.skip(i); - getSession().write(packet, this, i); + _session.write(packet, this, i); } } catch(Exception e){ diff --git a/src/main/java/com/jcraft/jsch/ChannelSftp.java b/src/main/java/com/jcraft/jsch/ChannelSftp.java index 0157752a..5c368333 100644 --- a/src/main/java/com/jcraft/jsch/ChannelSftp.java +++ b/src/main/java/com/jcraft/jsch/ChannelSftp.java @@ -593,9 +593,10 @@ public void _put(InputStream src, String dst, boolean dontcopy=true; + int buffer_margin=session.getBufferMargin(); if(!dontcopy){ // This case will not work anymore. data=new byte[obuf.buffer.length - -(5+13+21+handle.length+Session.buffer_margin + -(5+13+21+handle.length+buffer_margin ) ]; } @@ -616,7 +617,7 @@ public void _put(InputStream src, String dst, else{ data=obuf.buffer; _s=5+13+21+handle.length; - _datalen=obuf.buffer.length-_s-Session.buffer_margin; + _datalen=obuf.buffer.length-_s-buffer_margin; } int bulk_requests = rq.size(); @@ -664,7 +665,7 @@ public void _put(InputStream src, String dst, foo-=sendWRITE(handle, offset, data, 0, foo); if(data!=obuf.buffer){ data=obuf.buffer; - _datalen=obuf.buffer.length-_s-Session.buffer_margin; + _datalen=obuf.buffer.length-_s-buffer_margin; } } else { @@ -2581,8 +2582,10 @@ private int sendWRITE(byte[] handle, long offset, byte[] data, int start, int length) throws Exception{ int _length=length; opacket.reset(); - if(obuf.buffer.length=11){ config.put("xdh", "com.jcraft.jsch.jce.XDH"); @@ -222,12 +230,16 @@ public class JSch{ config.put("ssh-ed25519", "com.jcraft.jsch.bc.SignatureEd25519"); config.put("ssh-ed448", "com.jcraft.jsch.bc.SignatureEd448"); } + config.put("keypairgen_fromprivate.eddsa", "com.jcraft.jsch.bc.KeyPairGenEdDSA"); config.put("StrictHostKeyChecking", "ask"); config.put("HashKnownHosts", "no"); config.put("PreferredAuthentications", Util.getSystemProperty("jsch.preferred_authentications", "gssapi-with-mic,publickey,keyboard-interactive,password")); config.put("PubkeyAcceptedAlgorithms", Util.getSystemProperty("jsch.client_pubkey", "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256")); + config.put("enable_pubkey_auth_query", Util.getSystemProperty("jsch.enable_pubkey_auth_query", "yes")); + config.put("try_additional_pubkey_algorithms", Util.getSystemProperty("jsch.try_additional_pubkey_algorithms", "yes")); + config.put("enable_auth_none", Util.getSystemProperty("jsch.enable_auth_none", "yes")); config.put("CheckCiphers", Util.getSystemProperty("jsch.check_ciphers", "chacha20-poly1305@openssh.com")); config.put("CheckMacs", Util.getSystemProperty("jsch.check_macs", "")); diff --git a/src/main/java/com/jcraft/jsch/JulLogger.java b/src/main/java/com/jcraft/jsch/JulLogger.java index 9316d2fa..2984bdf0 100644 --- a/src/main/java/com/jcraft/jsch/JulLogger.java +++ b/src/main/java/com/jcraft/jsch/JulLogger.java @@ -5,16 +5,9 @@ public class JulLogger implements com.jcraft.jsch.Logger { - private static final Logger stlogger = Logger.getLogger(JSch.class.getName()); - private final Logger logger; - - public JulLogger() { - this(stlogger); - } + private static final Logger logger = Logger.getLogger(JSch.class.getName()); - JulLogger(Logger logger) { - this.logger = logger; - } + public JulLogger() {} @Override public boolean isEnabled(int level) { diff --git a/src/main/java/com/jcraft/jsch/KDF.java b/src/main/java/com/jcraft/jsch/KDF.java new file mode 100644 index 00000000..e9361121 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/KDF.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface KDF { + byte[] getKey(byte[] pass, int size); +} diff --git a/src/main/java/com/jcraft/jsch/KeyPair.java b/src/main/java/com/jcraft/jsch/KeyPair.java index e53640a0..6d2d5019 100644 --- a/src/main/java/com/jcraft/jsch/KeyPair.java +++ b/src/main/java/com/jcraft/jsch/KeyPair.java @@ -30,12 +30,15 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING package com.jcraft.jsch; import java.io.*; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Hashtable; -import java.util.Vector; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public abstract class KeyPair{ - + + /** DEFERRED should not be be used. */ public static final int DEFERRED = -1; public static final int ERROR=0; public static final int DSA=1; @@ -50,6 +53,7 @@ public abstract class KeyPair{ static final int VENDOR_PUTTY=2; static final int VENDOR_PKCS8=3; static final int VENDOR_OPENSSH_V1 = 4; + static final int VENDOR_PUTTY_V3 = 5; int vendor=VENDOR_OPENSSH; @@ -97,14 +101,13 @@ public void setPublicKeyComment(String publicKeyComment){ JSch jsch=null; protected Cipher cipher; + private KDF kdf; + private HASH sha1; private HASH hash; private Random random; private byte[] passphrase; - protected String kdfName; - protected byte[] kdfOptions; - public KeyPair(JSch jsch){ this.jsch=jsch; } @@ -168,6 +171,9 @@ public void writePrivateKey(OutputStream out, byte[] passphrase){ //out.close(); } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to write private key", e); + } } } @@ -211,6 +217,9 @@ public void writePublicKey(OutputStream out, String comment){ out.write(cr); } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to write public key", e); + } } } @@ -248,6 +257,9 @@ public void writeSECSHPublicKey(OutputStream out, String comment){ out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); out.write(cr); } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to write public key", e); + } } } @@ -326,7 +338,9 @@ private byte[] encrypt(byte[] plain, byte[][] _iv, byte[] passphrase){ cipher.update(encoded, 0, encoded.length, encoded, 0); } catch(Exception e){ - //System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to encrypt key", e); + } } Util.bzero(key); return encoded; @@ -345,7 +359,9 @@ private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv){ return plain; } catch(Exception e){ - //System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to decrypt key", e); + } } return null; } @@ -411,7 +427,11 @@ private Random genRandom(){ Class c=Class.forName(JSch.getConfig("random")).asSubclass(Random.class); random=c.getDeclaredConstructor().newInstance(); } - catch(Exception e){ System.err.println("connect: random "+e); } + catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to create random", e); + } + } } return random; } @@ -423,6 +443,9 @@ private HASH genHash(){ hash.init(); } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to create hash", e); + } } return hash; } @@ -432,6 +455,9 @@ private Cipher genCipher(){ cipher=c.getDeclaredConstructor().newInstance(); } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to create cipher", e); + } } return cipher; } @@ -463,6 +489,12 @@ synchronized byte[] genKey(byte[] passphrase, byte[] iv){ } System.arraycopy(hn, 0, key, 0, key.length); } + else if(vendor==VENDOR_OPENSSH_V1){ + tmp=kdf.getKey(passphrase, cipher.getBlockSize()+cipher.getIVSize()); + System.arraycopy(tmp, 0, key, 0, key.length); + System.arraycopy(tmp, key.length, iv, 0, iv.length); + Util.bzero(tmp); + } else if(vendor==VENDOR_FSECURE){ for(int index=0; index+hsize<=hn.length;){ if(tmp!=null){ hash.update(tmp, 0, tmp.length); } @@ -474,21 +506,32 @@ else if(vendor==VENDOR_FSECURE){ System.arraycopy(hn, 0, key, 0, key.length); } else if(vendor==VENDOR_PUTTY){ - Class c=Class.forName(JSch.getConfig("sha-1")).asSubclass(HASH.class); - HASH sha1=c.getDeclaredConstructor().newInstance(); - tmp = new byte[4]; - key = new byte[20*2]; - for(int i = 0; i < 2; i++){ - sha1.init(); - tmp[3]=(byte)i; - sha1.update(tmp, 0, tmp.length); - sha1.update(passphrase, 0, passphrase.length); - System.arraycopy(sha1.digest(), 0, key, i*20, 20); - } + byte[] i=new byte[4]; + + sha1.update(i, 0, i.length); + sha1.update(passphrase, 0, passphrase.length); + tmp=sha1.digest(); + System.arraycopy(tmp, 0, key, 0, tmp.length); + Util.bzero(tmp); + + i[3]=(byte)1; + sha1.update(i, 0, i.length); + sha1.update(passphrase, 0, passphrase.length); + tmp=sha1.digest(); + System.arraycopy(tmp, 0, key, tmp.length, key.length-tmp.length); + Util.bzero(tmp); + } + else if(vendor==VENDOR_PUTTY_V3){ + tmp=kdf.getKey(passphrase, cipher.getBlockSize()+cipher.getIVSize()+32); + System.arraycopy(tmp, 0, key, 0, key.length); + System.arraycopy(tmp, key.length, iv, 0, iv.length); + Util.bzero(tmp); } } catch(Exception e){ - System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate key from passphrase", e); + } } return key; } @@ -539,10 +582,17 @@ public boolean decrypt(byte[] _passphrase){ byte[] bar=new byte[_passphrase.length]; System.arraycopy(_passphrase, 0, bar, 0, bar.length); _passphrase=bar; - byte[] foo=decrypt(data, _passphrase, iv); - Util.bzero(_passphrase); - if(parse(foo)){ - encrypted=false; + byte[] foo=null; + try{ + foo=decrypt(data, _passphrase, iv); + if(parse(foo)){ + encrypted=false; + Util.bzero(data); + } + } + finally{ + Util.bzero(_passphrase); + Util.bzero(foo); } return !encrypted; } @@ -600,8 +650,6 @@ public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchE int vendor=VENDOR_OPENSSH; String publicKeyComment = ""; Cipher cipher=null; - String kdfName = null; - byte[] kdfOptions = null; // prvkey from "ssh-add" command on the remote. if(pubkey==null && @@ -635,7 +683,7 @@ else if(_type.equals("ssh-ed448")){ kpair=KeyPairEd448.fromSSHAgent(jsch, buf); } else{ - throw new JSchException("privatekey: invalid key "+Util.byte2str(prvkey, 4, 7)); + throw new JSchException("privatekey: invalid key "+_type); } return kpair; } @@ -691,11 +739,12 @@ else if(i+8 < len && type=UNKNOWN; vendor=VENDOR_PKCS8; i+=5; - - } else if (isOpenSSHPrivateKey(buf, i, len)) { - type = UNKNOWN; - vendor = VENDOR_OPENSSH_V1; - } else { + } + else if (isOpenSSHPrivateKey(buf, i, len)){ + type = UNKNOWN; + vendor = VENDOR_OPENSSH_V1; + } + else{ throw new JSchException("invalid privatekey"); } i+=3; @@ -750,20 +799,20 @@ else if(i+8 < len && } continue; } - if(buf[i]==0x0d && i+14 && // FSecure data[0]==(byte)0x3f && data[1]==(byte)0x6f && @@ -831,16 +883,14 @@ else if(i+8 < len && _buf.getInt(); // 0x3f6ff9be _buf.getInt(); byte[]_type=_buf.getString(); - //System.err.println("type: "+Util.byte2str(_type)); String _cipher=Util.byte2str(_buf.getString()); - //System.err.println("cipher: "+_cipher); if(_cipher.equals("3des-cbc")){ _buf.getInt(); byte[] foo=new byte[data.length-_buf.getOffSet()]; _buf.getByte(foo); data=foo; encrypted=true; - throw new JSchException("unknown privatekey format"); + throw new JSchException("cipher " + _cipher + " is not supported for this privatekey format"); } else if(_cipher.equals("none")){ _buf.getInt(); @@ -852,42 +902,10 @@ else if(_cipher.equals("none")){ _buf.getByte(foo); data=foo; } - } - // OPENSSH V1 PRIVATE KEY - else if (data != null && - Util.array_equals(AUTH_MAGIC, Arrays.copyOfRange(data, 0, AUTH_MAGIC.length))) { - - vendor = VENDOR_OPENSSH_V1; - Buffer buffer = new Buffer(data); - byte[] magic = new byte[AUTH_MAGIC.length]; - buffer.getByte(magic); - - String cipherName = Util.byte2str(buffer.getString()); - kdfName = Util.byte2str(buffer.getString()); // string kdfname - kdfOptions = buffer.getString(); // string kdfoptions - - int nrKeys = buffer.getInt(); // int number of keys N; Should be 1 - if (nrKeys != 1) { - throw new IOException("We don't support having more than 1 key in the file (yet)."); - } - - pubkey = buffer.getString(); - - if ("none".equals(cipherName)) { - encrypted = false; - data = buffer.getString(); - type = readOpenSSHKeyv1(data); - } else if (Session.checkCipher(JSch.getConfig(cipherName))) { - encrypted = true; - Class c = Class.forName(JSch.getConfig(cipherName)).asSubclass(Cipher.class); - cipher = c.getDeclaredConstructor().newInstance(); - data = buffer.getString(); - // the type can only be determined after encryption, so we take this intermediate here: - type = DEFERRED; - } else { - throw new JSchException("cipher " + cipherName + " is not available"); + else{ + throw new JSchException("cipher " + _cipher + " is not supported for this privatekey format"); + } } - } if(pubkey!=null){ try{ @@ -898,14 +916,14 @@ else if (data != null && boolean valid=true; i=0; - do{i++;}while(buf.length>i && buf[i]!=0x0a); + do{i++;}while(buf.length>i && buf[i]!='\n'); if(buf.length<=i) {valid=false;} while(valid){ - if(buf[i]==0x0a){ + if(buf[i]=='\n'){ boolean inheader=false; for(int j=i+1; j0 && buf[i-1]==0x0d) i--; + if(i>0 && buf[i-1]=='\r') i--; if(start0 && buf[i-1]==0x0d) i--; + if(i>0 && buf[i-1]=='\r') i--; if(start c = Class.forName(JSch.getConfig(cipherName)).asSubclass(Cipher.class); + kpair.cipher = c.getDeclaredConstructor().newInstance(); + kpair.iv = new byte[kpair.cipher.getIVSize()]; + } catch (Exception e) { + throw new JSchException("cipher " + cipherName + " is not available", e); + } + } else { + throw new JSchException("cipher " + cipherName + " is not available"); + } + try { + Buffer kdfOpts = new Buffer(kdfOptions); + byte[] salt = kdfOpts.getString(); + int rounds = kdfOpts.getInt(); + Class c = Class.forName(JSch.getConfig(kdfName)).asSubclass(BCrypt.class); + BCrypt bcrypt = c.getDeclaredConstructor().newInstance(); + bcrypt.init(salt, rounds); + kpair.kdf = bcrypt; + } catch (Exception e) { + throw new JSchException("kdf " + kdfName + " is not available", e); + } + } + + return kpair; + } catch (Exception e) { + Util.bzero(kpair.data); + throw e; + } } private static boolean isOpenSSHPrivateKey(byte[] buf, int i, int len) { @@ -1094,81 +1139,174 @@ public void finalize(){ dispose(); } - private static final String[] header1 = { - "PuTTY-User-Key-File-2: ", - "Encryption: ", - "Comment: ", - "Public-Lines: " - }; - - private static final String[] header2 = { - "Private-Lines: " - }; - - private static final String[] header3 = { - "Private-MAC: " - }; - static KeyPair loadPPK(JSch jsch, byte[] buf) throws JSchException { byte[] pubkey = null; byte[] prvkey = null; + byte[] _prvkey = null; int lines = 0; Buffer buffer = new Buffer(buf); - Hashtable v = new Hashtable<>(); + Map v = new HashMap<>(); while(true){ if(!parseHeader(buffer, v)) break; } + int ppkVersion; String typ = v.get("PuTTY-User-Key-File-2"); if(typ == null){ - return null; + typ = v.get("PuTTY-User-Key-File-3"); + if(typ == null){ + return null; + } + else{ + ppkVersion = VENDOR_PUTTY_V3; + } + } + else{ + ppkVersion = VENDOR_PUTTY; } - lines = Integer.parseInt(v.get("Public-Lines")); - pubkey = parseLines(buffer, lines); + try{ + lines = Integer.parseInt(v.get("Public-Lines")); + pubkey = parseLines(buffer, lines); - while(true){ - if(!parseHeader(buffer, v)) - break; - } - - lines = Integer.parseInt(v.get("Private-Lines")); - prvkey = parseLines(buffer, lines); + while(true){ + if(!parseHeader(buffer, v)) + break; + } - while(true){ - if(!parseHeader(buffer, v)) - break; - } + lines = Integer.parseInt(v.get("Private-Lines")); + _prvkey = parseLines(buffer, lines); + + while(true){ + if(!parseHeader(buffer, v)) + break; + } + + prvkey = Util.fromBase64(_prvkey, 0, _prvkey.length); + pubkey = Util.fromBase64(pubkey, 0, pubkey.length); + + KeyPair kpair = parsePubkeyBlob(jsch, pubkey, typ); + kpair.encrypted = !v.get("Encryption").equals("none"); + kpair.publickeyblob = pubkey; + kpair.vendor = ppkVersion; + kpair.publicKeyComment = v.get("Comment"); + if(kpair.encrypted){ + if(Session.checkCipher(JSch.getConfig("aes256-cbc"))){ + try { + Class c=Class.forName(JSch.getConfig("aes256-cbc")).asSubclass(Cipher.class); + kpair.cipher=c.getDeclaredConstructor().newInstance(); + kpair.iv=new byte[kpair.cipher.getIVSize()]; + } + catch(Exception e){ + throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available.", e); + } + } + else { + throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available."); + } - prvkey = Util.fromBase64(prvkey, 0, prvkey.length); - pubkey = Util.fromBase64(pubkey, 0, pubkey.length); + if(ppkVersion==VENDOR_PUTTY){ + try { + Class c=Class.forName(JSch.getConfig("sha-1")).asSubclass(HASH.class); + HASH sha1=c.getDeclaredConstructor().newInstance(); + sha1.init(); + kpair.sha1=sha1; + } + catch(Exception e){ + throw new JSchException("'sha-1' is required, but it is not available.", e); + } + } + else{ + String argonTypeStr=v.get("Key-Derivation"); + String saltStr=v.get("Argon2-Salt"); + if(argonTypeStr == null || saltStr == null || (saltStr != null && saltStr.length() % 2 != 0)){ + throw new JSchException("Invalid argon2 params."); + } - KeyPair kpair = null; + int argonType; + switch(argonTypeStr){ + case "Argon2d": + argonType=Argon2.ARGON2D; + break; + case "Argon2i": + argonType=Argon2.ARGON2I; + break; + case "Argon2id": + argonType=Argon2.ARGON2ID; + break; + default: + throw new JSchException("Invalid argon2 params."); + } - if(typ.equals("ssh-rsa")) { + try{ + int memory=Integer.parseInt(v.get("Argon2-Memory")); + int passes=Integer.parseInt(v.get("Argon2-Passes")); + int parallelism=Integer.parseInt(v.get("Argon2-Parallelism")); - Buffer _buf = new Buffer(pubkey); - _buf.skip(pubkey.length); + byte[] salt=new byte[saltStr.length()/2]; + for(int i=0; i c=Class.forName(JSch.getConfig("argon2")).asSubclass(Argon2.class); + Argon2 argon2=c.getDeclaredConstructor().newInstance(); + argon2.init(salt, passes, argonType, new byte[0], new byte[0], memory, parallelism, Argon2.V13); + kpair.kdf=argon2; + } + catch(NumberFormatException e){ + throw new JSchException("Invalid argon2 params.", e); + } + catch(Exception e){ + throw new JSchException("'argon2' is required, but it is not available.", e); + } + } + + kpair.data = prvkey; + } + else { + kpair.data = prvkey; + if(!kpair.parse(prvkey)){ + throw new JSchException("invalid privatekey"); + } + else{ + Util.bzero(prvkey); + } + } + return kpair; + } + catch (Exception e){ + Util.bzero(prvkey); + throw e; + } + finally{ + Util.bzero(_prvkey); + } + } + + private static KeyPair parsePubkeyBlob(JSch jsch, byte[] pubkeyblob, String typ) throws JSchException{ + Buffer _buf = new Buffer(pubkeyblob); + _buf.skip(pubkeyblob.length); + + String pubkeyType = Util.byte2str(_buf.getString()); + if(typ == null || typ.equals("")){ + typ = pubkeyType; + } else if (!typ.equals(pubkeyType)){ + throw new JSchException("pubkeyblob type ["+pubkeyType+"] does not match expected type ["+typ+"]"); + } + + if(typ.equals("ssh-rsa")) { byte[] pub_array = new byte[_buf.getInt()]; _buf.getByte(pub_array); byte[] n_array = new byte[_buf.getInt()]; _buf.getByte(n_array); - kpair = new KeyPairRSA(jsch, n_array, pub_array, null); + return new KeyPairRSA(jsch, n_array, pub_array, null); } else if(typ.equals("ssh-dss")){ - Buffer _buf = new Buffer(pubkey); - _buf.skip(pubkey.length); - - int len = _buf.getInt(); - _buf.getByte(new byte[len]); // ssh-dss - byte[] p_array = new byte[_buf.getInt()]; _buf.getByte(p_array); byte[] q_array = new byte[_buf.getInt()]; @@ -1178,39 +1316,34 @@ else if(typ.equals("ssh-dss")){ byte[] y_array = new byte[_buf.getInt()]; _buf.getByte(y_array); - kpair = new KeyPairDSA(jsch, p_array, q_array, g_array, y_array, null); - } - else { - return null; + return new KeyPairDSA(jsch, p_array, q_array, g_array, y_array, null); } + else if(typ.equals("ecdsa-sha2-nistp256") || typ.equals("ecdsa-sha2-nistp384") || typ.equals("ecdsa-sha2-nistp521")){ + byte[] name = _buf.getString(); // nistpXXX - if(kpair == null) - return null; + int len = _buf.getInt(); + int x04 = _buf.getByte(); // in case of x04 it is uncompressed https://tools.ietf.org/html/rfc5480#page-7 + byte[] r_array = new byte[(len - 1) / 2]; + byte[] s_array = new byte[(len - 1) / 2]; + _buf.getByte(r_array); + _buf.getByte(s_array); - kpair.encrypted = !v.get("Encryption").equals("none"); - kpair.vendor = VENDOR_PUTTY; - kpair.publicKeyComment = v.get("Comment"); - if(kpair.encrypted){ - if(Session.checkCipher(JSch.getConfig("aes256-cbc"))){ - try { - Class c=Class.forName(JSch.getConfig("aes256-cbc")).asSubclass(Cipher.class); - kpair.cipher=c.getDeclaredConstructor().newInstance(); - kpair.iv=new byte[kpair.cipher.getIVSize()]; - } - catch(Exception e){ - throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available."); - } + return new KeyPairECDSA(jsch, name, r_array, s_array, null); + } + else if(typ.equals("ssh-ed25519") || typ.equals("ssh-ed448")){ + byte[] pub_array = new byte[_buf.getInt()]; + _buf.getByte(pub_array); + + if(typ.equals("ssh-ed25519")){ + return new KeyPairEd25519(jsch, pub_array, null); } - else { - throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available."); + else{ + return new KeyPairEd448(jsch, pub_array, null); } - kpair.data = prvkey; } - else { - kpair.data = prvkey; - kpair.parse(prvkey); + else{ + throw new JSchException("key type " + typ + " is not supported"); } - return kpair; } private static byte[] parseLines(Buffer buffer, int lines){ @@ -1221,22 +1354,24 @@ private static byte[] parseLines(Buffer buffer, int lines){ int i = index; while(lines-->0){ while(buf.length > i){ - if(buf[i++] == 0x0d){ + byte c = buf[i++]; + if(c == '\r' || c == '\n'){ + int len = i - index - 1; if(data == null){ - data = new byte[i - index - 1]; - System.arraycopy(buf, index, data, 0, i - index - 1); + data = new byte[len]; + System.arraycopy(buf, index, data, 0, len); } - else { - byte[] tmp = new byte[data.length + i - index - 1]; + else if(len > 0){ + byte[] tmp = new byte[data.length + len]; System.arraycopy(data, 0, tmp, 0, data.length); - System.arraycopy(buf, index, tmp, data.length, i - index -1); - for(int j = 0; j < data.length; j++) data[j] = 0; // clear + System.arraycopy(buf, index, tmp, data.length, len); + Util.bzero(data); // clear data = tmp; } break; } } - if(buf[i]==0x0a) + if(i < buf.length && buf[i]=='\n') i++; index=i; } @@ -1247,13 +1382,16 @@ private static byte[] parseLines(Buffer buffer, int lines){ return data; } - private static boolean parseHeader(Buffer buffer, Hashtable v){ + private static boolean parseHeader(Buffer buffer, Map v){ byte[] buf = buffer.buffer; int index = buffer.index; String key = null; String value = null; for(int i = index; i < buf.length; i++){ - if(buf[i] == 0x0d){ + if(buf[i] == '\r' || buf[i] == '\n'){ + if(i+1 < buf.length && buf[i+1] == '\n'){ + i++; + } break; } if(buf[i] == ':'){ @@ -1271,10 +1409,10 @@ private static boolean parseHeader(Buffer buffer, Hashtable v){ return false; for(int i = index; i < buf.length; i++){ - if(buf[i] == 0x0d){ + if(buf[i] == '\r' || buf[i] == '\n'){ value = Util.byte2str(buf, index, i - index); i++; - if(i < buf.length && buf[i] == 0x0a){ + if(i < buf.length && buf[i] == '\n'){ i++; } index = i; @@ -1301,7 +1439,7 @@ static class ASN1Exception extends Exception { private static final long serialVersionUID=-1L; } - class ASN1 { + static class ASN1 { byte[] buf; int start; int length; @@ -1330,6 +1468,24 @@ boolean isOBJECT() { boolean isOCTETSTRING() { return getType()==(0x04&0xff); } + boolean isNULL() { + return getType()==(0x05&0xff); + } + boolean isBITSTRING() { + return getType()==(0x03&0xff); + } + boolean isCONTEXTPRIMITIVE(int tag) { + if((tag&~0xff)!=0 || (tag&0x60)!=0){ + throw new IllegalArgumentException(); + } + return getType()==((tag|0x80)&0xff); + } + boolean isCONTEXTCONSTRUCTED(int tag) { + if((tag&~0xff)!=0 || (tag&0x40)!=0){ + throw new IllegalArgumentException(); + } + return getType()==((tag|0xa0)&0xff); + } private int getLength(int[] indexp) { int index=indexp[0]; int length=buf[index++]&0xff; @@ -1358,7 +1514,7 @@ ASN1[] getContents() throws ASN1Exception { return new ASN1[0]; } int index=indexp[0]; - Vector values = new Vector<>(); + List values = new ArrayList<>(); while(length>0) { index++; length--; int tmp=index; @@ -1366,14 +1522,12 @@ ASN1[] getContents() throws ASN1Exception { int l=getLength(indexp); index=indexp[0]; length-=(index-tmp); - values.addElement(new ASN1(buf, tmp-1, 1+(index-tmp)+l)); + values.add(new ASN1(buf, tmp-1, 1+(index-tmp)+l)); index+=l; length-=l; } ASN1[] result = new ASN1[values.size()]; - for(int i = 0; i T requireDecrypted(T obj) { - if (obj == null) - throw new IllegalStateException("encrypted key has not been decrypted yet."); - return obj; - } -} diff --git a/src/main/java/com/jcraft/jsch/KeyPairECDSA.java b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java index 283e520b..86a316e3 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairECDSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairECDSA.java @@ -177,22 +177,24 @@ boolean parse(byte[] plain){ */ return false; } - else if(vendor==VENDOR_PUTTY){ - /* + else if(vendor==VENDOR_PUTTY || vendor==VENDOR_PUTTY_V3){ Buffer buf=new Buffer(plain); buf.skip(plain.length); try { byte[][] tmp = buf.getBytes(1, ""); prv_array = tmp[0]; + key_size = prv_array.length>=64 ? 521 : + (prv_array.length>=48 ? 384 : 256); } catch(JSchException e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } return false; } return true; - */ - return false; } // OPENSSH Key v1 Format @@ -305,8 +307,9 @@ else if(vendor==VENDOR_PUTTY){ (prv_array.length>=48 ? 384 : 256); } catch(Exception e){ - //System.err.println(e); - //e.printStackTrace(); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } return false; } return true; @@ -361,7 +364,9 @@ public byte[] getSignature(byte[] data){ return Buffer.fromBytes(tmp).buffer; } catch(Exception e){ - //System.err.println("e "+e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + } } return null; } @@ -390,7 +395,9 @@ public Signature getVerifier(){ return ecdsa; } catch(Exception e){ - //System.err.println("e "+e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + } } return null; } diff --git a/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java b/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java index 48246d40..0279ef3d 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java @@ -58,7 +58,6 @@ void generate(int key_size) throws JSchException{ keypairgen=null; } catch(Exception | NoClassDefFoundError e){ - //System.err.println("KeyPairEdDSA: "+e); throw new JSchException(e.toString(), e); } } @@ -74,27 +73,67 @@ void generate(int key_size) throws JSchException{ @Override boolean parse(byte [] plain){ + if(vendor==VENDOR_PUTTY || vendor==VENDOR_PUTTY_V3){ + Buffer buf=new Buffer(plain); + buf.skip(plain.length); - // Only OPENSSH Key v1 Format supported for EdDSA keys - if(vendor != VENDOR_OPENSSH_V1) return false; - try{ - // OPENSSH Key v1 Format - final Buffer buf = new Buffer(plain); - int checkInt1 = buf.getInt(); // uint32 checkint1 - int checkInt2 = buf.getInt(); // uint32 checkint2 - if (checkInt1 != checkInt2) { - throw new JSchException("check failed"); + try { + byte[][] tmp = buf.getBytes(1, ""); + prv_array = tmp[0]; + } + catch(JSchException e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; } - String keyType = Util.byte2str(buf.getString()); // string keytype - pub_array = buf.getString(); // public key - // OpenSSH stores private key in first half of string and duplicate copy of public key in second half of string - byte[] tmp = buf.getString(); // secret key (private key + public key) - prv_array = Arrays.copyOf(tmp, getKeySize()); - publicKeyComment = Util.byte2str(buf.getString()); + return true; } - catch(Exception e){ - //System.err.println(e); + else if(vendor==VENDOR_OPENSSH_V1){ + try{ + // OPENSSH Key v1 Format + final Buffer buf = new Buffer(plain); + int checkInt1 = buf.getInt(); // uint32 checkint1 + int checkInt2 = buf.getInt(); // uint32 checkint2 + if (checkInt1 != checkInt2) { + throw new JSchException("check failed"); + } + String keyType = Util.byte2str(buf.getString()); // string keytype + pub_array = buf.getString(); // public key + // OpenSSH stores private key in first half of string and duplicate copy of public key in second half of string + byte[] tmp = buf.getString(); // secret key (private key + public key) + prv_array = Arrays.copyOf(tmp, getKeySize()); + publicKeyComment = Util.byte2str(buf.getString()); + return true; + } + catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + } + else if(vendor==VENDOR_PKCS8){ + try{ + Class c=Class.forName(JSch.getConfig("keypairgen_fromprivate.eddsa")).asSubclass(KeyPairGenEdDSA.class); + KeyPairGenEdDSA keypairgen=c.getDeclaredConstructor().newInstance(); + keypairgen.init(getJceName(), plain); + pub_array=keypairgen.getPub(); + prv_array=keypairgen.getPrv(); + return true; + } + catch(Exception | NoClassDefFoundError e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + } + else { + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key"); + } return false; } } @@ -135,6 +174,9 @@ public byte[] getSignature(byte[] data, String alg){ return Buffer.fromBytes(tmp).buffer; } catch(Exception | NoClassDefFoundError e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + } } return null; } @@ -161,6 +203,9 @@ public Signature getVerifier(String alg){ return eddsa; } catch(Exception | NoClassDefFoundError e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + } } return null; } diff --git a/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java b/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java index e6983524..839d3df6 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java @@ -30,7 +30,10 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING package com.jcraft.jsch; public interface KeyPairGenEdDSA{ - void init(String Name, int keylen) throws Exception; + void init(String name, int keylen) throws Exception; byte[] getPub(); byte[] getPrv(); + default void init(String name, byte[] prv) throws Exception{ + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java index fa1f3f06..8ab277e7 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java +++ b/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java @@ -29,8 +29,9 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING package com.jcraft.jsch; -import java.util.Vector; import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; class KeyPairPKCS8 extends KeyPair { private static final byte[] rsaEncryption = { @@ -40,7 +41,35 @@ class KeyPairPKCS8 extends KeyPair { private static final byte[] dsaEncryption = { (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0xce, - (byte)0x38, (byte)0x04, (byte)0x1 + (byte)0x38, (byte)0x04, (byte)0x01 + }; + + private static final byte[] ecPublicKey = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0xce, + (byte)0x3d, (byte)0x02, (byte)0x01 + }; + + private static final byte[] ed25519 = { + (byte)0x2b, (byte)0x65, (byte)0x70 + }; + + private static final byte[] ed448 = { + (byte)0x2b, (byte)0x65, (byte)0x71 + }; + + private static final byte[] secp256r1 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0xce, + (byte)0x3d, (byte)0x03, (byte)0x01, (byte)0x07 + }; + + private static final byte[] secp384r1 = { + (byte)0x2b, (byte)0x81, (byte)0x04, (byte)0x00, + (byte)0x22 + }; + + private static final byte[] secp521r1 = { + (byte)0x2b, (byte)0x81, (byte)0x04, (byte)0x00, + (byte)0x23 }; private static final byte[] pbes2 = { @@ -53,6 +82,46 @@ class KeyPairPKCS8 extends KeyPair { (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0c }; + private static final byte[] scrypt = { + (byte)0x2b, (byte)0x06, (byte)0x01, (byte)0x04, (byte)0x01, + (byte)0xda, (byte)0x47, (byte)0x04, (byte)0x0b + }; + + private static final byte[] hmacWithSha1 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x07 + }; + + private static final byte[] hmacWithSha224 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x08 + }; + + private static final byte[] hmacWithSha256 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x09 + }; + + private static final byte[] hmacWithSha384 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x0a + }; + + private static final byte[] hmacWithSha512 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x0b + }; + + private static final byte[] hmacWithSha512224 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x0c + }; + + private static final byte[] hmacWithSha512256 = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x02, (byte)0x0d + }; + private static final byte[] aes128cbc = { (byte)0x60, (byte)0x86, (byte)0x48, (byte)0x01, (byte)0x65, (byte)0x03, (byte)0x04, (byte)0x01, (byte)0x02 @@ -68,11 +137,56 @@ class KeyPairPKCS8 extends KeyPair { (byte)0x03, (byte)0x04, (byte)0x01, (byte)0x2a }; + private static final byte[] descbc = { + (byte)0x2b, (byte)0x0e, (byte)0x03, (byte)0x02, (byte)0x07 + }; + + private static final byte[] des3cbc = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x03, (byte)0x07 + }; + + private static final byte[] rc2cbc = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x03, (byte)0x02 + }; + + private static final byte[] rc5cbc = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x03, (byte)0x09 + }; + + private static final byte[] pbeWithMD2AndDESCBC = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x01 + }; + + private static final byte[] pbeWithMD2AndRC2CBC = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x04 + }; + private static final byte[] pbeWithMD5AndDESCBC = { (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x03 }; + private static final byte[] pbeWithMD5AndRC2CBC = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x06 + }; + + private static final byte[] pbeWithSHA1AndDESCBC = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0a + }; + + private static final byte[] pbeWithSHA1AndRC2CBC = { + (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0x86, (byte)0xf7, + (byte)0x0d, (byte)0x01, (byte)0x05, (byte)0x0b + }; + + private KeyPair kpair = null; KeyPairPKCS8(JSch jsch){ @@ -113,36 +227,97 @@ boolean parse(byte[] plain){ } */ + byte[] _data = null; + byte[] prv_array = null; + byte[] _plain = null; + KeyPair _key = null; try{ - Vector values = new Vector<>(); + ASN1[] contents; - ASN1[] contents = null; ASN1 asn1 = new ASN1(plain); + if(!asn1.isSEQUENCE()){ + throw new ASN1Exception(); + } + contents = asn1.getContents(); + if(contents.length<3 || contents.length>4){ + throw new ASN1Exception(); + } + if(!contents[0].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[1].isSEQUENCE()){ + throw new ASN1Exception(); + } + if(!contents[2].isOCTETSTRING()){ + throw new ASN1Exception(); + } + // attributes [0] IMPLICIT Attributes OPTIONAL + if(contents.length>3 && !contents[3].isCONTEXTCONSTRUCTED(0)){ + throw new ASN1Exception(); + } + + int version = parseASN1IntegerAsInt(contents[0].getContent()); + if(version!=0){ + throw new ASN1Exception(); + } ASN1 privateKeyAlgorithm = contents[1]; ASN1 privateKey = contents[2]; contents = privateKeyAlgorithm.getContents(); - byte[] privateKeyAlgorithmID = contents[0].getContent(); - contents = contents[1].getContents(); - if(contents.length>0){ - for(int i = 0; i < contents.length; i++){ - values.addElement(contents[i].getContent()); - } + if(contents.length==0){ + throw new ASN1Exception(); + } + if(!contents[0].isOBJECT()){ + throw new ASN1Exception(); } + byte[] privateKeyAlgorithmID = contents[0].getContent(); - byte[] _data = privateKey.getContent(); + _data = privateKey.getContent(); KeyPair _kpair = null; if(Util.array_equals(privateKeyAlgorithmID, rsaEncryption)){ + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[1].isNULL()){ + throw new ASN1Exception(); + } + _kpair = new KeyPairRSA(jsch); _kpair.copy(this); if(_kpair.parse(_data)){ kpair = _kpair; - } + return true; + } + else { + throw new JSchException("failed to parse RSA"); + } } else if(Util.array_equals(privateKeyAlgorithmID, dsaEncryption)){ + List values = new ArrayList<>(3); + + if(contents.length>1 && contents[1].isSEQUENCE()){ + contents = contents[1].getContents(); + if(contents.length!=3){ + throw new ASN1Exception(); + } + if(!contents[0].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[1].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[2].isINTEGER()){ + throw new ASN1Exception(); + } + + values.add(contents[0].getContent()); + values.add(contents[1].getContent()); + values.add(contents[2].getContent()); + } + asn1 = new ASN1(_data); if(values.size() == 0) { // embedded DSA parameters format /* @@ -153,62 +328,264 @@ else if(Util.array_equals(privateKeyAlgorithmID, dsaEncryption)){ INTEGER // G_array INTEGER // prv_array */ + if(!asn1.isSEQUENCE()){ + throw new ASN1Exception(); + } + contents = asn1.getContents(); - byte[] bar = contents[1].getContent(); + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[0].isSEQUENCE()){ + throw new ASN1Exception(); + } + if(!contents[1].isINTEGER()){ + throw new ASN1Exception(); + } + + prv_array = contents[1].getContent(); + contents = contents[0].getContents(); - for(int i = 0; i < contents.length; i++){ - values.addElement(contents[i].getContent()); + if(contents.length!=3){ + throw new ASN1Exception(); + } + if(!contents[0].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[1].isINTEGER()){ + throw new ASN1Exception(); } - values.addElement(bar); + if(!contents[2].isINTEGER()){ + throw new ASN1Exception(); + } + + values.add(contents[0].getContent()); + values.add(contents[1].getContent()); + values.add(contents[2].getContent()); } else { /* INTEGER // prv_array */ - values.addElement(asn1.getContent()); + if(!asn1.isINTEGER()){ + throw new ASN1Exception(); + } + prv_array = asn1.getContent(); } - byte[] P_array = values.elementAt(0); - byte[] Q_array = values.elementAt(1); - byte[] G_array = values.elementAt(2); - byte[] prv_array = values.elementAt(3); + byte[] P_array = values.get(0); + byte[] Q_array = values.get(1); + byte[] G_array = values.get(2); // Y = g^X mode p byte[] pub_array = (new BigInteger(G_array)). modPow(new BigInteger(prv_array), new BigInteger(P_array)). toByteArray(); - KeyPairDSA _key = new KeyPairDSA(jsch, - P_array, Q_array, G_array, - pub_array, prv_array); - plain = _key.getPrivateKey(); + _key = new KeyPairDSA(jsch, + P_array, Q_array, G_array, + pub_array, prv_array); + _plain = _key.getPrivateKey(); _kpair = new KeyPairDSA(jsch); _kpair.copy(this); - if(_kpair.parse(plain)){ + if(_kpair.parse(_plain)){ + kpair = _kpair; + return true; + } + else { + throw new JSchException("failed to parse DSA"); + } + } + else if(Util.array_equals(privateKeyAlgorithmID, ecPublicKey)){ + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[1].isOBJECT()){ + throw new ASN1Exception(); + } + + byte[] namedCurve = contents[1].getContent(); + byte[] name; + if(!Util.array_equals(namedCurve, secp256r1)){ + name = Util.str2byte("nistp256"); + } + else if(!Util.array_equals(namedCurve, secp384r1)){ + name = Util.str2byte("nistp384"); + } + else if(!Util.array_equals(namedCurve, secp521r1)){ + name = Util.str2byte("nistp521"); + } + else { + throw new JSchException("unsupported named curve oid: "+Util.toHex(namedCurve)); + } + + ASN1 ecPrivateKey = new ASN1(_data); + if(!ecPrivateKey.isSEQUENCE()){ + throw new ASN1Exception(); + } + + // ECPrivateKey ::= SEQUENCE { + // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + // privateKey OCTET STRING, + // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + // publicKey [1] BIT STRING OPTIONAL + // } + contents = ecPrivateKey.getContents(); + if(contents.length<3 || contents.length>4){ + throw new ASN1Exception(); + } + if(!contents[0].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[1].isOCTETSTRING()){ + throw new ASN1Exception(); + } + + version = parseASN1IntegerAsInt(contents[0].getContent()); + if(version!=1){ + throw new ASN1Exception(); + } + prv_array = contents[1].getContent(); + + // publicKey is required here since there is no other way to derive it. + ASN1 publicKey; + if(contents.length==3){ + publicKey = contents[2]; + } + else { + publicKey = contents[3]; + + // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL + if(!contents[2].isCONTEXTCONSTRUCTED(0)){ + throw new ASN1Exception(); + } + + // NamedCurve isn't required here since it is already known. + // But if it is included, they should be the same... + ASN1[] goo = contents[2].getContents(); + if(goo.length!=1){ + throw new ASN1Exception(); + } + if(!goo[0].isOBJECT()){ + throw new ASN1Exception(); + } + if(!Util.array_equals(goo[0].getContent(), namedCurve)){ + throw new ASN1Exception(); + } + } + + // publicKey [1] BIT STRING OPTIONAL + if(!publicKey.isCONTEXTCONSTRUCTED(1)){ + throw new ASN1Exception(); + } + contents = publicKey.getContents(); + if(contents.length!=1){ + throw new ASN1Exception(); + } + if(!contents[0].isBITSTRING()){ + throw new ASN1Exception(); + } + + byte[] Q_array = contents[0].getContent(); + byte[][] tmp = KeyPairECDSA.fromPoint(Q_array); + byte[] r_array = tmp[0]; + byte[] s_array = tmp[1]; + + _key = new KeyPairECDSA(jsch, name, r_array, s_array, prv_array); + _plain = _key.getPrivateKey(); + + _kpair = new KeyPairECDSA(jsch); + _kpair.copy(this); + if(_kpair.parse(_plain)){ + kpair = _kpair; + return true; + } + else { + throw new JSchException("failed to parse ECDSA"); + } + } + else if(Util.array_equals(privateKeyAlgorithmID, ed25519) || + Util.array_equals(privateKeyAlgorithmID, ed448)){ + if(contents.length!=1){ + throw new ASN1Exception(); + } + ASN1 curvePrivateKey = new ASN1(_data); + if(!curvePrivateKey.isOCTETSTRING()){ + throw new ASN1Exception(); + } + + prv_array=curvePrivateKey.getContent(); + if(Util.array_equals(privateKeyAlgorithmID, ed25519)){ + _kpair = new KeyPairEd25519(jsch); + } + else { + _kpair = new KeyPairEd448(jsch); + } + _kpair.copy(this); + if(_kpair.parse(prv_array)){ kpair = _kpair; + return true; + } + else { + throw new JSchException("failed to parse EdDSA"); } } + else { + throw new JSchException("unsupported privateKeyAlgorithm oid: "+Util.toHex(privateKeyAlgorithmID)); + } } catch(ASN1Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "PKCS8: failed to parse key: ASN1 parsing error", e); + } return false; } catch(Exception e){ - //System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "PKCS8: failed to parse key: "+e.getMessage(), e); + } return false; } - return kpair != null; + finally{ + Util.bzero(_data); + Util.bzero(prv_array); + Util.bzero(_plain); + if(_key!=null){ + _key.dispose(); + } + } } @Override public byte[] getPublicKeyBlob(){ - return kpair.getPublicKeyBlob(); + if(kpair!=null){ + return kpair.getPublicKeyBlob(); + } + else { + return super.getPublicKeyBlob(); + } } @Override - byte[] getKeyTypeName(){ return kpair.getKeyTypeName();} + byte[] getKeyTypeName(){ + if(kpair!=null){ + return kpair.getKeyTypeName(); + } + else { + return new byte[0]; + } + } + @Override - public int getKeyType(){return kpair.getKeyType();} + public int getKeyType(){ + if(kpair!=null){ + return kpair.getKeyType(); + } + else { + return UNKNOWN; + } + } @Override public int getKeySize(){ @@ -264,6 +641,25 @@ public boolean decrypt(byte[] _passphrase){ OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 OCTET STRING [HEX DUMP]: +or + + SEQUENCE + SEQUENCE + OBJECT :PBES2 + SEQUENCE + SEQUENCE + OBJECT :PBKDF2 + SEQUENCE + OCTET STRING [HEX DUMP]:E4E24ADC9C00BD4D + INTEGER :0800 + SEQUENCE + OBJECT :hmacWithSHA256 + NULL + SEQUENCE + OBJECT :aes-128-cbc + OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 + OCTET STRING [HEX DUMP]: + or SEQUENCE @@ -275,112 +671,344 @@ public boolean decrypt(byte[] _passphrase){ OCTET STRING [HEX DUMP] */ + byte[] _data = null; + byte[] key = null; + byte[] plain = null; try{ + ASN1[] contents; - ASN1[] contents = null; ASN1 asn1 = new ASN1(data); + if(!asn1.isSEQUENCE()){ + throw new ASN1Exception(); + } - contents = asn1.getContents(); - - byte[] _data = contents[1].getContent(); + contents = asn1.getContents(); + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[0].isSEQUENCE()){ + throw new ASN1Exception(); + } + if(!contents[1].isOCTETSTRING()){ + throw new ASN1Exception(); + } + _data = contents[1].getContent(); ASN1 pbes = contents[0]; + contents = pbes.getContents(); + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[0].isOBJECT()){ + throw new ASN1Exception(); + } + if(!contents[1].isSEQUENCE()){ + throw new ASN1Exception(); + } + byte[] pbesid = contents[0].getContent(); ASN1 pbesparam = contents[1]; - byte[] salt = null; - int iterations = 0; - byte[] iv = null; - byte[] encryptfuncid = null; + String kdfname; + KDF kdfinst; + byte[] encryptfuncid; + ASN1 encryptparams; if(Util.array_equals(pbesid, pbes2)){ contents = pbesparam.getContents(); - ASN1 pbkdf = contents[0]; + if(contents.length!=2){ + throw new ASN1Exception(); + } + + ASN1 kdf = contents[0]; ASN1 encryptfunc = contents[1]; - contents = pbkdf.getContents(); - byte[] pbkdfid = contents[0].getContent(); - ASN1 pbkdffunc = contents[1]; - contents = pbkdffunc.getContents(); - salt = contents[0].getContent(); - iterations = - Integer.parseInt((new BigInteger(contents[1].getContent())).toString()); + + if(!kdf.isSEQUENCE()){ + throw new ASN1Exception(); + } + if(!encryptfunc.isSEQUENCE()){ + throw new ASN1Exception(); + } contents = encryptfunc.getContents(); + + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[0].isOBJECT()){ + throw new ASN1Exception(); + } + encryptfuncid = contents[0].getContent(); - iv = contents[1].getContent(); - } - else if(Util.array_equals(pbesid, pbeWithMD5AndDESCBC)){ - // not supported - return false; - } - else { - return false; - } + encryptparams = contents[1]; + + contents = kdf.getContents(); + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[0].isOBJECT()){ + throw new ASN1Exception(); + } + if(!contents[1].isSEQUENCE()){ + throw new ASN1Exception(); + } - Cipher cipher=getCipher(encryptfuncid); - if(cipher==null) return false; + byte[] kdfid = contents[0].getContent(); - byte[] key=null; - try{ - Class c=Class.forName(JSch.getConfig("pbkdf")).asSubclass(PBKDF.class); - PBKDF tmp=c.getDeclaredConstructor().newInstance(); - key = tmp.getKey(_passphrase, salt, iterations, cipher.getBlockSize()); + if(Util.array_equals(kdfid, pbkdf2)){ + ASN1 pbkdf2func = contents[1]; + if(!pbkdf2func.isSEQUENCE()){ + throw new ASN1Exception(); + } + + ASN1 prf = null; + contents = pbkdf2func.getContents(); + if(contents.length<2 || contents.length>4){ + throw new ASN1Exception(); + } + if(!contents[0].isOCTETSTRING()){ + throw new ASN1Exception(); + } + if(!contents[1].isINTEGER()){ + throw new ASN1Exception(); + } + + if(contents.length==4){ + if(!contents[2].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[3].isSEQUENCE()){ + throw new ASN1Exception(); + } + prf = contents[3]; + } + else if(contents.length==3){ + if(contents[2].isSEQUENCE()){ + prf = contents[2]; + } + else if(!contents[2].isINTEGER()){ + throw new ASN1Exception(); + } + } + + byte[] prfid = null; + byte[] salt = contents[0].getContent(); + int iterations = parseASN1IntegerAsInt(contents[1].getContent()); + + if(prf!=null){ + contents = prf.getContents(); + if(contents.length!=2){ + throw new ASN1Exception(); + } + if(!contents[0].isOBJECT()){ + throw new ASN1Exception(); + } + if(!contents[1].isNULL()){ + throw new ASN1Exception(); + } + + prfid = contents[0].getContent(); + } + + kdfname = getPBKDF2Name(prfid); + PBKDF2 pbkdf2kdf = getPBKDF2(kdfname); + pbkdf2kdf.init(salt, iterations); + kdfinst = pbkdf2kdf; + } + else if(Util.array_equals(kdfid, scrypt)){ + contents = contents[1].getContents(); + if(contents.length<4 || contents.length>5){ + throw new ASN1Exception(); + } + if(!contents[0].isOCTETSTRING()){ + throw new ASN1Exception(); + } + if(!contents[1].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[2].isINTEGER()){ + throw new ASN1Exception(); + } + if(!contents[3].isINTEGER()){ + throw new ASN1Exception(); + } + if(contents.length>4 && !contents[4].isINTEGER()){ + throw new ASN1Exception(); + } + + byte[] salt = contents[0].getContent(); + int cost = parseASN1IntegerAsInt(contents[1].getContent()); + int blocksize = parseASN1IntegerAsInt(contents[2].getContent()); + int parallel = parseASN1IntegerAsInt(contents[3].getContent()); + + kdfname = "scrypt"; + SCrypt scryptkdf = getSCrypt(); + scryptkdf.init(salt, cost, blocksize, parallel); + kdfinst = scryptkdf; + } + else { + throw new JSchException("unsupported kdf oid: "+Util.toHex(kdfid)); + } } - catch(Exception ee){ + else { + String message; + if(Util.array_equals(pbesid, pbeWithMD2AndDESCBC)){ + message="pbeWithMD2AndDES-CBC unsupported"; + } + else if(Util.array_equals(pbesid, pbeWithMD2AndRC2CBC)){ + message="pbeWithMD2AndRC2-CBC unsupported"; + } + else if(Util.array_equals(pbesid, pbeWithMD5AndDESCBC)){ + message="pbeWithMD5AndDES-CBC unsupported"; + } + else if(Util.array_equals(pbesid, pbeWithMD5AndRC2CBC)){ + message="pbeWithMD5AndRC2-CBC unsupported"; + } + else if(Util.array_equals(pbesid, pbeWithSHA1AndDESCBC)){ + message="pbeWithSHA1AndDES-CBC unsupported"; + } + else if(Util.array_equals(pbesid, pbeWithSHA1AndRC2CBC)){ + message="pbeWithSHA1AndRC2-CBC unsupported"; + } + else { + message="unsupported encryption oid: "+Util.toHex(pbesid); + } + throw new JSchException(message); } + byte[][] ivp = new byte[1][]; + Cipher cipher=getCipher(encryptfuncid, encryptparams, ivp); + byte[] iv = ivp[0]; + + key = kdfinst.getKey(_passphrase, cipher.getBlockSize()); if(key==null){ - return false; + throw new JSchException("failed to generate key from KDF "+kdfname); } - cipher.init(Cipher.DECRYPT_MODE, key, iv); - Util.bzero(key); - byte[] plain=new byte[_data.length]; + plain=new byte[_data.length]; cipher.update(_data, 0, _data.length, plain, 0); if(parse(plain)){ encrypted=false; + Util.bzero(data); return true; } + else { + throw new JSchException("failed to parse decrypted key"); + } } catch(ASN1Exception e){ - // System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: ASN1 parsing error", e); + } + return false; } catch(Exception e){ - // System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: "+e.getMessage(), e); + } + return false; } + finally{ + Util.bzero(_data); + Util.bzero(key); + Util.bzero(plain); + } + } - return false; + static String getPBKDF2Name(byte[] id) throws JSchException{ + String name = null; + if(id==null || Util.array_equals(id, hmacWithSha1)){ + name="pbkdf2-hmac-sha1"; + } + else if(Util.array_equals(id, hmacWithSha224)){ + name="pbkdf2-hmac-sha224"; + } + else if(Util.array_equals(id, hmacWithSha256)){ + name="pbkdf2-hmac-sha256"; + } + else if(Util.array_equals(id, hmacWithSha384)){ + name="pbkdf2-hmac-sha384"; + } + else if(Util.array_equals(id, hmacWithSha512)){ + name="pbkdf2-hmac-sha512"; + } + else if(Util.array_equals(id, hmacWithSha512224)){ + throw new JSchException("unsupported pbkdf2 function: pbkdf2-hmac-sha512-224"); + } + else if(Util.array_equals(id, hmacWithSha512256)){ + throw new JSchException("unsupported pbkdf2 function: pbkdf2-hmac-sha512-256"); + } + + if(name==null){ + throw new JSchException("unsupported pbkdf2 function oid: "+Util.toHex(id)); + } + return name; + } + + static PBKDF2 getPBKDF2(String name) throws JSchException{ + try{ + Class c=Class.forName(JSch.getConfig(name)).asSubclass(PBKDF2.class); + return c.getDeclaredConstructor().newInstance(); + } + catch(Exception e){ + throw new JSchException(name+" is not supported", e); + } } - Cipher getCipher(byte[] id){ - Cipher cipher=null; + static SCrypt getSCrypt() throws JSchException{ + try{ + Class c=Class.forName(JSch.getConfig("scrypt")).asSubclass(SCrypt.class); + return c.getDeclaredConstructor().newInstance(); + } + catch(Exception e){ + throw new JSchException("scrypt is not supported", e); + } + } + + static Cipher getCipher(byte[] id, ASN1 encryptparams, byte[][] ivp) throws Exception{ String name = null; + if(Util.array_equals(id, aes128cbc)){ + name="aes128-cbc"; + } + else if(Util.array_equals(id, aes192cbc)){ + name="aes192-cbc"; + } + else if(Util.array_equals(id, aes256cbc)){ + name="aes256-cbc"; + } + else if(Util.array_equals(id, descbc)){ + throw new JSchException("unsupported cipher function: des-cbc"); + } + else if(Util.array_equals(id, des3cbc)){ + throw new JSchException("unsupported cipher function: 3des-cbc"); + } + else if(Util.array_equals(id, rc2cbc)){ + throw new JSchException("unsupported cipher function: rc2-cbc"); + } + else if(Util.array_equals(id, rc5cbc)){ + throw new JSchException("unsupported cipher function: rc5-cbc"); + } + + if(name==null){ + throw new JSchException("unsupported cipher function oid: "+Util.toHex(id)); + } + + if(!encryptparams.isOCTETSTRING()){ + throw new ASN1Exception(); + } + ivp[0] = encryptparams.getContent(); + try{ - if(Util.array_equals(id, aes128cbc)){ - name="aes128-cbc"; - } - else if(Util.array_equals(id, aes192cbc)){ - name="aes192-cbc"; - } - else if(Util.array_equals(id, aes256cbc)){ - name="aes256-cbc"; - } Class c=Class.forName(JSch.getConfig(name)).asSubclass(Cipher.class); - cipher=c.getDeclaredConstructor().newInstance(); + return c.getDeclaredConstructor().newInstance(); } catch(Exception e){ - if(jsch.getInstanceLogger().isEnabled(Logger.FATAL)){ - String message=""; - if(name==null){ - message="unknown oid: "+Util.toHex(id); - } - else { - message="function "+name+" is not supported"; - } - jsch.getInstanceLogger().log(Logger.FATAL, "PKCS8: "+message); - } + throw new JSchException(name+" is not supported", e); } - return cipher; + } + + static int parseASN1IntegerAsInt(byte[] content){ + return new BigInteger(content).intValueExact(); } } diff --git a/src/main/java/com/jcraft/jsch/KeyPairRSA.java b/src/main/java/com/jcraft/jsch/KeyPairRSA.java index 24f17dca..dc597597 100644 --- a/src/main/java/com/jcraft/jsch/KeyPairRSA.java +++ b/src/main/java/com/jcraft/jsch/KeyPairRSA.java @@ -81,7 +81,6 @@ void generate(int key_size) throws JSchException{ keypairgen=null; } catch(Exception e){ - //System.err.println("KeyPairRSA: "+e); throw new JSchException(e.toString(), e); } } @@ -132,7 +131,7 @@ boolean parse(byte [] plain){ int index=0; int length=0; - if(vendor==VENDOR_PUTTY){ + if(vendor==VENDOR_PUTTY || vendor==VENDOR_PUTTY_V3){ Buffer buf = new Buffer(plain); buf.skip(plain.length); @@ -144,6 +143,9 @@ boolean parse(byte [] plain){ c_array = tmp[3]; } catch(JSchException e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } return false; } @@ -172,6 +174,9 @@ boolean parse(byte [] plain){ return true; } + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key"); + } return false; } @@ -193,6 +198,7 @@ boolean parse(byte [] plain){ if(n_array!=null){ key_size = (new BigInteger(n_array)).bitLength(); } + publicKeyComment=Util.byte2str(prvKEyBuffer.getString()); getEPArray(); getEQArray(); @@ -318,7 +324,9 @@ boolean parse(byte [] plain){ } catch(Exception e){ - //System.err.println(e); + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to parse key", e); + } return false; } return true; @@ -369,6 +377,9 @@ public byte[] getSignature(byte[] data, String alg){ return Buffer.fromBytes(tmp).buffer; } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to generate signature", e); + } } return null; } @@ -396,6 +407,9 @@ public Signature getVerifier(String alg){ return rsa; } catch(Exception e){ + if(jsch.getInstanceLogger().isEnabled(Logger.ERROR)){ + jsch.getInstanceLogger().log(Logger.ERROR, "failed to create verifier", e); + } } return null; } diff --git a/src/main/java/com/jcraft/jsch/Log4j2Logger.java b/src/main/java/com/jcraft/jsch/Log4j2Logger.java index 27ee8ca5..291e5fa5 100644 --- a/src/main/java/com/jcraft/jsch/Log4j2Logger.java +++ b/src/main/java/com/jcraft/jsch/Log4j2Logger.java @@ -22,14 +22,14 @@ public void log(int level, String message) { @Override public void log(int level, String message, Throwable cause) { - if (cause != null) { + if (cause == null) { logger.log(getLevel(level), message); return; } logger.log(getLevel(level), message, cause); } - private static Level getLevel(int level) { + static Level getLevel(int level) { switch (level) { case com.jcraft.jsch.Logger.DEBUG: return Level.DEBUG; diff --git a/src/main/java/com/jcraft/jsch/PBKDF.java b/src/main/java/com/jcraft/jsch/PBKDF.java index 44619a37..4151303e 100644 --- a/src/main/java/com/jcraft/jsch/PBKDF.java +++ b/src/main/java/com/jcraft/jsch/PBKDF.java @@ -29,6 +29,10 @@ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING package com.jcraft.jsch; +/** + * Use PBKDF2 instead. + */ +@Deprecated public interface PBKDF { byte[] getKey(byte[] pass, byte[] salt, int iteration, int size); } diff --git a/src/main/java/com/jcraft/jsch/PBKDF2.java b/src/main/java/com/jcraft/jsch/PBKDF2.java new file mode 100644 index 00000000..b578c76c --- /dev/null +++ b/src/main/java/com/jcraft/jsch/PBKDF2.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface PBKDF2 extends KDF{ + void init(byte[] salt, int iteration) throws Exception; +} diff --git a/src/main/java/com/jcraft/jsch/SCrypt.java b/src/main/java/com/jcraft/jsch/SCrypt.java new file mode 100644 index 00000000..66b52ebd --- /dev/null +++ b/src/main/java/com/jcraft/jsch/SCrypt.java @@ -0,0 +1,34 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch; + +public interface SCrypt extends KDF{ + void init(byte[] salt, int cost, int blocksize, int parallel) throws Exception; +} diff --git a/src/main/java/com/jcraft/jsch/Session.java b/src/main/java/com/jcraft/jsch/Session.java index a88b6c73..7c4393df 100644 --- a/src/main/java/com/jcraft/jsch/Session.java +++ b/src/main/java/com/jcraft/jsch/Session.java @@ -135,10 +135,6 @@ public class Session{ SocketFactory socket_factory=null; - static final int buffer_margin = 32 + // maximum padding length - 64 + // maximum mac length - 32; // margin for deflater; deflater may inflate data - private Hashtable config=null; private Proxy proxy=null; @@ -562,7 +558,7 @@ public void connect(int connectTimeout) throws JSchException{ if(isConnected){ String message = e.toString(); packet.reset(); - buf.checkFreeSize(1+4*3+message.length()+2+buffer_margin); + buf.checkFreeSize(1+4*3+message.length()+2+getBufferMargin()); buf.putByte((byte)SSH_MSG_DISCONNECT); buf.putInt(3); buf.putString(Util.str2byte(message)); @@ -3117,6 +3113,9 @@ private void applyConfig() throws JSchException { checkConfig(config, "kex"); checkConfig(config, "server_host_key"); checkConfig(config, "prefer_known_host_key_types"); + checkConfig(config, "enable_pubkey_auth_query"); + checkConfig(config, "try_additional_pubkey_algorithms"); + checkConfig(config, "enable_auth_none"); checkConfig(config, "cipher.c2s"); checkConfig(config, "cipher.s2c"); @@ -3287,4 +3286,27 @@ public Logger getLogger() { public void setLogger(Logger logger) { this.logger = logger; } + + int getBufferMargin() { + int buffer_margin = 32 + // maximum padding length + 32; // margin for deflater; deflater may inflate data + + Cipher _c2scipher = c2scipher; + MAC _c2smac = c2smac; + + // maximum mac length + int mac_length = 20; + if (_c2scipher != null && (_c2scipher.isChaCha20() || _c2scipher.isAEAD())) { + if (_c2scipher.getTagSize() > mac_length) { + mac_length = _c2scipher.getTagSize(); + } + } else if (_c2smac != null) { + if (_c2smac.getBlockSize() > mac_length) { + mac_length = _c2smac.getBlockSize(); + } + } + buffer_margin += mac_length; + + return buffer_margin; + } } diff --git a/src/main/java/com/jcraft/jsch/Slf4jLogger.java b/src/main/java/com/jcraft/jsch/Slf4jLogger.java index ab05714a..c24c1563 100644 --- a/src/main/java/com/jcraft/jsch/Slf4jLogger.java +++ b/src/main/java/com/jcraft/jsch/Slf4jLogger.java @@ -8,19 +8,9 @@ */ public class Slf4jLogger implements com.jcraft.jsch.Logger { - private static final Logger stlogger = LoggerFactory.getLogger(JSch.class); - private final Logger logger; + private static final Logger logger = LoggerFactory.getLogger(JSch.class); - /** - * Creates a new instance of Slf4jLogger - */ - public Slf4jLogger() { - this(stlogger); - } - - Slf4jLogger(Logger logger) { - this.logger = logger; - } + public Slf4jLogger() {} @Override public boolean isEnabled(int level) { diff --git a/src/main/java/com/jcraft/jsch/UserAuthNone.java b/src/main/java/com/jcraft/jsch/UserAuthNone.java index 8eef355b..6d9f648c 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthNone.java +++ b/src/main/java/com/jcraft/jsch/UserAuthNone.java @@ -66,6 +66,9 @@ public boolean start(Session session) throws Exception{ if(!result) return false; + if(!session.getConfig("enable_auth_none").equals("yes")) + return false; + byte[] _username=null; _username=Util.str2byte(username); diff --git a/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java index d18cf731..2884ae80 100644 --- a/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java +++ b/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java @@ -125,6 +125,9 @@ private boolean _start(Session session, List identities, List return false; } + boolean use_pk_auth_query=session.getConfig("enable_pubkey_auth_query").equals("yes"); + boolean try_other_pkmethods=session.getConfig("try_additional_pubkey_algorithms").equals("yes"); + List rsamethods=new ArrayList<>(); List nonrsamethods=new ArrayList<>(); for(String pkmethod : pkmethods){ @@ -156,32 +159,126 @@ private boolean _start(Session session, List identities, List String _ipkmethod=identity.getAlgName(); List ipkmethods=null; if(_ipkmethod.equals("ssh-rsa")){ - ipkmethods=rsamethods; + ipkmethods=new ArrayList<>(rsamethods); } else if(nonrsamethods.contains(_ipkmethod)){ - ipkmethods=Collections.singletonList(_ipkmethod); + ipkmethods=new ArrayList<>(1); + ipkmethods.add(_ipkmethod); } - if(ipkmethods==null) { + if(ipkmethods==null || ipkmethods.isEmpty()) { if(session.getLogger().isEnabled(Logger.DEBUG)){ session.getLogger().log(Logger.DEBUG, _ipkmethod+" cannot be used as public key type for identity "+identity.getName()); } - continue; + continue iloop; } - byte[] pubkeyblob=identity.getPublicKeyBlob(); - List pkmethodsuccesses=null; + ipkmethodloop: + while(!ipkmethods.isEmpty() && session.auth_failures < session.max_auth_tries){ + byte[] pubkeyblob=identity.getPublicKeyBlob(); + List pkmethodsuccesses=null; + + if(pubkeyblob!=null && use_pk_auth_query){ + command=SSH_MSG_USERAUTH_FAILURE; + Iterator it=ipkmethods.iterator(); + loop3: + while(it.hasNext()){ + String ipkmethod=it.next(); + it.remove(); + if(not_available_pks.contains(ipkmethod) && !(identity instanceof AgentIdentity)){ + if(session.getLogger().isEnabled(Logger.DEBUG)){ + session.getLogger().log(Logger.DEBUG, + ipkmethod+" not available for identity "+identity.getName()); + } + continue loop3; + } + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen FALSE + // string public key algorithm name + // string public key blob + packet.reset(); + buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte)0); + buf.putString(Util.str2byte(ipkmethod)); + buf.putString(pubkeyblob); + session.write(packet); + + loop1: + while(true){ + buf=session.read(buf); + command=buf.getCommand()&0xff; + + if(command==SSH_MSG_USERAUTH_PK_OK){ + if(session.getLogger().isEnabled(Logger.DEBUG)){ + session.getLogger().log(Logger.DEBUG, + ipkmethod + " preauth success"); + } + pkmethodsuccesses=new ArrayList<>(1); + pkmethodsuccesses.add(ipkmethod); + break loop3; + } + else if(command==SSH_MSG_USERAUTH_FAILURE){ + if(session.getLogger().isEnabled(Logger.DEBUG)){ + session.getLogger().log(Logger.DEBUG, + ipkmethod + " preauth failure"); + } + continue loop3; + } + else if(command==SSH_MSG_USERAUTH_BANNER){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] _message=buf.getString(); + byte[] lang=buf.getString(); + String message=Util.byte2str(_message); + if(userinfo!=null){ + userinfo.showMessage(message); + } + continue loop1; + } + else{ + //System.err.println("USERAUTH fail ("+command+")"); + //throw new JSchException("USERAUTH fail ("+command+")"); + if(session.getLogger().isEnabled(Logger.DEBUG)){ + session.getLogger().log(Logger.DEBUG, + ipkmethod + " preauth failure command (" + command + ")"); + } + continue loop3; + } + } + } + + if(command!=SSH_MSG_USERAUTH_PK_OK){ + continue iloop; + } + } + + if(identity.isEncrypted()) continue iloop; + if(pubkeyblob==null) pubkeyblob=identity.getPublicKeyBlob(); + + //System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob); - if(pubkeyblob!=null){ - command=SSH_MSG_USERAUTH_FAILURE; - loop3: - for(String ipkmethod : ipkmethods){ - if(not_available_pks.contains(ipkmethod) && !(identity instanceof AgentIdentity)){ + if(pubkeyblob==null) continue iloop; + if(pkmethodsuccesses==null) pkmethodsuccesses=ipkmethods; + if(pkmethodsuccesses.isEmpty()) continue iloop; + + Iterator it=pkmethodsuccesses.iterator(); + loop4: + while(it.hasNext() && session.auth_failures < session.max_auth_tries){ + String pkmethodsuccess=it.next(); + it.remove(); + if(not_available_pks.contains(pkmethodsuccess) && !(identity instanceof AgentIdentity)){ if(session.getLogger().isEnabled(Logger.DEBUG)){ session.getLogger().log(Logger.DEBUG, - ipkmethod+" not available for identity "+identity.getName()); + pkmethodsuccess+" not available for identity "+identity.getName()); } - continue loop3; + continue loop4; } // send @@ -189,38 +286,54 @@ else if(nonrsamethods.contains(_ipkmethod)){ // string user name // string service name ("ssh-connection") // string "publickey" - // boolen FALSE + // boolen TRUE // string public key algorithm name // string public key blob + // string signature packet.reset(); buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); buf.putString(_username); buf.putString(Util.str2byte("ssh-connection")); buf.putString(Util.str2byte("publickey")); - buf.putByte((byte)0); - buf.putString(Util.str2byte(ipkmethod)); + buf.putByte((byte)1); + buf.putString(Util.str2byte(pkmethodsuccess)); buf.putString(pubkeyblob); + + //byte[] tmp=new byte[buf.index-5]; + //System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length); + //buf.putString(signature); + + byte[] sid=session.getSessionId(); + int sidlen=sid.length; + byte[] tmp=new byte[4+sidlen+buf.index-5]; + tmp[0]=(byte)(sidlen>>>24); + tmp[1]=(byte)(sidlen>>>16); + tmp[2]=(byte)(sidlen>>>8); + tmp[3]=(byte)(sidlen); + System.arraycopy(sid, 0, tmp, 4, sidlen); + System.arraycopy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5); + byte[] signature=identity.getSignature(tmp, pkmethodsuccess); + if(signature==null){ // for example, too long key length. + if(session.getLogger().isEnabled(Logger.DEBUG)){ + session.getLogger().log(Logger.DEBUG, + pkmethodsuccess + " signature failure"); + } + continue loop4; + } + buf.putString(signature); session.write(packet); - loop1: + loop2: while(true){ buf=session.read(buf); command=buf.getCommand()&0xff; - if(command==SSH_MSG_USERAUTH_PK_OK){ + if(command==SSH_MSG_USERAUTH_SUCCESS){ if(session.getLogger().isEnabled(Logger.DEBUG)){ session.getLogger().log(Logger.DEBUG, - ipkmethod + " preauth success"); + pkmethodsuccess + " auth success"); } - pkmethodsuccesses=Collections.singletonList(ipkmethod); - break loop3; - } - else if(command==SSH_MSG_USERAUTH_FAILURE){ - if(session.getLogger().isEnabled(Logger.DEBUG)){ - session.getLogger().log(Logger.DEBUG, - ipkmethod + " preauth failure"); - } - continue loop3; + return true; } else if(command==SSH_MSG_USERAUTH_BANNER){ buf.getInt(); buf.getByte(); buf.getByte(); @@ -230,132 +343,41 @@ else if(command==SSH_MSG_USERAUTH_BANNER){ if(userinfo!=null){ userinfo.showMessage(message); } - continue loop1; + continue loop2; } - else{ - //System.err.println("USERAUTH fail ("+command+")"); - //throw new JSchException("USERAUTH fail ("+command+")"); + else if(command==SSH_MSG_USERAUTH_FAILURE){ + buf.getInt(); buf.getByte(); buf.getByte(); + byte[] foo=buf.getString(); + int partial_success=buf.getByte(); + //System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if(partial_success!=0){ + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; if(session.getLogger().isEnabled(Logger.DEBUG)){ session.getLogger().log(Logger.DEBUG, - ipkmethod + " preauth failure command (" + command + ")"); + pkmethodsuccess + " auth failure"); } - continue loop3; - } - } - } - - if(command!=SSH_MSG_USERAUTH_PK_OK){ - continue iloop; - } - } - - - if(identity.isEncrypted()) continue; - if(pubkeyblob==null) pubkeyblob=identity.getPublicKeyBlob(); - -//System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob); - - if(pubkeyblob==null) continue; - if(pkmethodsuccesses==null) pkmethodsuccesses=ipkmethods; - - loop4: - for(String pkmethodsuccess : pkmethodsuccesses){ - if(not_available_pks.contains(pkmethodsuccess) && !(identity instanceof AgentIdentity)){ - if(session.getLogger().isEnabled(Logger.DEBUG)){ - session.getLogger().log(Logger.DEBUG, - pkmethodsuccess+" not available for identity "+identity.getName()); - } - continue loop4; - } - - // send - // byte SSH_MSG_USERAUTH_REQUEST(50) - // string user name - // string service name ("ssh-connection") - // string "publickey" - // boolen TRUE - // string public key algorithm name - // string public key blob - // string signature - packet.reset(); - buf.putByte((byte)SSH_MSG_USERAUTH_REQUEST); - buf.putString(_username); - buf.putString(Util.str2byte("ssh-connection")); - buf.putString(Util.str2byte("publickey")); - buf.putByte((byte)1); - buf.putString(Util.str2byte(pkmethodsuccess)); - buf.putString(pubkeyblob); - -// byte[] tmp=new byte[buf.index-5]; -// System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length); -// buf.putString(signature); - - byte[] sid=session.getSessionId(); - int sidlen=sid.length; - byte[] tmp=new byte[4+sidlen+buf.index-5]; - tmp[0]=(byte)(sidlen>>>24); - tmp[1]=(byte)(sidlen>>>16); - tmp[2]=(byte)(sidlen>>>8); - tmp[3]=(byte)(sidlen); - System.arraycopy(sid, 0, tmp, 4, sidlen); - System.arraycopy(buf.buffer, 5, tmp, 4+sidlen, buf.index-5); - byte[] signature=identity.getSignature(tmp, pkmethodsuccess); - if(signature==null){ // for example, too long key length. - if(session.getLogger().isEnabled(Logger.DEBUG)){ - session.getLogger().log(Logger.DEBUG, - pkmethodsuccess + " signature failure"); - } - continue loop4; - } - buf.putString(signature); - session.write(packet); - - loop2: - while(true){ - buf=session.read(buf); - command=buf.getCommand()&0xff; - - if(command==SSH_MSG_USERAUTH_SUCCESS){ - if(session.getLogger().isEnabled(Logger.DEBUG)){ - session.getLogger().log(Logger.DEBUG, - pkmethodsuccess + " auth success"); - } - return true; - } - else if(command==SSH_MSG_USERAUTH_BANNER){ - buf.getInt(); buf.getByte(); buf.getByte(); - byte[] _message=buf.getString(); - byte[] lang=buf.getString(); - String message=Util.byte2str(_message); - if(userinfo!=null){ - userinfo.showMessage(message); - } - continue loop2; - } - else if(command==SSH_MSG_USERAUTH_FAILURE){ - buf.getInt(); buf.getByte(); buf.getByte(); - byte[] foo=buf.getString(); - int partial_success=buf.getByte(); - //System.err.println(new String(foo)+ - // " partial_success:"+(partial_success!=0)); - if(partial_success!=0){ - throw new JSchPartialAuthException(Util.byte2str(foo)); + if(session.auth_failures >= session.max_auth_tries){ + return false; + } + else if(try_other_pkmethods){ + break loop2; + } + else{ + continue iloop; + } } - session.auth_failures++; + //System.err.println("USERAUTH fail ("+command+")"); + //throw new JSchException("USERAUTH fail ("+command+")"); if(session.getLogger().isEnabled(Logger.DEBUG)){ session.getLogger().log(Logger.DEBUG, - pkmethodsuccess + " auth failure"); + pkmethodsuccess + " auth failure command (" + command +")"); } break loop2; } - //System.err.println("USERAUTH fail ("+command+")"); - //throw new JSchException("USERAUTH fail ("+command+")"); - if(session.getLogger().isEnabled(Logger.DEBUG)){ - session.getLogger().log(Logger.DEBUG, - pkmethodsuccess + " auth failure command (" + command +")"); - } - break loop2; } } } diff --git a/src/main/java/com/jcraft/jsch/bc/Argon2.java b/src/main/java/com/jcraft/jsch/bc/Argon2.java new file mode 100644 index 00000000..20372762 --- /dev/null +++ b/src/main/java/com/jcraft/jsch/bc/Argon2.java @@ -0,0 +1,90 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.bc; + +import com.jcraft.jsch.JSchException; +import org.bouncycastle.crypto.generators.Argon2BytesGenerator; +import org.bouncycastle.crypto.params.Argon2Parameters; + +public class Argon2 implements com.jcraft.jsch.Argon2{ + private Argon2BytesGenerator generator; + + @Override + public void init(byte[] salt, int iteration, int type, byte[] additional, byte[] secret, int memory, int parallelism, int version) throws Exception{ + switch(type){ + case com.jcraft.jsch.Argon2.ARGON2D: + type=Argon2Parameters.ARGON2_d; + break; + case com.jcraft.jsch.Argon2.ARGON2I: + type=Argon2Parameters.ARGON2_i; + break; + case com.jcraft.jsch.Argon2.ARGON2ID: + type=Argon2Parameters.ARGON2_id; + break; + default: + throw new JSchException("Invalid argon2 type."); + } + + switch(version){ + case com.jcraft.jsch.Argon2.V10: + version=Argon2Parameters.ARGON2_VERSION_10; + break; + case com.jcraft.jsch.Argon2.V13: + version=Argon2Parameters.ARGON2_VERSION_13; + break; + default: + throw new JSchException("Invalid argon2 version."); + } + + try{ + Argon2Parameters params=new Argon2Parameters.Builder(type) + .withSalt(salt) + .withAdditional(additional) + .withSecret(secret) + .withIterations(iteration) + .withMemoryAsKB(memory) + .withParallelism(parallelism) + .withVersion(version) + .build(); + generator=new Argon2BytesGenerator(); + generator.init(params); + } + catch(NoClassDefFoundError e){ + throw new JSchException("argon2 unavailable", e); + } + } + + @Override + public byte[] getKey(byte[] pass, int size){ + byte[] key=new byte[size]; + generator.generateBytes(pass, key); + return key; + } +} diff --git a/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java b/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java index ec8eecba..d6396850 100644 --- a/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java +++ b/src/main/java/com/jcraft/jsch/bc/KeyPairGenEdDSA.java @@ -58,7 +58,26 @@ public void init(String name, int keylen) throws Exception{ } } @Override - public byte[] getPrv(){return pub;} + public byte[] getPrv(){return prv;} @Override - public byte[] getPub(){return prv;} + public byte[] getPub(){return pub;} + @Override + public void init(String name, byte[] prv) throws Exception{ + if(!name.equals("Ed25519") && !name.equals("Ed448")){ + throw new NoSuchAlgorithmException("invalid curve " + name); + } + this.name = name; + + if(name.equals("Ed25519")){ + Ed25519PrivateKeyParameters privateKey = new Ed25519PrivateKeyParameters(prv); + pub = privateKey.generatePublicKey().getEncoded(); + this.prv = privateKey.getEncoded(); + } + else{ + Ed448PrivateKeyParameters privateKey = new Ed448PrivateKeyParameters(prv); + pub = privateKey.generatePublicKey().getEncoded(); + this.prv = privateKey.getEncoded(); + } + this.keylen = this.prv.length; + } } diff --git a/src/main/java/com/jcraft/jsch/bc/SCrypt.java b/src/main/java/com/jcraft/jsch/bc/SCrypt.java new file mode 100644 index 00000000..298ab9ba --- /dev/null +++ b/src/main/java/com/jcraft/jsch/bc/SCrypt.java @@ -0,0 +1,59 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.bc; + +import com.jcraft.jsch.JSchException; + +public class SCrypt implements com.jcraft.jsch.SCrypt{ + private Class ignore; + private byte[] salt; + private int cost; + private int blocksize; + private int parallel; + + @Override + public void init(byte[] salt, int cost, int blocksize, int parallel) throws Exception{ + try{ + ignore=org.bouncycastle.crypto.generators.SCrypt.class; + this.salt=salt; + this.cost=cost; + this.blocksize=blocksize; + this.parallel=parallel; + } + catch(NoClassDefFoundError e){ + throw new JSchException("scrypt unavailable", e); + } + } + + @Override + public byte[] getKey(byte[] pass, int size){ + return org.bouncycastle.crypto.generators.SCrypt.generate(pass, salt, cost, blocksize, parallel, size); + } +} diff --git a/src/main/java/com/jcraft/jsch/jbcrypt/JBCrypt.java b/src/main/java/com/jcraft/jsch/jbcrypt/JBCrypt.java new file mode 100644 index 00000000..d443edae --- /dev/null +++ b/src/main/java/com/jcraft/jsch/jbcrypt/JBCrypt.java @@ -0,0 +1,50 @@ +/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ +/* +Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.jcraft.jsch.jbcrypt; + +public class JBCrypt implements com.jcraft.jsch.BCrypt{ + private BCrypt bcrypt; + private byte[] salt; + private int iteration; + + @Override + public void init(byte[] salt, int iteration) throws Exception{ + bcrypt=new BCrypt(); + this.salt=salt; + this.iteration=iteration; + } + + @Override + public byte[] getKey(byte[] pass, int size){ + byte[] key=new byte[size]; + bcrypt.pbkdf(pass, salt, iteration, key); + return key; + } +} diff --git a/src/main/java/com/jcraft/jsch/jce/ECDHN.java b/src/main/java/com/jcraft/jsch/jce/ECDHN.java index 37a6d277..5682a63f 100644 --- a/src/main/java/com/jcraft/jsch/jce/ECDHN.java +++ b/src/main/java/com/jcraft/jsch/jce/ECDHN.java @@ -134,17 +134,14 @@ private byte[] insert0(byte[] buf){ if ((buf[0] & 0x80) == 0) return buf; byte[] tmp = new byte[buf.length+1]; System.arraycopy(buf, 0, tmp, 1, buf.length); - bzero(buf); + Util.bzero(buf); return tmp; } private byte[] chop0(byte[] buf){ if(buf[0]!=0) return buf; byte[] tmp = new byte[buf.length-1]; System.arraycopy(buf, 1, tmp, 0, tmp.length); - bzero(buf); + Util.bzero(buf); return tmp; } - private void bzero(byte[] buf){ - for(int i = 0; i sshd = + new GenericContainer<>( + new ImageFromDockerfile() + .withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") + .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") + .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath( + "ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath( + "ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath( + "ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath( + "ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh") + .withBuildArg("MAX_PKTSIZE", Integer.toString(maxPktSize()))) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + protected abstract int maxPktSize(); + + protected void doTestSftp(String cipher, String mac, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("mac.s2c", mac); + session.setConfig("mac.c2s", mac); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doSftp(session, true); + } + + protected void doTestScp(String cipher, String mac, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("mac.s2c", mac); + session.setConfig("mac.c2s", mac); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doScp(session, true); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void doScp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelExec scp; + + scp = (ChannelExec) session.openChannel("exec"); + try (InputStream is = scp.getInputStream()) { + try (OutputStream os = scp.getOutputStream()) { + scp.setCommand("scp -t /root/test"); + scp.connect(timeout); + checkAck(is); + String cmd = "C0644 102400 test\n"; + os.write(cmd.getBytes(UTF_8)); + os.flush(); + checkAck(is); + Files.copy(in, os); + os.flush(); + sendAck(os); + checkAck(is); + } + } + while (scp.isConnected()) { + Thread.sleep(100L); + } + + scp = (ChannelExec) session.openChannel("exec"); + try (OutputStream os = scp.getOutputStream()) { + try (InputStream is = scp.getInputStream()) { + scp.setCommand("scp -f /root/test"); + scp.connect(timeout); + sendAck(os); + int c = checkAck(is); + if (c == 'C') { + byte[] buf = new byte[17]; + is.read(buf, 0, 17); + sendAck(os); + Files.copy(ByteStreams.limit(is, 100L * 1024L), out); + checkAck(is); + sendAck(os); + } + } + } + while (scp.isConnected()) { + Thread.sleep(100L); + } + + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private static int checkAck(InputStream is) throws IOException { + int b = is.read(); + if (b == 0) { + return b; + } else if (b == -1) { + throw new IOException("no response"); + } + + StringBuilder sb = new StringBuilder(); + if (b == 1 || b == 2) { + int c = is.read(); + while (c > 0 && c != '\n') { + sb.append((char) c); + c = is.read(); + } + } + + switch (b) { + case 1: + throw new IOException("error: " + sb); + case 2: + throw new IOException("fatal error: " + sb); + default: + return b; + } + } + + private static void sendAck(OutputStream os) throws IOException { + byte[] ack = new byte[1]; + ack[0] = 0; + os.write(ack); + os.flush(); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/Algorithms2IT.java b/src/test/java/com/jcraft/jsch/Algorithms2IT.java index b04b85b0..47559265 100644 --- a/src/test/java/com/jcraft/jsch/Algorithms2IT.java +++ b/src/test/java/com/jcraft/jsch/Algorithms2IT.java @@ -1,25 +1,25 @@ package com.jcraft.jsch; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.condition.JRE.JAVA_11; import static org.junit.jupiter.api.condition.JRE.JAVA_15; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,7 +28,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -41,9 +40,8 @@ public class Algorithms2IT { // Python can be slow for DH group 18 private static final int timeout = 10000; private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); - private static final ListAppender jschAppender = getListAppender(JSch.class); - private static final ListAppender sshdAppender = - getListAppender(AlgorithmsIT.class); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(Algorithms2IT.class); @TempDir public Path tmpDir; private Path in; @@ -86,7 +84,7 @@ public static void beforeAll() { @BeforeEach public void beforeEach() throws IOException { if (sshdLogConsumer == null) { - sshdLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(Algorithms2IT.class)); + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); sshd.followOutput(sshdLogConsumer); } @@ -102,18 +100,15 @@ public void beforeEach() throws IOException { } hash = sha256sum.digestAsHex(in); - jschAppender.list.clear(); - sshdAppender.list.clear(); - jschAppender.start(); - sshdAppender.start(); + jschLogger.clearAll(); + sshdLogger.clearAll(); } - @AfterEach - public void afterEach() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.clear(); - sshdAppender.list.clear(); + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); } @Test @@ -199,7 +194,8 @@ public void testDHGEXSizes(String kex, String size) throws Exception { doSftp(session, true); String expectedKex = String.format("kex: algorithm: %s.*", kex); - String expectedSizes = String.format("SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); + String expectedSizes = + String.format("SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); checkLogs(expectedKex); checkLogs(expectedSizes); } @@ -265,11 +261,7 @@ public void testRSA(String keyType) throws Exception { } @ParameterizedTest - @CsvSource( - value = { - "seed-cbc@ssh.com,none", - "seed-cbc@ssh.com,zlib@openssh.com" - }) + @CsvSource(value = {"seed-cbc@ssh.com,none", "seed-cbc@ssh.com,zlib@openssh.com"}) public void testCiphers(String cipher, String compression) throws Exception { JSch ssh = createRSAIdentity(); Session session = createSession(ssh); @@ -370,7 +362,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { @@ -390,8 +382,6 @@ private void doSftp(Session session, boolean debugException) throws Exception { sftp.get("/root/test", out.toString()); sftp.disconnect(); session.disconnect(); - jschAppender.stop(); - sshdAppender.stop(); } catch (Exception e) { if (debugException) { printInfo(); @@ -403,20 +393,22 @@ private void doSftp(Session session, boolean debugException) throws Exception { assertEquals(hash, sha256sum.digestAsHex(out)); } - private static void printInfo() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshdAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); System.out.println(""); System.out.println(""); System.out.println(""); } - private static void checkLogs(String expected) { + private void checkLogs(String expected) { Optional actualJsch = - jschAppender.list.stream() - .map(ILoggingEvent::getFormattedMessage) + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) .filter(msg -> msg.matches(expected)) .findFirst(); try { @@ -430,11 +422,4 @@ private static void checkLogs(String expected) { private String getResourceFile(String fileName) { return ResourceUtil.getResourceFile(getClass(), fileName); } - - private static ListAppender getListAppender(Class clazz) { - Logger logger = (Logger) LoggerFactory.getLogger(clazz); - ListAppender listAppender = new ListAppender2<>(); - logger.addAppender(listAppender); - return listAppender; - } } diff --git a/src/test/java/com/jcraft/jsch/Algorithms3IT.java b/src/test/java/com/jcraft/jsch/Algorithms3IT.java index a2560e88..314c4d1d 100644 --- a/src/test/java/com/jcraft/jsch/Algorithms3IT.java +++ b/src/test/java/com/jcraft/jsch/Algorithms3IT.java @@ -1,29 +1,28 @@ package com.jcraft.jsch; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -35,9 +34,8 @@ public class Algorithms3IT { private static final int timeout = 2000; private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); - private static final ListAppender jschAppender = getListAppender(JSch.class); - private static final ListAppender sshdAppender = - getListAppender(AlgorithmsIT.class); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(Algorithms3IT.class); @TempDir public Path tmpDir; private Path in; @@ -62,7 +60,7 @@ public static void beforeAll() { @BeforeEach public void beforeEach() throws IOException { if (sshdLogConsumer == null) { - sshdLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(Algorithms3IT.class)); + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); sshd.followOutput(sshdLogConsumer); } @@ -78,18 +76,15 @@ public void beforeEach() throws IOException { } hash = sha256sum.digestAsHex(in); - jschAppender.list.clear(); - sshdAppender.list.clear(); - jschAppender.start(); - sshdAppender.start(); + jschLogger.clearAll(); + sshdLogger.clearAll(); } - @AfterEach - public void afterEach() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.clear(); - sshdAppender.list.clear(); + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); } @ParameterizedTest @@ -121,7 +116,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { @@ -146,8 +141,6 @@ private void doSftp(Session session, boolean debugException) throws Exception { sftp.get("/root/test", out.toString()); sftp.disconnect(); session.disconnect(); - jschAppender.stop(); - sshdAppender.stop(); } catch (Exception e) { if (debugException) { printInfo(); @@ -159,20 +152,22 @@ private void doSftp(Session session, boolean debugException) throws Exception { assertEquals(hash, sha256sum.digestAsHex(out)); } - private static void printInfo() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshdAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); System.out.println(""); System.out.println(""); System.out.println(""); } - private static void checkLogs(String expected) { + private void checkLogs(String expected) { Optional actualJsch = - jschAppender.list.stream() - .map(ILoggingEvent::getFormattedMessage) + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) .filter(msg -> msg.matches(expected)) .findFirst(); try { @@ -186,11 +181,4 @@ private static void checkLogs(String expected) { private String getResourceFile(String fileName) { return ResourceUtil.getResourceFile(getClass(), fileName); } - - private static ListAppender getListAppender(Class clazz) { - Logger logger = (Logger) LoggerFactory.getLogger(clazz); - ListAppender listAppender = new ListAppender2<>(); - logger.addAppender(listAppender); - return listAppender; - } } diff --git a/src/test/java/com/jcraft/jsch/AlgorithmsIT.java b/src/test/java/com/jcraft/jsch/AlgorithmsIT.java index 09292f0c..bcd9a350 100644 --- a/src/test/java/com/jcraft/jsch/AlgorithmsIT.java +++ b/src/test/java/com/jcraft/jsch/AlgorithmsIT.java @@ -2,26 +2,26 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.toList; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.condition.JRE.JAVA_11; import static org.junit.jupiter.api.condition.JRE.JAVA_15; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,7 +30,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -42,9 +41,8 @@ public class AlgorithmsIT { private static final int timeout = 2000; private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); - private static final ListAppender jschAppender = getListAppender(JSch.class); - private static final ListAppender sshdAppender = - getListAppender(AlgorithmsIT.class); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(AlgorithmsIT.class); @TempDir public Path tmpDir; private Path in; @@ -85,7 +83,7 @@ public static void beforeAll() { @BeforeEach public void beforeEach() throws IOException { if (sshdLogConsumer == null) { - sshdLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(AlgorithmsIT.class)); + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); sshd.followOutput(sshdLogConsumer); } @@ -101,18 +99,15 @@ public void beforeEach() throws IOException { } hash = sha256sum.digestAsHex(in); - jschAppender.list.clear(); - sshdAppender.list.clear(); - jschAppender.start(); - sshdAppender.start(); + jschLogger.clearAll(); + sshdLogger.clearAll(); } - @AfterEach - public void afterEach() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.clear(); - sshdAppender.list.clear(); + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); } @ParameterizedTest @@ -192,7 +187,8 @@ public void testDHGEXSizes(String kex, String size) throws Exception { doSftp(session, true); String expectedKex = String.format("kex: algorithm: %s.*", kex); - String expectedSizes = String.format("SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); + String expectedSizes = + String.format("SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); checkLogs(expectedKex); checkLogs(expectedSizes); } @@ -534,7 +530,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { @@ -554,8 +550,6 @@ private void doSftp(Session session, boolean debugException) throws Exception { sftp.get("/root/test", out.toString()); sftp.disconnect(); session.disconnect(); - jschAppender.stop(); - sshdAppender.stop(); } catch (Exception e) { if (debugException) { printInfo(); @@ -567,26 +561,29 @@ private void doSftp(Session session, boolean debugException) throws Exception { assertEquals(hash, sha256sum.digestAsHex(out)); } - private static void printInfo() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshdAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); System.out.println(""); System.out.println(""); System.out.println(""); } - private static void checkLogs(String expected) { + private void checkLogs(String expected) { Optional actualJsch = - jschAppender.list.stream() - .map(ILoggingEvent::getFormattedMessage) + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) .filter(msg -> msg.matches(expected)) .findFirst(); - // Skip OpenSSH log checks, as log output from Docker falls behind and these assertions frequently run before they are output + // Skip OpenSSH log checks, as log output from Docker falls behind and these assertions + // frequently run before they are output // Optional actualSshd = - // sshdAppender.list.stream() - // .map(ILoggingEvent::getFormattedMessage) + // sshdLogger.getAllLoggingEvents().stream() + // .map(LoggingEvent::getFormattedMessage) // .filter(msg -> msg.matches("STDERR: debug1: " + expected)) // .findFirst(); try { @@ -601,11 +598,4 @@ private static void checkLogs(String expected) { private String getResourceFile(String fileName) { return ResourceUtil.getResourceFile(getClass(), fileName); } - - private static ListAppender getListAppender(Class clazz) { - Logger logger = (Logger) LoggerFactory.getLogger(clazz); - ListAppender listAppender = new ListAppender2<>(); - logger.addAppender(listAppender); - return listAppender; - } } diff --git a/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java b/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java index ceb238e1..c507b0b8 100644 --- a/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java +++ b/src/test/java/com/jcraft/jsch/JSchAlgoNegoFailExceptionIT.java @@ -1,12 +1,12 @@ package com.jcraft.jsch; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -96,7 +96,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { diff --git a/src/test/java/com/jcraft/jsch/JulLoggerTest.java b/src/test/java/com/jcraft/jsch/JulLoggerTest.java index 88e21a70..735e2161 100644 --- a/src/test/java/com/jcraft/jsch/JulLoggerTest.java +++ b/src/test/java/com/jcraft/jsch/JulLoggerTest.java @@ -3,31 +3,54 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class JulLoggerTest { - private LinkedList messages; - private Exception testException = new Exception("dummy exception"); +public class JulLoggerTest { + + private static final Logger logger = Logger.getLogger(JSch.class.getName()); + private static final ListHandler handler = new ListHandler(); + + private final Exception testException = new Exception("dummy exception"); + + @BeforeAll + public static void beforeAll() { + LogManager.getLogManager().reset(); + Arrays.stream(logger.getHandlers()).forEach(logger::removeHandler); + logger.addHandler(handler); + logger.setLevel(Level.ALL); + logger.setUseParentHandlers(false); + Logger.getLogger("").setLevel(Level.OFF); + } + @BeforeEach - void resetLogger() { - messages = new LinkedList<>(); - Logger logger = Logger.getLogger(getClass().getName()); - Arrays.stream(logger.getHandlers()) - .forEach(logger::removeHandler); - + public void beforeEach() { + handler.clear(); + } + + @AfterAll + public static void afterAll() { + LogManager.getLogManager().reset(); } + @Test - void testGetLevel() { + public void testGetLevel() { assertEquals(Level.FINER, JulLogger.getLevel(-1)); - + assertEquals(Level.FINE, JulLogger.getLevel(com.jcraft.jsch.Logger.DEBUG)); assertEquals(Level.SEVERE, JulLogger.getLevel(com.jcraft.jsch.Logger.ERROR)); assertEquals(Level.SEVERE, JulLogger.getLevel(com.jcraft.jsch.Logger.FATAL)); @@ -36,21 +59,19 @@ void testGetLevel() { assertEquals(Level.FINER, JulLogger.getLevel(Integer.MAX_VALUE)); } - + @Test - void testIsEnabled() { - Logger logger = LogManager.getLogManager().getLogger(getClass().getName()); - - JulLogger jl = new JulLogger(logger); + public void testIsEnabled() { + JulLogger jl = new JulLogger(); + logger.setLevel(Level.FINEST); - assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertTrue(jl.isEnabled(-1), "trace should be enabled"); - + logger.setLevel(Level.FINE); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); @@ -58,7 +79,7 @@ void testIsEnabled() { assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(jl.isEnabled(-1), "trace should not be enabled"); - + logger.setLevel(Level.SEVERE); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); @@ -66,7 +87,7 @@ void testIsEnabled() { assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); assertFalse(jl.isEnabled(-1), "trace should not be enabled"); - + logger.setLevel(Level.INFO); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); @@ -74,7 +95,7 @@ void testIsEnabled() { assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(jl.isEnabled(-1), "trace should not be enabled"); - + logger.setLevel(Level.OFF); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should not be enabled"); @@ -82,7 +103,7 @@ void testIsEnabled() { assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); assertFalse(jl.isEnabled(-1), "trace should not be enabled"); - + logger.setLevel(Level.FINER); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); @@ -90,7 +111,7 @@ void testIsEnabled() { assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertTrue(jl.isEnabled(-1), "trace should be enabled"); - + logger.setLevel(Level.WARNING); assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); @@ -98,65 +119,85 @@ void testIsEnabled() { assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(jl.isEnabled(-1), "trace should not be enabled"); - } - + @Test - void testLogging() { - Logger logger = Logger.getLogger(getClass().getName()); - TestHandler handler = new TestHandler(messages); - - logger.addHandler(handler); + public void testLogging() { + JulLogger jl = new JulLogger(); + + List expectedMessages = + Arrays.asList("debug message", "debug message with null cause", "debug message with cause"); + List> expectedExceptions = + Arrays.asList(Optional.empty(), Optional.ofNullable(null), Optional.of(testException)); + logger.setLevel(Level.ALL); - JulLogger jl = new JulLogger(logger); - jl.log(-1, "debug message"); jl.log(-1, "debug message with null cause", null); jl.log(-1, "debug message with cause", testException); - assertEquals("FINER: debug message (without cause)\r\n" + - "FINER: debug message with null cause (without cause)\r\n" + - "FINER: debug message with cause (with cause java.lang.Exception, dummy exception)", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); + checkMessages(expectedMessages, expectedExceptions); + + logger.setLevel(Level.ALL); jl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); - assertEquals("SEVERE: debug message (without cause)\r\n" + - "SEVERE: debug message with null cause (without cause)\r\n" + - "SEVERE: debug message with cause (with cause java.lang.Exception, dummy exception)", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); - + checkMessages(expectedMessages, expectedExceptions); + logger.setLevel(Level.SEVERE); jl.log(-1, "debug message"); jl.log(-1, "debug message with null cause", null); jl.log(-1, "debug message with cause", testException); - assertEquals("", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); + checkMessages(Collections.emptyList(), Collections.emptyList()); + + logger.setLevel(Level.SEVERE); jl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); - assertEquals("SEVERE: debug message (without cause)\r\n" + - "SEVERE: debug message with null cause (without cause)\r\n" + - "SEVERE: debug message with cause (with cause java.lang.Exception, dummy exception)", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); + checkMessages(expectedMessages, expectedExceptions); + } + + private void checkMessages( + List expectedMessages, List> expectedExceptions) { + List records = handler.getRecords(); + handler.clear(); + List actualMessages = + records.stream().map(LogRecord::getMessage).collect(Collectors.toList()); + List> actualExceptions = + records.stream() + .map(LogRecord::getThrown) + .map(Optional::ofNullable) + .collect(Collectors.toList()); + assertEquals(expectedMessages, actualMessages, "mismatch in logged messages"); + assertEquals(expectedExceptions, actualExceptions, "mismatch in logged exceptions"); } - - private static class TestHandler extends Handler { - private LinkedList messages; - TestHandler(LinkedList messages) { - this.messages = messages; + public static class ListHandler extends Handler { + private final List records; + + public ListHandler() { + super(); + records = Collections.synchronizedList(new ArrayList<>()); } + @Override public void publish(LogRecord record) { - Throwable cause = record.getThrown(); - messages.add(record.getLevel() + ": " + record.getMessage() + (cause == null ? " (without cause)" : " (with cause " + cause.getClass().getName() + ", " + cause.getMessage() + ")")); + records.add(record); } @Override - public void flush() { - - } + public void flush() {} @Override - public void close() throws SecurityException { - + public void close() throws SecurityException {} + + public List getRecords() { + return Collections.unmodifiableList(new ArrayList<>(records)); + } + + public void clear() { + synchronized (records) { + records.clear(); + setLevel(Level.ALL); + } } - } } diff --git a/src/test/java/com/jcraft/jsch/KeyPair2IT.java b/src/test/java/com/jcraft/jsch/KeyPair2IT.java new file mode 100644 index 00000000..f65f2ec7 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/KeyPair2IT.java @@ -0,0 +1,131 @@ +package com.jcraft.jsch; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.net.URISyntaxException; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Testcontainers +public class KeyPair2IT { + + // Python can be slow for DH group 18 + private static final int timeout = 10000; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") + .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") + .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys.KeyPairIT") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh")).withExposedPorts(22); + + @ParameterizedTest + @MethodSource("com.jcraft.jsch.KeyPair2Test#keyArgs") + void connectWithPublicKey(String path, String password, String keyType) throws Exception { + + final JSch jSch = createIdentity(path, password); + + Session session = createSession(jSch); + + if (keyType != null) { + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + } + try { + session.connect(timeout); + assertTrue(session.isConnected()); + } finally { + session.disconnect(); + } + + } + + @ParameterizedTest + @MethodSource("com.jcraft.jsch.KeyPair2Test#keyArgs") + void connectWithPublicKeyAndUserInfo(String path, String password, String keyType) throws Exception { + + final JSch jSch = new JSch(); + + jSch.addIdentity(Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath()); + + Session session = createSession(jSch); + session.setUserInfo(new UserInfo() { + @Override + public String getPassphrase() { + return password; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public boolean promptPassword(String message) { + return false; + } + + @Override + public boolean promptPassphrase(String message) { + return true; + } + + @Override + public boolean promptYesNo(String message) { + return false; + } + + @Override + public void showMessage(String message) { + + } + }); + + if (keyType != null) { + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + } + try { + session.connect(timeout); + assertTrue(session.isConnected()); + } finally { + session.disconnect(); + } + + } + + private JSch createIdentity(String path, String password) throws JSchException, URISyntaxException { + JSch ssh = new JSch(); + if (password != null) { + ssh.addIdentity(Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath(), + password); + } else { + ssh.addIdentity(Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath()); + } + return ssh; + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "no"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } +} diff --git a/src/test/java/com/jcraft/jsch/KeyPair2Test.java b/src/test/java/com/jcraft/jsch/KeyPair2Test.java new file mode 100644 index 00000000..24b38723 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/KeyPair2Test.java @@ -0,0 +1,54 @@ +package com.jcraft.jsch; + +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.File; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class KeyPair2Test { + + @TempDir + public Path tmpDir; + + static Stream keyArgs() { + return Stream.of( + // PuTTY v2 keys + Arguments.of("ppkv2_ed448_unix.ppk", null, "ssh-ed448"), + Arguments.of("ppkv2_ed448_unix_encrypted.ppk", "secret123", "ssh-ed448"), + Arguments.of("ppkv2_ed448_windows.ppk", null, "ssh-ed448"), + Arguments.of("ppkv2_ed448_windows_encrypted.ppk", "secret123", "ssh-ed448"), + // PuTTY v3 keys + Arguments.of("ppkv3_ed448_unix.ppk", null, "ssh-ed448"), + Arguments.of("ppkv3_ed448_unix_encrypted.ppk", "secret123", "ssh-ed448"), + Arguments.of("ppkv3_ed448_windows.ppk", null, "ssh-ed448"), + Arguments.of("ppkv3_ed448_windows_encrypted.ppk", "secret123", "ssh-ed448"), + // PKCS8 keys + Arguments.of("pkcs8_ed448", null, "ssh-ed448"), + Arguments.of("pkcs8_ed448_encrypted_scrypt", "secret123", "ssh-ed448") + ); + } + + @ParameterizedTest + @MethodSource("keyArgs") + void loadKey(String path, String password, String keyType) throws URISyntaxException, JSchException { + final JSch jSch = new JSch(); + final String prvkey = Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath(); + assertTrue(new File(prvkey).exists()); + assertDoesNotThrow(() -> { + if (null != password) { + jSch.addIdentity(prvkey, password); + } else { + jSch.addIdentity(prvkey); + } + }); + assertEquals(keyType, jSch.getIdentityRepository().getIdentities().get(0).getAlgName()); + } +} diff --git a/src/test/java/com/jcraft/jsch/KeyPairIT.java b/src/test/java/com/jcraft/jsch/KeyPairIT.java index f3ccfd68..46de7d6a 100644 --- a/src/test/java/com/jcraft/jsch/KeyPairIT.java +++ b/src/test/java/com/jcraft/jsch/KeyPairIT.java @@ -1,6 +1,5 @@ package com.jcraft.jsch; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.containers.GenericContainer; @@ -22,11 +21,6 @@ public class KeyPairIT { .withFileFromClasspath("authorized_keys", "docker/authorized_keys.KeyPairIT") .withFileFromClasspath("Dockerfile", "docker/Dockerfile.KeyPairIT")).withExposedPorts(22); - @BeforeAll - public static void beforeAll() { - JSch.setLogger(new Slf4jLogger()); - } - @ParameterizedTest @MethodSource("com.jcraft.jsch.KeyPairTest#keyArgs") void connectWithPublicKey(String path, String password, String keyType) throws Exception { diff --git a/src/test/java/com/jcraft/jsch/KeyPairTest.java b/src/test/java/com/jcraft/jsch/KeyPairTest.java index 66262e44..8dafb13b 100644 --- a/src/test/java/com/jcraft/jsch/KeyPairTest.java +++ b/src/test/java/com/jcraft/jsch/KeyPairTest.java @@ -1,6 +1,5 @@ package com.jcraft.jsch; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -22,11 +21,6 @@ class KeyPairTest { @TempDir public Path tmpDir; - @BeforeAll - static void init() { - JSch.setLogger(new Slf4jLogger()); - } - static Stream keyArgs() { return Stream.of( // docker/id_rsa rsa @@ -49,7 +43,72 @@ static Stream keyArgs() { Arguments.of("docker/ssh_host_ecdsa384_key", null, "ecdsa-sha2-nistp384"), Arguments.of("docker/ssh_host_ecdsa521_key", null, "ecdsa-sha2-nistp521"), // encrypted ecdsa - Arguments.of("encrypted_openssh_private_key_ecdsa", "secret123", "ecdsa-sha2-nistp256") + Arguments.of("encrypted_openssh_private_key_ecdsa", "secret123", "ecdsa-sha2-nistp256"), + // PuTTY v2 keys + Arguments.of("ppkv2_dsa_unix.ppk", null, "ssh-dss"), + Arguments.of("ppkv2_dsa_unix_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv2_dsa_windows.ppk", null, "ssh-dss"), + Arguments.of("ppkv2_dsa_windows_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv2_rsa_unix.ppk", null, "ssh-rsa"), + Arguments.of("ppkv2_rsa_unix_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv2_rsa_windows.ppk", null, "ssh-rsa"), + Arguments.of("ppkv2_rsa_windows_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv2_ecdsa256_unix.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa256_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa384_unix.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa384_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa521_unix.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ecdsa521_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ecdsa256_windows.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa256_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa384_windows.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa384_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa521_windows.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ecdsa521_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ed25519_unix.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv2_ed25519_unix_encrypted.ppk", "secret123", "ssh-ed25519"), + Arguments.of("ppkv2_ed25519_windows.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv2_ed25519_windows_encrypted.ppk", "secret123", "ssh-ed25519"), + // PuTTY v3 keys + Arguments.of("ppkv3_dsa_unix.ppk", null, "ssh-dss"), + Arguments.of("ppkv3_dsa_unix_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv3_dsa_windows.ppk", null, "ssh-dss"), + Arguments.of("ppkv3_dsa_windows_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv3_rsa_unix.ppk", null, "ssh-rsa"), + Arguments.of("ppkv3_rsa_unix_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv3_rsa_windows.ppk", null, "ssh-rsa"), + Arguments.of("ppkv3_rsa_windows_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv3_ecdsa256_unix.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa256_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa384_unix.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa384_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa521_unix.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ecdsa521_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ecdsa256_windows.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa256_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa384_windows.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa384_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa521_windows.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ecdsa521_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ed25519_unix.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv3_ed25519_unix_encrypted.ppk", "secret123", "ssh-ed25519"), + Arguments.of("ppkv3_ed25519_windows.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv3_ed25519_windows_encrypted.ppk", "secret123", "ssh-ed25519"), + // PKCS8 keys + Arguments.of("pkcs8_dsa", null, "ssh-dss"), + Arguments.of("pkcs8_dsa_encrypted_hmacsha1", "secret123", "ssh-dss"), + Arguments.of("pkcs8_dsa_encrypted_hmacsha256", "secret123", "ssh-dss"), + Arguments.of("pkcs8_rsa", null, "ssh-rsa"), + Arguments.of("pkcs8_rsa_encrypted_hmacsha1", "secret123", "ssh-rsa"), + Arguments.of("pkcs8_rsa_encrypted_hmacsha256", "secret123", "ssh-rsa"), + Arguments.of("pkcs8_ecdsa256", null, "ecdsa-sha2-nistp256"), + Arguments.of("pkcs8_ecdsa256_encrypted_scrypt", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("pkcs8_ecdsa384", null, "ecdsa-sha2-nistp384"), + Arguments.of("pkcs8_ecdsa384_encrypted_scrypt", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("pkcs8_ecdsa521", null, "ecdsa-sha2-nistp521"), + Arguments.of("pkcs8_ecdsa521_encrypted_scrypt", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("pkcs8_ed25519", null, "ssh-ed25519"), + Arguments.of("pkcs8_ed25519_encrypted_scrypt", "secret123", "ssh-ed25519") ); } @@ -87,18 +146,6 @@ void genKeypairEncrypted() { }); } - @Test - void loadAndAcessEncryptedKeyWithoutPassword() throws URISyntaxException, JSchException { - final JSch jSch = new JSch(); - final String prvkey = Paths.get(ClassLoader.getSystemResource("encrypted_openssh_private_key_dsa").toURI()).toFile().getAbsolutePath(); - assertTrue(new File(prvkey).exists()); - jSch.addIdentity(prvkey); - - assertThrows(IllegalStateException.class, () -> { - jSch.getIdentityRepository().getIdentities().get(0).getAlgName(); - }); - } - @ParameterizedTest @ValueSource(strings = {"encrypted_openssh_private_key_rsa", "encrypted_openssh_private_key_dsa", "encrypted_openssh_private_key_ecdsa"}) void decryptEncryptedOpensshKey(String keyFile) throws URISyntaxException, JSchException { diff --git a/src/test/java/com/jcraft/jsch/ListAppender2.java b/src/test/java/com/jcraft/jsch/ListAppender2.java deleted file mode 100644 index 2d72b8a2..00000000 --- a/src/test/java/com/jcraft/jsch/ListAppender2.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jcraft.jsch; - -import ch.qos.logback.core.read.ListAppender; - -public class ListAppender2 extends ListAppender { - - @Override - protected void append(E e) { - // Avoid append messages after appender is stopped to avoid ConcurrentModificationException's - // when examining the List of events. - if (super.started) { - super.append(e); - } - } -} diff --git a/src/test/java/com/jcraft/jsch/Log4j2LoggerTest.java b/src/test/java/com/jcraft/jsch/Log4j2LoggerTest.java new file mode 100644 index 00000000..98e8b69a --- /dev/null +++ b/src/test/java/com/jcraft/jsch/Log4j2LoggerTest.java @@ -0,0 +1,174 @@ +package com.jcraft.jsch; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.Configurator; +import org.apache.logging.log4j.junit.LoggerContextSource; +import org.apache.logging.log4j.junit.Named; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@LoggerContextSource("Log4j2LoggerTest.xml") +public class Log4j2LoggerTest { + + private final Exception testException = new Exception("dummy exception"); + private final ListAppender appender; + + public Log4j2LoggerTest(@Named("List") ListAppender appender) { + this.appender = appender; + } + + @BeforeEach + public void beforeEach() { + appender.clear(); + } + + @Test + public void testGetLevel() { + assertEquals(Level.TRACE, Log4j2Logger.getLevel(-1)); + + assertEquals(Level.DEBUG, Log4j2Logger.getLevel(com.jcraft.jsch.Logger.DEBUG)); + assertEquals(Level.ERROR, Log4j2Logger.getLevel(com.jcraft.jsch.Logger.ERROR)); + assertEquals(Level.FATAL, Log4j2Logger.getLevel(com.jcraft.jsch.Logger.FATAL)); + assertEquals(Level.INFO, Log4j2Logger.getLevel(com.jcraft.jsch.Logger.INFO)); + assertEquals(Level.WARN, Log4j2Logger.getLevel(com.jcraft.jsch.Logger.WARN)); + + assertEquals(Level.TRACE, Log4j2Logger.getLevel(Integer.MAX_VALUE)); + } + + @Test + public void testIsEnabled() { + Log4j2Logger ll = new Log4j2Logger(); + + setLevel(Level.ALL); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertTrue(ll.isEnabled(-1), "trace should be enabled"); + + setLevel(Level.DEBUG); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertFalse(ll.isEnabled(-1), "trace should not be enabled"); + + setLevel(Level.ERROR); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); + assertFalse(ll.isEnabled(-1), "trace should not be enabled"); + + setLevel(Level.FATAL); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should not be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); + assertFalse(ll.isEnabled(-1), "trace should not be enabled"); + + setLevel(Level.INFO); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertFalse(ll.isEnabled(-1), "trace should not be enabled"); + + setLevel(Level.OFF); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); + assertFalse(ll.isEnabled(-1), "trace should not be enabled"); + + setLevel(Level.TRACE); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertTrue(ll.isEnabled(-1), "trace should be enabled"); + + setLevel(Level.WARN); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertFalse(ll.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertTrue(ll.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertFalse(ll.isEnabled(-1), "trace should not be enabled"); + } + + @Test + public void testLogging() { + Log4j2Logger ll = new Log4j2Logger(); + + List expectedMessages = + Arrays.asList("debug message", "debug message with null cause", "debug message with cause"); + List> expectedExceptions = + Arrays.asList(Optional.empty(), Optional.ofNullable(null), Optional.of(testException)); + + setLevel(Level.TRACE); + ll.log(-1, "debug message"); + ll.log(-1, "debug message with null cause", null); + ll.log(-1, "debug message with cause", testException); + checkMessages(expectedMessages, expectedExceptions); + + setLevel(Level.TRACE); + ll.log(com.jcraft.jsch.Logger.FATAL, "debug message"); + ll.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); + ll.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); + checkMessages(expectedMessages, expectedExceptions); + + setLevel(Level.ERROR); + ll.log(-1, "debug message"); + ll.log(-1, "debug message with null cause", null); + ll.log(-1, "debug message with cause", testException); + checkMessages(Collections.emptyList(), Collections.emptyList()); + + setLevel(Level.ERROR); + ll.log(com.jcraft.jsch.Logger.FATAL, "debug message"); + ll.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); + ll.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); + checkMessages(expectedMessages, expectedExceptions); + } + + private void checkMessages( + List expectedMessages, List> expectedExceptions) { + List events = appender.getEvents(); + appender.clear(); + List actualMessages = + events.stream() + .map(LogEvent::getMessage) + .map(Message::getFormattedMessage) + .collect(Collectors.toList()); + List> actualExceptions = + events.stream() + .map(LogEvent::getThrown) + .map(Optional::ofNullable) + .collect(Collectors.toList()); + assertEquals(expectedMessages, actualMessages, "mismatch in logged messages"); + assertEquals(expectedExceptions, actualExceptions, "mismatch in logged exceptions"); + } + + private static void setLevel(Level level) { + Configurator.setLevel(JSch.class, level); + } +} diff --git a/src/test/java/com/jcraft/jsch/LoggerTest.java b/src/test/java/com/jcraft/jsch/LoggerTest.java index c90166c3..fa73ed97 100644 --- a/src/test/java/com/jcraft/jsch/LoggerTest.java +++ b/src/test/java/com/jcraft/jsch/LoggerTest.java @@ -1,56 +1,60 @@ package com.jcraft.jsch; import static org.junit.jupiter.api.Assertions.assertEquals; + import java.io.PrintWriter; import java.io.StringWriter; -import java.util.LinkedList; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Test; -class LoggerTest { - +public class LoggerTest { + + private final Exception testException = new Exception("dummy exception"); + @Test - void testLogWithCause() { - LinkedList messages = new LinkedList<>(); + public void testLogging() { + List actualMessages = new ArrayList<>(); boolean[] enabledResult = new boolean[1]; - Logger logger = new Logger() { - @Override - public void log(int level, String message) { - messages.add(level + ":" + message); - } - @Override - public boolean isEnabled(int level) { - return enabledResult[0]; - } - }; - - Exception ex = new Exception("dummy exception"); + Logger logger = + new Logger() { + @Override + public void log(int level, String message) { + if (isEnabled(level)) { + actualMessages.add(level + ":" + message); + } + } + + @Override + public boolean isEnabled(int level) { + return enabledResult[0]; + } + }; + + actualMessages.clear(); + enabledResult[0] = false; + logger.log(Logger.ERROR, "debug message"); + logger.log(Logger.ERROR, "debug message with null cause", null); + logger.log(Logger.ERROR, "debug message with cause", testException); + assertEquals(Collections.emptyList(), actualMessages, "mismatch in logged messages"); + StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - ex.printStackTrace(pw); - String expectedTrace = sw.toString(); - - logger.log(Logger.ERROR, "some message", null); - logger.log(Logger.ERROR, "some message with trace", ex); - assertEquals("", getMessageLines(messages), "message mismatch"); - - enabledResult[0] = true; - logger.log(Logger.ERROR, "some message", null); - logger.log(Logger.ERROR, "some message with trace", ex); - assertEquals( - Logger.ERROR + ":some message\r\n" + - Logger.ERROR + ":some message with trace" + System.lineSeparator() + - expectedTrace + - "", getMessageLines(messages), "message mismatch"); - } - - static String getMessageLines(LinkedList messages) { - try { - return messages.stream() - .collect(Collectors.joining("\r\n")); - } - finally { - messages.clear(); + try (PrintWriter pw = new PrintWriter(sw, true)) { + testException.printStackTrace(pw); } + List expectedMessages = + Arrays.asList( + Logger.ERROR + ":debug message", + Logger.ERROR + ":debug message with null cause", + Logger.ERROR + ":debug message with cause" + System.lineSeparator() + sw); + + actualMessages.clear(); + enabledResult[0] = true; + logger.log(Logger.ERROR, "debug message"); + logger.log(Logger.ERROR, "debug message with null cause", null); + logger.log(Logger.ERROR, "debug message with cause", testException); + assertEquals(expectedMessages, actualMessages, "mismatch in logged messages"); } } diff --git a/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java b/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java index f4b83046..65c33459 100644 --- a/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java +++ b/src/test/java/com/jcraft/jsch/OpenSSH74ServerSigAlgsIT.java @@ -1,29 +1,28 @@ package com.jcraft.jsch; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.io.TempDir; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -36,9 +35,9 @@ public class OpenSSH74ServerSigAlgsIT { private static final int timeout = 2000; private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); - private static final ListAppender jschAppender = getListAppender(JSch.class); - private static final ListAppender sshdAppender = - getListAppender(OpenSSH74ServerSigAlgsIT.class); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(OpenSSH74ServerSigAlgsIT.class); @TempDir public Path tmpDir; private Path in; @@ -79,7 +78,7 @@ public static void beforeAll() { @BeforeEach public void beforeEach() throws IOException { if (sshdLogConsumer == null) { - sshdLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(OpenSSH74ServerSigAlgsIT.class)); + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); sshd.followOutput(sshdLogConsumer); } @@ -95,23 +94,21 @@ public void beforeEach() throws IOException { } hash = sha256sum.digestAsHex(in); - jschAppender.list.clear(); - sshdAppender.list.clear(); - jschAppender.start(); - sshdAppender.start(); + jschLogger.clearAll(); + sshdLogger.clearAll(); } - @AfterEach - public void afterEach() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.clear(); - sshdAppender.list.clear(); + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); } @Test public void testServerSigAlgs() throws Exception { - String algos = "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + String algos = + "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; JSch ssh = createRSAIdentity(); Session session = createSession(ssh); session.setConfig("PubkeyAcceptedAlgorithms", algos); @@ -120,10 +117,14 @@ public void testServerSigAlgs() throws Exception { String expectedKex = "kex: host key algorithm: rsa-sha2-512"; String expectedExtInfo = "SSH_MSG_EXT_INFO received"; - String expectedServerSigAlgs = "server-sig-algs="; - String expectedOpenSSH74Bug = "OpenSSH 7.4 detected: adding rsa-sha2-256 & rsa-sha2-512 to server-sig-algs"; - String expectedPubkeysKnown = "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]"; - String expectedPubkeysUnknown = "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[ssh-rsa-sha512@ssh.com, ssh-rsa-sha384@ssh.com, ssh-rsa-sha256@ssh.com, ssh-rsa-sha224@ssh.com\\]"; + String expectedServerSigAlgs = + "server-sig-algs="; + String expectedOpenSSH74Bug = + "OpenSSH 7.4 detected: adding rsa-sha2-256 & rsa-sha2-512 to server-sig-algs"; + String expectedPubkeysKnown = + "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]"; + String expectedPubkeysUnknown = + "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[ssh-rsa-sha512@ssh.com, ssh-rsa-sha384@ssh.com, ssh-rsa-sha256@ssh.com, ssh-rsa-sha224@ssh.com\\]"; String expectedPreauth = "rsa-sha2-512 preauth success"; String expectedAuth = "rsa-sha2-512 auth success"; checkLogs(expectedKex); @@ -148,7 +149,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { @@ -168,8 +169,6 @@ private void doSftp(Session session, boolean debugException) throws Exception { sftp.get("/root/test", out.toString()); sftp.disconnect(); session.disconnect(); - jschAppender.stop(); - sshdAppender.stop(); } catch (Exception e) { if (debugException) { printInfo(); @@ -181,20 +180,22 @@ private void doSftp(Session session, boolean debugException) throws Exception { assertEquals(hash, sha256sum.digestAsHex(out)); } - private static void printInfo() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshdAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); System.out.println(""); System.out.println(""); System.out.println(""); } - private static void checkLogs(String expected) { + private void checkLogs(String expected) { Optional actualJsch = - jschAppender.list.stream() - .map(ILoggingEvent::getFormattedMessage) + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) .filter(msg -> msg.matches(expected)) .findFirst(); try { @@ -208,11 +209,4 @@ private static void checkLogs(String expected) { private String getResourceFile(String fileName) { return ResourceUtil.getResourceFile(getClass(), fileName); } - - private static ListAppender getListAppender(Class clazz) { - Logger logger = (Logger) LoggerFactory.getLogger(clazz); - ListAppender listAppender = new ListAppender2<>(); - logger.addAppender(listAppender); - return listAppender; - } } diff --git a/src/test/java/com/jcraft/jsch/PktSize128IT.java b/src/test/java/com/jcraft/jsch/PktSize128IT.java new file mode 100644 index 00000000..29c05418 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/PktSize128IT.java @@ -0,0 +1,55 @@ +package com.jcraft.jsch; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize128IT extends AbstractBufferMargin { + + @ParameterizedTest + @CsvSource( + value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", + "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", + "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", + "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", + "aes128-ctr,hmac-sha1,zlib@openssh.com" + }) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @ParameterizedTest + @CsvSource( + value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", + "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", + "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", + "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", + "aes128-ctr,hmac-sha1,zlib@openssh.com", + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", + "aes128-ctr,hmac-sha2-256,zlib@openssh.com" + }) + public void testScp(String cipher, String mac, String compression) throws Exception { + doTestScp(cipher, mac, compression); + } + + @Override + protected int maxPktSize() { + return 128; + } +} diff --git a/src/test/java/com/jcraft/jsch/PktSize160IT.java b/src/test/java/com/jcraft/jsch/PktSize160IT.java new file mode 100644 index 00000000..60fca96b --- /dev/null +++ b/src/test/java/com/jcraft/jsch/PktSize160IT.java @@ -0,0 +1,34 @@ +package com.jcraft.jsch; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize160IT extends AbstractBufferMargin { + + @ParameterizedTest + @CsvSource( + value = { + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", + "aes128-ctr,hmac-sha2-256,zlib@openssh.com" + }) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @ParameterizedTest + @CsvSource( + value = { + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", + "aes128-ctr,hmac-sha2-512,zlib@openssh.com" + }) + public void testScp(String cipher, String mac, String compression) throws Exception { + doTestScp(cipher, mac, compression); + } + + @Override + protected int maxPktSize() { + return 160; + } +} diff --git a/src/test/java/com/jcraft/jsch/PktSize192IT.java b/src/test/java/com/jcraft/jsch/PktSize192IT.java new file mode 100644 index 00000000..7ed19b6c --- /dev/null +++ b/src/test/java/com/jcraft/jsch/PktSize192IT.java @@ -0,0 +1,23 @@ +package com.jcraft.jsch; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize192IT extends AbstractBufferMargin { + + @ParameterizedTest + @CsvSource( + value = { + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", + "aes128-ctr,hmac-sha2-512,zlib@openssh.com" + }) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @Override + protected int maxPktSize() { + return 192; + } +} diff --git a/src/test/java/com/jcraft/jsch/PktSize32768IT.java b/src/test/java/com/jcraft/jsch/PktSize32768IT.java new file mode 100644 index 00000000..7ff73b1b --- /dev/null +++ b/src/test/java/com/jcraft/jsch/PktSize32768IT.java @@ -0,0 +1,64 @@ +package com.jcraft.jsch; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize32768IT extends AbstractBufferMargin { + + @ParameterizedTest + @CsvSource( + value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", + "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", + "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", + "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", + "aes128-ctr,hmac-sha1,zlib@openssh.com", + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", + "aes128-ctr,hmac-sha2-256,zlib@openssh.com", + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", + "aes128-ctr,hmac-sha2-512,zlib@openssh.com" + }) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @ParameterizedTest + @CsvSource( + value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", + "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", + "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", + "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", + "aes128-ctr,hmac-sha1,zlib@openssh.com", + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", + "aes128-ctr,hmac-sha2-256,zlib@openssh.com", + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", + "aes128-ctr,hmac-sha2-512,zlib@openssh.com" + }) + public void testScp(String cipher, String mac, String compression) throws Exception { + doTestScp(cipher, mac, compression); + } + + @Override + protected int maxPktSize() { + return 32768; + } +} diff --git a/src/test/java/com/jcraft/jsch/SSHAgentIT.java b/src/test/java/com/jcraft/jsch/SSHAgentIT.java index 31daa774..ee15674f 100644 --- a/src/test/java/com/jcraft/jsch/SSHAgentIT.java +++ b/src/test/java/com/jcraft/jsch/SSHAgentIT.java @@ -1,22 +1,22 @@ package com.jcraft.jsch; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.condition.JRE.JAVA_16; import static org.junit.jupiter.api.condition.OS.LINUX; import static org.testcontainers.containers.BindMode.READ_WRITE; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; import com.sun.jna.platform.unix.LibC; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; @@ -30,7 +30,6 @@ import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -43,9 +42,10 @@ public class SSHAgentIT { private static final int timeout = 2000; private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); - private static final ListAppender jschAppender = getListAppender(JSch.class); - private static final ListAppender sshdAppender = getListAppender(SSHAgentIT.class); - private static final ListAppender sshAgentAppender = getListAppender(AgentProxy.class); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(SSHAgentIT.class); + private static final TestLogger sshAgentLogger = + TestLoggerFactory.getTestLogger(AgentProxy.class); @TempDir public static Path tmpDir; private static String testuid; private static String testgid; @@ -104,12 +104,12 @@ public static void beforeAll() throws IOException { @BeforeEach public void beforeEach() throws IOException { if (sshdLogConsumer == null) { - sshdLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(SSHAgentIT.class)); + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); sshd.followOutput(sshdLogConsumer); } if (sshAgentLogConsumer == null) { - sshAgentLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(AgentProxy.class)); + sshAgentLogConsumer = new Slf4jLogConsumer(sshAgentLogger); sshAgent.followOutput(sshAgentLogConsumer); } @@ -126,23 +126,13 @@ public void beforeEach() throws IOException { } hash = sha256sum.digestAsHex(in); - jschAppender.list.clear(); - sshdAppender.list.clear(); - sshAgentAppender.list.clear(); - jschAppender.start(); - sshdAppender.start(); - sshAgentAppender.start(); + jschLogger.clearAll(); + sshdLogger.clearAll(); + sshAgentLogger.clearAll(); } @AfterEach public void afterEach() { - jschAppender.stop(); - sshdAppender.stop(); - sshAgentAppender.stop(); - jschAppender.list.clear(); - sshdAppender.list.clear(); - sshAgentAppender.list.clear(); - try { Files.deleteIfExists(sshAgentSock); } catch (IOException ignore) { @@ -166,6 +156,11 @@ public void afterEach() { @AfterAll public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + sshAgentLogger.clearAll(); + try { Files.deleteIfExists(sshAgentSock.getParent()); } catch (IOException ignore) { @@ -277,7 +272,8 @@ public void testDSAUnixDomainSocketFactory() throws Exception { } private JSch createRSAIdentity(USocketFactory factory) throws Exception { - IdentityRepository ir = new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); @@ -290,7 +286,8 @@ private JSch createRSAIdentity(USocketFactory factory) throws Exception { } private JSch createECDSA256Identity(USocketFactory factory) throws Exception { - IdentityRepository ir = new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); @@ -303,7 +300,8 @@ private JSch createECDSA256Identity(USocketFactory factory) throws Exception { } private JSch createECDSA384Identity(USocketFactory factory) throws Exception { - IdentityRepository ir = new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); @@ -317,7 +315,8 @@ private JSch createECDSA384Identity(USocketFactory factory) throws Exception { } private JSch createECDSA521Identity(USocketFactory factory) throws Exception { - IdentityRepository ir = new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); @@ -331,7 +330,8 @@ private JSch createECDSA521Identity(USocketFactory factory) throws Exception { } private JSch createDSAIdentity(USocketFactory factory) throws Exception { - IdentityRepository ir = new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); @@ -344,7 +344,8 @@ private JSch createDSAIdentity(USocketFactory factory) throws Exception { } private JSch createEd25519Identity(USocketFactory factory) throws Exception { - IdentityRepository ir = new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); @@ -361,7 +362,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { @@ -381,9 +382,6 @@ private void doSftp(Session session, boolean debugException) throws Exception { sftp.get("/root/test", out.toString()); sftp.disconnect(); session.disconnect(); - jschAppender.stop(); - sshdAppender.stop(); - sshAgentAppender.stop(); } catch (Exception e) { if (debugException) { printInfo(); @@ -395,13 +393,16 @@ private void doSftp(Session session, boolean debugException) throws Exception { assertEquals(hash, sha256sum.digestAsHex(out)); } - private static void printInfo() { - jschAppender.stop(); - sshdAppender.stop(); - sshAgentAppender.stop(); - jschAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshdAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshAgentAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshAgentLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); System.out.println(""); System.out.println(""); System.out.println(""); @@ -410,11 +411,4 @@ private static void printInfo() { private String getResourceFile(String fileName) { return ResourceUtil.getResourceFile(getClass(), fileName); } - - private static ListAppender getListAppender(Class clazz) { - Logger logger = (Logger) LoggerFactory.getLogger(clazz); - ListAppender listAppender = new ListAppender2<>(); - logger.addAppender(listAppender); - return listAppender; - } } diff --git a/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java b/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java index 095d9789..0096c754 100644 --- a/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java +++ b/src/test/java/com/jcraft/jsch/ServerSigAlgsIT.java @@ -1,28 +1,27 @@ package com.jcraft.jsch; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.commons.codec.binary.Base64.decodeBase64; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.read.ListAppender; +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Base64; import java.util.List; import java.util.Optional; import java.util.Random; import org.apache.commons.codec.digest.DigestUtils; -import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.images.builder.ImageFromDockerfile; @@ -34,9 +33,9 @@ public class ServerSigAlgsIT { private static final int timeout = 2000; private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); - private static final ListAppender jschAppender = getListAppender(JSch.class); - private static final ListAppender sshdAppender = - getListAppender(ServerSigAlgsIT.class); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); @TempDir public Path tmpDir; private Path in; @@ -77,7 +76,7 @@ public static void beforeAll() { @BeforeEach public void beforeEach() throws IOException { if (sshdLogConsumer == null) { - sshdLogConsumer = new Slf4jLogConsumer(LoggerFactory.getLogger(ServerSigAlgsIT.class)); + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); sshd.followOutput(sshdLogConsumer); } @@ -93,23 +92,21 @@ public void beforeEach() throws IOException { } hash = sha256sum.digestAsHex(in); - jschAppender.list.clear(); - sshdAppender.list.clear(); - jschAppender.start(); - sshdAppender.start(); + jschLogger.clearAll(); + sshdLogger.clearAll(); } - @AfterEach - public void afterEach() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.clear(); - sshdAppender.list.clear(); + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); } @Test public void testServerSigAlgs() throws Exception { - String algos = "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + String algos = + "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; JSch ssh = createRSAIdentity(); Session session = createSession(ssh); session.setConfig("PubkeyAcceptedAlgorithms", algos); @@ -118,9 +115,12 @@ public void testServerSigAlgs() throws Exception { String expectedKex = "kex: host key algorithm: rsa-sha2-512"; String expectedExtInfo = "SSH_MSG_EXT_INFO received"; - String expectedServerSigAlgs = "server-sig-algs="; - String expectedPubkeysKnown = "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]"; - String expectedPubkeysUnknown = "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[ssh-rsa-sha512@ssh.com, ssh-rsa-sha384@ssh.com, ssh-rsa-sha256@ssh.com, ssh-rsa-sha224@ssh.com\\]"; + String expectedServerSigAlgs = + "server-sig-algs="; + String expectedPubkeysKnown = + "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]"; + String expectedPubkeysUnknown = + "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[ssh-rsa-sha512@ssh.com, ssh-rsa-sha384@ssh.com, ssh-rsa-sha256@ssh.com, ssh-rsa-sha224@ssh.com\\]"; String expectedPreauth = "rsa-sha2-512 preauth success"; String expectedAuth = "rsa-sha2-512 auth success"; checkLogs(expectedKex); @@ -134,7 +134,8 @@ public void testServerSigAlgs() throws Exception { @Test public void testNoServerSigAlgs() throws Exception { - String algos = "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + String algos = + "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; JSch ssh = createRSAIdentity(); Session session = createSession(ssh); session.setConfig("enable_server_sig_algs", "no"); @@ -143,7 +144,8 @@ public void testNoServerSigAlgs() throws Exception { doSftp(session, true); String expectedKex = "kex: host key algorithm: rsa-sha2-512"; - String expectedPubkeysNoServerSigs = String.format("No server-sig-algs found, using PubkeyAcceptedAlgorithms = %s", algos); + String expectedPubkeysNoServerSigs = + String.format("No server-sig-algs found, using PubkeyAcceptedAlgorithms = %s", algos); String expectedPreauthFail1 = "ssh-rsa-sha512@ssh.com preauth failure"; String expectedPreauthFail2 = "ssh-rsa-sha384@ssh.com preauth failure"; String expectedPreauthFail3 = "ssh-rsa-sha256@ssh.com preauth failure"; @@ -171,7 +173,7 @@ private HostKey readHostKey(String fileName) throws Exception { List lines = Files.readAllLines(Paths.get(fileName), UTF_8); String[] split = lines.get(0).split("\\s+"); String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); - return new HostKey(hostname, decodeBase64(split[1])); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); } private Session createSession(JSch ssh) throws Exception { @@ -191,8 +193,6 @@ private void doSftp(Session session, boolean debugException) throws Exception { sftp.get("/root/test", out.toString()); sftp.disconnect(); session.disconnect(); - jschAppender.stop(); - sshdAppender.stop(); } catch (Exception e) { if (debugException) { printInfo(); @@ -204,20 +204,22 @@ private void doSftp(Session session, boolean debugException) throws Exception { assertEquals(hash, sha256sum.digestAsHex(out)); } - private static void printInfo() { - jschAppender.stop(); - sshdAppender.stop(); - jschAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); - sshdAppender.list.stream().map(ILoggingEvent::getFormattedMessage).forEach(System.out::println); + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); System.out.println(""); System.out.println(""); System.out.println(""); } - private static void checkLogs(String expected) { + private void checkLogs(String expected) { Optional actualJsch = - jschAppender.list.stream() - .map(ILoggingEvent::getFormattedMessage) + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) .filter(msg -> msg.matches(expected)) .findFirst(); try { @@ -231,11 +233,4 @@ private static void checkLogs(String expected) { private String getResourceFile(String fileName) { return ResourceUtil.getResourceFile(getClass(), fileName); } - - private static ListAppender getListAppender(Class clazz) { - Logger logger = (Logger) LoggerFactory.getLogger(clazz); - ListAppender listAppender = new ListAppender2<>(); - logger.addAppender(listAppender); - return listAppender; - } } diff --git a/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java b/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java index 1e410b9a..056361e1 100644 --- a/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java +++ b/src/test/java/com/jcraft/jsch/Slf4jLoggerTest.java @@ -2,93 +2,82 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.LinkedList; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.pattern.ThrowableProxyConverter; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.IThrowableProxy; -import ch.qos.logback.core.AppenderBase; - -class Slf4jLoggerTest { - private LinkedList messages; - private Exception testException = new Exception("dummy exception"); - private ThrowableProxyConverter tpc = new ThrowableProxyConverter(); +import uk.org.lidalia.slf4jext.ConventionalLevelHierarchy; + +public class Slf4jLoggerTest { + + private static final TestLogger logger = TestLoggerFactory.getTestLogger(JSch.class); + + private final Exception testException = new Exception("dummy exception"); @BeforeEach - void resetLogger() { - Logger logger = LoggerFactory.getLogger(getClass()); - assertNotNull(logger, "logger should not be null"); - assertEquals("ch.qos.logback.classic.Logger", logger.getClass().getName(), "we need logback as backend for slf4j to test"); - ch.qos.logback.classic.Logger lbLogger = (ch.qos.logback.classic.Logger) logger; - lbLogger.iteratorForAppenders().forEachRemaining(lbLogger::detachAppender); - messages = new LinkedList<>(); - - tpc.start(); + public void beforeEach() { + logger.clearAll(); } - + + @AfterAll + public static void afterAll() { + logger.clearAll(); + } + @Test - void testIsEnabled() { - LoggerContext ct = (LoggerContext) LoggerFactory.getILoggerFactory(); - ch.qos.logback.classic.Logger lbLogger = ct.getLogger(getClass()); - lbLogger.addAppender(new TestAppender(messages)); - lbLogger.setLevel(Level.ALL); - Slf4jLogger sl = new Slf4jLogger(lbLogger); - - assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); - assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); - assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); - assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); - assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); - assertTrue(sl.isEnabled(-1), "trace should be enabled"); - - lbLogger.setLevel(Level.DEBUG); + public void testIsEnabled() { + Slf4jLogger sl = new Slf4jLogger(); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.DEBUG_LEVELS); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - - lbLogger.setLevel(Level.ERROR); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.ERROR_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - - lbLogger.setLevel(Level.INFO); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.INFO_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - - lbLogger.setLevel(Level.OFF); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.OFF_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); - - lbLogger.setLevel(Level.TRACE); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.TRACE_LEVELS); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertTrue(sl.isEnabled(-1), "trace should be enabled"); - - lbLogger.setLevel(Level.WARN); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.WARN_LEVELS); assertFalse(sl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); @@ -96,60 +85,50 @@ void testIsEnabled() { assertTrue(sl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); assertFalse(sl.isEnabled(-1), "trace should not be enabled"); } - + @Test - void testLogging() { - LoggerContext ct = (LoggerContext) LoggerFactory.getILoggerFactory(); - ch.qos.logback.classic.Logger lbLogger = ct.getLogger(getClass()); - TestAppender app = new TestAppender(messages); - app.setContext(ct); - app.start(); - lbLogger.addAppender(app); - lbLogger.setAdditive(false); - lbLogger.setLevel(Level.ALL); - Slf4jLogger sl = new Slf4jLogger(lbLogger); - + public void testLogging() { + Slf4jLogger sl = new Slf4jLogger(); + + List expectedMessages = + Arrays.asList("debug message", "debug message with null cause", "debug message with cause"); + List> expectedExceptions = + Arrays.asList(Optional.empty(), Optional.ofNullable(null), Optional.of(testException)); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.TRACE_LEVELS); sl.log(-1, "debug message"); sl.log(-1, "debug message with null cause", null); sl.log(-1, "debug message with cause", testException); - assertEquals("TRACE: debug message (without cause)\r\n" + - "TRACE: debug message with null cause (without cause)\r\n" + - "TRACE: debug message with cause (with cause java.lang.Exception, dummy exception)", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); + checkMessages(expectedMessages, expectedExceptions); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.TRACE_LEVELS); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); - assertEquals("ERROR: debug message (without cause)\r\n" + - "ERROR: debug message with null cause (without cause)\r\n" + - "ERROR: debug message with cause (with cause java.lang.Exception, dummy exception)", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); - - lbLogger.setLevel(Level.ERROR); + checkMessages(expectedMessages, expectedExceptions); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.ERROR_LEVELS); sl.log(-1, "debug message"); sl.log(-1, "debug message with null cause", null); sl.log(-1, "debug message with cause", testException); - assertEquals("", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); + checkMessages(Collections.emptyList(), Collections.emptyList()); + + logger.setEnabledLevelsForAllThreads(ConventionalLevelHierarchy.ERROR_LEVELS); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); sl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); - assertEquals("ERROR: debug message (without cause)\r\n" + - "ERROR: debug message with null cause (without cause)\r\n" + - "ERROR: debug message with cause (with cause java.lang.Exception, dummy exception)", LoggerTest.getMessageLines(messages), "mismatch in logged messages"); + checkMessages(expectedMessages, expectedExceptions); } - - static class TestAppender extends AppenderBase { - private LinkedList messages; - TestAppender(LinkedList messages) { - this.messages = messages; - } - @Override - protected void append(ILoggingEvent eventObject) { - try { - IThrowableProxy thp = eventObject.getThrowableProxy(); - - messages.add(eventObject.getLevel()+ ": " + eventObject.getMessage() + (thp == null ? " (without cause)" : " (with cause " + thp.getClassName() + ", " + thp.getMessage() + ")")); - } - catch(Exception e) { - e.printStackTrace(); - } - } + + private void checkMessages( + List expectedMessages, List> expectedExceptions) { + List events = logger.getAllLoggingEvents(); + logger.clearAll(); + List actualMessages = + events.stream().map(LoggingEvent::getFormattedMessage).collect(Collectors.toList()); + List> actualExceptions = + events.stream().map(LoggingEvent::getThrowable).collect(Collectors.toList()); + assertEquals(expectedMessages, actualMessages, "mismatch in logged messages"); + assertEquals(expectedExceptions, actualExceptions, "mismatch in logged exceptions"); } } diff --git a/src/test/java/com/jcraft/jsch/UserAuthIT.java b/src/test/java/com/jcraft/jsch/UserAuthIT.java new file mode 100644 index 00000000..996c1783 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/UserAuthIT.java @@ -0,0 +1,202 @@ +package com.jcraft.jsch; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class UserAuthIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = + new GenericContainer<>( + new ImageFromDockerfile() + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath( + "ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath( + "ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath( + "ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath( + "ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testAuthNoneEnabledPubkeyAuthQueryEnabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "yes"); + session.setConfig("enable_pubkey_auth_query", "yes"); + doSftp(session, true); + } + + @Test + public void testAuthNoneEnabledPubkeyAuthQueryDisabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "yes"); + session.setConfig("enable_pubkey_auth_query", "no"); + doSftp(session, true); + } + + @Test + public void testAuthNoneDisabledPubkeyAuthQueryEnabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "no"); + session.setConfig("enable_pubkey_auth_query", "yes"); + doSftp(session, true); + } + + @Test + public void testAuthNoneDisabledPubkeyAuthQueryDisabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "no"); + session.setConfig("enable_pubkey_auth_query", "no"); + doSftp(session, true); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = String.format("[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + assertDoesNotThrow( + () -> { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + }); + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java b/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java new file mode 100644 index 00000000..b3e09d95 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/jzlib/Adler32Test.java @@ -0,0 +1,74 @@ +package com.jcraft.jsch.jzlib; + +import static com.jcraft.jsch.jzlib.Package.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class Adler32Test { + private Adler32 adler; + + @BeforeEach + public void before() { + adler = new Adler32(); + } + + @AfterEach + public void after() {} + + @Test + public void testAdler32IsCompatibleWithJavaUtilZipAdler32() { + byte[] buf1 = randombuf(1024); + java.util.zip.Adler32 juza = new java.util.zip.Adler32(); + juza.update(buf1, 0, buf1.length); + long expected = juza.getValue(); + long actual = getValue(Arrays.asList(buf1)); + + assertEquals(expected, actual); + } + + @Test + public void testAdler32CanCopyItself() { + byte[] buf1 = randombuf(1024); + byte[] buf2 = randombuf(1024); + + Adler32 adler1 = new Adler32(); + + adler1.update(buf1, 0, buf1.length); + + Adler32 adler2 = adler1.copy(); + + adler1.update(buf2, 0, buf1.length); + adler2.update(buf2, 0, buf1.length); + + long expected = adler1.getValue(); + long actual = adler2.getValue(); + + assertEquals(expected, actual); + } + + @Test + public void testAdler32CanCombineValues() { + + byte[] buf1 = randombuf(1024); + byte[] buf2 = randombuf(1024); + + long adler1 = getValue(Arrays.asList(buf1)); + long adler2 = getValue(Arrays.asList(buf2)); + long expected = getValue(Arrays.asList(buf1, buf2)); + + long actual = Adler32.combine(adler1, adler2, buf2.length); + + assertEquals(expected, actual); + } + + private synchronized long getValue(List buf) { + adler.reset(); + buf.forEach(b -> adler.update(b, 0, b.length)); + return adler.getValue(); + } +} diff --git a/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java b/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java new file mode 100644 index 00000000..b30acd83 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/jzlib/CRC32Test.java @@ -0,0 +1,74 @@ +package com.jcraft.jsch.jzlib; + +import static com.jcraft.jsch.jzlib.Package.*; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CRC32Test { + private CRC32 crc; + + @BeforeEach + public void before() { + crc = new CRC32(); + } + + @AfterEach + public void after() {} + + @Test + public void testCRC32IsCompatibleWithJavaUtilZipCRC32() { + byte[] buf1 = randombuf(1024); + java.util.zip.CRC32 juza = new java.util.zip.CRC32(); + juza.update(buf1, 0, buf1.length); + long expected = juza.getValue(); + long actual = getValue(Arrays.asList(buf1)); + + assertEquals(expected, actual); + } + + @Test + public void testCRC2CanCopyItself() { + byte[] buf1 = randombuf(1024); + byte[] buf2 = randombuf(1024); + + CRC32 crc1 = new CRC32(); + + crc1.update(buf1, 0, buf1.length); + + CRC32 crc2 = crc1.copy(); + + crc1.update(buf2, 0, buf1.length); + crc2.update(buf2, 0, buf1.length); + + long expected = crc1.getValue(); + long actual = crc2.getValue(); + + assertEquals(expected, actual); + } + + @Test + public void testCRC32CanCombineValues() { + + byte[] buf1 = randombuf(1024); + byte[] buf2 = randombuf(1024); + + long crc1 = getValue(Arrays.asList(buf1)); + long crc2 = getValue(Arrays.asList(buf2)); + long expected = getValue(Arrays.asList(buf1, buf2)); + + long actual = CRC32.combine(crc1, crc2, buf2.length); + + assertEquals(expected, actual); + } + + private synchronized long getValue(List buf) { + crc.reset(); + buf.forEach(b -> crc.update(b, 0, b.length)); + return crc.getValue(); + } +} diff --git a/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java b/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java new file mode 100644 index 00000000..9136135c --- /dev/null +++ b/src/test/java/com/jcraft/jsch/jzlib/DeflateInflateTest.java @@ -0,0 +1,353 @@ +package com.jcraft.jsch.jzlib; + +import static com.jcraft.jsch.jzlib.JZlib.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DeflateInflateTest { + private final int comprLen = 40000; + private final int uncomprLen = comprLen; + private byte[] compr; + private byte[] uncompr; + + private Deflater deflater; + private Inflater inflater; + private int err; + + @BeforeEach + public void before() { + compr = new byte[comprLen]; + uncompr = new byte[uncomprLen]; + + deflater = new Deflater(); + inflater = new Inflater(); + + err = Z_OK; + } + + @AfterEach + public void after() {} + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateDataInLargeBuffer() { + err = deflater.init(Z_BEST_SPEED); + assertEquals(Z_OK, err); + + deflater.setInput(uncompr); + deflater.setOutput(compr); + + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + + assertEquals(0, deflater.avail_in); + + deflater.params(Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + deflater.setInput(compr); + deflater.avail_in = comprLen / 2; + + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + + deflater.params(Z_BEST_COMPRESSION, Z_FILTERED); + deflater.setInput(uncompr); + deflater.avail_in = uncomprLen; + + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + + err = deflater.deflate(JZlib.Z_FINISH); + assertEquals(Z_STREAM_END, err); + + err = deflater.end(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + err = inflater.init(); + assertEquals(Z_OK, err); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + + assertEquals(2 * uncomprLen + comprLen / 2, total_out); + } + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateDataInSmallBuffer() { + byte[] data = "hello, hello!".getBytes(); + + err = deflater.init(Z_DEFAULT_COMPRESSION); + assertEquals(Z_OK, err); + + deflater.setInput(data); + deflater.setOutput(compr); + + while (deflater.total_in < data.length && deflater.total_out < comprLen) { + deflater.avail_in = 1; + deflater.avail_out = 1; + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + } + + do { + deflater.avail_out = 1; + err = deflater.deflate(Z_FINISH); + } while (err != Z_STREAM_END); + + err = deflater.end(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.setOutput(uncompr); + + err = inflater.init(); + assertEquals(Z_OK, err); + + boolean loop = true; + while (inflater.total_out < uncomprLen && inflater.total_in < comprLen && loop) { + inflater.avail_in = 1; // force small buffers + inflater.avail_out = 1; // force small buffers + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(data, actual); + } + + @Test + public void testDeflaterAndInflaterSupportDictionary() { + byte[] hello = "hello".getBytes(); + byte[] dictionary = "hello, hello!".getBytes(); + + err = deflater.init(Z_DEFAULT_COMPRESSION); + assertEquals(Z_OK, err); + + deflater.setDictionary(dictionary, dictionary.length); + assertEquals(Z_OK, err); + + long dictID = deflater.getAdler(); + + deflater.setInput(hello); + deflater.setOutput(compr); + + err = deflater.deflate(Z_FINISH); + assertEquals(Z_STREAM_END, err); + + err = deflater.end(); + assertEquals(Z_OK, err); + + err = inflater.init(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.setOutput(uncompr); + + boolean loop = true; + do { + err = inflater.inflate(JZlib.Z_NO_FLUSH); + switch (err) { + case Z_STREAM_END: + loop = false; + break; + case Z_NEED_DICT: + assertEquals(inflater.getAdler(), dictID); + err = inflater.setDictionary(dictionary, dictionary.length); + assertEquals(Z_OK, err); + break; + default: + assertEquals(Z_OK, err); + break; + } + } while (loop); + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(hello, actual); + } + + @Test + public void testDeflaterAndInflaterSupportSync() { + byte[] hello = "hello".getBytes(); + + err = deflater.init(Z_DEFAULT_COMPRESSION); + assertEquals(Z_OK, err); + + deflater.setInput(hello); + deflater.avail_in = 3; + deflater.setOutput(compr); + + err = deflater.deflate(Z_FULL_FLUSH); + assertEquals(Z_OK, err); + + compr[3] = (byte) (compr[3] + 1); + deflater.avail_in = hello.length - 3; + + err = deflater.deflate(Z_FINISH); + assertEquals(Z_STREAM_END, err); + int comprLen = (int) deflater.total_out; + + err = deflater.end(); + assertEquals(Z_OK, err); + + err = inflater.init(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.avail_in = 2; + + inflater.setOutput(uncompr); + + err = inflater.inflate(JZlib.Z_NO_FLUSH); + assertEquals(Z_OK, err); + + inflater.avail_in = comprLen - 2; + err = inflater.sync(); + + err = inflater.inflate(Z_FINISH); + assertEquals(Z_DATA_ERROR, err); + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertEquals(new String(hello), "hel" + new String(actual)); + } + + @Test + public void testInflaterCanInflateGzipData() { + byte[] hello = "foo".getBytes(); + byte[] data = { + (byte) 0x1f, + (byte) 0x8b, + (byte) 0x08, + (byte) 0x18, + (byte) 0x08, + (byte) 0xeb, + (byte) 0x7a, + (byte) 0x0b, + (byte) 0x00, + (byte) 0x0b, + (byte) 0x58, + (byte) 0x00, + (byte) 0x59, + (byte) 0x00, + (byte) 0x4b, + (byte) 0xcb, + (byte) 0xcf, + (byte) 0x07, + (byte) 0x00, + (byte) 0x21, + (byte) 0x65, + (byte) 0x73, + (byte) 0x8c, + (byte) 0x03, + (byte) 0x00, + (byte) 0x00, + (byte) 0x00 + }; + + err = inflater.init(15 + 32); + assertEquals(Z_OK, err); + + inflater.setInput(data); + inflater.setOutput(uncompr); + + int comprLen = data.length; + + boolean loop = true; + while (inflater.total_out < uncomprLen && inflater.total_in < comprLen && loop) { + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(hello, actual); + } + + @Test + public void testInflaterAndDeflaterCanSupportGzipData() { + byte[] data = "hello, hello!".getBytes(); + + err = deflater.init(Z_DEFAULT_COMPRESSION, 15 + 16); + assertEquals(Z_OK, err); + + deflater.setInput(data); + deflater.setOutput(compr); + + while (deflater.total_in < data.length && deflater.total_out < comprLen) { + deflater.avail_in = 1; + deflater.avail_out = 1; + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + } + + do { + deflater.avail_out = 1; + err = deflater.deflate(Z_FINISH); + } while (err != Z_STREAM_END); + + err = deflater.end(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.setOutput(uncompr); + + err = inflater.init(15 + 32); + assertEquals(Z_OK, err); + + boolean loop = true; + while (inflater.total_out < uncomprLen && inflater.total_in < comprLen && loop) { + inflater.avail_in = 1; // force small buffers + inflater.avail_out = 1; // force small buffers + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(data, actual); + } +} diff --git a/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java b/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java new file mode 100644 index 00000000..703aaace --- /dev/null +++ b/src/test/java/com/jcraft/jsch/jzlib/DeflaterInflaterStreamTest.java @@ -0,0 +1,126 @@ +package com.jcraft.jsch.jzlib; + +import static com.jcraft.jsch.jzlib.Package.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DeflaterInflaterStreamTest { + + @BeforeEach + public void before() {} + + @AfterEach + public void after() {} + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateDataOneByOne() throws IOException { + byte[] data1 = randombuf(1024); + byte[] buf = new byte[1]; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream gos = new DeflaterOutputStream(baos); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + readIS(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray())), baos2, buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + } + + @Test + public void testDeflaterOutputStreamAndInflaterInputStreamCanDeflateAndInflate() + throws IOException { + + for (int i = 1; i < 100; i += 3) { + + byte[] buf = new byte[i]; + + byte[] data1 = randombuf(10240); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream gos = new DeflaterOutputStream(baos); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + readIS(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray())), baos2, buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + } + } + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateNowrapData() throws IOException { + + for (int i = 1; i < 100; i += 3) { + + byte[] buf = new byte[i]; + + byte[] data1 = randombuf(10240); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION, JZlib.DEF_WBITS, true); + DeflaterOutputStream gos = new DeflaterOutputStream(baos, deflater); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(JZlib.DEF_WBITS, true); + readIS( + new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray()), inflater), + baos2, + buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + } + } + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateNowrapDataWithMaxWbits() { + byte[] buf = new byte[100]; + + Arrays.asList( + randombuf(10240), + "{\"color\":2,\"id\":\"EvLd4UG.CXjnk35o1e8LrYYQfHu0h.d*SqVJPoqmzXM::Ly::Snaps::Store::Commit\"}" + .getBytes()) + .forEach( + uncheckedConsumer( + data1 -> { + Deflater deflater = + new Deflater(JZlib.Z_DEFAULT_COMPRESSION, JZlib.MAX_WBITS, true); + + Inflater inflater = new Inflater(JZlib.MAX_WBITS, true); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream gos = new DeflaterOutputStream(baos, deflater); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + readIS( + new InflaterInputStream( + new ByteArrayInputStream(baos.toByteArray()), inflater), + baos2, + buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + })); + } +} diff --git a/src/test/java/com/jcraft/jsch/jzlib/Package.java b/src/test/java/com/jcraft/jsch/jzlib/Package.java new file mode 100644 index 00000000..413a8e4a --- /dev/null +++ b/src/test/java/com/jcraft/jsch/jzlib/Package.java @@ -0,0 +1,61 @@ +package com.jcraft.jsch.jzlib; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.Random; +import java.util.function.Consumer; +import java.util.function.Function; + +public class Package { + public static void readIS(InputStream is, OutputStream out, byte[] buf) throws IOException { + int i; + while ((i = is.read(buf)) != -1) { + out.write(buf, 0, i); + } + is.close(); + } + + public static void readArray(byte[] is, OutputStream out, byte[] buf) throws IOException { + readIS(new ByteArrayInputStream(is), out, buf); + } + + public static byte[] randombuf(int n) { + Random random = new Random(); + byte[] ret = new byte[n]; + random.nextBytes(ret); + return ret; + } + + public static Consumer uncheckedConsumer(IOConsumer consumer) { + return t -> { + try { + consumer.accept(t); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + + public static Function uncheckedFunction(IOFunction function) { + return t -> { + try { + return function.apply(t); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + + @FunctionalInterface + public interface IOConsumer { + void accept(T t) throws IOException; + } + + @FunctionalInterface + public interface IOFunction { + R apply(T t) throws IOException; + } +} diff --git a/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java b/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java new file mode 100644 index 00000000..f0693072 --- /dev/null +++ b/src/test/java/com/jcraft/jsch/jzlib/WrapperTypeTest.java @@ -0,0 +1,274 @@ +package com.jcraft.jsch.jzlib; + +import static com.jcraft.jsch.jzlib.JZlib.*; +import static com.jcraft.jsch.jzlib.Package.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class WrapperTypeTest { + private final byte[] data = "hello, hello!".getBytes(); + + private final int comprLen = 40000; + private final int uncomprLen = comprLen; + private byte[] compr; + private byte[] uncompr; + private int err; + + private final List cases = + Arrays.asList( + /* success fail */ + new Case(W_ZLIB, Arrays.asList(W_ZLIB, W_ANY), Arrays.asList(W_GZIP, W_NONE)), + new Case(W_GZIP, Arrays.asList(W_GZIP, W_ANY), Arrays.asList(W_ZLIB, W_NONE)), + new Case(W_NONE, Arrays.asList(W_NONE, W_ANY), Arrays.asList(W_ZLIB, W_GZIP))); + + @BeforeEach + public void before() { + compr = new byte[comprLen]; + uncompr = new byte[uncomprLen]; + + err = Z_OK; + } + + @AfterEach + public void after() {} + + @Test + public void testDeflaterCanDetectDataTypeOfInput() { + byte[] buf = compr; + + cases.forEach( + uncheckedConsumer( + c -> { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(Z_DEFAULT_COMPRESSION, DEF_WBITS, 9, c.iflag); + DeflaterOutputStream gos = new DeflaterOutputStream(baos, deflater); + readArray(data, gos, buf); + gos.close(); + + byte[] deflated = baos.toByteArray(); + + c.good.stream() + .map( + uncheckedFunction( + w -> { + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(w); + readIS( + new InflaterInputStream( + new ByteArrayInputStream(deflated), inflater), + baos2, + buf); + byte[] data1 = baos2.toByteArray(); + assertEquals(data.length, data1.length); + assertArrayEquals(data, data1); + return new Tuple( + inflater.avail_in, + inflater.avail_out, + inflater.total_in, + inflater.total_out); + })) + .reduce( + (x, y) -> { + assertEquals(y, x); + return x; + }); + + c.bad.forEach( + uncheckedConsumer( + w -> { + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(w); + assertThrows( + IOException.class, + () -> + readIS( + new InflaterInputStream( + new ByteArrayInputStream(deflated), inflater), + baos2, + buf)); + })); + })); + } + + @Test + public void testZStreamCanDetectDataTypeOfInput() { + cases.forEach( + c -> { + ZStream deflater = new ZStream(); + + err = deflater.deflateInit(Z_BEST_SPEED, DEF_WBITS, 9, c.iflag); + assertEquals(Z_OK, err); + + deflate(deflater, data, compr); + + c.good.forEach( + w -> { + ZStream inflater = inflate(compr, uncompr, w); + int total_out = (int) inflater.total_out; + assertEquals(new String(data), new String(uncompr, 0, total_out)); + }); + + c.bad.forEach( + w -> { + inflate_fail(compr, uncompr, w); + }); + }); + } + + @Test + public void testDeflaterCanSupportWbitsPlus32() { + + Deflater deflater = new Deflater(); + err = deflater.init(Z_BEST_SPEED, DEF_WBITS, 9); + assertEquals(Z_OK, err); + + deflate(deflater, data, compr); + + Inflater inflater = new Inflater(); + err = inflater.init(DEF_WBITS + 32); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + assertEquals(new String(data), new String(uncompr, 0, total_out)); + + deflater = new Deflater(); + err = deflater.init(Z_BEST_SPEED, DEF_WBITS + 16, 9); + assertEquals(Z_OK, err); + + deflate(deflater, data, compr); + + inflater = new Inflater(); + err = inflater.init(DEF_WBITS + 32); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + err = inflater.end(); + assertEquals(Z_OK, err); + + total_out = (int) inflater.total_out; + assertEquals(new String(data), new String(uncompr, 0, total_out)); + } + + private void deflate(ZStream deflater, byte[] data, byte[] compr) { + deflater.setInput(data); + deflater.setOutput(compr); + + err = deflater.deflate(JZlib.Z_FINISH); + assertEquals(Z_STREAM_END, err); + + err = deflater.end(); + assertEquals(Z_OK, err); + } + + private ZStream inflate(byte[] compr, byte[] uncompr, WrapperType w) { + ZStream inflater = new ZStream(); + err = inflater.inflateInit(w); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else assertEquals(Z_OK, err); + } + err = inflater.end(); + assertEquals(Z_OK, err); + + return inflater; + } + + private void inflate_fail(byte[] compr, byte[] uncompr, WrapperType w) { + ZStream inflater = new ZStream(); + + err = inflater.inflateInit(w); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) loop = false; + else { + assertEquals(Z_DATA_ERROR, err); + loop = false; + } + } + } + + static class Case { + final WrapperType iflag; + final List good; + final List bad; + + Case(WrapperType iflag, List good, List bad) { + this.iflag = iflag; + this.good = good; + this.bad = bad; + } + } + + static class Tuple { + private final int a; + private final int b; + private final long c; + private final long d; + + Tuple(int a, int b, long c, long d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Tuple)) return false; + else if (a != ((Tuple) obj).a) return false; + else if (b != ((Tuple) obj).b) return false; + else if (c != ((Tuple) obj).c) return false; + else if (d != ((Tuple) obj).d) return false; + else return true; + } + + @Override + public int hashCode() { + return Objects.hash(a, b, c, d); + } + } +} diff --git a/src/test/resources/Log4j2LoggerTest.xml b/src/test/resources/Log4j2LoggerTest.xml new file mode 100644 index 00000000..31187168 --- /dev/null +++ b/src/test/resources/Log4j2LoggerTest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/test/resources/docker/Dockerfile.asyncssh b/src/test/resources/docker/Dockerfile.asyncssh index 0b34d900..734a0e6c 100644 --- a/src/test/resources/docker/Dockerfile.asyncssh +++ b/src/test/resources/docker/Dockerfile.asyncssh @@ -1,4 +1,5 @@ FROM python:3.9 +ARG MAX_PKTSIZE=32768 RUN pip install 'asyncssh[bcrypt]' && \ mkdir /root/.ssh && \ chmod 700 /root/.ssh @@ -20,4 +21,5 @@ COPY ssh_host_dsa_key.pub /etc/ssh/ COPY authorized_keys /root/.ssh/ RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys RUN passwd -u root +ENV MAX_PKTSIZE=${MAX_PKTSIZE} ENTRYPOINT ["python", "/asyncsshd.py"] diff --git a/src/test/resources/docker/asyncsshd.py b/src/test/resources/docker/asyncsshd.py index d82f0fec..a76843b1 100755 --- a/src/test/resources/docker/asyncsshd.py +++ b/src/test/resources/docker/asyncsshd.py @@ -2,6 +2,7 @@ import asyncio import asyncssh +import os import sys @@ -11,8 +12,8 @@ def __init__(self, chan): super().__init__(chan, chroot=root) -async def start_server(): - await asyncssh.listen('', 22, sftp_factory=MySFTPServer, allow_scp=True, +async def start_server(max_pktsize: int): + await asyncssh.listen('', 22, sftp_factory=MySFTPServer, allow_scp=True, max_pktsize=max_pktsize, authorized_client_keys='/root/.ssh/authorized_keys', server_host_keys=['/etc/ssh/ssh_host_ecdsa256_key', '/etc/ssh/ssh_host_ecdsa384_key', '/etc/ssh/ssh_host_ecdsa521_key', '/etc/ssh/ssh_host_ed448_key', @@ -51,8 +52,9 @@ async def start_server(): loop = asyncio.get_event_loop() try: - loop.run_until_complete(start_server()) -except (OSError, asyncssh.Error) as exc: + max_pktsize = int(os.getenv("MAX_PKTSIZE")) + loop.run_until_complete(start_server(max_pktsize)) +except (ValueError, OSError, asyncssh.Error) as exc: sys.exit('Error starting server: ' + str(exc)) loop.run_forever() diff --git a/src/test/resources/docker/authorized_keys.KeyPairIT b/src/test/resources/docker/authorized_keys.KeyPairIT index afb8b812..fc007f26 100644 --- a/src/test/resources/docker/authorized_keys.KeyPairIT +++ b/src/test/resources/docker/authorized_keys.KeyPairIT @@ -13,3 +13,75 @@ ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPKyvhX/ ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEhmKgKa4VuG9k/oiIGUZMLKwIGJ1rwKhHHZ/m4ZGhaWbvBApeDPkSj8WG0uz4o+LnQHD/i9rzWiRr5sd27quHErAHFzzLbUUoRvKpVjGEKWt+5yWoK9kkj/shi4Rv5UOiwZRr7dcHX+5L9ff1KpkxsGUD9mq6dVSVttU4Bpygw5PP5tw== test ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKHuAe5N1uLPUpY3t5kyYuISOxUobPZfK8H+CQaJTCALTMFrT63UDDYLyI2xroS67T2bWHkuhX1BHiTGP6JpwL8= test ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPzeZvkHBTpjn1KGR0ZeW1mCEJhpL0TjvO3nei+16zUh6z3D6rs09rSbzwz9ovDzGd0SHN1vTeanm987Ebkpij4oIlypz3D6xSWXQZTQwKxB0JnOpccdeC7FxcLmHFFecg== test +ssh-dss AAAAB3NzaC1kc3MAAAEBAO8/3Ymo6sXoRb7gfTBqwAYPO+ck8aKsoBdQIn2oD9cX2EIjXaoUXv/gK8tVN4EN0C8XADEFXpE3AQU5WhVZ11bv0AXOqGOreoP381pN72aphh9o989Ee2PET+wEXZnZ4PmftMcuhtHWC8Pq0coht7CO6Xt56ItQuvj3nxMwYc0hvEFDOpHaFCyqiann9q3W5bLB9+ek2tJg5s1AVQKnlGCwn1fj+FXY1cRFDsp7AxGJZPDSNjq4awEZwECG/QLQYiWT0FVILFv68SxQeGk6OlOGip4dKkdXPIhMqHy0GT0diBUm//UweV44S9IXPF9Xsiidq1Dn0vDe9e5OYNmV8kcAAAAVAKpXVF5GLMgMtlhSvjICPOy/A+T7AAABAQDEFLE91TMvZkCGeAHHXHkjF02m3CafJ7qLej8RoTEeZxV8Op/XlRluKNn97XF5G3DHp/DWGqBgawbBwqEq7l3V97zRHESMlivPr0UuCvjSmFR/on5nFCbl3RtZJOHOUkamtv51zBfk124uw7+bpnm9aiDNUs+AHNC3SLSyvdBbz6+sgt3kqD+FZyk+7TC/sOm+zuNmducFDDQIjldI7gx78b8D4BBx33+P5qW12PSlKGrQ7E1Rnk+o9WMSJosq/D7lV56waSV6/AJE9lruQr3LGEwN28DrvbVWen8KtvivGlJUwl1OK/hMBS5u+QZTIzN5M6iGB2hfJjBbF+LUONDNAAABAHBA/s/1ShLjRiIfenBfnInfjl3a3DSF9M3/o1fuY6EFkNs8MR4P2aeOfk0zE4gmO3vlT7TFs960tdUdIHm4TFOo+PFp4pjvVSO3K93cAjzOTwMI4sPhB3rRJxrDmx1CWVdnBrWQKO78kLpLf63/QB71qEe7MirH4FXZRLFP39gRcWknBtHmez92CFlzKzqLQXAMfaSbZ95lPh/bwgQ8OCuYExuB3aw3QmoQQdzSJRfWfuCEsUMmJ3Aub/yUMkpfe+hmQwkG2v730MXp/uDXfDXhZKkoh+v7Mhm04wzpxtB/C1dYpmfSP/i5ews4+9Lo9me6l54xy3+k05PPBBhnYzw= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAKU4cgWySBcfMzYy9aSOQwvH5JM1/M4UMNIB5BOZocC2FvJbEok0gTTbLCzGI9A0pe2bnr6EWx78v+aIH6p1GOmbhxZVY4+vkEUMH9Ee8v7XMYqz+CCefWWiAH2bXNa1v7LC9mImiuKmcDdHiVLJwrj/TH3c2EUYlcyQSUGRyOmiw5/auITPrAlmwhyB2ZXLC0X1qNKn+nJiwWxe+SsTXV6F38jbZo+fepbnVEqZ3wTUVnW2gpk0z+LRiLM+1mhASykpmRCyggxW7H2NyNGW+9jVTXD8WCqB/z0o+7PVCPS85qpyfNi6PhH+etXiYnyG8iGI19BWZxDmMCG8Sgr+AnEAAAAVALkJetYWk3MacexWEA0Xa78/o9RfAAABAQCeNE28P6R13v2OUqUtBe6oYQPHOHzMFdEk+qJoXyzKmz7QuQiCGuJ90YUST3fhvsBCOY6lPc6zGfeaVb/r2loa6zm3/0DoOXc/dnU4sbB0auohPlwmjFXDwTQF5BRyxuRWtvPuNgKlgwFd/GcnIwlgW/jRIF3j5wopM3m12+yhZnW+gh575hjvf9edpra7Y1u1VR/jYEQRg+GuXFDvcGE3OTWpphAe0zfjwqIOMYYvx/JQ4HMnB+FxJ4svs5aUOPwBXhMCT9YPWEWVr+1GBVf/VlKTrbapNODYEp0tuL2z8FvakgquJ2XGY74+6M1wbhrrDZ+LPd0Iuwcr7N0ghtVYAAABAEU47Jcr0BB3XEcmy/LsL+hSKudgmczV6QD+fxelS4mY3//8INn+wMRyQzgix4B5yGL6OzQ2RBGlqho2TdfLo5UOjIoQzOpZsjZu2TLZezS5Lpx85ZVuLWwqezx1NZqGDXhtDz2tdUV8eNbePWb5788WXYXOW43ya6Z5RMoywkO7sZRziHH4I+WwlorUpDMgzzfGs2j91bpejsBYcSnqcUDcHJedbMmcUQ53OWocK+/MwoYi8+F/UBuwAoZ8wYauasMZ/ph2k8ygzWHCv7NNteyVAY3o5OvrML4sfu9kmgBIZKKfGZHeFptjQdQcOj7fTI3tuqocwiWVohYBF7laKvI= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAIRPWjGVTl5F+y9uOPu8PPtFyjFy+Qclpqww/vdKq7GVAYPU5zdDHPvmEBZKfklfcROB5Ody3Bf7miR3RoLmmK9SZLDqEint+FdbvSWEooFYexZmYukhS9FeGUwttMOz4K4JIAPMVBxbGMPOL2AzPhE45BT2PPUpXMNZTLft2YHYhSIg30nsx+gc7tK7dkdY38pJ3Qtazc3jpyHnxT9o5qA1OdrA09nYHs9osneC4O29uru4Qz+foNP8XbGbkl1XxfooweQiElA9kvoOX+IgtJGgX2fuexAl0VcRmeb8lYxC0KOo9QL/HRW84nC8ZvwhiOXwbvN6X+tmKqTpcYQXgOcAAAAVAKearN3tBfL6HPzkCHzgGrUbSK1zAAABABW+7UNnhWAvSwiHShVSWm+eHjY56rss8OoYCvVRxDTLYhnZs8GvjlHJTTvuPbNMiWYe/vqwKhBDkNJe2cw2on58GsJwLLmdhsZzGRuCZ5twFLusZsjjRImjBTY5ZNYhdbhM4/527a7bSRLT+QPsedmaEvJHeSMHwDYwEb/WMsYnL7XnQTaPRjetz1a64zWIYQU6PTHW+HRrQDT2Dh/knymQvcw3QC5/ZwNf9k1HS8FdE5HC9G9eQfPCGjiFGLfVfAKbq6HslLZO4Ea8WnFLm5CHz3+mFF+1fHXkahGxlOkPGMr/kSeIvcneiMAqUDINC7VcXShVAvATGyaVh324uwoAAAEBAIM8p/hq3+3N5V885fGyBuO6H9RFmmFsuA5c70CXm3A7CHLNh1+ql72WzaRs3DyTICexOtEy/m+2jFd2suKaSa4F74OMQv4KaeeXrGuC8+EGdWFMN0Tf5UkvR/3igCFdxUbhfEa7qbMJBi5fOkkY1/OCldoHHWwWmp7uJA9oY7AVaFcwVyzOOQtUxBL6rtAE8Bcff88H2TFT2vf0WX/fKMKss69uzbphIIxnqnk152lA/gVWcT/mhCWa6NBIGJqKTNppKExTcLBcLlglqmLFi616A7gm8Y1+gi3dnDVu/hWiyz8uOSE31LB4oJGX4fkhOcKgwMJbB00F2ZGPT7JM4JU= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAOex/Q9Z8ZKXJbCIQnLHKuJdhGNpdIIOsmFYpNGCClPz4ZTZmk0Dbw7sn2KrshVAbi2RXAIz4gJn+MfHRF7okwgAgN94mMP6VG+D4B2ZpgyyqWRYSSdV5AmbKc2VDX+DVK9qKmHlEX3Wjm4Yfw3s1kGN1/eWwI5MrHB7w5tA8Fw8iUfsRLnwTyGPBXbR1ATqT/obLAic6vogcpGojg1s61k8Rlz2bVMaYRu/RQsIdxvUVi27jn00ClTaMGWKH1ZMhnmjedvN7qI7GcAUgNXFjOGa786aPXzJDC0JdNcmDoei9G/ynTJOpMMo8oy4bJLnVqL/kS8v5k+P4JeIhwvgp+MAAAAVALGpbWxdf1geBNR9INuHLoevUHjvAAABAQDijQTRDHymwb7TO5Lxy2P5MBvxlhTlRN4C+KIo5rnZMX+oFeEX5/hG18grUn7FYdx5/4AHP5EhK4GIcoKYJqnEjXvfSuvBvD2OxES8y9QI5Y0DZSGyAsV0p0/9N6a/khkdJ5Zs9S8iFeeJkevIVsCskLRkz6rrBbsuiXB/N3ojtetEQfFuznFVgEJyBNrk7QuVvu43nWnzYaF98ah2o4C4HdjAds57XzyhDks5kJsi1mryl5jvbCSxcf4iZW9OsOHYcjifwfuM+hz2oYYf87obEIIMdOtsvHjMEfTmBqechUqqsRP6wzv0u6g8rReMBldaoG2HFGpwjvMM+PGU/zdWAAABAAhLKqKGhmXzW3ZugZftycNwrxXFeVXFBywlC1qtDH8N/0aW4AvhWXBk2gw+rDXQudpNlVbtV9sT/+9JBM5iQ9txRfDjVeEzdOuXR2ch51p9Ep7KTYoSaBrBqL5KCxsz12G1+KJazMmlbpxQbdWYrAJrlRjTcHnYCDuhyUV/yCnrR+7VMAx5FyIduuRtYrCgo/+Z/Z9UklPU2596/pp7uozkCBR+RkOm7+9ldU+U9YiaUWymVmXhohFJvy9O6tqj3kZYQll2h7NnGpKVeIqp8wNW5BoCyVwV+fIuyCVsWg5vOKTBu5GR2Kc3AMc18Uq+WEeC/jsXZS53ItONrdNsQNk= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsUXXjApfYYCtRK2LrymKvcZpS4cf6agyOj1/VW48g1GVtJM4g5kOjHWRqqsQZJCb8R9CvChCsJp4LPOS3tC36dSS52loeMoti7Z3GtKORjPGKDO/byt7V68l02KZdaO6U91D73a7y9v0g+JsLtR4xkZMd1zXLK3NnT43rtLg116YBsLxAxbAC8eeC1xBF7MNzdLbmg8Gp9dA6iNnDlUhDsf2/FJ8a43WbU8inaNJh6eGd6NtL1fb9iDWho+JEhw7WbyoNrBAf4YlqO+3Y0oO1A4ezOspv3Vx8CkoZTrtyCzVCPEIV/3wSK40VQaMxt4ptbPwQK9mSUI2dG3kuzkxV test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCXX5GPy2zMByhViM5aW+lVH3oPVBVvsfXjb7Afy2ldttHHkDMhoQKMNUScBTdyLwHrje6c0C99aYNkSl5TLXePWx4nIkNFrEyhOfooY8CHPTuhCjfy+4A/psxeE5aoI5YGfffSr0uOVOUdhV3X3p1SmQsybzSiFz0THULrtirxoCbf5a1UGNafYoXPvtSS3/qGJnsnbxE/gL2mTEvOyLI7o3ffFIgbfOranbuow/HSboX/Uc9i2FsbJR4i6K5A6KB3NIL3gFJHxzjumJB4QXt7mUXzo0FWnOXLORFrzYd1Rf26AVcUc24wwStVAOtXO3EIr/AylwgsqWqoStOzoRFB test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdPnSRJDt/YuU5Oj1gq6JKYsJacWbgsdU+vpzAT7IiIVOoGB7oPi8EGcZSt1kIJBc9Lf70uRYCbCmq3v3EOLGpSdhV16oKQygSgSZawq2YwYHzHnonp6akLfQQXwE/nnubX3S6iYAaJSrJ50APhKfQXYsz4CH2alrzYb9Y2TDl4hKfxYABgMza3O02BFooK2tbxy2+0fbOKB/qjamGVE/CoMcv4ChB9FDGdUr0AVy7BYtfELAZ1dPrUh7lMCXa6JRRhFtdRWQGKmfM1nH/KpuzB+yZsl8kfHf47qiX/mwTQM87JMxm7HuV8pHHr7DNFs9g30CreF+hbY1Cx+IKh+eR test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCPJDCMNOM7Huv+NNeP626Slq54aPceMLEaoqlfvLJiQf11b8jXcKfFgLlp/f0Ltk10p+5hAGlzBw/nlYRU85i7Eq75kSb6LPcQ3SNY1/NkrNcZcFf4p/AKeC0s1qgbypvjWy7Zk8AUooPFrhIo/kKaPMouq4hcyENz2VKgpdJ21SzTxjDASj7zd9DZP8uc1Lsg3ftbr8Mi+MFFQRnFPLv/w2zIoLo6xwO0UplUIyAGAsRPDdRZQ50h9b5BFyFLnXUlwI/Jrpgs2J31Vg8J2j2Y3BFDgn/Mf/P9Rsvdo4fp/y+W195198VQ2gsH2+Az+3Id6ySsGhztBP5Pl2Z2K5Pt test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCo/5qxk4V6ZZGDFHDeVBr7ayBkc85+7SPuuY2OnBSrObqo06lw3nlH8dcloRzCt7qYvbmWTutlTujgSOJuvd3A= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAJ3Mot6VLEkkG5HscBLY3geI20FnwrgQi2wzAKiktPDhF39ZavVlOMzvkIGgQDxmo8WoQllY52ed4e8+7KQ76M= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCDseBAbWBVvO3A6gqqd2+Qm9uSTBPEFvvj/huI35w7iYhMmIjWAYwi9av867hF9GsRJaPGSkoWUIcA6QBXarAM= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKIrHi0RIW1gbd9+rJLTPJ5qAyXzNjEHidpxBzRnN22jDcnvBjqnAtfWPBjazs2KHSn/RP+KtyejY1Q+mMikDDk= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBCTReLXxPo93rpl1xlrrx7yYZgD0DzRieTvFO9C3a8Z8blHuZHNy2iljsIYeCPOtK9lcivx+faOrp5FdE2VDroGPdDY2ZAuwF4/UxBiGNNEXWBhRFkupBcM6RbyjvCXaA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBRylqfIuL/Bzq92j3g4UHd3TOlU2aG1U/2W76hrf1svwNPNe5d81xXA6j7Uh+AxOYfYrJRQr9KotRvd8WcxbikMZs5O/dKG+xx8WmTcgvu+jVW4lCbZrZk4S7/hqIlVjw== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPATBzcVXx8g+unFg+8RAEvjiY/ZSb98vuIQ6MAtFCkNU2zgUHI9m/Jn9FitFd/F7KlIh4G5/wMB2dGhks/FYSk+OxgAmhjif6lKLfD5veRCLhRnVbhTQm1fOmU0Sy+FSA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGxZq9s9JfIvKluYcWj/f+OuiV0bJ9RLE8rT+8qF0syMvzZZVRqLmF+a9VxJ4uxxRVvmMQ1CXb5TGtLVdBv1SrviKWMoaBw/sosSMr8v3eoQ04f4bs32fx8/dN4JQbCZEg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACuthNYwDJn4I/b0fdhJdD3ykWe5gMBS91H9qWt0NHh4QTYxVyu3npbLveRVr9AWoF36UYIVF0Z3WjFV4u4fOPapQFNlGDf1DNn8aipy/5iUKY02z+AxnWIIjToN0IPPizMw3LApwmy0UaBSw7sZyfxNHD0Nu8wXsP8A6HG+Y01zSMAqg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEUP4llO/qJcKrD5gZ6L+8+JzEU5RLICud1jgVh+QgtuuAZQUztdee3GEPmR+C9Mv5cAHg138OCNcyRI1eukjFNDgAq3xeh0V6BwjZZur+v5zyTnXQASBwuI+iofZZqS0z8lsM48eIluTZuo3siTlv6uvFLs4ovN/cg67QyGIpsKusESQ== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGIVlZVYKq71fKe0Uxj73A5WPmvTuohTARpvnJ0VB5zIHFEtHRuI7Qkn6pRGv8rbKDRKygv1kZc6rFZ2fgfhLxXcQHG1LwWM8x+XXoWMJM0C0kpCEErKd/tCZstQqg4v2JO0iG5+wmoZ90Cru912t24fhFGfVeFIbLN/xqznIm0dZTN/Q== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACcIEokqqdXr1cB4vJksHNPuIb4HhwaRqaCi22yZa1slGr5+KQhWc6TaRP2yEsXaskuwThVy5hNjfONP/uB5IMj6QCxnR3bA/knuB2n7YRFlbIICLmCxr6QI5bz271MxCB+ccJO1L8sSfEsS6IcxH7O3rYv7hDadwcY9t5VmsJ3t7A67w== test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINknjInRhIeQB0yCD/N+ilxpTNwD2OGA776pYHI8bOdZ test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYLKsn61Pg/YYmumVe7oXik0o+Ca/P3vvN0Om3KCHqo test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHh90f1nUVIpoKQoYxtD66cTGgLZ33D3hCxVqS/N5R3u test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILhbcrIlE43dp+IlosYszkS0c4zpwC60ec1MRwZuxwvr test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADnTDrr21+87QbcPF+2Ayh61ot/Jtf0c9yTy126ki8Um/UUEW6uy87s/9gpS0OHyoH3GO3WgsUoe/AA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADnwdqOa0pIeeorEzsEY7kskGeyZfhpkYZtbA/iR5Nyp0cbPZnl9UFsfKQIYKJwPWwnim8BXTkEq6YA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADk5uIOJgx7OfFkbkRV43yMP37dcnIVemLPjlaH/WRKjLTwueFIgN5Mc4UYcIZtlbAaUhiiEvW7aYQA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkVNtCwBIBWO41k+QYmuMromfii6QbjVKRxjxbTYGPDaw8u+gSCHAnqgaxi9qkc7HU0U01BJI5tJgA= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAM6Xy9zencF2KDhq1r0gTXBdA4AL5Rtg5GYd4tx6qlSxk3ZX634ude5bV8Io2UrsgvIDQHSfj+1J4sDpWnhMtT12KucddwAzLhb+/5gYEr1UCZtPn1WlsvOXrJ9pmAt/MB1KppyG38UX3QK6Z04oWspqHWJBfO0oquUUGx5Ua9JSneJCrzk++Sd1GkhjZElR51Hm7MKV9KZejn/tUZQK2FRjpfP50GsIlAs1v31OkY1glhgs90/6v7PyV1ljp27Nw1aNL+CxOjA/wYnFo+jE5+EsW81oT8C06n4yrcFySWFR96wOfb2fAuCl4K9Ma4jN+7J0RLMY1++KzCsAtppi4KMAAAAVAMbFE/Q0VL6xGj975l/7AqEVxWH7AAABAQCbU609B2d7d5EipWyjlETAs64vaKBXOAREcQr8HQfAkWmSUBr//brBdNWeA4TVla4hyKL1CBiP1Hi6pDQ12lCB+J30ikRqdi45y2MvOR8OfEDy3sn94sLwswARnMwdfHWrPepfyVdqOnIlhsYWzLpc5EhfOThvL8YZ78tPTCqyui8COi3TznOFfW/do0ydEEmfQwJTlHi6dpFiokh+mpQVOlstehmgKzrolLEjccS/vnxSqsdWDwKErbtg0bYhhV3rztiMcMozoBblgCSDiVmvGBAO8GnIoyX5/J+UfuwWYQKYrtuZRfB4hRyRU4zKopPfQrZkc1Jummk959KgfeTIAAABAQCayyiO1dYHPHF6g21swekxrukaXix6sab4m+OLm0bNRa+7nI3A09wG+otKajznna/mdjcIaWuhdG5S4+l+aPypIUWTdM0krUu0A+9Pu9YpIgVgEFKVQX9PBcP5+WHanMaeYmLOCEP1WqFMJ5HiRCJmssOKwZJygH/KEMIGaDGhYnSmN6R+41j8sDAoY9/00WlSTKqpmVGTe7bJGWomnhT08RsATIyvM2i/J13qrh5UfGprj6GsaE9q+Zjg47oMdZIRSgyp4jw9coQoR68TemOmkODmXaoixfrGT1FC9LbqOmN/iDwv0QvLUen3RTbbB2lE3LwObBXJRpd44RjeMo7c test +ssh-dss AAAAB3NzaC1kc3MAAAEBALvd0uyCDZFuKcpphdcnMEYVXnoy16rW5XIFhlxmvVSUDDz3DAGyXISyhyQ43BhI8R0OeOr6VpivujFu0srXrcxcgsAy6yv/6uAvaeLfekc/JErS7OqVqACFmt5ISkEBcvWKnUseFRwoy2KCeiEo2GyKwyV00caLnYb1Th2CUNRFZcH5p5XLj3gwFbPf7EdkKV0PHWV4E5ptFVVT/aRijCJRQn//FlSNlG6iOfUF4IRUpz05qivVGaHLWNOw1fSXK1Pc53qQqCAaEict3arhQCwSEKl1xktSNBDVmeapLPj7LNcZIGvAA52Tpg3qR1rp/8a+bdtteNkbRg4Gfx2uBuMAAAAVAKFdLforDVztYQzh7pFjN8qAjogVAAABACTsvZOw0aPNwdQFiaQik1F08HQrjlp298vgbjIZFrgABE4xeuWMydEcxAKKEhy7Y9Go4l9shUddkHgk3Y+OLvAsifowqfc8Fpsqxh1WKLxnw9NO5rjenO+kNeiwhX/oWa1VBMOa/O+iGTvCy1XcW3u0E1LqGVAx3DdkP6EHV91ma3d1LWih/RR8ClERNaovnETash8NQCHAmKejIObneLtNRqyaiNohYK0t5EAtP4JutVwrsnj3ZoSKrCZct2wVrMUkkqCsKcKygCsr29uk1xpVYBkv9zUn6V199ZB/uXanRLcL/vq0UYWGV9nXU0shVanMwHNQaMk8oWnd6hTPB6EAAAEADwMN1uDvQqkyKel83xNG4Er3LsSCx6iARNWpaV1ZLNrtf2h6MnYCKdSdyVgRjIL5coBQB5yZb/+CXm9QgysH+K5FHXgnh4hjz8W8/Magj+qm3/SKDTmbLAsNvvvenQvayTWUGNQlfQET/tHvGRy7gz4r95phWGmxS8Zu1NkT0RHl6xxcSqWmvvjG/m2MuNo+5lfKFa2+j+Is59HwN3ZK5umpPlJl4uM5SkyFD+iUbptOVCXdVV4xRhIzYy4wllADQQHkbNQtNGJT7JEHuot92J3FWOCEQVw4F06j8NsdghKWIwGSlcHLHAbA8zZDq11R6vnhOFDwmfekxCwLXaBlGQ== test +ssh-dss AAAAB3NzaC1kc3MAAAEBAOGfXe0SXEr7JIMEZQo6rkTc6CfPIWsgqDYem72p84pn6KykGD6RonyqDEfDoS0XpwIY2sCZLmwauEhTQtBZpMyix4sD49w+SjaHUP1aoAT7iPFvsXXGNw9/jgK5a67NzWxmcl5XMQG/VDjtVLWzNfl9OXHMSNmGUKQmlPZKXRrm06SYVZMDsRbgSJgi6PNOatuVvfR+b2hK+ppMpQNKnHXe/vpgcblZ1ViPUTH4vG+04PkidIbwHIIl5Yv69lUYOcycxR3yzd8ZC3a7qzVkxet+tGWCD4/PzfWEP+/G2TWXbRuxV6YEaq9POmI0JuZa7CAZ8iA/OFtYua3TB4cUsSsAAAAVAJzJoUVy/fnPi6UuPLR8cCyhqB3HAAABAQDH61jRVXoAqKJRHgIWhBsrQMmAvOqVHyrALSlAchNT5cso4e7Rd9ajJFg+hVZGKu5PoQdhp/8ERk8mnABKue6r9qO/6qT8NvCRuI0Wb8Sxgw5q/4hzo4BVdx4UhLqTRl9rF7B5wLSjV75HIsqW61ZcbT9+VdYpRaNg8/Te1jfqwgrTndaD7jhkhU2P0Z7vHSRlzaeSksF6b+SRFu+bnQ6neBxZZTuMrSfKOSpp2ltLUvWrQ48GJ0I/uE1HbQehz9Tn0Q3VA2BjpfKgj8bLE5YFokveuqjFULF7EiL4CmAt6qhiQOl/f6zKR4gaLUjkVuvU/uyTr/da7/FfOJouETpFAAABAERldAO0JWHLr1nClDX+ZPN8XbbpjSaWO+fFLQO0A7QE/2gKJ8lt5qi39GOOhmUO10johHaOupqRqe4iCRnBxMJeLxXmS2KbcwDke+IBRaid81ccocpcPOrpov582DjJuwZ0394Akyrc7uMonjBy+JJaPwpXHQS4SXRe7NybuEE4S+DsV6DNx9WG5n5PFh4YELbzdP2oJZ3hyZ2MiZgo02ShziEDPI1vQ4BBAsknp6Ubzt40Gt1yjyNMbGpv8IIRZzu+H8CIRnzbCYKT8mRKe0HvTG48c6la0/wvvG62zfmbFYgkw0tQxgekpf3ojNpRJZ1vcQPgtvQ5IjkCYSF/ss4= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAIYJqKE0jtG+HpbHurIu+kVIuQLrMrgXhL7woINHOHGfb/0O85u+3DsSy+s7f/M9bWUIqRtt2+JV2J/V28mDgF7IEeiP3OjSYspmoxnCy8d6g313NcYYoCqG9f/yWrzSXQtK3hD8+KNqm68GQx2L6lceyyHn3CgUFTwHw0MNntOfAk94PCoM5wOZCfqx9sZfTVk+uQb++aG8xcAqPoPQdTgaqrFa9lrp2Ul8RhM/eDpCmByg8I5tIsbk17IVzS0gLp9slfInhwr2UjX2wIOlVOnNmS8UHLAwhDtdtHcWYdFbEMpO9hFMJCz39JqnJ7g8qjct/bfMxAEqhwlGpgbYrz0AAAAVAL66GQDORVX2on1ic6drzCN/9f4ZAAABAA8eU3kzUacUj50ggFzpTLuBBztILvjIpNGnXy7SUG5EWCZXLvBPTjJf9idxL/0P42AlJqKpaZSsdDLeaPbyUsQ0B9R+eTVDKQd5po5kBmWcxqMJh0f1XU1CYFZgN2cvzmtyx9euekEka/j1mqGLuXHRpX0vak/wsy3T8mEPlqLr09c6hPUDG5U8NXEy3aWS5gRRj7wPDLAi1sFGo4ReUfjn6LJglbCi71tSirnV0dJJvVGpXA17RzetDgZmIjdDur6XdGz+6B07puj/OUTTCFSFzJC0wNKGS/OjY9+59ziFj+M7LCA7HUAlYeL4n83EsZp4fHI09JyuLJppPo/4xxAAAAEAU2ThrVHZ3fT8JuXT4dgJVtgoBTQVf1vbzKA/o5yrOCKj+pPLFv+mXSJxLWvdSUeHkaRZF76hCwSpy6K+Wt9sRSuL6qTPog8TT7Yhk0YgfMd/wXwYlhhoH6HqRIIQRHd1bIOn+n1DUWQHS2w6Ksd6jnVDd5cte+sBRNFeNjRJPUg2dzFqYQUgf5bfDu6/mhISduez7BctGb0cWtVPyjzQivJRUVnLiUJJE3V0saEC2FYOalknyW2x99tolUn4YwOLjUuQm1oKZ+BEbXlgGe4pKe/JxB3nYHT78wP7ArnhImPGK8b0+/JD4nAA15GEkFLRV/u9qyZJe/iwbpqKz2vB/Q== test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCclEvT+gGh6exRwmgPhsN+PLMpcNltztLQ1x8iKOsxCCbXWXRxfvvG/ye8lp4kEOWIZ1224UoG1401aNBVTGNUT7SVyAFSdnHG7AGgFnLwJcxrsEHLX/f+gj/m4syNJfAg1PrS4O8IeaEnTdql9x4cYkzd+Jpa/OHZLgltc8SHezhmxrc5Ylm1IMHu6gxJb0WwQ1mIZ44IAIaYvNflal7dP8v//bY+6kvS5iARk6hDfiR/wH3926r7U0pB3siNDdz5yq7rjvTg6igtOhQDSnd9Drp0yHjBCyQoiDqP1P2jhyfz1iDdf6u71CFhZK20rfbsUUHCjWilB00onvjjIDYJ test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCMTxeJiE+rerxDpf3UhJRVvaWulraY8VZryHhSPnHCODQw3MsQ0X6ECmTs8c3YeNRBRhiaB6zOQorUFqFbAwO2XIFQgDGk9qSJF1kwsVXKnReFqUQ4MQGjRt5odjHc4+00KnmfdLUubvLGJmzHYJ4Ia1+EEyzkUik4nVTxdreXrvFEIEej/QHJlhVSqsL2oKXKVMLAJ6OvGt9K1pFSJRCyedHVmR/z3tfsiFqpw0R2P2WhhkK9jXG47tJcG9v5HPrGJqd3D+W982/YUFNFvLMVkq5qU4wEDFVh1u7Z95dABtZm2TwzhrYfxdAIcYbZLpbFozWX+f2mEQRAbWws2YE7 test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyu2eTD/3HRZnFhKMiy7YLGW7WUsT1uEgRCUufHFbjwZqSdUMXplB7wD5g/iombD36oZtFGpZWjiReT8hh2k2dR5pzF7Hg9yTZosuBu8YRSTjwicc/NHTcDfl5kQFSGz40azFXWn1xJqPRQesDGZ2rQVloCxW771AVS5hZvAybtM/qf6En7PxHJE4Eg7klvjH2bzhacDQch56u/ij+/ipwtqkWt40k6sMQ7xtRIQH4AUNlqQ+3qpZxKfAl+NCbJ/V8Mj9BM3WuyeD6DYPwBT1mHXug9KWi7aii2c/UvDv1xpF7u0mTRSW4Dkm7FTEbX8iPP1QF3ze87YEaF1qRkeMf test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnTnSVyVw7nyqnxy/WWUUuFswXUsdwvNtojMVuqltV3CTDHfX6+uMu+XBC6Gqr/AzHvwzOPvzR+/k+m5fV5zLvjfe3zTU1gs5EmOtu3YbOnn6il/IvVvU9ILq/5Dg89aIbSYG9R28iu8KciZxHOOpjgFpmoCiMAg8x4OWRHc6irKj5VKTXMJC+5pmb4QZlSKal7KPqKyAsKII6IxMjpPNJejZm/Omw3QZ19YIN02ttbM8rwoLuPLi1DoZiAUvF7J3XTEUcDAn5y5upltk1Dx+bWhuth/sYZ26qksxPr+KmtHwJ5R8pWJ4CXtENlwxQiAWftVWW8MeQlyN17qPW+Z9x test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLoiF7se+l2KwZh9oxhfTASh5diPDNvYbVgS98Q4yPE9uExiDDMeX2Nu9XLtnevuTWrmcAF6u6/ozI3BIkeHiBQ= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGrSiZUMuXa3aTNDF/H+rTZb4wUBapMIf9QXzfoJI6hOYPcCtHGH8KChdeZ2U9W3ohzX/WZIX8IQ9fpxcGyvpbc= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDMTBUib+twute16i5Rs2zKlVDMam5/RMWuFcXs2dvIs0kLP5N06diHYHBqvMPT9N336KMChz0/O4S/dXR1pwHE= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMQNLejYwJxwVlAGIVi55Lb0zfkz18CEAwqDtrr6xs2sz+vqRU1xadOgEzHeWovBqCXdC9wVja202PyupH5QPsg= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBnR7wyGL+IOT7A7vKr4Do1OGlsMhBFN28GuKUohpkd5rvvZfe/9UNbdobFa38KmLWpxzAlL9Go3AK6+7OpWz6HE8JHdrJGyRYoS87XVv4oIIzmp8B8GThUYiDt8Qh8FmA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGdfKrIhLUFtiMfBOFdF1LulPAF3OKNqlahGbA0bf/LXPf7tmbJUs+GvozsqYAbsMhed1vmwfBwpaQdIG97JRSK4MK4yIrCjDjGwqmakX/hg/WUQZPi17463t9zSzFZ8xA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNShRribjCP+pkCkAMwdj2LjxNrZjIKHtylcTdohEgfHJTXzub4tbq6odNS3izPwgMEfX6M6Vl5NBIstk3c34+kDuJ/a+Cyn7lZ9HZPdDuaKE66PNp3zGK9linfzn3iFBA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBKGDqZyyEHaKkg0ijDH9nRu86AiXmUNJcKWC05ksthEMheDRMyZtyqzIIwQI9Goum+Tktu3LeQS4fBh9aNHwMP2xN5uvj/uUYxN1dmQmLQ4NsQhpz+DY5TtxHJ/tUKvNpA== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADwW+/zw0OOBYVS2jEOym6OctYKqb1/uSTegImb7XOcklV6HZSnkGDrp89gzXL6AgoFWMQkySgR93l1j22kCwxGWQFQBLalq5ekPGpqBiE31QNot0QKPat2f4O8bw0DO5sUNPmH7ImKmyxAjHGiCPhi5egoIgrgzHoGnx1ynMr32Pntkg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGCln1Y+fp/uQTIVhjoNHuuRGVSzHc8shdMTGUQz0q+TpDSEf+SViws/gS0+V73QggV5TcUqOIOra4uD8mGSieuUwHea4ieXw+cbcts9lpYdjWkFmgSGwYfIVyosFZ+aE07We7fNRqSUpH5gyBpdSpFKAiBTwtv73FOIwz88I82E4HVQw== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAA37JVxrRSMTbtG8SUUuPzzwjsrumeJD+100IbXoAcXK0mPbGei04TdyMTpfy1SDWL3YND5CSzcuHMOjApNKrxJ6wB8A5LE178vFKb7kvYo5SJq+YAbv7utQqlMH4sdKdywGwvdSTSZXCu+mzx9ebdoyRsmPS7vBrJYKcQPHiBDa9iVMA== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAKniAtk8UOBIVa/348YbbZAyiLgJKpbH+2rEB0tHG7YYf+fAAGAZJsE7K7bl68XzunBT1HguytUpcjzNgOS2hx2AAVJT1ObdU0IK8+B0DTXrntG8BMKpDjpHZJr/RQLEDljHt6kc355TLoKZygF7vUujNjceWFjSmBlFeCbr5Fl4KoXw== test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFrJ3kAPWlhD8uR/Mo1dqhqtJIYACuH6vIh7/oP+r8UY test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINnWA1NU5+6rlguJ4vDwB7Ro3wTO+ntaSPfmWIXjp384 test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGGj5a3dN4+iyqsAXenA/CzejFmBNyZinnYHhXXcjbBV test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEy7J6rEkUafMJmtqE8SpWZg/uYx6EcME5FH3E3HB3rR test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkhFs87N5abAoXAcsLiYTDOCujVJngP6C9j5dSttY/EBgCQBMK60a0vt1lF7b7Drby5kqg7+IvkGgA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADldNmjRx57BM40rrc4DabuF0L+RhEkOcDC8/jAhtbdTO0X+sqgITai5gSk1JGlIrCRIF83Kvs0TKIA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkVoUhABT+RHfYEGAMKySSyUSoJFj6HHcz87U+io4Dpe4ye88DGUU2Lnxfux/WQq61DPHWxQHo4GoA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkdO1HUptQ2ksEmblqqTM8loY2Q/Ep003qRxW753XVaz8eh9CejJvTO2FYtyHNib5syBD814By7C4A= test +ssh-dss AAAAB3NzaC1kc3MAAACBAOGamTQ4i4AEWhMGssPosXwsaLBWIsldlycRQEfRCRlmi0hzdmYvuJJn+YQ2Jf1QJ49jTTxlCuFJLrDct+UxpittxRtDb+AdDpmhGp/zMeNdPBsfeLX9REfANyveQrfT9hqZ2gIbMOzJ0wRPgInmSRi22A0It7vxGaw47J5bAEf/AAAAFQD/Q9R8eCV6qL7aQr4E4aNOzZ5JdwAAAIAaUAApSPx+S6f+H49ml7sHdAQC4lVyKVQVh0RG55/rsIrd/dSYgo6LZ522vzNgxw2BmBeFJUpNZkQzIGmxN2FkTmlSgOxZkRH2oZA2j04vcskJgIiN8K1rhrpRUtJjcKXnF9YRn0eR4o1xph74ySPmIWX12GULB4R1j4nysqDmbwAAAIBL1zviIOohyjKKILovL9rPlTLHlcdXkyd5S3PZImq3CXDY5mQFlxH8r/hEnwMBBTAHmun7ptcdH/R5kZVB6v7xsAdJXDYiYOyoLI8yL4aWbjs3flriPXVmCb0hCh4n6pSKVR5IHLzsABABlJTULxSkvWcKwMms7arQm46WR6MPlQ== test +ssh-dss AAAAB3NzaC1kc3MAAACBAMui93cyTI7R14Prqeet0joIvIWWGSw09hk6qDZvgKW2zzs5X1BTScrnLMnaKOgYtwDbFN4OQsBLNZu+PzcAqfwJBeTD4mGLRVk1k1zK6jXpdPr3V1sqQP7OEVa6MF6XsIf/Yu3zFvqpwkoNnrCcSrZxLMnnW83Ya6NG5d7WGBw/AAAAFQCHbVl3CZvAlhbRo/yMAY4do6Gg3QAAAIBb2u2D16qhuFq8B8UjBt1HSo7R/KPjRI0YzrCpJNoYjB743wARQMl5yDU67IuiIAzjxFZBiK5u+mTKpuO1v7MeTc9IFQ3ObR6N9PO1wUAZ1uv0KSesnxjgbZNIHwGwzJ88T43LEbugZleVEifbbIZl7puEgQdFko9zEgqGhOu3VQAAAIBKDmTnJZliffrAxCElJnnrM2SLo+Dl/cdtCDdI+3RBG7wTPWqMF8C9VNC5ssbi8QLDwl+OPrSIERPb3VXtjkVxQna36JK3od0aSx90VZkv7RPeL1Gt6Q/F+Td6D7p5ZCGjHaQkEk4kfPjTUnLxKn5FGxmY1HV7ceXo17AElmfmyg== test +ssh-dss AAAAB3NzaC1kc3MAAACBALDWcFnpSWiwGbuyqVzo3ieAm2K9+BIUYjPlA69UCM1GulxzCj9ImbQDvMsuQ3tKpMH6925NoGU5G/atRnqXouJFNcOi43kqtHx8tE1vV9v8im3+gGsOF97pVgtBMHNQr8sjDN0FrtB5MvLMc9c5b/1lhrUj5PeupHIJzrhvAvsHAAAAFQCeztLdFSYoOE+Nq/XDQh0BuuuM+wAAAIEAh8p4L7O0vUW76jSJvE3v/yPG8bcBpZporUTvG2DqKP/rISyRblRoYlwmlaTzyrC0/HrUOqw/1fiyorr+KoxJVhOavlKVq/YSGpxmrgt4eS+VXr5BJyRGxDaPF4kTAu+2/FVMu4D1clK8BuR++phu9DWN32sWAzw2IYwOfhB0b+0AAACBAIUb/bDKE9DAGpG+zMyida0JTIIPHqWvkSa2dUXU9J+4iV+Bws+X3bEggZDeeDAKd9FmFTujek25ihIJD7tOu/CCFWEcUZ46aff/RQ5bfCKsJ2SyoSRvFGGal3541YvgK5A3DgzRjEGhlF5KfJ7HIcAdTq5CYA8uDBZngA8oeQfz test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCOSYk6+5VS4tJqK2Z095cPYHjlUT4Gqm2MM5xdzM2nK9bd4slUyz8fFYpWAIvu1G6Q5z9e/2Kfcme86lCjN/lidSNHQBpz6xW2WQTuTPfSPW2lBblRovlezhypdHTdMt1NB7B499py1BF1iLO5Y8URPCztTXawneMnUjvUp8g6dDKVDGNBssgwZ3TR8Fj/vrOkgafwtJAl/X4CJBXdt45dLre5eRXEoWDxTdW/GxUw/PtV/mW4Psheo3/RoIFequh2m7U8uSdXKvGqm0ZlpSTSoen7uELSd+9iY3E4ULdm03Pom/O3WO+DOCmRb5YUTNFKZUVop3DKizjEggy9mbb5W2iJp+6K31YWGWHCR/HhFr/61R6yQomXzHhbqAEGH1FsBmpRDa6c7a/b8Xl4w4kbBlOOs21G+tvL9l3hYqBfWpPE8A72WGdkzaOltvt9TEDroNI2yopNWTVXuUvJ2maFqiwf/4cYpGZqu3V4ccBpTJbhHQ3G3w5XVQ6Np42ez8= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDIUGLfSbG/8i18Y53PL59UJ71RN4XTKJEvVljZM9K0HvFkc0wOlzTOXoOAEJiTIq6JXVWjq31BJoC/x20ySmaj6hSOs6NGrnyxXzSbM4E+PzJo74avh+NXZ6ojD3CKg7XuShgX5+PUqEiAffK3TdzRX4NlkIfiIaHi1O+Q5drSGwnoB1C5sNeimLDhO9IPo699OvQu1Obh06gygWh5pnu0NBuiv7rWwBx9S80fJvqhAQATxv2RWFDqcvn/T0WApJs5FCpyLxKozTpOenB9OGaEEz3mpg6H4KPL6UwhyC7YLr3rRKyjff08uELvdQzsO8q6tO8uOaQngLFhLe5lrlV test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCszdi6vvJvN331KkQI2SVEtrwBHQunxWGfMP3HnSn4aH656Mcx/9a33V/W5KwKoSgVeRsHVnbjby10yP1PaU898hILUDFBazlJUyfxvMybiP6TC6LMomCFqoncaNJ+hR2BsEQmrIfmD+cvIFADz8zPtjDeOSAPQumP42C5uXJKNKYM8nmpCUrHo8qSvbVLTX/UC8mZweenxY21IxnKSZkkuLkRtqp7+p+xc6bYNfXcdb6LyrR/jmD2GsnEXSmWFPxvYGAErUZBSAMF2xYGG34l+lCB7Pzvpfv/mB0MK46YL+pdKC/B3JjDu2izt7cEBTlhd/TwzNah5OfsXtVJBNlYi9FU/Ee7A6+ylb5WnNasU165ngIVBBL5UNg8Hg/W6XJJmAVZg1PsRbikNNszKeaRrTC3X35PEr6uab5hlcORcAWV2FYoYWN8iIv0rFJyaE7InpxSHp/G3lE6XHddcJrq6BdmURez3jShrCYg7DdnE9Nx7dBX1rbJ46lLxKY5lQs= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGwG9ZwJjv29NIb0Cx1QsCboiidCStHoza+zQQMvs9nCBMRnABQL9ecKQeUGClj22ll8qRg6vWbHrQn8+ZUCgZg= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKjOhkR/tN6oSku7uJXEqDZh3HsYFeCi6F1rnDR1roSpLy2HQYGLPjalt+E+sQhayJBOLJd8j2idjIRNK9trrYA= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDTDzvrOOKrQoRhWJCf5ZyYk2ugd2wAayLt13XYBPJHArsgwCKMktyW6+dmKmskqnVDEPqQ42RBO0kcJ78y+3l2i1iNXr5dDSkJ/2J28L4YxzEOU6HhVn8wBA0wPYNpsgw== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGYBJ8F7NOBihGiGXVBApYIXiLlNVW+SSYANC3okjqsax5eFcwyfQBRSWSJxAXKA1aV5vtYMN/nBwvg5kEgftHi3L3Pye4VA1PKyoa73E4T7AsAJ1eRrJp31ecSXi1f9HQ== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABdYMYEOU8AxcsHYPdHwtbndVLfJZsCFFQ8oMUt+xHi/MwISVkojGjk2FOtUeUnsc8SWXHEUC6GDSqy25+42GsxWQGL7/IyR2X0Iu9rUooKrKg4EsvQNEDfq9Joj7zuWOmAQBQK392fbXdYYILjSAaG5at9mhiw8Kr65ShK2IUv4n4dnA== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACWNfRwfrfQb+K8d5+VutrDWme+Pk6UyiDvbuS48bCg3e9ptaWHQVtQ6xm1yLYUeF/ZVWn20+s2DntLGKX8dFCaSwEHVmX4oj6hx2V5zprevf3GShfyz+HXt4qzoPzVMyOPY/po4d5oxM1RM7oWaqNwvuVhU9Zk5MbJSzKq3atWJv/+AQ== test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINKulAsCNhbGFNT4DlsMmQt57Y1/i40T2EWpeUWjiDL6 test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2oQIAotMhMO8LyWecYBIXFlxSFRoxm8A0Hpbn1mEOJ test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADlrhnFj5z0wgxD0IY7ECoZE3/8v9cUWP7VJnyp0YPY0aLKBLnagj5/GMDV71bYW56Tl7AFx3EQvxYA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADn6JKamPkXumrMxQnui53u1e7U0WkROtrmraifd6LEDfbEW78xEmNPSVZARg0ydaeNYzc+iEGG/cYA= test diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml deleted file mode 100644 index 45bee0de..00000000 --- a/src/test/resources/logback-test.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - ALL - DENY - DENY - - - %msg%n - - - - - - diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties deleted file mode 100644 index 571d2905..00000000 --- a/src/test/resources/logging.properties +++ /dev/null @@ -1 +0,0 @@ -.level=OFF diff --git a/src/test/resources/pkcs8_dsa b/src/test/resources/pkcs8_dsa new file mode 100644 index 00000000..1b06e2b8 --- /dev/null +++ b/src/test/resources/pkcs8_dsa @@ -0,0 +1,9 @@ +-----BEGIN PRIVATE KEY----- +MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBAOGamTQ4i4AEWhMGssPosXwsaLBW +IsldlycRQEfRCRlmi0hzdmYvuJJn+YQ2Jf1QJ49jTTxlCuFJLrDct+UxpittxRtD +b+AdDpmhGp/zMeNdPBsfeLX9REfANyveQrfT9hqZ2gIbMOzJ0wRPgInmSRi22A0I +t7vxGaw47J5bAEf/AhUA/0PUfHgleqi+2kK+BOGjTs2eSXcCgYAaUAApSPx+S6f+ +H49ml7sHdAQC4lVyKVQVh0RG55/rsIrd/dSYgo6LZ522vzNgxw2BmBeFJUpNZkQz +IGmxN2FkTmlSgOxZkRH2oZA2j04vcskJgIiN8K1rhrpRUtJjcKXnF9YRn0eR4o1x +ph74ySPmIWX12GULB4R1j4nysqDmbwQWAhRf04nB8DdbuA1JAbbkaSwORY/Mdw== +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_dsa.pub b/src/test/resources/pkcs8_dsa.pub new file mode 100644 index 00000000..19adc149 --- /dev/null +++ b/src/test/resources/pkcs8_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAOGamTQ4i4AEWhMGssPosXwsaLBWIsldlycRQEfRCRlmi0hzdmYvuJJn+YQ2Jf1QJ49jTTxlCuFJLrDct+UxpittxRtDb+AdDpmhGp/zMeNdPBsfeLX9REfANyveQrfT9hqZ2gIbMOzJ0wRPgInmSRi22A0It7vxGaw47J5bAEf/AAAAFQD/Q9R8eCV6qL7aQr4E4aNOzZ5JdwAAAIAaUAApSPx+S6f+H49ml7sHdAQC4lVyKVQVh0RG55/rsIrd/dSYgo6LZ522vzNgxw2BmBeFJUpNZkQzIGmxN2FkTmlSgOxZkRH2oZA2j04vcskJgIiN8K1rhrpRUtJjcKXnF9YRn0eR4o1xph74ySPmIWX12GULB4R1j4nysqDmbwAAAIBL1zviIOohyjKKILovL9rPlTLHlcdXkyd5S3PZImq3CXDY5mQFlxH8r/hEnwMBBTAHmun7ptcdH/R5kZVB6v7xsAdJXDYiYOyoLI8yL4aWbjs3flriPXVmCb0hCh4n6pSKVR5IHLzsABABlJTULxSkvWcKwMms7arQm46WR6MPlQ== test diff --git a/src/test/resources/pkcs8_dsa_encrypted_hmacsha1 b/src/test/resources/pkcs8_dsa_encrypted_hmacsha1 new file mode 100644 index 00000000..959a4c42 --- /dev/null +++ b/src/test/resources/pkcs8_dsa_encrypted_hmacsha1 @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBnzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIILw8uFzY0qcCAggA +MB0GCWCGSAFlAwQBKgQQw6RQtTSUAB05u3PwE4Qt9wSCAVCIpjeuXs1WOfohxzCa +AZYsQQ0hvJFa1DN5UTu0YwL8No71FAb86zoOcjKcrx96Vu+D7VnCCiJuYVpFalL3 +OoYpOpHY3m2ri24C31bR+2BY60GfCncK2LqlQcJbgQck4JGKJL1laN+9+6x1qXnQ +72D0gEWDWSLHdgnB8lAwcuh/B6ZBwwqJYh4njOtjAwOWb7ynG1pKm8C4Vk55UwMi +JT5gTpRMVONuj7w4NtROJmIe1eLAzZn9nZWo2y2dVzafCH0RcOZaEBJ4nBEYi0hf +5eU0irwWXdoVzegPYR3j/xFSjVLSb14rR6XBjgF+kaH9KPIDTqKzCqXplrNK70+v +H/IZbIgAKZh3YMU7GrBOArEBD9N4qdxJqO3OvgQ+WJbXXE5gauxgVFMYdXpUJXCV +q+FdjhzDJAZzNlj4OsZx4YQqHroeixFV7sdg/T2/elypkCE= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_dsa_encrypted_hmacsha1.pub b/src/test/resources/pkcs8_dsa_encrypted_hmacsha1.pub new file mode 100644 index 00000000..be0e70c6 --- /dev/null +++ b/src/test/resources/pkcs8_dsa_encrypted_hmacsha1.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAMui93cyTI7R14Prqeet0joIvIWWGSw09hk6qDZvgKW2zzs5X1BTScrnLMnaKOgYtwDbFN4OQsBLNZu+PzcAqfwJBeTD4mGLRVk1k1zK6jXpdPr3V1sqQP7OEVa6MF6XsIf/Yu3zFvqpwkoNnrCcSrZxLMnnW83Ya6NG5d7WGBw/AAAAFQCHbVl3CZvAlhbRo/yMAY4do6Gg3QAAAIBb2u2D16qhuFq8B8UjBt1HSo7R/KPjRI0YzrCpJNoYjB743wARQMl5yDU67IuiIAzjxFZBiK5u+mTKpuO1v7MeTc9IFQ3ObR6N9PO1wUAZ1uv0KSesnxjgbZNIHwGwzJ88T43LEbugZleVEifbbIZl7puEgQdFko9zEgqGhOu3VQAAAIBKDmTnJZliffrAxCElJnnrM2SLo+Dl/cdtCDdI+3RBG7wTPWqMF8C9VNC5ssbi8QLDwl+OPrSIERPb3VXtjkVxQna36JK3od0aSx90VZkv7RPeL1Gt6Q/F+Td6D7p5ZCGjHaQkEk4kfPjTUnLxKn5FGxmY1HV7ceXo17AElmfmyg== test diff --git a/src/test/resources/pkcs8_dsa_encrypted_hmacsha256 b/src/test/resources/pkcs8_dsa_encrypted_hmacsha256 new file mode 100644 index 00000000..e71e348e --- /dev/null +++ b/src/test/resources/pkcs8_dsa_encrypted_hmacsha256 @@ -0,0 +1,12 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIf91MBHTU55UCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCKkOxM17NPpYz8mDja3yvOBIIB +ULDh7/2bZa2i5p529GCVrTjLYFWT9wLkkN6TSAGbVHeyjHm5hasrDlfK7Gni93Qt +Hmaii/0PBvWkcf2mQPKujl0gwnPel+li28duaysYOCNwMgadlN9cTs2y/+yAXzoa +q7UrQLxckpuDB3/hxn6WWDO7GRhthW3QmR6yzh2E0CzKhB2YU0tTGK7KHzGp+cS7 +Z0bwYlRlDE3YnDgi3Rpt3O5BT+uA0WOeSw1xd07jm/+okfhB3xHmM+vfO97476um +2POcDsth0a66j3p1bZkNb4NHQEp8Ud+ST+4j90lb9Eu76bXGIOHubWRN+ytyITBn +2O014+oOIdn00b38/yA/ej17fZPumEYh1fQHOlmOGi3fV9kEiTZYtiRYYET4keY4 +nduV4MVBjAFvtRWArEnkdeUqAqyEFbIzWADFxRZtfPCv6Pxr1So5DQatpe/UJiQC +qA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_dsa_encrypted_hmacsha256.pub b/src/test/resources/pkcs8_dsa_encrypted_hmacsha256.pub new file mode 100644 index 00000000..b69d6df0 --- /dev/null +++ b/src/test/resources/pkcs8_dsa_encrypted_hmacsha256.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBALDWcFnpSWiwGbuyqVzo3ieAm2K9+BIUYjPlA69UCM1GulxzCj9ImbQDvMsuQ3tKpMH6925NoGU5G/atRnqXouJFNcOi43kqtHx8tE1vV9v8im3+gGsOF97pVgtBMHNQr8sjDN0FrtB5MvLMc9c5b/1lhrUj5PeupHIJzrhvAvsHAAAAFQCeztLdFSYoOE+Nq/XDQh0BuuuM+wAAAIEAh8p4L7O0vUW76jSJvE3v/yPG8bcBpZporUTvG2DqKP/rISyRblRoYlwmlaTzyrC0/HrUOqw/1fiyorr+KoxJVhOavlKVq/YSGpxmrgt4eS+VXr5BJyRGxDaPF4kTAu+2/FVMu4D1clK8BuR++phu9DWN32sWAzw2IYwOfhB0b+0AAACBAIUb/bDKE9DAGpG+zMyida0JTIIPHqWvkSa2dUXU9J+4iV+Bws+X3bEggZDeeDAKd9FmFTujek25ihIJD7tOu/CCFWEcUZ46aff/RQ5bfCKsJ2SyoSRvFGGal3541YvgK5A3DgzRjEGhlF5KfJ7HIcAdTq5CYA8uDBZngA8oeQfz test diff --git a/src/test/resources/pkcs8_ecdsa256 b/src/test/resources/pkcs8_ecdsa256 new file mode 100644 index 00000000..e7536772 --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa256 @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFQCSuRDPQE9G+4NK +nZDeTFNJ0lJUy0aqUr3/hToG8yyhRANCAARsBvWcCY79vTSG9AsdULAm6IonQkrR +6M2vs0EDL7PZwgTEZwAUC/XnCkHlBgpY9tpZfKkYOr1mx60J/PmVAoGY +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ecdsa256.pub b/src/test/resources/pkcs8_ecdsa256.pub new file mode 100644 index 00000000..359fcf5c --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGwG9ZwJjv29NIb0Cx1QsCboiidCStHoza+zQQMvs9nCBMRnABQL9ecKQeUGClj22ll8qRg6vWbHrQn8+ZUCgZg= test diff --git a/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt b/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt new file mode 100644 index 00000000..4b5fe134 --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt @@ -0,0 +1,7 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHkME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhlStjRMGQ7hwICQAAC +AQgCAQEwHQYJYIZIAWUDBAECBBAdI6Cy/NhVCMbKL+x3uaw9BIGQgUJxrLtS+ygm +BYp4b24PS568lpErhJy38FXl82atJimDxQhEECU8ZsMj5mdnGocGEyR9WvwFQ45Q +MAuuPQO24WgvWcDLQXBF2c1ZtyJwrFLNEZ1eW2XxZqr3UEqyRgIPtUcjpOftTeH2 +BKksRQ1tixUBL4TuFX1ph1uuhO57pf4QDD/3cUeg829dg85qG9U1 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt.pub b/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt.pub new file mode 100644 index 00000000..4f629d80 --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKjOhkR/tN6oSku7uJXEqDZh3HsYFeCi6F1rnDR1roSpLy2HQYGLPjalt+E+sQhayJBOLJd8j2idjIRNK9trrYA= test diff --git a/src/test/resources/pkcs8_ecdsa384 b/src/test/resources/pkcs8_ecdsa384 new file mode 100644 index 00000000..1eb4c26d --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa384 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDArsAu86PviKZkcTLOV +i7Xch/I8gwEH+RPFb5VTH+N5USzfRqDzFbjSBQDyl8+uteOhZANiAAQ0w876zjiq +0KEYViQn+WcmJNroHdsAGsi7dd12ATyRwK7IMAijJLcluvnZiprJKp1QxD6kONkQ +TtJHCe/Mvt5dotYjV6+XQ0pCf9idvC+GMcxDlOh4VZ/MAQNMD2DabIM= +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ecdsa384.pub b/src/test/resources/pkcs8_ecdsa384.pub new file mode 100644 index 00000000..fb1380f2 --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDTDzvrOOKrQoRhWJCf5ZyYk2ugd2wAayLt13XYBPJHArsgwCKMktyW6+dmKmskqnVDEPqQ42RBO0kcJ78y+3l2i1iNXr5dDSkJ/2J28L4YxzEOU6HhVn8wBA0wPYNpsgw== test diff --git a/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt b/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt new file mode 100644 index 00000000..2827d46b --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt @@ -0,0 +1,8 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBFDBPBgkqhkiG9w0BBQ0wQjAhBgkrBgEEAdpHBAswFAQIAICpko7c3GsCAkAA +AgEIAgEBMB0GCWCGSAFlAwQBFgQQYR9mSZAzK41nexevamz0sQSBwG37I4qWSkb7 +wpldjYIYd3YMX0h+E+9uGgxZeBW74bQ/yM2F2GoWjDnw38YEJZceNh+RA/COh6b4 +MqD+cIjmi5aXhtDKnnqyuYeEAZtRRpWEKO8xCwIkY0kG5dNBb4r9mJRbDGJU68zt +bJGT2j5YXebwgszCt3UUhTXqF1bObYDdwx1SMOOMpkjn5PfGS9//EAF+xXBEXMLJ +h802rFX81ZRe2d6WhW5ypZ6ZAmusbgdtlY00R/iKLvzznx/zkHeH3w== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt.pub b/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt.pub new file mode 100644 index 00000000..0b2aa22e --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGYBJ8F7NOBihGiGXVBApYIXiLlNVW+SSYANC3okjqsax5eFcwyfQBRSWSJxAXKA1aV5vtYMN/nBwvg5kEgftHi3L3Pye4VA1PKyoa73E4T7AsAJ1eRrJp31ecSXi1f9HQ== test diff --git a/src/test/resources/pkcs8_ecdsa521 b/src/test/resources/pkcs8_ecdsa521 new file mode 100644 index 00000000..48da0fbd --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa521 @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAu4Fi4AfgUdlwwYC3 +9ciPeVaUz8vZzqC1bXVtpPsRO5hCbtFrAQBdIQGyJ3chfDt3mi6UHToPXPEMGvSL +weiJ+0KhgYkDgYYABABdYMYEOU8AxcsHYPdHwtbndVLfJZsCFFQ8oMUt+xHi/MwI +SVkojGjk2FOtUeUnsc8SWXHEUC6GDSqy25+42GsxWQGL7/IyR2X0Iu9rUooKrKg4 +EsvQNEDfq9Joj7zuWOmAQBQK392fbXdYYILjSAaG5at9mhiw8Kr65ShK2IUv4n4d +nA== +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ecdsa521.pub b/src/test/resources/pkcs8_ecdsa521.pub new file mode 100644 index 00000000..add64a97 --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABdYMYEOU8AxcsHYPdHwtbndVLfJZsCFFQ8oMUt+xHi/MwISVkojGjk2FOtUeUnsc8SWXHEUC6GDSqy25+42GsxWQGL7/IyR2X0Iu9rUooKrKg4EsvQNEDfq9Joj7zuWOmAQBQK392fbXdYYILjSAaG5at9mhiw8Kr65ShK2IUv4n4dnA== test diff --git a/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt b/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt new file mode 100644 index 00000000..ee46d85f --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt @@ -0,0 +1,10 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBVTBPBgkqhkiG9w0BBQ0wQjAhBgkrBgEEAdpHBAswFAQIf54qi5gx2/wCAkAA +AgEIAgEBMB0GCWCGSAFlAwQBKgQQKNwzbVYlv85zx1qRSDIuNwSCAQA6KsoUMRQS +BHjCTbclDicOr16QQ5G0weLTjfy2xxaefnWlU0Kb8bZ795Z69+UfBcIPKeTi16EK +QdamyrEqxioimHtLrSGY5wFXGD3SNXWOULKA7HIMwbvpD4w4ccuD2vv2uYmoMXeL +s1l8eOC2gPb5Tip10bn9ZuHK5xEVMAAzFjO5ycT8d2j6ZUAE7EgJXdjCv6l+59iU +texwnhXfX9Zla5K+wrMclWV4TIokjNfckPP4ZwBIrYsYG2SXtpszGWWBviZrb6Fz +1DI/aApGZzw0MiZoYQ0HuzIE39TCdH/nvjMtWK1LZy/fNxipBtfVHOIB9LxpNyYO +ijzcUbrY0bU2 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt.pub b/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt.pub new file mode 100644 index 00000000..f5c9b1f9 --- /dev/null +++ b/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACWNfRwfrfQb+K8d5+VutrDWme+Pk6UyiDvbuS48bCg3e9ptaWHQVtQ6xm1yLYUeF/ZVWn20+s2DntLGKX8dFCaSwEHVmX4oj6hx2V5zprevf3GShfyz+HXt4qzoPzVMyOPY/po4d5oxM1RM7oWaqNwvuVhU9Zk5MbJSzKq3atWJv/+AQ== test diff --git a/src/test/resources/pkcs8_ed25519 b/src/test/resources/pkcs8_ed25519 new file mode 100644 index 00000000..7114dd30 --- /dev/null +++ b/src/test/resources/pkcs8_ed25519 @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIBdzIGiV1WyXNDyPVUcXZrG3jBYaXHG7TBZK6rrRYRUX +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ed25519.pub b/src/test/resources/pkcs8_ed25519.pub new file mode 100644 index 00000000..6ad5b046 --- /dev/null +++ b/src/test/resources/pkcs8_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINKulAsCNhbGFNT4DlsMmQt57Y1/i40T2EWpeUWjiDL6 test diff --git a/src/test/resources/pkcs8_ed25519_encrypted_scrypt b/src/test/resources/pkcs8_ed25519_encrypted_scrypt new file mode 100644 index 00000000..ff0f5bb8 --- /dev/null +++ b/src/test/resources/pkcs8_ed25519_encrypted_scrypt @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhFrC34mnc2UgICQAAC +AQgCAQEwHQYJYIZIAWUDBAECBBC4Yl2xDb6OxiknqsqFrNJSBECqIsjJq1c947Wj +AVEwaAqDUkDPywSKR5R4b9lDAEclbUHfZAHDkDX9kk2+zwT1toPOuECGq3FN55BO +wOUhPR0o +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ed25519_encrypted_scrypt.pub b/src/test/resources/pkcs8_ed25519_encrypted_scrypt.pub new file mode 100644 index 00000000..51f914e6 --- /dev/null +++ b/src/test/resources/pkcs8_ed25519_encrypted_scrypt.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2oQIAotMhMO8LyWecYBIXFlxSFRoxm8A0Hpbn1mEOJ test diff --git a/src/test/resources/pkcs8_ed448 b/src/test/resources/pkcs8_ed448 new file mode 100644 index 00000000..0d44b40a --- /dev/null +++ b/src/test/resources/pkcs8_ed448 @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOei82miP3EX0EZ0HhIhYRIP5r4+MqIPN/x+Qz/9LbyPb +arD2I/6g3IU94gr6yne5fp3/coL8xBX+QA== +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ed448.pub b/src/test/resources/pkcs8_ed448.pub new file mode 100644 index 00000000..b56426cf --- /dev/null +++ b/src/test/resources/pkcs8_ed448.pub @@ -0,0 +1 @@ +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADlrhnFj5z0wgxD0IY7ECoZE3/8v9cUWP7VJnyp0YPY0aLKBLnagj5/GMDV71bYW56Tl7AFx3EQvxYA= test diff --git a/src/test/resources/pkcs8_ed448_encrypted_scrypt b/src/test/resources/pkcs8_ed448_encrypted_scrypt new file mode 100644 index 00000000..0393dc02 --- /dev/null +++ b/src/test/resources/pkcs8_ed448_encrypted_scrypt @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhr6TolSPij1wICQAAC +AQgCAQEwHQYJYIZIAWUDBAEqBBB73QBoaoKyPGPrWSkWZauEBFDiTfYOM29sHJFC +psVERa9DH3Ir1vvCqAL/IldUx+UcdzjQxQkYqZeFiNZrFuNlDPua8fLlvuH2JNCK +E7qNRbNDX/S/wk9ADpIrrkYFbmhTEg== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_ed448_encrypted_scrypt.pub b/src/test/resources/pkcs8_ed448_encrypted_scrypt.pub new file mode 100644 index 00000000..4bc65e8f --- /dev/null +++ b/src/test/resources/pkcs8_ed448_encrypted_scrypt.pub @@ -0,0 +1 @@ +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADn6JKamPkXumrMxQnui53u1e7U0WkROtrmraifd6LEDfbEW78xEmNPSVZARg0ydaeNYzc+iEGG/cYA= test diff --git a/src/test/resources/pkcs8_rsa b/src/test/resources/pkcs8_rsa new file mode 100644 index 00000000..903916e1 --- /dev/null +++ b/src/test/resources/pkcs8_rsa @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAAoIBgQDCOSYk6+5VS4tJ +qK2Z095cPYHjlUT4Gqm2MM5xdzM2nK9bd4slUyz8fFYpWAIvu1G6Q5z9e/2Kfcme +86lCjN/lidSNHQBpz6xW2WQTuTPfSPW2lBblRovlezhypdHTdMt1NB7B499py1BF +1iLO5Y8URPCztTXawneMnUjvUp8g6dDKVDGNBssgwZ3TR8Fj/vrOkgafwtJAl/X4 +CJBXdt45dLre5eRXEoWDxTdW/GxUw/PtV/mW4Psheo3/RoIFequh2m7U8uSdXKvG +qm0ZlpSTSoen7uELSd+9iY3E4ULdm03Pom/O3WO+DOCmRb5YUTNFKZUVop3DKizj +Eggy9mbb5W2iJp+6K31YWGWHCR/HhFr/61R6yQomXzHhbqAEGH1FsBmpRDa6c7a/ +b8Xl4w4kbBlOOs21G+tvL9l3hYqBfWpPE8A72WGdkzaOltvt9TEDroNI2yopNWTV +XuUvJ2maFqiwf/4cYpGZqu3V4ccBpTJbhHQ3G3w5XVQ6Np42ez8CAwEAAQKCAYA0 +Ssi/VhpkMqO84EJlUUrkENdg2/amyh75Y6ihmhNa19LK0KPRF9Tb4eoc1Yo5Kbj2 +am/hO7nmyLk5J6dhuKYrmfF1UOKkmnpvI7azLMEPlAg78SE898KAta1cCNM2mJKS +6saBM4YaaNgjBWV3yQy9y1X3PInUbVlcrZhOfzNC3FEPuJ1it/qGxjplAUPLVYJX +ja9k4kJTi9Z4wm2Cbwmj9I6/pqvU2bLC6J1euTW9mMibFX2opshWmjWTDnvBV+ZX +jT0i+D2p0sRHO2oo0O5WKvII6UI2csEM5VVCR7wReNxvIjCFgQP6+P+msIl7YSvm +0Pz0RWbFVsbP+EqyAl8DDxLfq209Fq99V17T1lVu/4y9HEaI+4+hsO6JBCNNezK2 +nn9bq4GYlZ0xrxQuvEUG3BjaIJe0B7XIC2XUBA+530bImN8D70sO+3Q1gydB2+Sw +uf1sYXMSfJwyy6qOsw+mJUNxYowDcCo44MRcB23XFQDLEb2MAFC3/lIvNKlQSEkC +gcEA8P8UqV9J+ItSDjDtsA/un5T9DCy3zOJ8p9TMszJjuteCJw3huKFPG3oJIBiR +uex2kwLiS6bNnZiiyW6eFy8e+w/xOVed3XKjculIhoT1ZL0IiBCsaDa/+L9hBqG1 +257k9yfgTl5j1ZppRzy0JMEE1Y/hQ5CZxxZjw+Am3XgRFzqpLqhd5vi0oGXgL1PX +zHeiRp+oSnqr+JWw3DBhLbQ1SwbDXco0vyQj5nR1+EV7A/DeptPBeCn5Ox/p3g0R +PcxjAoHBAM5QnQHYVeNFIC4zK4WC42wnSLaqzQS0YCx20NSs79xmeqiOhkkqbnEk +2E/l4LKb0j2xtNEAIs389QfKoqwXlZB01Q8oOHMd6HKd7eelh+bkV7mig6rV1O64 +7TC1jTVlCKXL/jADVHd/DKCZ74C9AOubU8ONjSH0na6Stq/nYQrAj7aHKKJ2wBP2 +WRI3lj6Ma7Ebl3OtyXIay+gBegEMoAt/eJaJbcI4LirVl6vDTBXOGHtNSuJf9BEZ +plgsvCdGdQKBwADi3z+UMyBv2rhko6sfE+CQWrHdxDtDpfO0C6CpEcbRHhBos6jL +JxBRzZDJpleJsBHwU8a1cVIgCpE1D+3D5ZhEwb1VPiZfUoyMoRClkoVxUIO3k/q6 +INCYW5H7rECHgA4Mnn2LSVCyxapWZc8wyoTCh7CI6pfZ1DoXK+1qkJ2GQLMEOXws +8/UXCtEBvOxqkDU0RxknBFTgsArPPrw9SmjhOHyyzqjZSCyDxx9HmiE0lI0GgFKh +zOHTxzdi0upoQwKBwF0VAcFTWWywQ1SUwY36/6BMGQJRDS6SMtHcyVsqqw1FLxHd +O7jG7A0gEnf5vubWZoRvKKUi6pEFD4f0ZHBAM7p4+6da/hzx4W3U0wEwLaB+ZRXb +vIvW7brGtDzFrG/qpXwoDQacef9v0sKisOvkWgXyjlgsBQDM3Fdm2gWC3sV7G1u+ +nxatfPuEqasOaTrPH8dIK4yFW92fOV/zSpgumgaV/FHurxZxQmMQ+t0Dv9AWq/T1 +alO39uXALMybYkhWAQKBwCKNkCzsGc3yMzi32go5v9ysg1Pfj7qqIUzdxXWXFd71 +5e/1I4nfRi/6cZjmYQn6Br9oN/GkzMZXiPE3DUjwCvHPK3L9EqPPDGnxrFc05GnD +2JsTzYT1XqlYraCgKLljlULjAdQgAm3YaXEoBY3o46yvG7/7N19OBF1j5SkcRHyk +Pi7fBAKkIgBoPDCA9SO4Z/9aBmzImXmbkSQpawHOEYTk2xTebjYLl1oQiuy72b6s +F1BlgpW6GHpeDNVV+j8vTw== +-----END PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_rsa.pub b/src/test/resources/pkcs8_rsa.pub new file mode 100644 index 00000000..6e2a0178 --- /dev/null +++ b/src/test/resources/pkcs8_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCOSYk6+5VS4tJqK2Z095cPYHjlUT4Gqm2MM5xdzM2nK9bd4slUyz8fFYpWAIvu1G6Q5z9e/2Kfcme86lCjN/lidSNHQBpz6xW2WQTuTPfSPW2lBblRovlezhypdHTdMt1NB7B499py1BF1iLO5Y8URPCztTXawneMnUjvUp8g6dDKVDGNBssgwZ3TR8Fj/vrOkgafwtJAl/X4CJBXdt45dLre5eRXEoWDxTdW/GxUw/PtV/mW4Psheo3/RoIFequh2m7U8uSdXKvGqm0ZlpSTSoen7uELSd+9iY3E4ULdm03Pom/O3WO+DOCmRb5YUTNFKZUVop3DKizjEggy9mbb5W2iJp+6K31YWGWHCR/HhFr/61R6yQomXzHhbqAEGH1FsBmpRDa6c7a/b8Xl4w4kbBlOOs21G+tvL9l3hYqBfWpPE8A72WGdkzaOltvt9TEDroNI2yopNWTVXuUvJ2maFqiwf/4cYpGZqu3V4ccBpTJbhHQ3G3w5XVQ6Np42ez8= test diff --git a/src/test/resources/pkcs8_rsa_encrypted_hmacsha1 b/src/test/resources/pkcs8_rsa_encrypted_hmacsha1 new file mode 100644 index 00000000..2bb176b2 --- /dev/null +++ b/src/test/resources/pkcs8_rsa_encrypted_hmacsha1 @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIj8U2DOUAM48CAggA +MB0GCWCGSAFlAwQBKgQQgkPCvl5Jsbu0bwVn+H4d1QSCBNCXNIf7CZi2cVPX3lbs +r2D0aka8fzyVyazOWK1BTTyrr0lA3EIUDHDwp1tXoGXmTfyD4cto+pCwqCGLJOWr +wwC4d0Av/Atdo+N4iePxrt2HBhDSWQsX7YyhntjBJD1Lk2LVt5qmlcxU+maizcLP +afTdCOHIhRIbxf2dhrxOD4Vk6ccq4cvzDLRZ2r97H/saB5xQDyZkQn29jRFBMWuU +nCbcVqgpfGV7shz1oecn8KyT6+B4Yxzv3SuxkUchZMX4FMj91Unr6B2XQ72X6ab8 +Ura6xaWuxIjkqTxKgwu4dVyujp3xTFA2DzIxdKogRurMLOvWBpAezOXr6whVrziT +5gF0JfFyDevdNWAB0YM0MGgoCncI4lsR8hwaxXOTqh2Z8o/L3+ec9u7vckNOQinT +fF0t7Gpup98Iu7uo3k2dqNtmUiHfX1PLhqtqdGhDjQTtjiUzHaOX82o3M3oGb5u4 +79c+R3snNtG8PlJDnR90YHns2YbRkJrFCqJ1qtxbN8qeWwUv5/Dc2Tmv2MwP9tUW +34/0gmz9ylZAXK53mRBCdqPVboDKJd6rWK512Z41Yqire0T37qO0GrauEJMtVuCV +dvO86jEc75sJcs/mGM0EGYWL9PlOphq/IHyFD1DHdA2WoieIyIg8o/DBsgw5xKW4 +wpu1Yaas3qG/X6XIMIGGlFl+H3g7gmSgmh1SjMsSnMFgVTMRmLgn1hnOaXNLrBDv +nZlhrfVMKk4dCE9JfTnlC2J2OdoGE9bmW3c4IKLtlCTyoyZn9hlzfW6zprjV/Cz+ +ZtnCv40nO4GozXBlixXd+OPPRF7cLLBwPYzMs19zbqZcnF1ZYVZuEoarSG+yKMOi +ZA9KPOwu8BVGoLW7WKG9XvTPpYeEUG1dNBnJyUGxg+m7CVM8f/2q4YBaJoZdmDBa +3AxCAFckhJIk1SUGt3+C5+QBwPU2g905umZIZ8cH2X3w6T4M+bjYdDYj2+CfYV9R +x1dSNwxMM4fVF4Q6/H1efyFjLeIpweSzqU9ZayxiDsVE+3LqPeJrcDOSDi4Qoaxz +Lub/4KJDkrFw9FtDDkM4VS9FVy0BzbHayla3gWqmSBnjc/7TEuWEfKl2oULAklME +aUAsKzw3pTXi63nFtS2nJyWDr/Te6peHmpXoTtXbUi5rIt2tcRqPEOowxjrSflnb +Xv+1CWbTqGkmf/gXpapbcNgqKQQiR5lsBboXApwOutGy8rw5lhUc/88rI4oS44el +vWhFdvBoQSuZLOXku8OoJvsv0HVLYa0apsDdCaI0bcO/0KZZKAy3VTaMZq5isgyT +oAs4DHU7LY2SVFCWFt9Abv10Zzsjl2Ai5br6wfhP04tV9/VdC29MFN8Mv8PfA0H0 +cMgm8UdWwRFdDSYyqh+/sdfMXRsoXUU4aDWcisn7CAIOqfFj3dkUkfYZZaFeTQEs +5k5pDn4xeyXX8wSyqjTiei7iK/rOmRPD4RyuOCG4KMmO8fJDNNuOQX2IhiKJSHGu +BeMrXUDZ4zGmjy1Rk8gxK96HYZRV7uEmaV5deVYh3w1CejpK3nwN+uVCM2sAdLV5 ++853Qp7/GzrCujzGbOUAp+h9nQy7YpdTRDeoffE2TKBc5jVXnvKRUS0JyVVnEtTG +H8gn/MacxmPzp72vKecGoelMMg== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_rsa_encrypted_hmacsha1.pub b/src/test/resources/pkcs8_rsa_encrypted_hmacsha1.pub new file mode 100644 index 00000000..76c1efdd --- /dev/null +++ b/src/test/resources/pkcs8_rsa_encrypted_hmacsha1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDIUGLfSbG/8i18Y53PL59UJ71RN4XTKJEvVljZM9K0HvFkc0wOlzTOXoOAEJiTIq6JXVWjq31BJoC/x20ySmaj6hSOs6NGrnyxXzSbM4E+PzJo74avh+NXZ6ojD3CKg7XuShgX5+PUqEiAffK3TdzRX4NlkIfiIaHi1O+Q5drSGwnoB1C5sNeimLDhO9IPo699OvQu1Obh06gygWh5pnu0NBuiv7rWwBx9S80fJvqhAQATxv2RWFDqcvn/T0WApJs5FCpyLxKozTpOenB9OGaEEz3mpg6H4KPL6UwhyC7YLr3rRKyjff08uELvdQzsO8q6tO8uOaQngLFhLe5lrlV test diff --git a/src/test/resources/pkcs8_rsa_encrypted_hmacsha256 b/src/test/resources/pkcs8_rsa_encrypted_hmacsha256 new file mode 100644 index 00000000..6008d876 --- /dev/null +++ b/src/test/resources/pkcs8_rsa_encrypted_hmacsha256 @@ -0,0 +1,42 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIHbTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQINQ6pBY4N8X4CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCj+GNhaK8p2R+feHgNRcWVBIIH +EE01wtlgC5EVkPa/OgdN7MA26FSIDHTNqj+0rDhzvpgrw8R3IFsb7WZf6klE5Qns +Ct/mZWt6ddMJwiI3xjPOQX6E4aJqgK5NanwftcuUjKICrOPseXEi8l+P0vwInn1t +zaDxFgIqo+N7L/0MXiFbubk2eoUfpj/xPaGz2DxQqilQabJfWGy6S3VSw1yvQqKP +q6IFJ9N+bRXbrrIRD3RgAkMfJr5MhFdwQPrWoUUr5U4s2JHY6r9hEaVn2UqCnopF +FUj3aX2JQtLuJaTsRw4MmnDcPysob83w+njx9+1dldXXNW3hfWZ3y0RhFH7CfbAl +h59yzS26rKweI0ir5Va+SVfdjHQWx8ICYr90rTt1tL79DHnmrwK48RJe0/mJ+ITc +eQdA9X8VcI0mRQRdACHt84N+fzNG0/w7iiCK9jpS5hePO6cUlgkpC9/WoNrTSV7R +NFTEmSp6LfTdMcWS0mt/8VdpwOcOlxe/UuXb8JSMk45ByutkYjrrvmDjx6hQLLhd +urPeSYiC28k3zBHQB+PVdbrX5waHIQQkEykYjwQZR+ICh0fwveDPn8Xhl/ZslkTm +xGmez+ZPssAtMLvlY+oDm43QjLaUBkwP8BH+vbhZ4Tmv2+Fv34+Dors4L4IvwXlf +nun4UilFHoMbnZKWDTAQ2DvPU8DPnEVVCuE65nhOLuHhxdjMBc+Bl3+RdMuNGKb4 +fvW965RrDUNMkqQK0mqHVMtvv4eJiZAR4FQa2SzQ+9bHBv14L2uwxI/d8nNUFbgh +imG4mnOJ7LacUDX3ewI/G2EgdJDVV7e1CJbTPu/EftX4uIkPite3JIcwF0ajr9+d +tm3yYAr8jLp1ZR7vh89eBFj7NPAjG8FmxjEVMU11lovND7ioL/f+FITl5blUqZok +4OCoOppa7BoPGULwB+fV5xiPa5V9KB4OqsJpIoCoMrxVMGsEkCgu358vdmbrMckg +B9yMPFfU8dF+SI6Galfz3/iR3x6FKW4TiqgqkrZbpb7zs1HhnzHPPdvniSatgOEa +QUSb2yZdO46++/2j+kfmhb7cx78ZaBQFMk1onv8sDBUY67LLD8ma3QkTJb3aKhpV +gwK6NIv9D+F3kNrpnscfp7OsvZBxIXmMBsmH86TI3k/x0/tjTD3Q1VYSL3OAetvc +uXz/WTide/T1EVSTdrv8H91QGypvxX9RIrf33KRZzCAdeGa7LABD2aNmhgif0gzi +0yzilnfBjNZIZ18Zb0cbJYPr4Dvx3A8iqhCjJczN5yxe5sjQB+sxE6O1bQWOIG1r +R1crXRjRTK3oauBiAfKTHmlmNUl805QBgjnMmeQLNGUOyVC0IdV3d/Q/dc4WEM9z +QoAN6OPWNhZJLVNCLYl7kDtj3GcjHde/QzGJjkrB/ycP989MXk8B646m3uEYgVFP +0HZseOF30gTvnLHNV99nDL16di6rr0St92hXtwOWR3NrGSWnzxSewP/d+adL2DEp +TIF+WAFmbXr/iaAMPuiI2TegGiMf1YuWuth9vNN3E57l3vKpPXizh0q0617nCS1l +yOkN/9ekjD4Cb8NjVdAxWlI9ibpO+ZNH7p+FzkxyiWbc5HcxGQnVcPpoZGiPpCMh +TEwlaj0YAuqb6wWksEpOeb7FAKslGkQR2Azw3JH5hhpRKsVdJcVXVEKlzizazC0N +wwe7Fm/CUdOiM5pW0/ZkvX13lRxxwWYRczpZ4hcu76DF8SMfO0Sks+BF1qNU87Ay +Ic23Nc4Dr9YiTlf2/MQmmQBBZ6l+9Ju/NbZYSaPC69Mfkt+ENn8EYcX108Plaioc +/rO1gkqE8g96ABvmddCRvqk0O9aV6odMCBBzeaV1X+teH2gkGTnhsE1QQhjScVxz +LXU3kgtfeTZG0hwHI0ZZm4KKPn7gb+vNQ8oOz1Dm1lGaatUuiwuOMu88M/hqWwh7 +V2l61LEe/ugCGZuCJFOyDHZpDeaTtX90pl7ijDuUfVZnTBfGkoyoIQScz8mJkKnr +yOV9oONevpF5Lf7uFihRpgWDBc1UsjV+lWeZo+IEra+W4hx5ADwqsThXKV9slhdq +/xJlbmD1hvFRHhdSBbiZ6UUCmfP8XxIKDi4Ghfgd0ysrrVWDOZPuws0lg1WsgUOn +kwHwfN0KXYfbdl+VQ3S51inRJUWAWDDQklAwBvWA+S5grhFNHRONk5OOBLvX8Atw +86nFW63a0PR3X0AFzLAJZrqF+EZbeloQgX2EX8gx8XFIPTy7XoS1T8/Ezq8AIfXk +JjF6aeBy8YAnRJn+4sPGAqX5QqktWcvIkD+kv2dZIADOhSRXxQq5ZA0WZchC6l18 +1lnAq+XXHq8NxupJ32rrZ1IRf1VIYCsJRMNguXKTWKZUHPAQxWhLuKW0ZBQJM3AE +t0Q+gYvgja/p8WiC3l6WnioqJdWD5/FEsRGLy2GnWsvI +-----END ENCRYPTED PRIVATE KEY----- diff --git a/src/test/resources/pkcs8_rsa_encrypted_hmacsha256.pub b/src/test/resources/pkcs8_rsa_encrypted_hmacsha256.pub new file mode 100644 index 00000000..8647cd1c --- /dev/null +++ b/src/test/resources/pkcs8_rsa_encrypted_hmacsha256.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCszdi6vvJvN331KkQI2SVEtrwBHQunxWGfMP3HnSn4aH656Mcx/9a33V/W5KwKoSgVeRsHVnbjby10yP1PaU898hILUDFBazlJUyfxvMybiP6TC6LMomCFqoncaNJ+hR2BsEQmrIfmD+cvIFADz8zPtjDeOSAPQumP42C5uXJKNKYM8nmpCUrHo8qSvbVLTX/UC8mZweenxY21IxnKSZkkuLkRtqp7+p+xc6bYNfXcdb6LyrR/jmD2GsnEXSmWFPxvYGAErUZBSAMF2xYGG34l+lCB7Pzvpfv/mB0MK46YL+pdKC/B3JjDu2izt7cEBTlhd/TwzNah5OfsXtVJBNlYi9FU/Ee7A6+ylb5WnNasU165ngIVBBL5UNg8Hg/W6XJJmAVZg1PsRbikNNszKeaRrTC3X35PEr6uab5hlcORcAWV2FYoYWN8iIv0rFJyaE7InpxSHp/G3lE6XHddcJrq6BdmURez3jShrCYg7DdnE9Nx7dBX1rbJ46lLxKY5lQs= test diff --git a/src/test/resources/ppkv2_dsa_unix.ppk b/src/test/resources/ppkv2_dsa_unix.ppk new file mode 100644 index 00000000..de591a35 --- /dev/null +++ b/src/test/resources/ppkv2_dsa_unix.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAO8/3Ymo6sXoRb7gfTBqwAYPO+ck8aKsoBdQIn2oD9cX +2EIjXaoUXv/gK8tVN4EN0C8XADEFXpE3AQU5WhVZ11bv0AXOqGOreoP381pN72ap +hh9o989Ee2PET+wEXZnZ4PmftMcuhtHWC8Pq0coht7CO6Xt56ItQuvj3nxMwYc0h +vEFDOpHaFCyqiann9q3W5bLB9+ek2tJg5s1AVQKnlGCwn1fj+FXY1cRFDsp7AxGJ +ZPDSNjq4awEZwECG/QLQYiWT0FVILFv68SxQeGk6OlOGip4dKkdXPIhMqHy0GT0d +iBUm//UweV44S9IXPF9Xsiidq1Dn0vDe9e5OYNmV8kcAAAAVAKpXVF5GLMgMtlhS +vjICPOy/A+T7AAABAQDEFLE91TMvZkCGeAHHXHkjF02m3CafJ7qLej8RoTEeZxV8 +Op/XlRluKNn97XF5G3DHp/DWGqBgawbBwqEq7l3V97zRHESMlivPr0UuCvjSmFR/ +on5nFCbl3RtZJOHOUkamtv51zBfk124uw7+bpnm9aiDNUs+AHNC3SLSyvdBbz6+s +gt3kqD+FZyk+7TC/sOm+zuNmducFDDQIjldI7gx78b8D4BBx33+P5qW12PSlKGrQ +7E1Rnk+o9WMSJosq/D7lV56waSV6/AJE9lruQr3LGEwN28DrvbVWen8KtvivGlJU +wl1OK/hMBS5u+QZTIzN5M6iGB2hfJjBbF+LUONDNAAABAHBA/s/1ShLjRiIfenBf +nInfjl3a3DSF9M3/o1fuY6EFkNs8MR4P2aeOfk0zE4gmO3vlT7TFs960tdUdIHm4 +TFOo+PFp4pjvVSO3K93cAjzOTwMI4sPhB3rRJxrDmx1CWVdnBrWQKO78kLpLf63/ +QB71qEe7MirH4FXZRLFP39gRcWknBtHmez92CFlzKzqLQXAMfaSbZ95lPh/bwgQ8 +OCuYExuB3aw3QmoQQdzSJRfWfuCEsUMmJ3Aub/yUMkpfe+hmQwkG2v730MXp/uDX +fDXhZKkoh+v7Mhm04wzpxtB/C1dYpmfSP/i5ews4+9Lo9me6l54xy3+k05PPBBhn +Yzw= +Private-Lines: 1 +AAAAFBtGbtke8qQ6YY4xizrBFRlokJMi +Private-MAC: 10e180bb416d61cf0196c6b1c9b75382f092c20c diff --git a/src/test/resources/ppkv2_dsa_unix_encrypted.ppk b/src/test/resources/ppkv2_dsa_unix_encrypted.ppk new file mode 100644 index 00000000..59293ec2 --- /dev/null +++ b/src/test/resources/ppkv2_dsa_unix_encrypted.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAKU4cgWySBcfMzYy9aSOQwvH5JM1/M4UMNIB5BOZocC2 +FvJbEok0gTTbLCzGI9A0pe2bnr6EWx78v+aIH6p1GOmbhxZVY4+vkEUMH9Ee8v7X +MYqz+CCefWWiAH2bXNa1v7LC9mImiuKmcDdHiVLJwrj/TH3c2EUYlcyQSUGRyOmi +w5/auITPrAlmwhyB2ZXLC0X1qNKn+nJiwWxe+SsTXV6F38jbZo+fepbnVEqZ3wTU +VnW2gpk0z+LRiLM+1mhASykpmRCyggxW7H2NyNGW+9jVTXD8WCqB/z0o+7PVCPS8 +5qpyfNi6PhH+etXiYnyG8iGI19BWZxDmMCG8Sgr+AnEAAAAVALkJetYWk3MacexW +EA0Xa78/o9RfAAABAQCeNE28P6R13v2OUqUtBe6oYQPHOHzMFdEk+qJoXyzKmz7Q +uQiCGuJ90YUST3fhvsBCOY6lPc6zGfeaVb/r2loa6zm3/0DoOXc/dnU4sbB0auoh +PlwmjFXDwTQF5BRyxuRWtvPuNgKlgwFd/GcnIwlgW/jRIF3j5wopM3m12+yhZnW+ +gh575hjvf9edpra7Y1u1VR/jYEQRg+GuXFDvcGE3OTWpphAe0zfjwqIOMYYvx/JQ +4HMnB+FxJ4svs5aUOPwBXhMCT9YPWEWVr+1GBVf/VlKTrbapNODYEp0tuL2z8Fva +kgquJ2XGY74+6M1wbhrrDZ+LPd0Iuwcr7N0ghtVYAAABAEU47Jcr0BB3XEcmy/Ls +L+hSKudgmczV6QD+fxelS4mY3//8INn+wMRyQzgix4B5yGL6OzQ2RBGlqho2TdfL +o5UOjIoQzOpZsjZu2TLZezS5Lpx85ZVuLWwqezx1NZqGDXhtDz2tdUV8eNbePWb5 +788WXYXOW43ya6Z5RMoywkO7sZRziHH4I+WwlorUpDMgzzfGs2j91bpejsBYcSnq +cUDcHJedbMmcUQ53OWocK+/MwoYi8+F/UBuwAoZ8wYauasMZ/ph2k8ygzWHCv7NN +teyVAY3o5OvrML4sfu9kmgBIZKKfGZHeFptjQdQcOj7fTI3tuqocwiWVohYBF7la +KvI= +Private-Lines: 1 +WJAxySL5c2ikIb8JusI0EABRvX2f8O3tBbAuCx4o/i0= +Private-MAC: 3384ce7d2ed81d15d76e9f80c3196813800d241c diff --git a/src/test/resources/ppkv2_dsa_windows.ppk b/src/test/resources/ppkv2_dsa_windows.ppk new file mode 100644 index 00000000..023f2c67 --- /dev/null +++ b/src/test/resources/ppkv2_dsa_windows.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAIRPWjGVTl5F+y9uOPu8PPtFyjFy+Qclpqww/vdKq7GV +AYPU5zdDHPvmEBZKfklfcROB5Ody3Bf7miR3RoLmmK9SZLDqEint+FdbvSWEooFY +exZmYukhS9FeGUwttMOz4K4JIAPMVBxbGMPOL2AzPhE45BT2PPUpXMNZTLft2YHY +hSIg30nsx+gc7tK7dkdY38pJ3Qtazc3jpyHnxT9o5qA1OdrA09nYHs9osneC4O29 +uru4Qz+foNP8XbGbkl1XxfooweQiElA9kvoOX+IgtJGgX2fuexAl0VcRmeb8lYxC +0KOo9QL/HRW84nC8ZvwhiOXwbvN6X+tmKqTpcYQXgOcAAAAVAKearN3tBfL6HPzk +CHzgGrUbSK1zAAABABW+7UNnhWAvSwiHShVSWm+eHjY56rss8OoYCvVRxDTLYhnZ +s8GvjlHJTTvuPbNMiWYe/vqwKhBDkNJe2cw2on58GsJwLLmdhsZzGRuCZ5twFLus +ZsjjRImjBTY5ZNYhdbhM4/527a7bSRLT+QPsedmaEvJHeSMHwDYwEb/WMsYnL7Xn +QTaPRjetz1a64zWIYQU6PTHW+HRrQDT2Dh/knymQvcw3QC5/ZwNf9k1HS8FdE5HC +9G9eQfPCGjiFGLfVfAKbq6HslLZO4Ea8WnFLm5CHz3+mFF+1fHXkahGxlOkPGMr/ +kSeIvcneiMAqUDINC7VcXShVAvATGyaVh324uwoAAAEBAIM8p/hq3+3N5V885fGy +BuO6H9RFmmFsuA5c70CXm3A7CHLNh1+ql72WzaRs3DyTICexOtEy/m+2jFd2suKa +Sa4F74OMQv4KaeeXrGuC8+EGdWFMN0Tf5UkvR/3igCFdxUbhfEa7qbMJBi5fOkkY +1/OCldoHHWwWmp7uJA9oY7AVaFcwVyzOOQtUxBL6rtAE8Bcff88H2TFT2vf0WX/f +KMKss69uzbphIIxnqnk152lA/gVWcT/mhCWa6NBIGJqKTNppKExTcLBcLlglqmLF +i616A7gm8Y1+gi3dnDVu/hWiyz8uOSE31LB4oJGX4fkhOcKgwMJbB00F2ZGPT7JM +4JU= +Private-Lines: 1 +AAAAFQCNWViCVJUIHWjoUKL46EXL0dwMeg== +Private-MAC: 93e11b58c7d05304857d602a2b0db8b8047414f3 diff --git a/src/test/resources/ppkv2_dsa_windows_encrypted.ppk b/src/test/resources/ppkv2_dsa_windows_encrypted.ppk new file mode 100644 index 00000000..8bd2afe1 --- /dev/null +++ b/src/test/resources/ppkv2_dsa_windows_encrypted.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAOex/Q9Z8ZKXJbCIQnLHKuJdhGNpdIIOsmFYpNGCClPz +4ZTZmk0Dbw7sn2KrshVAbi2RXAIz4gJn+MfHRF7okwgAgN94mMP6VG+D4B2Zpgyy +qWRYSSdV5AmbKc2VDX+DVK9qKmHlEX3Wjm4Yfw3s1kGN1/eWwI5MrHB7w5tA8Fw8 +iUfsRLnwTyGPBXbR1ATqT/obLAic6vogcpGojg1s61k8Rlz2bVMaYRu/RQsIdxvU +Vi27jn00ClTaMGWKH1ZMhnmjedvN7qI7GcAUgNXFjOGa786aPXzJDC0JdNcmDoei +9G/ynTJOpMMo8oy4bJLnVqL/kS8v5k+P4JeIhwvgp+MAAAAVALGpbWxdf1geBNR9 +INuHLoevUHjvAAABAQDijQTRDHymwb7TO5Lxy2P5MBvxlhTlRN4C+KIo5rnZMX+o +FeEX5/hG18grUn7FYdx5/4AHP5EhK4GIcoKYJqnEjXvfSuvBvD2OxES8y9QI5Y0D +ZSGyAsV0p0/9N6a/khkdJ5Zs9S8iFeeJkevIVsCskLRkz6rrBbsuiXB/N3ojtetE +QfFuznFVgEJyBNrk7QuVvu43nWnzYaF98ah2o4C4HdjAds57XzyhDks5kJsi1mry +l5jvbCSxcf4iZW9OsOHYcjifwfuM+hz2oYYf87obEIIMdOtsvHjMEfTmBqechUqq +sRP6wzv0u6g8rReMBldaoG2HFGpwjvMM+PGU/zdWAAABAAhLKqKGhmXzW3ZugZft +ycNwrxXFeVXFBywlC1qtDH8N/0aW4AvhWXBk2gw+rDXQudpNlVbtV9sT/+9JBM5i +Q9txRfDjVeEzdOuXR2ch51p9Ep7KTYoSaBrBqL5KCxsz12G1+KJazMmlbpxQbdWY +rAJrlRjTcHnYCDuhyUV/yCnrR+7VMAx5FyIduuRtYrCgo/+Z/Z9UklPU2596/pp7 +uozkCBR+RkOm7+9ldU+U9YiaUWymVmXhohFJvy9O6tqj3kZYQll2h7NnGpKVeIqp +8wNW5BoCyVwV+fIuyCVsWg5vOKTBu5GR2Kc3AMc18Uq+WEeC/jsXZS53ItONrdNs +QNk= +Private-Lines: 1 +zn+CjCYuUbnPlsKh8Ge/gwCgzNkTwlBn4oeqV6/G3oY= +Private-MAC: e0dc648a379bcbf2087192cb32fd1ab061ddba04 diff --git a/src/test/resources/ppkv2_ecdsa256_unix.ppk b/src/test/resources/ppkv2_ecdsa256_unix.ppk new file mode 100644 index 00000000..1dd19011 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa256_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCo/5qxk4V6Z +ZGDFHDeVBr7ayBkc85+7SPuuY2OnBSrObqo06lw3nlH8dcloRzCt7qYvbmWTutlT +ujgSOJuvd3A= +Private-Lines: 1 +AAAAIEfIpXqum0vHuX3aJmumOiRTGEcPs8cB6B5cmKV7j8Pq +Private-MAC: c2c907c13abcd1e429a8a53c162fb27fa32bf88d diff --git a/src/test/resources/ppkv2_ecdsa256_unix_encrypted.ppk b/src/test/resources/ppkv2_ecdsa256_unix_encrypted.ppk new file mode 100644 index 00000000..cbd6d7d5 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa256_unix_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAJ3Mot6VLEk +kG5HscBLY3geI20FnwrgQi2wzAKiktPDhF39ZavVlOMzvkIGgQDxmo8WoQllY52e +d4e8+7KQ76M= +Private-Lines: 1 +Giq4fU1ffVMQzpRS7vPtl4eViHxNmagS8TmYqnvb4KsMip0XwtRkRd1tqV9UGS2F +Private-MAC: 9a55718d397e4bbbc0c00003bdcf5b8648da7f2c diff --git a/src/test/resources/ppkv2_ecdsa256_windows.ppk b/src/test/resources/ppkv2_ecdsa256_windows.ppk new file mode 100644 index 00000000..b571979d --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa256_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCDseBAbWBVv +O3A6gqqd2+Qm9uSTBPEFvvj/huI35w7iYhMmIjWAYwi9av867hF9GsRJaPGSkoWU +IcA6QBXarAM= +Private-Lines: 1 +AAAAIQCY9fEH+jqw1o/AtlCcSjksNFSuUILNENI6mcLywmdQLA== +Private-MAC: 9ed0ea63718ce130ee11733dec5f91abfc076d30 diff --git a/src/test/resources/ppkv2_ecdsa256_windows_encrypted.ppk b/src/test/resources/ppkv2_ecdsa256_windows_encrypted.ppk new file mode 100644 index 00000000..a57c1476 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa256_windows_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKIrHi0RIW1g +bd9+rJLTPJ5qAyXzNjEHidpxBzRnN22jDcnvBjqnAtfWPBjazs2KHSn/RP+Ktyej +Y1Q+mMikDDk= +Private-Lines: 1 +dYVv2mbM2lr/QhQ4Sro7ev9ztR0YAFD0ECI76rjyGRBTsbSH28KXEF4Vjq/eatWu +Private-MAC: 7034973c6f35c7da66e86c2bed7d8e8367bfd8ba diff --git a/src/test/resources/ppkv2_ecdsa384_unix.ppk b/src/test/resources/ppkv2_ecdsa384_unix.ppk new file mode 100644 index 00000000..7f705c81 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa384_unix.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBCTReLXxPo9 +3rpl1xlrrx7yYZgD0DzRieTvFO9C3a8Z8blHuZHNy2iljsIYeCPOtK9lcivx+faO +rp5FdE2VDroGPdDY2ZAuwF4/UxBiGNNEXWBhRFkupBcM6RbyjvCXaA== +Private-Lines: 2 +AAAAMH0T4NXp6wOqp8DWnEmXGlmAD+I5ytsSdlhznBdprZ2+wPZWcoa5XME85BUY +p3HhlA== +Private-MAC: df1b26fba219f717cf75002855565ce1b3c065b3 diff --git a/src/test/resources/ppkv2_ecdsa384_unix_encrypted.ppk b/src/test/resources/ppkv2_ecdsa384_unix_encrypted.ppk new file mode 100644 index 00000000..20df843c --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa384_unix_encrypted.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBRylqfIuL/B +zq92j3g4UHd3TOlU2aG1U/2W76hrf1svwNPNe5d81xXA6j7Uh+AxOYfYrJRQr9Ko +tRvd8WcxbikMZs5O/dKG+xx8WmTcgvu+jVW4lCbZrZk4S7/hqIlVjw== +Private-Lines: 2 +mKo1enxDPaVSSEiOYuEL76/0NHTZLDCZuwy/KVXskXb2gwUu4cdbr7Vwj1IHnkBN +GIBLGOWIDva5Spqj0XdDLA== +Private-MAC: 4666bb8eeacc4a0b137edf9e7a32632298f83910 diff --git a/src/test/resources/ppkv2_ecdsa384_windows.ppk b/src/test/resources/ppkv2_ecdsa384_windows.ppk new file mode 100644 index 00000000..5c66ebf4 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa384_windows.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPATBzcVXx8g ++unFg+8RAEvjiY/ZSb98vuIQ6MAtFCkNU2zgUHI9m/Jn9FitFd/F7KlIh4G5/wMB +2dGhks/FYSk+OxgAmhjif6lKLfD5veRCLhRnVbhTQm1fOmU0Sy+FSA== +Private-Lines: 2 +AAAAMQDHKqb0OEYb1K4KBaXYiXqkC+ybUGbleKdkx4V4VPfkDavMdGsOoFsyquuv +CRh3/b8= +Private-MAC: 8ef4699bc3c514b09b56ff672f30237da3d0956b diff --git a/src/test/resources/ppkv2_ecdsa384_windows_encrypted.ppk b/src/test/resources/ppkv2_ecdsa384_windows_encrypted.ppk new file mode 100644 index 00000000..3d26f178 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa384_windows_encrypted.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGxZq9s9JfIv +KluYcWj/f+OuiV0bJ9RLE8rT+8qF0syMvzZZVRqLmF+a9VxJ4uxxRVvmMQ1CXb5T +GtLVdBv1SrviKWMoaBw/sosSMr8v3eoQ04f4bs32fx8/dN4JQbCZEg== +Private-Lines: 2 +aTUgs7URruvHriTmaMe12Y4Ow5smlllupJ1BnErif5PwHd6MrXFCzYq0grADQKpS +0QaIGRrtXq4Hb7vvq+c6Mg== +Private-MAC: 79f4ff32f403112ce42a5206d0da4b9e53edf33a diff --git a/src/test/resources/ppkv2_ecdsa521_unix.ppk b/src/test/resources/ppkv2_ecdsa521_unix.ppk new file mode 100644 index 00000000..0f84e82c --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa521_unix.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACuthNYwDJn +4I/b0fdhJdD3ykWe5gMBS91H9qWt0NHh4QTYxVyu3npbLveRVr9AWoF36UYIVF0Z +3WjFV4u4fOPapQFNlGDf1DNn8aipy/5iUKY02z+AxnWIIjToN0IPPizMw3LApwmy +0UaBSw7sZyfxNHD0Nu8wXsP8A6HG+Y01zSMAqg== +Private-Lines: 2 +AAAAQXaGiZwhRZG+dlWCB8h9+wc0Qer7IjpJ3kiRyYXLTku+FloU3F0MXJiIOnFV +31joL2SC2QEHHLEmvBXMT48R8AYy +Private-MAC: 187e49c327364d784c54b3221adf18ad544339c3 diff --git a/src/test/resources/ppkv2_ecdsa521_unix_encrypted.ppk b/src/test/resources/ppkv2_ecdsa521_unix_encrypted.ppk new file mode 100644 index 00000000..e9d350a3 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa521_unix_encrypted.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEUP4llO/qJ +cKrD5gZ6L+8+JzEU5RLICud1jgVh+QgtuuAZQUztdee3GEPmR+C9Mv5cAHg138OC +NcyRI1eukjFNDgAq3xeh0V6BwjZZur+v5zyTnXQASBwuI+iofZZqS0z8lsM48eIl +uTZuo3siTlv6uvFLs4ovN/cg67QyGIpsKusESQ== +Private-Lines: 2 +4OKuO5v+JHd96qUoo18Tb440u71nbpZNpXqSgBPr2VK5OAxVy+lqeccRtpAcUyXu +TU8TTrQGHTxGMNMMUxun9HDcV02oZ8It2MsywvtfTRM= +Private-MAC: e5aeb1290b219010b58cde9376db795b17dc1755 diff --git a/src/test/resources/ppkv2_ecdsa521_windows.ppk b/src/test/resources/ppkv2_ecdsa521_windows.ppk new file mode 100644 index 00000000..4527fdbb --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa521_windows.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGIVlZVYKq7 +1fKe0Uxj73A5WPmvTuohTARpvnJ0VB5zIHFEtHRuI7Qkn6pRGv8rbKDRKygv1kZc +6rFZ2fgfhLxXcQHG1LwWM8x+XXoWMJM0C0kpCEErKd/tCZstQqg4v2JO0iG5+wmo +Z90Cru912t24fhFGfVeFIbLN/xqznIm0dZTN/Q== +Private-Lines: 2 +AAAAQQn76HAtrPDaRmvvO6XRFvuSRNt/onJPOFRF9pOFDZzBWbllnPf0vR5h7uOo +xn0UoPNYc1rQAd9s0a6LvEpMRUCJ +Private-MAC: d1deafe89f187d0f75d3d6eee2d292c5ee5f9dc1 diff --git a/src/test/resources/ppkv2_ecdsa521_windows_encrypted.ppk b/src/test/resources/ppkv2_ecdsa521_windows_encrypted.ppk new file mode 100644 index 00000000..676d5876 --- /dev/null +++ b/src/test/resources/ppkv2_ecdsa521_windows_encrypted.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACcIEokqqdX +r1cB4vJksHNPuIb4HhwaRqaCi22yZa1slGr5+KQhWc6TaRP2yEsXaskuwThVy5hN +jfONP/uB5IMj6QCxnR3bA/knuB2n7YRFlbIICLmCxr6QI5bz271MxCB+ccJO1L8s +SfEsS6IcxH7O3rYv7hDadwcY9t5VmsJ3t7A67w== +Private-Lines: 2 +plCduQfYJ2kW2Gbi2B6Rb0I0PXb+YgMTClhaOFJwpqV+z3GY9YK0VEwYj05cZya1 +HjGOtG69IWuPjrI523dcNYOeegI2BAYhFJLq05T4R/M= +Private-MAC: 3e73192522ebf55488bfefe8d5033294dc39a972 diff --git a/src/test/resources/ppkv2_ed25519_unix.ppk b/src/test/resources/ppkv2_ed25519_unix.ppk new file mode 100644 index 00000000..e3fa8763 --- /dev/null +++ b/src/test/resources/ppkv2_ed25519_unix.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAINknjInRhIeQB0yCD/N+ilxpTNwD2OGA776pYHI8 +bOdZ +Private-Lines: 1 +AAAAIKg8xC9HSZbf6mY5WU6F7OWr9VGuDnJWLayXM3omacpf +Private-MAC: ce1310f32622d44cb5d90198d482772bdd6bab72 diff --git a/src/test/resources/ppkv2_ed25519_unix_encrypted.ppk b/src/test/resources/ppkv2_ed25519_unix_encrypted.ppk new file mode 100644 index 00000000..e511c694 --- /dev/null +++ b/src/test/resources/ppkv2_ed25519_unix_encrypted.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIFYLKsn61Pg/YYmumVe7oXik0o+Ca/P3vvN0Om3K +CHqo +Private-Lines: 1 +QGanXsZXxLK4SWcL9Y1JnGZxcbfBlZD4h2wi4YsyefXbhaTKCnX/+7SaRdXmFRB9 +Private-MAC: 30b7b90fe3c142891eebd05cbdb9c5e06c6ba448 diff --git a/src/test/resources/ppkv2_ed25519_windows.ppk b/src/test/resources/ppkv2_ed25519_windows.ppk new file mode 100644 index 00000000..72eeaf00 --- /dev/null +++ b/src/test/resources/ppkv2_ed25519_windows.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIHh90f1nUVIpoKQoYxtD66cTGgLZ33D3hCxVqS/N +5R3u +Private-Lines: 1 +AAAAIH3OoVCpAhXZns3AaYJE7WXYQVUsY8RB7lwdJ9FXFS1u +Private-MAC: 5942475d67b84bc1c89ad2fdb502dd8537835e7b diff --git a/src/test/resources/ppkv2_ed25519_windows_encrypted.ppk b/src/test/resources/ppkv2_ed25519_windows_encrypted.ppk new file mode 100644 index 00000000..579c149a --- /dev/null +++ b/src/test/resources/ppkv2_ed25519_windows_encrypted.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAILhbcrIlE43dp+IlosYszkS0c4zpwC60ec1MRwZu +xwvr +Private-Lines: 1 +ARj/EPcb2v9tt/miQAJGKAf3Iz8HkzMHl6Egc5M5VCu+mkRRnB1DIN6wNYtp7ncY +Private-MAC: 39813a46a634fd277b5a8bf379c804223df99027 diff --git a/src/test/resources/ppkv2_ed448_unix.ppk b/src/test/resources/ppkv2_ed448_unix.ppk new file mode 100644 index 00000000..cb99a2fb --- /dev/null +++ b/src/test/resources/ppkv2_ed448_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADnTDrr21+87QbcPF+2Ayh61ot/Jtf0c9yTy126ki8Um +/UUEW6uy87s/9gpS0OHyoH3GO3WgsUoe/AA= +Private-Lines: 2 +AAAAObILm/ALMj5npvYMwKLrb2gJu3ZK1pSO78zy6XPjSJ2SxwAFNcVi1ig9elS/ +T8ZoChnKOv3P0qarAA== +Private-MAC: 55827a80d4951757730ab061c617901d2c3dcb7b diff --git a/src/test/resources/ppkv2_ed448_unix_encrypted.ppk b/src/test/resources/ppkv2_ed448_unix_encrypted.ppk new file mode 100644 index 00000000..53134039 --- /dev/null +++ b/src/test/resources/ppkv2_ed448_unix_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADnwdqOa0pIeeorEzsEY7kskGeyZfhpkYZtbA/iR5Nyp +0cbPZnl9UFsfKQIYKJwPWwnim8BXTkEq6YA= +Private-Lines: 2 +/A3mDpDv6Qz0Ou4TR1tZEKsJDYbC5lIotFETja/Q0ZGpRTBjUWZ1xlVZAQwZhDqe +bIXTpngJcoMuU9ca+FlhAw== +Private-MAC: 8c73debd89d60515a535a88c0e05cbd828592cd4 diff --git a/src/test/resources/ppkv2_ed448_windows.ppk b/src/test/resources/ppkv2_ed448_windows.ppk new file mode 100644 index 00000000..dc328645 --- /dev/null +++ b/src/test/resources/ppkv2_ed448_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADk5uIOJgx7OfFkbkRV43yMP37dcnIVemLPjlaH/WRKj +LTwueFIgN5Mc4UYcIZtlbAaUhiiEvW7aYQA= +Private-Lines: 2 +AAAAOSPpYS2oZf4SDKYbuFSxf+CZzYA5LC7mVwEObSg/NcsadJM3l7chtEgC+dPI +OnzXuIwAaMzPSs+KAA== +Private-MAC: 14cf577bd1d0851fb5bd13c5a92695e0a04ccecb diff --git a/src/test/resources/ppkv2_ed448_windows_encrypted.ppk b/src/test/resources/ppkv2_ed448_windows_encrypted.ppk new file mode 100644 index 00000000..6b8ad500 --- /dev/null +++ b/src/test/resources/ppkv2_ed448_windows_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkVNtCwBIBWO41k+QYmuMromfii6QbjVKRxjxbTYGPD +aw8u+gSCHAnqgaxi9qkc7HU0U01BJI5tJgA= +Private-Lines: 2 +eUusIDlLKMS0TWcSwgfEx+SSDzlYX7QNXkkCZeOv/oc2Tx6/Jtsv7CnwDrVybF3f +jbKnojgYqJ6OEV1uC0D8gg== +Private-MAC: 216027b1e8ec6ff47ee2ac9f69e6f743206f8076 diff --git a/src/test/resources/ppkv2_rsa_unix.ppk b/src/test/resources/ppkv2_rsa_unix.ppk new file mode 100644 index 00000000..3051221e --- /dev/null +++ b/src/test/resources/ppkv2_rsa_unix.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQDsUXXjApfYYCtRK2LrymKvcZpS4cf6agyO +j1/VW48g1GVtJM4g5kOjHWRqqsQZJCb8R9CvChCsJp4LPOS3tC36dSS52loeMoti +7Z3GtKORjPGKDO/byt7V68l02KZdaO6U91D73a7y9v0g+JsLtR4xkZMd1zXLK3Nn +T43rtLg116YBsLxAxbAC8eeC1xBF7MNzdLbmg8Gp9dA6iNnDlUhDsf2/FJ8a43Wb +U8inaNJh6eGd6NtL1fb9iDWho+JEhw7WbyoNrBAf4YlqO+3Y0oO1A4ezOspv3Vx8 +CkoZTrtyCzVCPEIV/3wSK40VQaMxt4ptbPwQK9mSUI2dG3kuzkxV +Private-Lines: 14 +AAABAQCKb84XhlVdtDir/Dae2pilHm/BTfLQh3+DpAoH+vMF0Gb6YS0/qKTG0Vka +A5+M+ti7wXZGlCbgMl8zRiDWXP7yUd2J4pgYSJPPCFP9K6UGhwKYVKvOnjGcL9x8 +Ts5muinJqngOk/cA7h+rSPfLC/b8IsOEH6artnCMfNYu1ldzchjmwt8u57xw0Yc1 +JqIqAnQD0ZFv50JsZzdHy719IgMFiqYnulAgK+mwGG6Fcbcz0Mi29dcqPkpDzFNz ++5Dm0oV+oipg4oSMHBpF4EaEBXPshSoPCMvid2IBYqZk/0P1VeHeTfD12Tz6J2ag +8bMxYh+BzBh9Lk23qLgbiy+zMOKhAAAAgQD3mVM5gnJvON4be5GoQHPsoZCLapFm +ykfGxbtexc/96T7k9iISHMzzFFss3+kf+wTiM+M/eWMCGMNfeDVqCLQKXvijhnGJ +zFsaEd2BAem7r0yBT35o0jcSNskHOFP9ZLmv1cx+QS3AMvW1d/Wgrdzg/n/N3oSy +6xG0lwYAP18CqwAAAIEA9FYmUBuUGP2fIev5INsIS6ffOiTiSGRjz2yRRc0SDVSL +PLknhdPrPDFJKvhOshkr+A3n2t/oqGI+ktbOuUxI7uhlPDL3D6Te/1ysObT0rXGR +9cV/SE8NKWe83J6wG21u0AZIgiOYKAgmUFSju93Pj22s8ECmmSTh0QwJV3If7P8A +AACBAPZa+3GZW7AwkHCPy2zjnCC49nnjVc/ZqodJiQ+95T4QQK8MzqlrQ+bTsoAS ++nN4WA9eOdvF6WQVrt5D/oLleA0I+c7iYd7JAOyiAVV75rHbBxpEUNgzP+VKymk4 +nR8N5IcbZJ/XXeUzQX7HaiRZansWPb+PvhbuYvKNsLD8pjr7 +Private-MAC: 456c364af11080a5032d3f01cf0adb5c425a13f6 diff --git a/src/test/resources/ppkv2_rsa_unix_encrypted.ppk b/src/test/resources/ppkv2_rsa_unix_encrypted.ppk new file mode 100644 index 00000000..319fdbe6 --- /dev/null +++ b/src/test/resources/ppkv2_rsa_unix_encrypted.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCXX5GPy2zMByhViM5aW+lVH3oPVBVvsfXj +b7Afy2ldttHHkDMhoQKMNUScBTdyLwHrje6c0C99aYNkSl5TLXePWx4nIkNFrEyh +OfooY8CHPTuhCjfy+4A/psxeE5aoI5YGfffSr0uOVOUdhV3X3p1SmQsybzSiFz0T +HULrtirxoCbf5a1UGNafYoXPvtSS3/qGJnsnbxE/gL2mTEvOyLI7o3ffFIgbfOra +nbuow/HSboX/Uc9i2FsbJR4i6K5A6KB3NIL3gFJHxzjumJB4QXt7mUXzo0FWnOXL +ORFrzYd1Rf26AVcUc24wwStVAOtXO3EIr/AylwgsqWqoStOzoRFB +Private-Lines: 14 +8ObwsQzQGN/DN+sRAL8BwisB6emytEjK0EMer4XyScCaZWg0rKrGwM8HnLaenAVk +kyNcE6/Pe7/jPLJjqG2qpDo7eFSWML1JZdrByEbzsJL0lSKEIgJQEWw3ozl7eLD0 +Mmhm5WC3qivygw8OGXM6IZyT8WnjsUZGFUlqt7o2cZ7hPlrVFoIumn4mWlFA96EY +wjyWEsQiYPVtwuKIudwXrLWD/BcFOpKajNJ2xiZHDe15zPvJPTXCUiLP0/1OqEFC +5MhUWbQ26igKx2rMjtx0fN2I+U9eVBIFuNnqAh/nQ0gMet55H5fBkSwWUVSA8Wt1 +SQBTlbtWx828EAeycBdLnUxJzhNWmBMz4ljda8cBecKTKHzAhRkxA7KgBiml3omE +jUR4ZpxFoaL/y3tdMI4EyNnOOgpaHARamAqIrMB5JUEXNuYw+aOOkQlXmi+2JwuI +2nggRicIV1+Tlo2Zi9lb723J43xlvcdSpULo8acFw60ybn9/Z+lkQjHRN1W/AR0d +xzB/ytvIAFO9pHiRJrBkJ8K7GBGOR5XIrE1mn64zh+lgxhGdPyw73f6uBeeDKl+N +QtPI9xAtX+2ENsJCXzbdVF6GSYCNQ/PomZz04LiyzvGPLe2xyt5EgGw5eR7ii7cA +YdJsFgOSyqZm12NnuDFGlJdIHiB+nK5g0ZG3Q5KvI9xhJvYFaeJfGplP8ky3B9SI +BP1xRUAzbmdxs81LUJMQtNZGZFC491iId4LAGy2SPGkiJ9OsxZ7UM3mtX8LBd+Ju +R2OQZwMLJgGXkAd3bEphQwTFUnIAYHql6KTVbzxoZU+rjFrgFaphWCoD8HqOYx0W +ELK8ereV7vQVFngbQIiazZaD/RyYbYkLJdBO+PE9Sv9LMqr4H8tiTrRCDg2EwYk1 +Private-MAC: e00e7d3fc277d5fe759477456f348ae71c78fbfe diff --git a/src/test/resources/ppkv2_rsa_windows.ppk b/src/test/resources/ppkv2_rsa_windows.ppk new file mode 100644 index 00000000..1b534e78 --- /dev/null +++ b/src/test/resources/ppkv2_rsa_windows.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCdPnSRJDt/YuU5Oj1gq6JKYsJacWbgsdU+ +vpzAT7IiIVOoGB7oPi8EGcZSt1kIJBc9Lf70uRYCbCmq3v3EOLGpSdhV16oKQygS +gSZawq2YwYHzHnonp6akLfQQXwE/nnubX3S6iYAaJSrJ50APhKfQXYsz4CH2alrz +Yb9Y2TDl4hKfxYABgMza3O02BFooK2tbxy2+0fbOKB/qjamGVE/CoMcv4ChB9FDG +dUr0AVy7BYtfELAZ1dPrUh7lMCXa6JRRhFtdRWQGKmfM1nH/KpuzB+yZsl8kfHf4 +7qiX/mwTQM87JMxm7HuV8pHHr7DNFs9g30CreF+hbY1Cx+IKh+eR +Private-Lines: 14 +AAABAQCG7sc8nWjpAUZOe2mcAOx9BI5e6h0sB65D73G3nSvxGcQd5MTw6huSW0PS +Sz99OusuNsAn5IO8hHClDkGZFkVuTc30q+JgeAx1BJqTG6e4A6WtqKOOT9Ex5bUg +L0Z0/1x2kc0rHT7uMKKtK2HPbzhKF1uSomzCdbWiUGjQp1/Mg38Hvy1KETfjAb4l +4skIgqvCp/iIqd/2o+qonID3VYGRohHuf1BfGcFuwAXjwy1UQztPopHn6eclSLKd +J/YpZHkw5t4UNN6NZ/vpTlU0H4DJPmXFsW9lPT6qTRjrT91vumumwlV5vaEFOZdZ +a/b4KSh0QRs8Ae/X+g+nIRfT5t75AAAAgQDdovwknLlpl70yMktt2fvFZ1z/Tx22 +lc2Gs7wgrRlj76R7Y0RhrpRVCt/Y+Z6trocw6maHrtYzpF5IUFSteSoYvKqIT5kq +HDCsxnD6wlH9jmKoVovGRLpDNzSlNtLlUVdmkyTQmbJGX/YjWb4/B93e3kzkFe5e +PqGuI3YjR8Y0NwAAAIEAtZ+mZsJUuJOImvE37AbBljd6LlxRyVXqaTuXx5hetJ7U +VK6tbLmMUXSL5F0f2Ftg6+i093Qc2HsaE3+Dt+LkkmiPEM+o9FIK8xwgey/S8RQk +uGdaP84Q+cqIC0MMxOUuk3MWK1yKRDOZFYgjkfZPvhywY7d7scCPgzzZjoW6bncA +AACAVZHXu+Xc6VTaLjAwlLj4aV6vgcFBlJVP1GSBnMdnRqHrqYWMpmodsh6xjT+K +DkX0wkheNiqcxFs4FCIsGePGDdo4kofnYo6aH1a7jjAcZMQWgS9DxRgJQstYvGfr +qnaDGse0dmD/w6tyqEmeBP6AGZJBWx9SvzJ441NtAv3+r2c= +Private-MAC: 5c60a7b96b51ae767b879915d64328bfbe2867a6 diff --git a/src/test/resources/ppkv2_rsa_windows_encrypted.ppk b/src/test/resources/ppkv2_rsa_windows_encrypted.ppk new file mode 100644 index 00000000..7f86ebf5 --- /dev/null +++ b/src/test/resources/ppkv2_rsa_windows_encrypted.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCPJDCMNOM7Huv+NNeP626Slq54aPceMLEa +oqlfvLJiQf11b8jXcKfFgLlp/f0Ltk10p+5hAGlzBw/nlYRU85i7Eq75kSb6LPcQ +3SNY1/NkrNcZcFf4p/AKeC0s1qgbypvjWy7Zk8AUooPFrhIo/kKaPMouq4hcyENz +2VKgpdJ21SzTxjDASj7zd9DZP8uc1Lsg3ftbr8Mi+MFFQRnFPLv/w2zIoLo6xwO0 +UplUIyAGAsRPDdRZQ50h9b5BFyFLnXUlwI/Jrpgs2J31Vg8J2j2Y3BFDgn/Mf/P9 +Rsvdo4fp/y+W195198VQ2gsH2+Az+3Id6ySsGhztBP5Pl2Z2K5Pt +Private-Lines: 14 +RU1YnbiIpjM2kSpm91PPq6ZEsvAJiM1ZBle98bdLIOI1KhrXzcP5GlDz0khoUHMF +8uFn3zS9pAV3t6UC38KYBH5/sRni1wDf6nfnJ12VrOxeSE4q+5lC8e5MipOyOx1b +RfwDb6o7tzUl8RHm+xmzihCHilUQB/05sDHy4JwMcN6QgA9hm23drSiCO8itd9tw +fhidYahW0JkGrQf5rNkgYgCssMfAqcecurng+RWTU/J8M0BMbp8AYEUVtPvOK95J +0kWz/lFG1d6oC1d5bcRL0LL4Oz6PWaVNf7+cjBRkJGfqJYtNi0zEN4R5MW8QTKye +C29Dqy2/+JLjaa2xVYbdFqwIgePENEJIu0/g+k6tAhMN0G8kLaApsclKEtYCdd83 +wlNdpFvPr31Xna1y5mfpXDgfEYYUf+t3vcD0+IluYMRzkDwv/BN6QGB1q00NmWZ7 +iWZv0NZzf2pVo7hFJC83pKXfnkhqaLlLywfyu8RYEmA3LwdFe45Dt0oS7xP6DnAq +bzgC3L+tMGoHrrKUhefqV8ma4m3rnBzobS8OCzwIRK8Jio1BgQuYX4EU4eu2DEmU +eVJENZDT03uNzvh6G5ews809qz3T04Gyj7gQLgh560BwZ+Q8CVm9LVyynht+DMgH +1CEGi7I8pW8aPZDcgY8EyS02NAf+kHUtHF9Y3nO2T0VyiBrHwyF13B6kuQATuWPu +SRJpQlcMytjdwAx/iPzQBg2Gdbc1hmPVzFi9N4LWpTLncSVdwMWBAEKF+qZBkXx+ +BjBal45UoAD8JiNa/jfRVU/BHclLEr0DU0gGeUcP+u1WGv1srHjrrDbpujj7qQfu +kIyq2p6IPkaCjz+6wHoZzlyY1fHGXS8c9qpMw7f/VohWDUkLP26hCErF+eiAtq1K +Private-MAC: f5e53c305287fa9b6b02422ee5de0dfee3ad1a5c diff --git a/src/test/resources/ppkv3_dsa_unix.ppk b/src/test/resources/ppkv3_dsa_unix.ppk new file mode 100644 index 00000000..9ac88344 --- /dev/null +++ b/src/test/resources/ppkv3_dsa_unix.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAM6Xy9zencF2KDhq1r0gTXBdA4AL5Rtg5GYd4tx6qlSx +k3ZX634ude5bV8Io2UrsgvIDQHSfj+1J4sDpWnhMtT12KucddwAzLhb+/5gYEr1U +CZtPn1WlsvOXrJ9pmAt/MB1KppyG38UX3QK6Z04oWspqHWJBfO0oquUUGx5Ua9JS +neJCrzk++Sd1GkhjZElR51Hm7MKV9KZejn/tUZQK2FRjpfP50GsIlAs1v31OkY1g +lhgs90/6v7PyV1ljp27Nw1aNL+CxOjA/wYnFo+jE5+EsW81oT8C06n4yrcFySWFR +96wOfb2fAuCl4K9Ma4jN+7J0RLMY1++KzCsAtppi4KMAAAAVAMbFE/Q0VL6xGj97 +5l/7AqEVxWH7AAABAQCbU609B2d7d5EipWyjlETAs64vaKBXOAREcQr8HQfAkWmS +UBr//brBdNWeA4TVla4hyKL1CBiP1Hi6pDQ12lCB+J30ikRqdi45y2MvOR8OfEDy +3sn94sLwswARnMwdfHWrPepfyVdqOnIlhsYWzLpc5EhfOThvL8YZ78tPTCqyui8C +Oi3TznOFfW/do0ydEEmfQwJTlHi6dpFiokh+mpQVOlstehmgKzrolLEjccS/vnxS +qsdWDwKErbtg0bYhhV3rztiMcMozoBblgCSDiVmvGBAO8GnIoyX5/J+UfuwWYQKY +rtuZRfB4hRyRU4zKopPfQrZkc1Jummk959KgfeTIAAABAQCayyiO1dYHPHF6g21s +wekxrukaXix6sab4m+OLm0bNRa+7nI3A09wG+otKajznna/mdjcIaWuhdG5S4+l+ +aPypIUWTdM0krUu0A+9Pu9YpIgVgEFKVQX9PBcP5+WHanMaeYmLOCEP1WqFMJ5Hi +RCJmssOKwZJygH/KEMIGaDGhYnSmN6R+41j8sDAoY9/00WlSTKqpmVGTe7bJGWom +nhT08RsATIyvM2i/J13qrh5UfGprj6GsaE9q+Zjg47oMdZIRSgyp4jw9coQoR68T +emOmkODmXaoixfrGT1FC9LbqOmN/iDwv0QvLUen3RTbbB2lE3LwObBXJRpd44Rje +Mo7c +Private-Lines: 1 +AAAAFE1NvhJa+HevZ3y1nFhhtzEh3Yi3 +Private-MAC: de384c789bdafb1acfb4ad240ede6a24ba92b4c45d22906b0ae348f5caeb4302 diff --git a/src/test/resources/ppkv3_dsa_unix_encrypted.ppk b/src/test/resources/ppkv3_dsa_unix_encrypted.ppk new file mode 100644 index 00000000..0630252f --- /dev/null +++ b/src/test/resources/ppkv3_dsa_unix_encrypted.ppk @@ -0,0 +1,30 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBALvd0uyCDZFuKcpphdcnMEYVXnoy16rW5XIFhlxmvVSU +DDz3DAGyXISyhyQ43BhI8R0OeOr6VpivujFu0srXrcxcgsAy6yv/6uAvaeLfekc/ +JErS7OqVqACFmt5ISkEBcvWKnUseFRwoy2KCeiEo2GyKwyV00caLnYb1Th2CUNRF +ZcH5p5XLj3gwFbPf7EdkKV0PHWV4E5ptFVVT/aRijCJRQn//FlSNlG6iOfUF4IRU +pz05qivVGaHLWNOw1fSXK1Pc53qQqCAaEict3arhQCwSEKl1xktSNBDVmeapLPj7 +LNcZIGvAA52Tpg3qR1rp/8a+bdtteNkbRg4Gfx2uBuMAAAAVAKFdLforDVztYQzh +7pFjN8qAjogVAAABACTsvZOw0aPNwdQFiaQik1F08HQrjlp298vgbjIZFrgABE4x +euWMydEcxAKKEhy7Y9Go4l9shUddkHgk3Y+OLvAsifowqfc8Fpsqxh1WKLxnw9NO +5rjenO+kNeiwhX/oWa1VBMOa/O+iGTvCy1XcW3u0E1LqGVAx3DdkP6EHV91ma3d1 +LWih/RR8ClERNaovnETash8NQCHAmKejIObneLtNRqyaiNohYK0t5EAtP4JutVwr +snj3ZoSKrCZct2wVrMUkkqCsKcKygCsr29uk1xpVYBkv9zUn6V199ZB/uXanRLcL +/vq0UYWGV9nXU0shVanMwHNQaMk8oWnd6hTPB6EAAAEADwMN1uDvQqkyKel83xNG +4Er3LsSCx6iARNWpaV1ZLNrtf2h6MnYCKdSdyVgRjIL5coBQB5yZb/+CXm9QgysH ++K5FHXgnh4hjz8W8/Magj+qm3/SKDTmbLAsNvvvenQvayTWUGNQlfQET/tHvGRy7 +gz4r95phWGmxS8Zu1NkT0RHl6xxcSqWmvvjG/m2MuNo+5lfKFa2+j+Is59HwN3ZK +5umpPlJl4uM5SkyFD+iUbptOVCXdVV4xRhIzYy4wllADQQHkbNQtNGJT7JEHuot9 +2J3FWOCEQVw4F06j8NsdghKWIwGSlcHLHAbA8zZDq11R6vnhOFDwmfekxCwLXaBl +GQ== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: e10608eedddc7992eff0f91aeac654b4 +Private-Lines: 1 +0+i6uWp69WUFuFgBYYmfzrEw7wjMgKINMsfXSRh+JL8= +Private-MAC: f962cf010508ca8b62f2562881179e2d70afe18bbea3d48939d98b695710cb93 diff --git a/src/test/resources/ppkv3_dsa_windows.ppk b/src/test/resources/ppkv3_dsa_windows.ppk new file mode 100644 index 00000000..a2f92132 --- /dev/null +++ b/src/test/resources/ppkv3_dsa_windows.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAOGfXe0SXEr7JIMEZQo6rkTc6CfPIWsgqDYem72p84pn +6KykGD6RonyqDEfDoS0XpwIY2sCZLmwauEhTQtBZpMyix4sD49w+SjaHUP1aoAT7 +iPFvsXXGNw9/jgK5a67NzWxmcl5XMQG/VDjtVLWzNfl9OXHMSNmGUKQmlPZKXRrm +06SYVZMDsRbgSJgi6PNOatuVvfR+b2hK+ppMpQNKnHXe/vpgcblZ1ViPUTH4vG+0 +4PkidIbwHIIl5Yv69lUYOcycxR3yzd8ZC3a7qzVkxet+tGWCD4/PzfWEP+/G2TWX +bRuxV6YEaq9POmI0JuZa7CAZ8iA/OFtYua3TB4cUsSsAAAAVAJzJoUVy/fnPi6Uu +PLR8cCyhqB3HAAABAQDH61jRVXoAqKJRHgIWhBsrQMmAvOqVHyrALSlAchNT5cso +4e7Rd9ajJFg+hVZGKu5PoQdhp/8ERk8mnABKue6r9qO/6qT8NvCRuI0Wb8Sxgw5q +/4hzo4BVdx4UhLqTRl9rF7B5wLSjV75HIsqW61ZcbT9+VdYpRaNg8/Te1jfqwgrT +ndaD7jhkhU2P0Z7vHSRlzaeSksF6b+SRFu+bnQ6neBxZZTuMrSfKOSpp2ltLUvWr +Q48GJ0I/uE1HbQehz9Tn0Q3VA2BjpfKgj8bLE5YFokveuqjFULF7EiL4CmAt6qhi +QOl/f6zKR4gaLUjkVuvU/uyTr/da7/FfOJouETpFAAABAERldAO0JWHLr1nClDX+ +ZPN8XbbpjSaWO+fFLQO0A7QE/2gKJ8lt5qi39GOOhmUO10johHaOupqRqe4iCRnB +xMJeLxXmS2KbcwDke+IBRaid81ccocpcPOrpov582DjJuwZ0394Akyrc7uMonjBy ++JJaPwpXHQS4SXRe7NybuEE4S+DsV6DNx9WG5n5PFh4YELbzdP2oJZ3hyZ2MiZgo +02ShziEDPI1vQ4BBAsknp6Ubzt40Gt1yjyNMbGpv8IIRZzu+H8CIRnzbCYKT8mRK +e0HvTG48c6la0/wvvG62zfmbFYgkw0tQxgekpf3ojNpRJZ1vcQPgtvQ5IjkCYSF/ +ss4= +Private-Lines: 1 +AAAAFB8185dI+D57PqqRGfFcBGxjParZ +Private-MAC: bdd028dd46f482f8aad5a4ba4a40db927350d41f129f6fb80c3907cb51a22d20 diff --git a/src/test/resources/ppkv3_dsa_windows_encrypted.ppk b/src/test/resources/ppkv3_dsa_windows_encrypted.ppk new file mode 100644 index 00000000..088f99ce --- /dev/null +++ b/src/test/resources/ppkv3_dsa_windows_encrypted.ppk @@ -0,0 +1,30 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAIYJqKE0jtG+HpbHurIu+kVIuQLrMrgXhL7woINHOHGf +b/0O85u+3DsSy+s7f/M9bWUIqRtt2+JV2J/V28mDgF7IEeiP3OjSYspmoxnCy8d6 +g313NcYYoCqG9f/yWrzSXQtK3hD8+KNqm68GQx2L6lceyyHn3CgUFTwHw0MNntOf +Ak94PCoM5wOZCfqx9sZfTVk+uQb++aG8xcAqPoPQdTgaqrFa9lrp2Ul8RhM/eDpC +mByg8I5tIsbk17IVzS0gLp9slfInhwr2UjX2wIOlVOnNmS8UHLAwhDtdtHcWYdFb +EMpO9hFMJCz39JqnJ7g8qjct/bfMxAEqhwlGpgbYrz0AAAAVAL66GQDORVX2on1i +c6drzCN/9f4ZAAABAA8eU3kzUacUj50ggFzpTLuBBztILvjIpNGnXy7SUG5EWCZX +LvBPTjJf9idxL/0P42AlJqKpaZSsdDLeaPbyUsQ0B9R+eTVDKQd5po5kBmWcxqMJ +h0f1XU1CYFZgN2cvzmtyx9euekEka/j1mqGLuXHRpX0vak/wsy3T8mEPlqLr09c6 +hPUDG5U8NXEy3aWS5gRRj7wPDLAi1sFGo4ReUfjn6LJglbCi71tSirnV0dJJvVGp +XA17RzetDgZmIjdDur6XdGz+6B07puj/OUTTCFSFzJC0wNKGS/OjY9+59ziFj+M7 +LCA7HUAlYeL4n83EsZp4fHI09JyuLJppPo/4xxAAAAEAU2ThrVHZ3fT8JuXT4dgJ +VtgoBTQVf1vbzKA/o5yrOCKj+pPLFv+mXSJxLWvdSUeHkaRZF76hCwSpy6K+Wt9s +RSuL6qTPog8TT7Yhk0YgfMd/wXwYlhhoH6HqRIIQRHd1bIOn+n1DUWQHS2w6Ksd6 +jnVDd5cte+sBRNFeNjRJPUg2dzFqYQUgf5bfDu6/mhISduez7BctGb0cWtVPyjzQ +ivJRUVnLiUJJE3V0saEC2FYOalknyW2x99tolUn4YwOLjUuQm1oKZ+BEbXlgGe4p +Ke/JxB3nYHT78wP7ArnhImPGK8b0+/JD4nAA15GEkFLRV/u9qyZJe/iwbpqKz2vB +/Q== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: b2f8dee868310abc40ef9d96abcd67cf +Private-Lines: 1 +HUZSXpBSW1DZJtQ9dCmjGPKRfqwKXlOFleqL2zzeK7Q= +Private-MAC: a7217ec852a154487b30da65849d2f25a78d822881b739471164df5fa24c08ef diff --git a/src/test/resources/ppkv3_ecdsa256_unix.ppk b/src/test/resources/ppkv3_ecdsa256_unix.ppk new file mode 100644 index 00000000..406de6b2 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa256_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLoiF7se+l2K +wZh9oxhfTASh5diPDNvYbVgS98Q4yPE9uExiDDMeX2Nu9XLtnevuTWrmcAF6u6/o +zI3BIkeHiBQ= +Private-Lines: 1 +AAAAIE+1J+zx/1g2rtov3b16NoLXn/cW+jdeTQrXgllPzlC/ +Private-MAC: 9549460921fa6dc12d723f3985618f1c8fb719fca05f388a3405f4c3eee7f7f8 diff --git a/src/test/resources/ppkv3_ecdsa256_unix_encrypted.ppk b/src/test/resources/ppkv3_ecdsa256_unix_encrypted.ppk new file mode 100644 index 00000000..5cf8a899 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa256_unix_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGrSiZUMuXa3 +aTNDF/H+rTZb4wUBapMIf9QXzfoJI6hOYPcCtHGH8KChdeZ2U9W3ohzX/WZIX8IQ +9fpxcGyvpbc= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 712b0ba1fdfd732b3dbdf061acadeb51 +Private-Lines: 1 ++RPUh/TDOJB+zgkGpLmCpmoaiQpD69WlNiES+pPbUQ1SF+fxZMhvme6k57zH6iem +Private-MAC: 58dd68fa08d7fdb217db1a69cc970320bd3fa45706b8a5b2a1feaa03efb25cb9 diff --git a/src/test/resources/ppkv3_ecdsa256_windows.ppk b/src/test/resources/ppkv3_ecdsa256_windows.ppk new file mode 100644 index 00000000..2c2b6179 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa256_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDMTBUib+twu +te16i5Rs2zKlVDMam5/RMWuFcXs2dvIs0kLP5N06diHYHBqvMPT9N336KMChz0/O +4S/dXR1pwHE= +Private-Lines: 1 +AAAAICNn2sFEDQ05pZvx8Wjcd+m/fHnlumgo7Rr7nE7XZd/0 +Private-MAC: a49e1637cc50016094abd341f817036d512f54f6a024947725e3ce9a2eacf67a diff --git a/src/test/resources/ppkv3_ecdsa256_windows_encrypted.ppk b/src/test/resources/ppkv3_ecdsa256_windows_encrypted.ppk new file mode 100644 index 00000000..3d059c69 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa256_windows_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMQNLejYwJxw +VlAGIVi55Lb0zfkz18CEAwqDtrr6xs2sz+vqRU1xadOgEzHeWovBqCXdC9wVja20 +2PyupH5QPsg= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 43ab4dfef809a010f8b46898613e7791 +Private-Lines: 1 +kChNCwDStowc8bfgLq8ZvT5PWqIe6tDfu4Cs6gwPAv5V9LOYJZhD0VzCLE4NsJg6 +Private-MAC: d1bca120202371a62af6c718c51d15cef782baa2d5bf87beb99585eef66569d3 diff --git a/src/test/resources/ppkv3_ecdsa384_unix.ppk b/src/test/resources/ppkv3_ecdsa384_unix.ppk new file mode 100644 index 00000000..976bfdb4 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa384_unix.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBnR7wyGL+IO +T7A7vKr4Do1OGlsMhBFN28GuKUohpkd5rvvZfe/9UNbdobFa38KmLWpxzAlL9Go3 +AK6+7OpWz6HE8JHdrJGyRYoS87XVv4oIIzmp8B8GThUYiDt8Qh8FmA== +Private-Lines: 2 +AAAAMQDFnuMXVDcejvYI0FcivZkI2cZ750s1tLgc9U/zQUFIYyybb8oHM4UsPeko +qZKs9xM= +Private-MAC: 3c36b58ea113af96aac49762d118793c374fcb399f293a3a79d9241e37bb4ab8 diff --git a/src/test/resources/ppkv3_ecdsa384_unix_encrypted.ppk b/src/test/resources/ppkv3_ecdsa384_unix_encrypted.ppk new file mode 100644 index 00000000..0adb9e71 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa384_unix_encrypted.ppk @@ -0,0 +1,16 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGdfKrIhLUFt +iMfBOFdF1LulPAF3OKNqlahGbA0bf/LXPf7tmbJUs+GvozsqYAbsMhed1vmwfBwp +aQdIG97JRSK4MK4yIrCjDjGwqmakX/hg/WUQZPi17463t9zSzFZ8xA== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 72bcd381a51296c255fb63a40cc2113e +Private-Lines: 2 +ROTdHYRD0zldAv4hvurg1k/BV7QnFgj7IdvsC5OA/kDSJmiXs0yJqyYKzdydS1xW +DrZV8KwWZO4GmvXUH0byDg== +Private-MAC: 4139eedb961ba84cb1375927930738cbe9ecf7feff471ae44a567b4264b5bf5c diff --git a/src/test/resources/ppkv3_ecdsa384_windows.ppk b/src/test/resources/ppkv3_ecdsa384_windows.ppk new file mode 100644 index 00000000..aaf79d09 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa384_windows.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNShRribjCP+ +pkCkAMwdj2LjxNrZjIKHtylcTdohEgfHJTXzub4tbq6odNS3izPwgMEfX6M6Vl5N +BIstk3c34+kDuJ/a+Cyn7lZ9HZPdDuaKE66PNp3zGK9linfzn3iFBA== +Private-Lines: 2 +AAAAMEWKgeXFZVyRT28hwsvRRB7H/ZhnII8d9uP0uhPPU4742NGWdsDIYtlEAW+N +54PC1A== +Private-MAC: cdca5f9ec10f19900830e393483c95a2f96056545ce560334f37e75c35169ccd diff --git a/src/test/resources/ppkv3_ecdsa384_windows_encrypted.ppk b/src/test/resources/ppkv3_ecdsa384_windows_encrypted.ppk new file mode 100644 index 00000000..b0a7c050 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa384_windows_encrypted.ppk @@ -0,0 +1,16 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBKGDqZyyEHaK +kg0ijDH9nRu86AiXmUNJcKWC05ksthEMheDRMyZtyqzIIwQI9Goum+Tktu3LeQS4 +fBh9aNHwMP2xN5uvj/uUYxN1dmQmLQ4NsQhpz+DY5TtxHJ/tUKvNpA== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 8de0354d6f5f58738c3756f7691e9b0d +Private-Lines: 2 +JuLWiPoGeL31caJQj5mk+sUE8BK4JsOqEZt24rIjNMcFU/zVrVD/GxnACvjwRAH7 +mlL0AuHbi/5L0pFOLaz4/w== +Private-MAC: 477fcc9f7ae260b3ba4715476a56c04871808e9e5353802dfa7b5caa3914c424 diff --git a/src/test/resources/ppkv3_ecdsa521_unix.ppk b/src/test/resources/ppkv3_ecdsa521_unix.ppk new file mode 100644 index 00000000..fb418278 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa521_unix.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADwW+/zw0OO +BYVS2jEOym6OctYKqb1/uSTegImb7XOcklV6HZSnkGDrp89gzXL6AgoFWMQkySgR +93l1j22kCwxGWQFQBLalq5ekPGpqBiE31QNot0QKPat2f4O8bw0DO5sUNPmH7ImK +myxAjHGiCPhi5egoIgrgzHoGnx1ynMr32Pntkg== +Private-Lines: 2 +AAAAQgDp0Jv8HmHaLVbfLM9oSHMOjGKJ3/uZY9gC8IDuBovmnapY10H8f1Uo9GD9 +Fpo7ZBTXNqAaHAh+KWvFYmUq5TVLWg== +Private-MAC: de2f0fc2577ac8c553ab52b838f127f3426c8ca9de22cfcbd11cbf5dfe8ff6f6 diff --git a/src/test/resources/ppkv3_ecdsa521_unix_encrypted.ppk b/src/test/resources/ppkv3_ecdsa521_unix_encrypted.ppk new file mode 100644 index 00000000..0ff44668 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa521_unix_encrypted.ppk @@ -0,0 +1,17 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGCln1Y+fp/ +uQTIVhjoNHuuRGVSzHc8shdMTGUQz0q+TpDSEf+SViws/gS0+V73QggV5TcUqOIO +ra4uD8mGSieuUwHea4ieXw+cbcts9lpYdjWkFmgSGwYfIVyosFZ+aE07We7fNRqS +UpH5gyBpdSpFKAiBTwtv73FOIwz88I82E4HVQw== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 2eb44562d24a9caf29194f4b74ad0af9 +Private-Lines: 2 +yJ0r68xbDPwIcsHmNsDMTQaWCPbEPF043Sm1oJJ48fVS3/Mid6iBTMffqbM4QdHs +uVTyA5l4/Rh9GKgb1NZr+NMNthCmbDF34gEAbgc332k= +Private-MAC: 3234726c3bd4ced9ad412585c8fd287fdf011c655e0d51e441be33180e571bc3 diff --git a/src/test/resources/ppkv3_ecdsa521_windows.ppk b/src/test/resources/ppkv3_ecdsa521_windows.ppk new file mode 100644 index 00000000..e688ff30 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa521_windows.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAA37JVxrRSM +TbtG8SUUuPzzwjsrumeJD+100IbXoAcXK0mPbGei04TdyMTpfy1SDWL3YND5CSzc +uHMOjApNKrxJ6wB8A5LE178vFKb7kvYo5SJq+YAbv7utQqlMH4sdKdywGwvdSTSZ +XCu+mzx9ebdoyRsmPS7vBrJYKcQPHiBDa9iVMA== +Private-Lines: 2 +AAAAQgFaFuLM1IvKBh6hChBTqbN2px8shq+rd1YV59GcnV6zVlaEgbZ9Lpdz3SZc +nKVT9/BjJ8Q0IRoko/jl5+fGsVp5Eg== +Private-MAC: 517745eb8ed80dad2395e80999921c3abcb4690fb01b2bb73e4e232f177fdaed diff --git a/src/test/resources/ppkv3_ecdsa521_windows_encrypted.ppk b/src/test/resources/ppkv3_ecdsa521_windows_encrypted.ppk new file mode 100644 index 00000000..66bfc879 --- /dev/null +++ b/src/test/resources/ppkv3_ecdsa521_windows_encrypted.ppk @@ -0,0 +1,17 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAKniAtk8UO +BIVa/348YbbZAyiLgJKpbH+2rEB0tHG7YYf+fAAGAZJsE7K7bl68XzunBT1Hguyt +UpcjzNgOS2hx2AAVJT1ObdU0IK8+B0DTXrntG8BMKpDjpHZJr/RQLEDljHt6kc35 +5TLoKZygF7vUujNjceWFjSmBlFeCbr5Fl4KoXw== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 9c4e53a5c080d688ac149f396ba7872e +Private-Lines: 2 +mpMJCAaqeTZ13LGQo68IepcXDAd0iq9nYG2pp9V+6dOVtXMHZVDVFsCYhD/vCeaj +lb247g8fSDyxQGvwk/CwVtq6uZXO/d+0W5K3ZgFOnVI= +Private-MAC: ec2a55039d54f981a8dc3126e820eac4c358800abdae76d00b91dd7226ec3374 diff --git a/src/test/resources/ppkv3_ed25519_unix.ppk b/src/test/resources/ppkv3_ed25519_unix.ppk new file mode 100644 index 00000000..8206d31d --- /dev/null +++ b/src/test/resources/ppkv3_ed25519_unix.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIFrJ3kAPWlhD8uR/Mo1dqhqtJIYACuH6vIh7/oP+ +r8UY +Private-Lines: 1 +AAAAIMTyr4DIkFlwt9d07Iw2GX2KJ9F90y/GciEu20RwqF0o +Private-MAC: 37c84944792d7dc96a40c3d92e6a1d3d32a5a238568503d48580bc032a23a678 diff --git a/src/test/resources/ppkv3_ed25519_unix_encrypted.ppk b/src/test/resources/ppkv3_ed25519_unix_encrypted.ppk new file mode 100644 index 00000000..e72cd5dd --- /dev/null +++ b/src/test/resources/ppkv3_ed25519_unix_encrypted.ppk @@ -0,0 +1,14 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAINnWA1NU5+6rlguJ4vDwB7Ro3wTO+ntaSPfmWIXj +p384 +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: aa1e739cbaa9d86e771fdf499ad93a59 +Private-Lines: 1 +oFIxq7q4Xb3LfYANl6QhPbx/3poc5/tvVG5Fj0BHer9iHh5GrofY3D2P7JcS/j6P +Private-MAC: c819ba8f6fc792f60400ad71bf577cc9baba13ffa696c610dca162564a405f6b diff --git a/src/test/resources/ppkv3_ed25519_windows.ppk b/src/test/resources/ppkv3_ed25519_windows.ppk new file mode 100644 index 00000000..a61f1be9 --- /dev/null +++ b/src/test/resources/ppkv3_ed25519_windows.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIGGj5a3dN4+iyqsAXenA/CzejFmBNyZinnYHhXXc +jbBV +Private-Lines: 1 +AAAAIDWvlS89N/MQ+7GMxk5N09vo7X6W8szaJ1mu6C4s9WsE +Private-MAC: 4eb0763c03faad6289f10d2dfdfeaed6b024b959cad06e1e042000b97e176745 diff --git a/src/test/resources/ppkv3_ed25519_windows_encrypted.ppk b/src/test/resources/ppkv3_ed25519_windows_encrypted.ppk new file mode 100644 index 00000000..99b825c8 --- /dev/null +++ b/src/test/resources/ppkv3_ed25519_windows_encrypted.ppk @@ -0,0 +1,14 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIEy7J6rEkUafMJmtqE8SpWZg/uYx6EcME5FH3E3H +B3rR +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: b26d22df894298dac8ce32d3189e35d8 +Private-Lines: 1 +mDG3fado6OIW/+xkwWRYgi2/bU29GW1KQvM0otOqWKcJPpoySjtPuuja43FNyh1Z +Private-MAC: f61593c72cbad92ea1443a8c3b844b8fe52f4feb43f0c03e6f73b84401dbd2a6 diff --git a/src/test/resources/ppkv3_ed448_unix.ppk b/src/test/resources/ppkv3_ed448_unix.ppk new file mode 100644 index 00000000..4130052a --- /dev/null +++ b/src/test/resources/ppkv3_ed448_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkhFs87N5abAoXAcsLiYTDOCujVJngP6C9j5dSttY/E +BgCQBMK60a0vt1lF7b7Drby5kqg7+IvkGgA= +Private-Lines: 2 +AAAAOaA4VTeL/Vg12PIVqPhDcT9qfUT0dIZolnGVRBbU71tD9DyyLbykR5LADjMG +YVuHR3tKi1qkRyxoAA== +Private-MAC: c2623760d10dbe581b7dc8dad41d3a40f437fb9ac2787e4a877c654776dfbae5 diff --git a/src/test/resources/ppkv3_ed448_unix_encrypted.ppk b/src/test/resources/ppkv3_ed448_unix_encrypted.ppk new file mode 100644 index 00000000..f516024a --- /dev/null +++ b/src/test/resources/ppkv3_ed448_unix_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADldNmjRx57BM40rrc4DabuF0L+RhEkOcDC8/jAhtbdT +O0X+sqgITai5gSk1JGlIrCRIF83Kvs0TKIA= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 42327c0d92de84745042deb048cb888b +Private-Lines: 2 +MD0ereDMhZNnTbZvgK5h0GlTlXZbdVNJfzaKrHth4q+ZMuZK3ONfTHbMgRKtsuFj +vRCgMJAJdq1qNCflJavE/w== +Private-MAC: 9bc62748d045aef6be0de10ecba4b626e41504636a7faf284f6ebac39a66d249 diff --git a/src/test/resources/ppkv3_ed448_windows.ppk b/src/test/resources/ppkv3_ed448_windows.ppk new file mode 100644 index 00000000..4ff9898b --- /dev/null +++ b/src/test/resources/ppkv3_ed448_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkVoUhABT+RHfYEGAMKySSyUSoJFj6HHcz87U+io4Dp +e4ye88DGUU2Lnxfux/WQq61DPHWxQHo4GoA= +Private-Lines: 2 +AAAAObCkYvIfcF/UyhGmrFg1BIzY+m/1BjSOl/G7BXbjb/afcugNYfD8Mx6c3kLa +05UFTkTX858guX/lAA== +Private-MAC: 6f19bb57b0ef41d6abdd1c555a6247507db32e5d022032c9744f3f735bceefe5 diff --git a/src/test/resources/ppkv3_ed448_windows_encrypted.ppk b/src/test/resources/ppkv3_ed448_windows_encrypted.ppk new file mode 100644 index 00000000..10228846 --- /dev/null +++ b/src/test/resources/ppkv3_ed448_windows_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkdO1HUptQ2ksEmblqqTM8loY2Q/Ep003qRxW753XVa +z8eh9CejJvTO2FYtyHNib5syBD814By7C4A= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: adfd1dcbca4e55bb379eee8123a41943 +Private-Lines: 2 +7v3Io0Ub3fTnNaE9qHuJ3Ua8KCPBQvDoznZ+ppbJdVV/gDrczoa2teEIQ6qonpYA +pIjHDQ/56tyVpt/yvkJGAg== +Private-MAC: 538f86533f231dce675843a305cf28bfe29f73434cb589bd08e6b9410f209a45 diff --git a/src/test/resources/ppkv3_rsa_unix.ppk b/src/test/resources/ppkv3_rsa_unix.ppk new file mode 100644 index 00000000..5297884e --- /dev/null +++ b/src/test/resources/ppkv3_rsa_unix.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCclEvT+gGh6exRwmgPhsN+PLMpcNltztLQ +1x8iKOsxCCbXWXRxfvvG/ye8lp4kEOWIZ1224UoG1401aNBVTGNUT7SVyAFSdnHG +7AGgFnLwJcxrsEHLX/f+gj/m4syNJfAg1PrS4O8IeaEnTdql9x4cYkzd+Jpa/OHZ +Lgltc8SHezhmxrc5Ylm1IMHu6gxJb0WwQ1mIZ44IAIaYvNflal7dP8v//bY+6kvS +5iARk6hDfiR/wH3926r7U0pB3siNDdz5yq7rjvTg6igtOhQDSnd9Drp0yHjBCyQo +iDqP1P2jhyfz1iDdf6u71CFhZK20rfbsUUHCjWilB00onvjjIDYJ +Private-Lines: 14 +AAABAHB5omK58xRA+e3dLW0BzEpRBg3dr4JOkGdOMGIUbKYDCguliZzBr9DJltzE +gQK9VHSAc/Qbr1Zs3lWgXg732V5GBx2U10ZKKP7Qp3Y8ygGx1T8CFLEn1ffvzkFn +Z7J4rx6WfzqeM3auEFIwfcC8W9fd0QOeQhrcDsw4YrNJ9sGuYIa/bo3VDNr4sbmc +odnbHSmxQFam9BV7bfxFcC3CPusvh4BdrT3vkQjlFR+RQOU6nDPpQ3Z3SXp12k3k +EoHClZuNKP1YCci8+uRWeobOo6rsJKJsmzFYnjNdPDH9ytQm2N7Pg93gB7Sqp27S +56XLoRSGzCeZqrW1Uc38wj/qX2UAAACBAO/PhzZFQOSOKydp+4d2U0ZM8YumRYYN +OBCd3XtypcThMjwj09smbZI2bMm7xGySwEW5VIRmry8swvy3bNFtybhlEV3/+SDl +spCCTKWUZykSCPJvWrt21LMe+DYvZRJhPyhjQvfStIjlDgyuJQVaPghx3KBaglvU +e6OGaBLbu9+nAAAAgQCnJldXgo0q9RXeSOJtiXKg8Y5ykLGALpkWudU6VdYGurbU +Q5Bu3gO3MyFeRV/GhHEZ3av0i63tfNBeOcWafm3OW1xaLaJDioHQ/KKlCfs5nXMC +Z6Qp+56mhk48unpdvOWSe3qorazrgo4XefKa/9nn8O6Im4DDOLPzAWxu8jJyzwAA +AIEAoX8jMlUD2McXRGBbRu3UoXRir0amRYONcYJN+kCeiptJl3WNETgni2Vt5Cgw +45mrIDQ3I+HkqhU+0cw6p8n9m4JkBQ7naWpyA32ofxdRPa9h0he5fZj+tDs3IQRY +OLh5cZMOxHsymrnHRCgLkddr+P976niOWEFc73MFvpxM3qE= +Private-MAC: 61abe803ffbf8f7ebd9c899ba3ef9279f08b433e9095eb248a28a38b45e6367e diff --git a/src/test/resources/ppkv3_rsa_unix_encrypted.ppk b/src/test/resources/ppkv3_rsa_unix_encrypted.ppk new file mode 100644 index 00000000..833eef7d --- /dev/null +++ b/src/test/resources/ppkv3_rsa_unix_encrypted.ppk @@ -0,0 +1,31 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCMTxeJiE+rerxDpf3UhJRVvaWulraY8VZr +yHhSPnHCODQw3MsQ0X6ECmTs8c3YeNRBRhiaB6zOQorUFqFbAwO2XIFQgDGk9qSJ +F1kwsVXKnReFqUQ4MQGjRt5odjHc4+00KnmfdLUubvLGJmzHYJ4Ia1+EEyzkUik4 +nVTxdreXrvFEIEej/QHJlhVSqsL2oKXKVMLAJ6OvGt9K1pFSJRCyedHVmR/z3tfs +iFqpw0R2P2WhhkK9jXG47tJcG9v5HPrGJqd3D+W982/YUFNFvLMVkq5qU4wEDFVh +1u7Z95dABtZm2TwzhrYfxdAIcYbZLpbFozWX+f2mEQRAbWws2YE7 +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 331886f5ea02ebe8d0aac746a408ede7 +Private-Lines: 14 +EpXqXpaS5gbssePDuppbh72EaQb4J17ReVx664sVo479mR07xjj1R24hP20H3FUr +zYif4C0NuWWqyUCZ4ULyjW5+08m3SgQ5WOva9bPLryWpI6CTDTZ8wVQqkmCgweKt +JtPVOtcrdCKze7udLa/Ah0PxNkFzsHNfu1aQfG6MnQ9p9orwzfy5A8pm36sxigwj +i6hHKMdcJY4+eA7Df5CkccVBiZlKr4nTChoSsSmBnLMKNSwR2EftW2H1uk9UzjxU +5VozBBeTkOqU9p6ph7pez5tDIZPN3krzQETIz1dXhTRrueard5l4slHV7N6W/k7t +JynUfkhiGfhwKEjD9GutdJ4oTTe1SNz/3ndVzRS/10uSCPEcn2SdL7QzxYG/lY3W +dAyS6hL8bR3V6NoxdTx/RWUqVrTfI0/dZpUN+h1iSsApB3M8Nth0nFhgsmveSPvT +K//weuOgfB7AtO0Jn+KG81sW0cBK4AlSr80BzfOhKpkgG5OciSw+P1VRE5eq3bzp +iRgbn8yKmvVXbY4NSX2Nued4C2z8MLMv1jHPyg7UOPhb52LyBGqWdT5iOURqWF/6 +Az37Tag7iDe8K/tFNBQUcazHNKFPnQpXPYJDo3ucRE2tV8txrhQws3WRvAh6n/Ag +Sb6Rz9OymLeSh/VAqcrWHb7PanGZGIo9XeWXRKKhU9SF0+vqqdPFCbVsleyVdoa4 +rIB7zOG15Rr81FaIVh2axTsKsMCIMiMNTOO6p55JT7ViVJr566LxgNA2FBZMEdhV +lXIfxz9JlLaP7a4/rTt4sp00PVorVjx/CeptKCRyptHYA2nM6In/tMHkkxd88l8d +6JESwNXAZz9JTWplWdMa0e0GjZ7hcazms0gWzd/5pMPAOjnixqqpEulGzmEX8EmK +Private-MAC: 1120af9034ed0384d24f636c7dcf926fdca1de80490322bc616b7f6bee225098 diff --git a/src/test/resources/ppkv3_rsa_windows.ppk b/src/test/resources/ppkv3_rsa_windows.ppk new file mode 100644 index 00000000..5b25a317 --- /dev/null +++ b/src/test/resources/ppkv3_rsa_windows.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCyu2eTD/3HRZnFhKMiy7YLGW7WUsT1uEgR +CUufHFbjwZqSdUMXplB7wD5g/iombD36oZtFGpZWjiReT8hh2k2dR5pzF7Hg9yTZ +osuBu8YRSTjwicc/NHTcDfl5kQFSGz40azFXWn1xJqPRQesDGZ2rQVloCxW771AV +S5hZvAybtM/qf6En7PxHJE4Eg7klvjH2bzhacDQch56u/ij+/ipwtqkWt40k6sMQ +7xtRIQH4AUNlqQ+3qpZxKfAl+NCbJ/V8Mj9BM3WuyeD6DYPwBT1mHXug9KWi7aii +2c/UvDv1xpF7u0mTRSW4Dkm7FTEbX8iPP1QF3ze87YEaF1qRkeMf +Private-Lines: 14 +AAABABoV6fb3xYU47kl6o0inzKjwDFIxgFeQjswVvDi7rR7Omd24SZOBDYwSMch8 +L/CtqZoTzhKW38xAMKSnJkrJzrwewDb+BVMdHv64mNUVb8IEGtBYe4EJCek4DOom +NLgkC78gyfHC3j4OcqhO45rvOSjOf9sEiyaDtf91qgWmwXxT9yfLeSXn+tbEBpez +1sUHw0Pk+Io2bG+jwrCEeA/sf0zBfoLAREAe9CcdZmhKpoXRadaN3falLpwH5fOk +wCnnjdxRCFgLpDZXsc3iA34hhM/mO7h2bnghe6h2KSrqgWJRdGL/+OpclJOx+l8s +L+n9n1/SMJtfLZ8mn6iZd74GAlEAAACBAPtVtmpNFNxw4S+arkhJGo5qplFQ6RNj +bbaCQWv7g7sYfZQuzRyq+ar4hn4Y71x8mLJKqlBNebtfctq0SzCSvFc/jOLqX+LH +/haOrM7WKh8QFVQ8UwtZZdzXImViEw4W1HGJvoLe/MksakTl6afYabLBsDUI04Fd +RekXhcy7sC5ZAAAAgQC2DLMio0Bgvz+hPotfHuSrBb49Gyqc/Nzt3jvFzvqFywU0 +lpXPjy/nCN7fEVuCusLaEM5KY0giQaLna/xDfcoFmpdfQR79E2NFu88nRf1nr4e/ +y/2etw0Pl1Eh/roWDjBi5bFpDBU3eDjF01ylcIbDTb6klJ4PON3gdHJx4WWeNwAA +AIBFMs9P/P9EIFebF91b4wvJTG1xS3b6aLE0jIHNv4inD/6u/4R8U3meGMlvzeZQ +xI56n6Hk8IaCEXehAj+VAfp9z2YHtPoAfUD8mbCKdBzgpW1P1AeOTIzvsi3+bHbm +ZM+e6cTsQGaZ9OJSRYCmGT7LiTRm6PQwyq+4FEP1t9VF3w== +Private-MAC: 20cf1acc614037e76bbc47942988e0aa3bc5582df1a173be2c468149139a406a diff --git a/src/test/resources/ppkv3_rsa_windows_encrypted.ppk b/src/test/resources/ppkv3_rsa_windows_encrypted.ppk new file mode 100644 index 00000000..a1190ad6 --- /dev/null +++ b/src/test/resources/ppkv3_rsa_windows_encrypted.ppk @@ -0,0 +1,31 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCnTnSVyVw7nyqnxy/WWUUuFswXUsdwvNto +jMVuqltV3CTDHfX6+uMu+XBC6Gqr/AzHvwzOPvzR+/k+m5fV5zLvjfe3zTU1gs5E +mOtu3YbOnn6il/IvVvU9ILq/5Dg89aIbSYG9R28iu8KciZxHOOpjgFpmoCiMAg8x +4OWRHc6irKj5VKTXMJC+5pmb4QZlSKal7KPqKyAsKII6IxMjpPNJejZm/Omw3QZ1 +9YIN02ttbM8rwoLuPLi1DoZiAUvF7J3XTEUcDAn5y5upltk1Dx+bWhuth/sYZ26q +ksxPr+KmtHwJ5R8pWJ4CXtENlwxQiAWftVWW8MeQlyN17qPW+Z9x +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 8bfc7fec5e9a97b5c271a2ff59bbd42a +Private-Lines: 14 +BdVaIWyb2IRejqogeZj7d55V+oiRVB87REDIRT+2nsqmRpv8d9wkEZxa2hZD4JJI +yPJoLHyl6LwKrGWwwCDXf0DHTePNAAASAcp5G60XY/rAz/DMtYUfjFKYfOB3SjUr +G1hjswV+RHm1OT9g5lz7tkeeF5jd3JkZ+6vdqeTxKrosUdqkLkN9SDbVtyLnEnfK +up5RVoMN6jd8UZG90HIsuwR8/G4UwYPwKpj/5RwjKUx26yKPyOCl7tZcrZbA8+fw +UNsODwlFSNdU4roo1SrmVKaDQ5LtV+HXunIOTuxt0p/YfvFcLupgaWgMysXzmhja +sAQyZt5RB7rJOlWXKVOXdd0UWwYOhYTgTItY7tljns8SffOQMt0j+DoLZdMJq1Ej +qWIy53yqCUs1Nyjqv9J9ptUCUiDP0xM50Qeob2YpFK1gpOpPTBOD1GrhF9P33uYT +lhlsz8Wvh4U6txuO3DaPkfmNn4t/0lVdBh6x2Tv1x7OWsebmUQy1Pj5ffJJ6N4wt +UbGdAeiJ2IsGzHHRThmBrjCTxI14QTjIrNpl4Ntbj7x2vPXHVoDfgUJrkeFD8NxW +NaTn+WdYRtod3wLcdM6YAf+rBtGs4gGWK+eKPlepJhia9onRc7Esue/svHYTVgm4 +jdPhQyHPvunLfN+ryD1yZkCRfir0c4HP7sniolfhVYM2UBvzBj+HiauEEWXetFeM +96Cdian5ouUtwvKmoIR0K5KT7g7iDtH94WreGK0Le13EGV9gwEk33HEenbcpn7Db +0ScHiwhHp4sXsiXh75b1kEWAJZVe8B6xbwLegIZesXvDztCMYq1RToBJ7rrMdEoN +uRkinI6LFI5JzxhLDRpsmfSEJQM1SqxRmyoBVn6Jrd02giL1CxuadULsNc4LV4B9 +Private-MAC: 7531943a8f9d37c31a318dbacb9de07b14346541d5b100bd665b36a195daf60f diff --git a/src/test/scala/Adler32Test.scala b/src/test/scala/Adler32Test.scala deleted file mode 100644 index c25a86a2..00000000 --- a/src/test/scala/Adler32Test.scala +++ /dev/null @@ -1,72 +0,0 @@ -/* -*-mode:scala; c-basic-offset:2; indent-tabs-mode:nil -*- */ -package com.jcraft.jsch.jzlib - -import org.scalatest._ -import org.scalatest.flatspec._ -import matchers.should._ - -import java.util.zip.{Adler32 => juzAdler32} - -class Adler32Test extends AnyFlatSpec with BeforeAndAfter with Matchers { - private var adler: Adler32 = _ - - before { - adler = new Adler32 - } - - after { - } - - behavior of "Adler32" - - it must "be compatible with java.util.zip.Adler32." in { - val buf1 = randombuf(1024) - val juza = new juzAdler32 - val expected = { - juza.update(buf1, 0, buf1.length) - juza.getValue - } - val actual = getValue(List(buf1)); - - actual should equal (expected) - } - - it can "copy itself." in { - val buf1 = randombuf(1024) - val buf2 = randombuf(1024) - - val adler1 = new Adler32 - - adler1.update(buf1, 0, buf1.length); - - val adler2 = adler1.copy - - adler1.update(buf2, 0, buf1.length); - adler2.update(buf2, 0, buf1.length); - - val expected = adler1.getValue - val actual = adler2.getValue - - actual should equal (expected) - } - - it can "combine adler values." in { - - val buf1 = randombuf(1024) - val buf2 = randombuf(1024) - - val adler1 = getValue(List(buf1)); - val adler2 = getValue(List(buf2)); - val expected = getValue(List(buf1, buf2)); - - val actual = Adler32.combine(adler1, adler2, buf2.length) - - actual should equal (expected) - } - - private def getValue(buf:Seq[Array[Byte]]) = synchronized { - adler.reset - buf.foreach { b => adler.update(b, 0, b.length) } - adler.getValue - } -} diff --git a/src/test/scala/CRC32Test.scala b/src/test/scala/CRC32Test.scala deleted file mode 100644 index a6aabdce..00000000 --- a/src/test/scala/CRC32Test.scala +++ /dev/null @@ -1,72 +0,0 @@ -/* -*-mode:scala; c-basic-offset:2; indent-tabs-mode:nil -*- */ -package com.jcraft.jsch.jzlib - -import org.scalatest._ -import org.scalatest.flatspec._ -import matchers.should._ - -import java.util.zip.{CRC32 => juzCRC32} - -class CRC32Test extends AnyFlatSpec with BeforeAndAfter with Matchers { - private var crc: CRC32 = _ - - before { - crc = new CRC32 - } - - after { - } - - behavior of "CRC32" - - it must "be compatible with java.util.zip.CRC32." in { - val buf1 = randombuf(1024) - val juza = new juzCRC32 - val expected = { - juza.update(buf1, 0, buf1.length) - juza.getValue - } - val actual = getValue(List(buf1)); - - actual should equal (expected) - } - - it can "copy itself." in { - val buf1 = randombuf(1024) - val buf2 = randombuf(1024) - - val crc1 = new CRC32 - - crc1.update(buf1, 0, buf1.length); - - val crc2 = crc1.copy - - crc1.update(buf2, 0, buf1.length); - crc2.update(buf2, 0, buf1.length); - - val expected = crc1.getValue - val actual = crc2.getValue - - actual should equal (expected) - } - - it can "combine crc values." in { - - val buf1 = randombuf(1024) - val buf2 = randombuf(1024) - - val crc1 = getValue(List(buf1)); - val crc2 = getValue(List(buf2)); - val expected = getValue(List(buf1, buf2)); - - val actual = CRC32.combine(crc1, crc2, buf2.length) - - actual should equal (expected) - } - - private def getValue(buf:Seq[Array[Byte]]) = synchronized { - crc.reset - buf.foreach { b => crc.update(b, 0, b.length) } - crc.getValue - } -} diff --git a/src/test/scala/DeflateInflateTest.scala b/src/test/scala/DeflateInflateTest.scala deleted file mode 100644 index eaadb26d..00000000 --- a/src/test/scala/DeflateInflateTest.scala +++ /dev/null @@ -1,337 +0,0 @@ -/* -*-mode:scala; c-basic-offset:2; indent-tabs-mode:nil -*- */ -package com.jcraft.jsch.jzlib - -import org.scalatest._ -import org.scalatest.flatspec._ -import matchers.should._ - -import JZlib._ - -class DeflateInflateTest extends AnyFlatSpec with BeforeAndAfter with Matchers { - val comprLen = 40000 - val uncomprLen = comprLen - var compr:Array[Byte] = _ - var uncompr:Array[Byte] = _ - - var deflater: Deflater = _ - var inflater: Inflater = _ - var err: Int = _ - - before { - compr = new Array[Byte](comprLen) - uncompr = new Array[Byte](uncomprLen) - - deflater = new Deflater - inflater = new Inflater - - err = Z_OK - } - - after { - } - - behavior of "Deflter and Inflater" - - it can "deflate and infate data in the large buffer." in { - val data = "hello, hello!".getBytes - - err = deflater.init(Z_BEST_SPEED) - err should equal (Z_OK) - - deflater.setInput(uncompr) - deflater.setOutput(compr) - - err = deflater.deflate(Z_NO_FLUSH) - err should equal (Z_OK) - - deflater.avail_in should equal (0) - - deflater.params(Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY) - deflater.setInput(compr) - deflater.avail_in = comprLen/2 - - err = deflater.deflate(Z_NO_FLUSH) - err should equal (Z_OK) - - deflater.params(Z_BEST_COMPRESSION, Z_FILTERED) - deflater.setInput(uncompr) - deflater.avail_in = uncomprLen - - err = deflater.deflate(Z_NO_FLUSH) - err should equal (Z_OK) - - err = deflater.deflate(JZlib.Z_FINISH); - err should equal (Z_STREAM_END) - - err = deflater.end - err should equal (Z_OK) - - inflater.setInput(compr) - - err = inflater.init - err should equal (Z_OK) - - var loop = true - while(loop) { - inflater.setOutput(uncompr) - err = inflater.inflate(Z_NO_FLUSH) - if(err == Z_STREAM_END) loop = false - else err should equal (Z_OK) - } - - err = inflater.end - err should equal (Z_OK) - - val total_out = inflater.total_out.asInstanceOf[Int] - - total_out should equal (2*uncomprLen + comprLen/2) - } - - it can "deflate and infate data in the small buffer." in { - val data = "hello, hello!".getBytes - - err = deflater.init(Z_DEFAULT_COMPRESSION) - err should equal (Z_OK) - - deflater.setInput(data) - deflater.setOutput(compr) - - while(deflater.total_in < data.length && - deflater.total_out < comprLen){ - deflater.avail_in = 1 - deflater.avail_out = 1 - err = deflater.deflate(Z_NO_FLUSH) - err should equal (Z_OK) - } - - do { - deflater.avail_out = 1 - err = deflater.deflate(Z_FINISH) - } - while(err != Z_STREAM_END); - - err = deflater.end - err should equal (Z_OK) - - inflater.setInput(compr) - inflater.setOutput(uncompr) - - err = inflater.init - err should equal (Z_OK) - - var loop = true - while(inflater.total_out - loop = false - case Z_NEED_DICT => - dictID should equal (inflater.getAdler) - err = inflater.setDictionary(dictionary, dictionary.length); - err should equal (Z_OK) - case _ => - err should equal (Z_OK) - } - } - while(loop) - - err = inflater.end - err should equal (Z_OK) - - val total_out = inflater.total_out.asInstanceOf[Int] - val actual = new Array[Byte](total_out) - System.arraycopy(uncompr, 0, actual, 0, total_out) - - actual should equal (hello) - } - - it should "support the sync." in { - val hello = "hello".getBytes - - err = deflater.init(Z_DEFAULT_COMPRESSION) - err should equal (Z_OK) - - deflater.setInput(hello) - deflater.avail_in = 3; - deflater.setOutput(compr) - - err = deflater.deflate(Z_FULL_FLUSH) - err should equal (Z_OK) - - compr(3) = (compr(3) + 1).asInstanceOf[Byte] - deflater.avail_in = hello.length - 3; - - err = deflater.deflate(Z_FINISH) - err should equal (Z_STREAM_END) - val comprLen= deflater.total_out.asInstanceOf[Int] - - err = deflater.end - err should equal (Z_OK) - - err = inflater.init - err should equal (Z_OK) - - inflater.setInput(compr) - inflater.avail_in = 2 - - inflater.setOutput(uncompr) - - err = inflater.inflate(JZlib.Z_NO_FLUSH) - err should equal (Z_OK) - - inflater.avail_in = comprLen-2 - err = inflater.sync - - err = inflater.inflate(Z_FINISH) - err should equal (Z_DATA_ERROR) - - err = inflater.end - err should equal (Z_OK) - - val total_out = inflater.total_out.asInstanceOf[Int] - val actual = new Array[Byte](total_out) - System.arraycopy(uncompr, 0, actual, 0, total_out) - - "hel"+new String(actual) should equal (new String(hello)) - } - - behavior of "Inflater" - - it can "inflate gzip data." in { - val hello = "foo".getBytes - val data = List(0x1f, 0x8b, 0x08, 0x18, 0x08, 0xeb, 0x7a, 0x0b, 0x00, 0x0b, - 0x58, 0x00, 0x59, 0x00, 0x4b, 0xcb, 0xcf, 0x07, 0x00, 0x21, - 0x65, 0x73, 0x8c, 0x03, 0x00, 0x00, 0x00). - map(_.asInstanceOf[Byte]). - toArray - - err = inflater.init(15 + 32) - err should equal (Z_OK) - - inflater.setInput(data) - inflater.setOutput(uncompr) - - val comprLen = data.length - - var loop = true - while(inflater.total_out BAOS, ByteArrayInputStream => BAIS} - -import JZlib._ - -class DeflaterInflaterStreamTest extends AnyFlatSpec with BeforeAndAfter with Matchers { - - before { - } - - after { - } - - behavior of "Deflter and Inflater" - - it can "deflate and infate data one by one." in { - val data1 = randombuf(1024) - implicit val buf = new Array[Byte](1) - - val baos = new BAOS - val gos = new DeflaterOutputStream(baos) - data1 -> gos - gos.close - - val baos2 = new BAOS - new InflaterInputStream(new BAIS(baos.toByteArray)) -> baos2 - val data2 = baos2.toByteArray - - data2.length should equal (data1.length) - data2 should equal (data1) - } - - behavior of "DeflterOutputStream and InflaterInputStream" - - it can "deflate and infate." in { - - (1 to 100 by 3).foreach { i => - - implicit val buf = new Array[Byte](i) - - val data1 = randombuf(10240) - - val baos = new BAOS - val gos = new DeflaterOutputStream(baos) - data1 -> gos - gos.close - - val baos2 = new BAOS - new InflaterInputStream(new BAIS(baos.toByteArray)) -> baos2 - val data2 = baos2.toByteArray - - data2.length should equal (data1.length) - data2 should equal (data1) - } - } - - behavior of "Deflter and Inflater" - - it can "deflate and infate nowrap data." in { - - (1 to 100 by 3).foreach { i => - - implicit val buf = new Array[Byte](i) - - val data1 = randombuf(10240) - - val baos = new BAOS - val deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION, - JZlib.DEF_WBITS, - true) - val gos = new DeflaterOutputStream(baos, deflater) - data1 -> gos - gos.close - - val baos2 = new BAOS - val inflater = new Inflater(JZlib.DEF_WBITS, true) - new InflaterInputStream(new BAIS(baos.toByteArray), inflater) -> baos2 - val data2 = baos2.toByteArray - - data2.length should equal (data1.length) - data2 should equal (data1) - } - } - - it can "deflate and infate nowrap data with MAX_WBITS." in { - implicit val buf = new Array[Byte](100) - - List(randombuf(10240), - """{"color":2,"id":"EvLd4UG.CXjnk35o1e8LrYYQfHu0h.d*SqVJPoqmzXM::Ly::Snaps::Store::Commit"}""".getBytes) foreach { data1 => - - val deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION, - JZlib.MAX_WBITS, - true) - - val inflater = new Inflater(JZlib.MAX_WBITS, true) - - val baos = new BAOS - val gos = new DeflaterOutputStream(baos, deflater) - data1 -> gos - gos.close - - val baos2 = new BAOS - new InflaterInputStream(new BAIS(baos.toByteArray), inflater) -> baos2 - val data2 = baos2.toByteArray - - data2.length should equal (data1.length) - data2 should equal (data1) - } - } -} diff --git a/src/test/scala/WrapperTypeTest.scala b/src/test/scala/WrapperTypeTest.scala deleted file mode 100644 index 09ee0b81..00000000 --- a/src/test/scala/WrapperTypeTest.scala +++ /dev/null @@ -1,210 +0,0 @@ -/* -*-mode:scala; c-basic-offset:2; indent-tabs-mode:nil -*- */ -package com.jcraft.jsch.jzlib - -import org.scalatest._ -import org.scalatest.flatspec._ -import matchers.should._ - -import scala.language.reflectiveCalls - -import java.io.{ByteArrayOutputStream => BAOS, ByteArrayInputStream => BAIS} - -import JZlib._ - -class WrapperTypeTest extends AnyFlatSpec with BeforeAndAfter with Matchers { - val data = "hello, hello!".getBytes - - val comprLen = 40000 - val uncomprLen = comprLen - var compr:Array[Byte] = _ - var uncompr:Array[Byte] = _ - var err: Int = _ - - val cases = /* success */ /* fail */ - List((W_ZLIB, (List(W_ZLIB, W_ANY), List(W_GZIP, W_NONE))), - (W_GZIP, (List(W_GZIP, W_ANY), List(W_ZLIB, W_NONE))), - (W_NONE, (List(W_NONE, W_ANY), List(W_ZLIB, W_GZIP)))) - - before { - compr = new Array[Byte](comprLen) - uncompr = new Array[Byte](uncomprLen) - - err = Z_OK - } - - after { - } - - behavior of "Deflater" - - it can "detect data type of input." in { - implicit val buf = compr - - cases foreach { case (iflag, (good, bad)) => - val baos = new BAOS - val deflater = new Deflater(Z_DEFAULT_COMPRESSION, DEF_WBITS, 9, iflag) - val gos = new DeflaterOutputStream(baos, deflater) - data -> gos - gos.close - - val deflated = baos.toByteArray - - good map { w => - val baos2 = new BAOS - val inflater = new Inflater(w) - new InflaterInputStream(new BAIS(deflated), inflater) -> baos2 - val data1 = baos2.toByteArray - data1.length should equal (data.length) - data1 should equal (data) - import inflater._ - (avail_in, avail_out, total_in, total_out) - } reduceLeft { (x, y) => x should equal (y); x } - - bad foreach { w => - val baos2 = new BAOS - val inflater = new Inflater(w) - try { - new InflaterInputStream(new BAIS(deflated), inflater) -> baos2 - fail("unreachable") - } - catch { - case e:java.io.IOException => - } - } - } - } - - behavior of "ZStream" - - it can "detect data type of input." in { - cases foreach { case (iflag, (good, bad)) => - val deflater = new ZStream - - err = deflater.deflateInit(Z_BEST_SPEED, DEF_WBITS, 9, iflag) - err should equal (Z_OK) - - deflate(deflater, data, compr) - - good foreach { w => - val inflater = inflate(compr, uncompr, w) - val total_out = inflater.total_out.asInstanceOf[Int] - new String(uncompr, 0, total_out) should equal (new String(data)) - } - - bad foreach { w => - inflate_fail(compr, uncompr, w) - } - } - } - - behavior of "Deflater" - - it should "support wbits+32." in { - - var deflater = new Deflater - err = deflater.init(Z_BEST_SPEED, DEF_WBITS, 9) - err should equal (Z_OK) - - deflate(deflater, data, compr) - - var inflater = new Inflater - err = inflater.init(DEF_WBITS + 32) - err should equal (Z_OK) - - inflater.setInput(compr) - - var loop = true - while(loop) { - inflater.setOutput(uncompr) - err = inflater.inflate(Z_NO_FLUSH) - if(err == Z_STREAM_END) loop = false - else err should equal (Z_OK) - } - err = inflater.end - err should equal (Z_OK) - - var total_out = inflater.total_out.asInstanceOf[Int] - new String(uncompr, 0, total_out) should equal (new String(data)) - - deflater = new Deflater - err = deflater.init(Z_BEST_SPEED, DEF_WBITS + 16, 9) - err should equal (Z_OK) - - deflate(deflater, data, compr) - - inflater = new Inflater - err = inflater.init(DEF_WBITS + 32) - err should equal (Z_OK) - - inflater.setInput(compr) - - loop = true - while(loop) { - inflater.setOutput(uncompr) - err = inflater.inflate(Z_NO_FLUSH) - if(err == Z_STREAM_END) loop = false - else err should equal (Z_OK) - } - err = inflater.end - err should equal (Z_OK) - - total_out = inflater.total_out.asInstanceOf[Int] - new String(uncompr, 0, total_out) should equal (new String(data)) - } - - private def deflate(deflater: ZStream, - data: Array[Byte], compr: Array[Byte]) = { - deflater.setInput(data) - deflater.setOutput(compr) - - err = deflater.deflate(JZlib.Z_FINISH) - err should equal (Z_STREAM_END) - - err = deflater.end - err should equal (Z_OK) - } - - private def inflate(compr: Array[Byte], - uncompr: Array[Byte], - w: WrapperType) = { - val inflater = new ZStream - err = inflater.inflateInit(w) - err should equal (Z_OK) - - inflater.setInput(compr) - - var loop = true - while(loop) { - inflater.setOutput(uncompr) - err = inflater.inflate(Z_NO_FLUSH) - if(err == Z_STREAM_END) loop = false - else err should equal (Z_OK) - } - err = inflater.end - err should equal (Z_OK) - - inflater - } - - private def inflate_fail(compr: Array[Byte], - uncompr: Array[Byte], - w: WrapperType) = { - val inflater = new ZStream - - err = inflater.inflateInit(w) - err should equal (Z_OK) - - inflater.setInput(compr) - - var loop = true - while(loop) { - inflater.setOutput(uncompr) - err = inflater.inflate(Z_NO_FLUSH) - if(err == Z_STREAM_END) loop = false - else { - err should equal (Z_DATA_ERROR) - loop = false - } - } - } -} diff --git a/src/test/scala/package.scala b/src/test/scala/package.scala deleted file mode 100644 index 1ff940bd..00000000 --- a/src/test/scala/package.scala +++ /dev/null @@ -1,40 +0,0 @@ -/* -*-mode:scala; c-basic-offset:2; indent-tabs-mode:nil -*- */ -package com.jcraft.jsch - -import scala.language.implicitConversions -import scala.language.postfixOps -import scala.language.reflectiveCalls - -import java.io._ - -package object jzlib { - implicit def readIS(is: InputStream) = new { - def ->(out: OutputStream) - (implicit buf: Array[Byte] = new Array[Byte](1024)) = { - LazyList. - continually(is.read(buf)). - takeWhile(-1 !=). - foreach(i => out.write(buf, 0, i)) - is.close - } - } - - // reading a resource file - implicit def fromResource(str: String ) = new { - def fromResource: Array[Byte] = - io.Source. - fromURL(getClass.getResource(str))(io.Codec.ISO8859). - map(_.toByte). - toArray - } - - implicit def readArray(is: Array[Byte]) = new { - def ->(out: OutputStream)(implicit buf: Array[Byte]) = { - new ByteArrayInputStream(is) -> (out) - } - } - - def randombuf(n: Int) = (0 to n).map{ _ => - util.Random.nextLong().asInstanceOf[Byte] - }.toArray -}