Skip to content

Commit

Permalink
eclipse-ditto#985 simplify fingerprint verification
Browse files Browse the repository at this point in the history
Signed-off-by: Dominik Guggemos <dominik.guggemos@bosch.io>
  • Loading branch information
dguggemos committed Mar 19, 2021
1 parent 41f0fef commit cc80346
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 219 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,43 @@

import java.net.SocketAddress;
import java.security.PublicKey;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.sshd.client.keyverifier.ServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.config.keys.KeyUtils;

/**
* Implementation of {@link org.apache.sshd.client.keyverifier.ServerKeyVerifier} that verifies the server key
* against a given list of fingerprints in the format {@code <algorithm>:<fingerprint>}, where {@code algorithm} is any
* supported digest algorithm and the {@code fingerprint} is a string of colon separated bytes in hex format. E.g.
* {@code SHA256:ae:2e:29:7b:11:93:ef:b3:4b:55:c6:83:28:8b:91:a4:12:c6:42:ca:21:f8:30:4d:3d:36:b5:4c:c3:b3:0f:44} is
* a valid fingerprint. The algorithm prefix is optional and defaults to {@code SHA-256} if not specified.
* against a given list of fingerprints in the format {@code <algorithm>:<fingerprint>}, where {@code algorithm} is the
* digest algorithm use to calculate the fingerprint and the actual {@code fingerprint} as it is generated by the
* ssh-keygen command line tool e.g. {@code sha256:MEULjymCqsBH6TkmQzKmA+G2qd+AJwarKwr84vUsQ+Y}.
*/
final class FingerprintVerifier implements ServerKeyVerifier {

private final Set<Fingerprint> knownHosts;
private final Set<String> knownHosts;

/**
* Instantiates a new {@code FingerprintVerifier}.
*
* @param knownHosts the list of known host fingerprints
*/
FingerprintVerifier(final List<String> knownHosts) {
checkNotNull(knownHosts, "knownHosts");
this.knownHosts = Collections.unmodifiableSet(new HashSet<>(
knownHosts.stream()
.flatMap(prefixedFingerprint -> Fingerprint.from(prefixedFingerprint).stream())
.collect(Collectors.toSet())));
this.knownHosts = Collections.unmodifiableSet(new HashSet<>(checkNotNull(knownHosts, "knownHosts")));
}

@Override
public boolean verifyServerKey(final ClientSession clientSession,
final SocketAddress remoteAddress,
final PublicKey serverKey) {
return knownHosts.stream().anyMatch(knownHost -> knownHost.matches(serverKey));
return knownHosts.stream()
.map(knownHostFingerprint -> KeyUtils.checkFingerPrint(knownHostFingerprint, serverKey))
.anyMatch(AbstractMap.SimpleImmutableEntry::getKey);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,13 +480,10 @@ public static final class Certificates {
public static final String SERVER_KEY = getCert("server.key");
public static final String SERVER_CRT = getCert("server.crt");
public static final PublicKey SERVER_PUBKEY = CERTIFICATE_EXTRACTOR.getCertificate(SERVER_CRT).getPublicKey();
public static final String SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX =
"ae:2e:29:7b:11:93:ef:b3:4b:55:c6:83:28:8b:91" +
":a4:12:c6:42:ca:21:f8:30:4d:3d:36:b5:4c:c3:b3:0f:44";
public static final String SERVER_PUBKEY_FINGERPRINT_SHA256 =
"SHA256:" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX;
"SHA256:MEULjymCqsBH6TkmQzKmA+G2qd+AJwarKwr84vUsQ+Y";
public static final String SERVER_PUBKEY_FINGERPRINT_MD5 =
"MD5:c5:64:48:5c:3a:fc:e1:2e:05:29:4d:78:1e:0f:ed:63";
"MD5:69:d8:52:ef:3d:df:c5:d1:10:fb:d4:3b:66:00:a8:f5";

// signed by CA_CRT
// no CN
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import static org.eclipse.ditto.services.connectivity.messaging.TestConstants.Certificates.SERVER_PUBKEY;
import static org.eclipse.ditto.services.connectivity.messaging.TestConstants.Certificates.SERVER_PUBKEY_FINGERPRINT_MD5;
import static org.eclipse.ditto.services.connectivity.messaging.TestConstants.Certificates.SERVER_PUBKEY_FINGERPRINT_SHA256;
import static org.eclipse.ditto.services.connectivity.messaging.TestConstants.Certificates.SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX;
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;

Expand All @@ -34,6 +33,10 @@

public class FingerprintVerifierTest {

private static final String MD5 = "MD5";
private static final String SHA256 = "SHA256";
private static final String SHA512 = "SHA512";

private ClientSession mockSession;
private SocketAddress mockAddress;

Expand All @@ -59,36 +62,16 @@ public void verifyServerKeySHA256() {
assertThat(underTest.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isTrue();
}

@Test
public void verifyServerKeySHA256Lowercase() {
final List<String> knownHosts = List.of("sha256:" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX.toLowerCase());
final ServerKeyVerifier underTest = new FingerprintVerifier(knownHosts);
assertThat(underTest.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isTrue();
}

@Test
public void verifyServerKeySHA256Uppercase() {
final List<String> knownHosts = List.of("sha256:" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX.toUpperCase());
final ServerKeyVerifier underTest = new FingerprintVerifier(knownHosts);
assertThat(underTest.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isTrue();
}

@Test
public void verifyServerKeySHA_256() {
final List<String> knownHosts = List.of("SHA-256:" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX);
final ServerKeyVerifier underTest = new FingerprintVerifier(knownHosts);
assertThat(underTest.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isTrue();
}

@Test
public void verifyServerKeyMD5() {
final ServerKeyVerifier underTest = new FingerprintVerifier(List.of(SERVER_PUBKEY_FINGERPRINT_MD5));
assertThat(underTest.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isTrue();
}

@Test
public void verifyServerKeyDefault() {
final ServerKeyVerifier underTest = new FingerprintVerifier(List.of(SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX));
public void verifyServerKeyDefaultMD5() {
final String noPrefix = SERVER_PUBKEY_FINGERPRINT_MD5.replaceFirst(MD5 + ":", "");
final ServerKeyVerifier underTest = new FingerprintVerifier(List.of(noPrefix));
assertThat(underTest.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isTrue();
}

Expand All @@ -100,29 +83,16 @@ public void verifyServerKeyFailsForEmptyList() {

@Test
public void verifyServerKeyFailsForWrongAlgorithm() {
final List<String> knownHosts = List.of("SHA512:" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX);
final ServerKeyVerifier fingerPrintVerifier = new FingerprintVerifier(knownHosts);
assertThat(fingerPrintVerifier.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isFalse();
}

@Test
public void verifyServerKeyFailsForUnknownAlgorithm() {
final List<String> knownHosts = List.of("MURMUR:" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX);
final List<String> knownHosts = List.of(SERVER_PUBKEY_FINGERPRINT_SHA256.replaceFirst(SHA256, SHA512));
final ServerKeyVerifier fingerPrintVerifier = new FingerprintVerifier(knownHosts);
assertThat(fingerPrintVerifier.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isFalse();
}

@Test
public void verifyServerKeyFailsForEmptyAlgorithm() {
final List<String> knownHosts = List.of(":" + SERVER_PUBKEY_FINGERPRINT_SHA256_NOPREFIX);
final List<String> knownHosts = List.of(SERVER_PUBKEY_FINGERPRINT_SHA256.replaceFirst(SHA256, SHA512));
final ServerKeyVerifier fingerPrintVerifier = new FingerprintVerifier(knownHosts);
assertThat(fingerPrintVerifier.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isFalse();
}

@Test
public void verifyServerKeyFailsForWrongFingerprint() {
final List<String> knownHosts = List.of("MD5:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00");
final ServerKeyVerifier fingerPrintVerifier = new FingerprintVerifier(knownHosts);
assertThat(fingerPrintVerifier.verifyServerKey(mockSession, mockAddress, SERVER_PUBKEY)).isFalse();
}
}

0 comments on commit cc80346

Please sign in to comment.