diff --git a/src/main/java/net/schmizz/sshj/common/ECDSAVariationsAdapter.java b/src/main/java/net/schmizz/sshj/common/ECDSAVariationsAdapter.java index 8663289c..8e7d8003 100644 --- a/src/main/java/net/schmizz/sshj/common/ECDSAVariationsAdapter.java +++ b/src/main/java/net/schmizz/sshj/common/ECDSAVariationsAdapter.java @@ -85,7 +85,7 @@ class ECDSAVariationsAdapter { X9ECParameters ecParams = NISTNamedCurves.getByName(name); ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN()); ECPoint p = new ECPoint(bigX, bigY); - ECPublicKeySpec publicKeySpec = new java.security.spec.ECPublicKeySpec(p, ecCurveSpec); + ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec); KeyFactory keyFactory = KeyFactory.getInstance("ECDSA"); return keyFactory.generatePublic(publicKeySpec); diff --git a/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java b/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java index 12432063..50f5b58d 100644 --- a/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java +++ b/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java @@ -197,6 +197,12 @@ final class KeyExchanger if (hkv.verify(transport.getRemoteHost(), transport.getRemotePort(), key)) return; } + log.error("Disconnecting because none of the configured Host key verifiers ({}) could verify '{}' host key with fingerprint {} for {}:{}", + hostVerifiers, + KeyType.fromKey(key), + SecurityUtils.getFingerprint(key), + transport.getRemoteHost(), + transport.getRemotePort()); throw new TransportException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE, "Could not verify `" + KeyType.fromKey(key) diff --git a/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java b/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java index 38b3c300..dfac6f1a 100644 --- a/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java +++ b/src/main/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHosts.java @@ -340,7 +340,7 @@ public class OpenSSHKnownHosts @Override public boolean verify(PublicKey key) throws IOException { - return key.equals(this.key) && marker != Marker.REVOKED; + return getKeyString(key).equals(getKeyString(this.key)) && marker != Marker.REVOKED; } public String getLine() { @@ -350,12 +350,12 @@ public class OpenSSHKnownHosts line.append(getHostPart()); line.append(" ").append(type.toString()); - line.append(" ").append(getKeyString()); + line.append(" ").append(getKeyString(key)); return line.toString(); } - private String getKeyString() { - final Buffer.PlainBuffer buf = new Buffer.PlainBuffer().putPublicKey(key); + private String getKeyString(PublicKey pk) { + final Buffer.PlainBuffer buf = new Buffer.PlainBuffer().putPublicKey(pk); return Base64.encodeBytes(buf.array(), buf.rpos(), buf.available()); } diff --git a/src/test/groovy/com/hierynomus/sshj/transport/verification/KnownHostMatchersTest.groovy b/src/test/groovy/com/hierynomus/sshj/transport/verification/KnownHostMatchersSpec.groovy similarity index 100% rename from src/test/groovy/com/hierynomus/sshj/transport/verification/KnownHostMatchersTest.groovy rename to src/test/groovy/com/hierynomus/sshj/transport/verification/KnownHostMatchersSpec.groovy diff --git a/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy b/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy new file mode 100644 index 00000000..84522661 --- /dev/null +++ b/src/test/groovy/com/hierynomus/sshj/transport/verification/OpenSSHKnownHostsSpec.groovy @@ -0,0 +1,126 @@ +/* + * Copyright (C)2009 - SSHJ Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.hierynomus.sshj.transport.verification + +import net.schmizz.sshj.common.KeyType +import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts +import net.schmizz.sshj.util.KeyUtil +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import spock.lang.Specification +import spock.lang.Unroll + +import java.security.GeneralSecurityException +import java.security.PublicKey + +import static org.hamcrest.CoreMatchers.equalTo +import static org.hamcrest.CoreMatchers.instanceOf +import static org.hamcrest.MatcherAssert.assertThat + +class OpenSSHKnownHostsSpec extends Specification { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + + @Unroll + def "should add comment lines"() { + given: + def file = writeKnownHosts(contents) + + when: + OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(file) + + then: + openSSHKnownHosts.entries().size() == 1 + openSSHKnownHosts.entries()[0] instanceof OpenSSHKnownHosts.CommentEntry + + where: + contents << ["", "# this is a comment"] + } + + def "should parse and verify plain host entry with RSA key"() { + given: + def f = writeKnownHosts("schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==") + final PublicKey key = KeyUtil + .newRSAPublicKey( + "e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365", + "23"); + + when: + OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(f) + + then: + openSSHKnownHosts.verify("schmizz.net", 22, key) + openSSHKnownHosts.verify("69.163.155.180", 22, key) + !openSSHKnownHosts.verify("69.163.155.18", 22, key) + } + + def "should parse and verify hashed host entry"() { + given: + def f = writeKnownHosts("|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ=="); + final PublicKey key = KeyUtil + .newRSAPublicKey( + "e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365", + "23"); + + when: + OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(f) + + then: + openSSHKnownHosts.verify("192.168.1.61", 22, key) + !openSSHKnownHosts.verify("192.168.1.2", 22, key) + } + + def "should parse and verify v1 host entry"() { + given: + def f = writeKnownHosts("test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327") + def key = KeyUtil.newRSAPublicKey("ae6983ed63a33afc69fe0b88b4ba14393120a0b66e1460916a8390ff109139cd14f4e1701ab5c5feeb479441fe2091d04c0ba7d3fa1756b80ed103657ab53b5d7daa38af22f59f9cbfc16892d4ef1f8fd3ae49663c295be1f568a160d54328fbc2c0598f48d32296b1b9942336234952c440cda1bfac904e3391db98e52f9b1de229adc18fc34a9a569717aa9a5b1145e73b8a8394354028d02054ca760243fb8fc1575490607dd098e698e02b5d8bdf22d55ec958245222ef4c65b8836b9f13674a2d2895a587bfd4423b4eeb6d3ef98451640e3d63d2fc6a761ffd34446abab028494caf36d67ffd65298d69f19f2d90bae4c207b671db563a08f1bb9bf237", + "23") + when: + OpenSSHKnownHosts knownHosts = new OpenSSHKnownHosts(f) + + then: + knownHosts.verify("test.com", 22, key) + } + + def "should ignore malformed line"() { + given: + def f = writeKnownHosts("M36Lo+Ik5ukNugvvoNFlpnyiHMmtKxt3FpyEfYuryXjNqMNWHn/ARVnpUIl5jRLTB7WBzyLYMG7X5nuoFL9zYqKGtHxChbDunxMVbspw5WXI9VN+qxcLwmITmpEvI9ApyS/Ox2ZyN7zw==") + + when: + OpenSSHKnownHosts knownHosts = new OpenSSHKnownHosts(f) + + then: + knownHosts.entries().size() == 0 + } + + File writeKnownHosts(String line) + throws IOException { + File known_hosts = temp.newFile("known_hosts"); + FileWriter fileWriter = new FileWriter(known_hosts); + BufferedWriter writer = new BufferedWriter(fileWriter); + writer.write(line); + writer.write("\r\n"); + writer.flush(); + writer.close(); + return known_hosts; + } + + +} diff --git a/src/test/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHostsTest.java b/src/test/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHostsTest.java deleted file mode 100644 index 7bd4a7b4..00000000 --- a/src/test/java/net/schmizz/sshj/transport/verification/OpenSSHKnownHostsTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C)2009 - SSHJ Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.schmizz.sshj.transport.verification; - -import net.schmizz.sshj.util.KeyUtil; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.PublicKey; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.*; - -public class OpenSSHKnownHostsTest { - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - public File writeKnownHosts(String line) - throws IOException { - File known_hosts = temp.newFile("known_hosts"); - FileWriter fileWriter = new FileWriter(known_hosts); - BufferedWriter writer = new BufferedWriter(fileWriter); - writer.write(line); - writer.write("\r\n"); - writer.flush(); - writer.close(); - return known_hosts; - } - - @Test - public void shouldAddCommentForEmptyLine() - throws IOException { - File file = writeKnownHosts(""); - OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(file); - assertThat(openSSHKnownHosts.entries().size(), equalTo(1)); - assertThat(openSSHKnownHosts.entries().get(0), instanceOf(OpenSSHKnownHosts.CommentEntry.class)); - } - - @Test - public void shouldAddCommentForCommentLine() - throws IOException { - File file = writeKnownHosts("# this is a comment"); - OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(file); - assertThat(openSSHKnownHosts.entries().size(), equalTo(1)); - assertThat(openSSHKnownHosts.entries().get(0), instanceOf(OpenSSHKnownHosts.CommentEntry.class)); - } - - @Test - public void testSchmizzEntry() - throws IOException, GeneralSecurityException { - OpenSSHKnownHosts kh = new OpenSSHKnownHosts(new File("src/test/resources/known_hosts")); - final PublicKey key = KeyUtil - .newRSAPublicKey( - "e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365", - "23"); - - assertTrue(kh.verify("schmizz.net", 22, key)); - assertTrue(kh.verify("69.163.155.180", 22, key)); - assertFalse(kh.verify("69.163.155.18", 22, key)); - } - - @Test - public void testVerifyIndexError() throws Exception { - final OpenSSHKnownHosts v = new OpenSSHKnownHosts(new File("src/test/resources/known_hosts.invalid")); - assertTrue(v.entries().isEmpty()); - } -} diff --git a/src/test/resources/known_hosts b/src/test/resources/known_hosts deleted file mode 100644 index 32a1df0e..00000000 --- a/src/test/resources/known_hosts +++ /dev/null @@ -1,4 +0,0 @@ -schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ== -# Above we have a plain line, Below we have a hashed line, Last is a v1 line, This is a garbage line. -|1|dy7xSefq6NmJms6AzANG3w45W28=|SSCTlHs4pZbc2uaRoPvjyEAHE1g= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAu64GJcCkdtckPGt8uKTyhG1ShT1Np1kh10eE49imQ4Nh9Y/IrSPzDtYUAazQ88ABc2NffuOKkdn2qtUwZ1ulfcdNfN3oTim3BiVHqa041pKG0L+onQe8Bo+CaG5KBLy/C24eNGM9EcfQvDQOnq1eD3lnR/l8fFckldzjfxZgar0yT9Bb3pwp50oN+1wSEINJEHOgMIW8kZBQmyNr/B+b7yX+Y1s1vuYIP/i4WimCVmkdi9G87Ga8w7GxKalRD2QOG6Xms2YWRQDN6M/MOn4tda3EKolbWkctEWcQf/PcVJffTH4Wv5f0RjVyrQv4ha4FZcNAv6RkRd9WkiCsiTKioQ== -test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327 diff --git a/src/test/resources/known_hosts.invalid b/src/test/resources/known_hosts.invalid deleted file mode 100644 index edd846e6..00000000 --- a/src/test/resources/known_hosts.invalid +++ /dev/null @@ -1 +0,0 @@ -M36Lo+Ik5ukNugvvoNFlpnyiHMmtKxt3FpyEfYuryXjNqMNWHn/ARVnpUIl5jRLTB7WBzyLYMG7X5nuoFL9zYqKGtHxChbDunxMVbspw5WXI9VN+qxcLwmITmpEvI9ApyS/Ox2ZyN7zw==