This is an automated email from the ASF dual-hosted git repository. tomaswolf pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit a0ef7a567a97c94206378d9e8f8ea0b072bef947 Author: Thomas Wolf <[email protected]> AuthorDate: Fri Apr 17 23:40:02 2026 +0200 Host certificates: check both public keys for not being revoked It could be that the certificate is perfectly valid, but the certified host key is listed itself as being revoked. --- .../keyverifier/KnownHostsServerKeyVerifier.java | 15 ++++++++++++++- .../common/signature/KnownHostsCertificateTest.java | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java index 10d97b1d6..414979330 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/keyverifier/KnownHostsServerKeyVerifier.java @@ -298,6 +298,18 @@ public class KnownHostsServerKeyVerifier .filter(e -> isCert == "cert-authority".equals(e.getHostEntry().getMarker())) .collect(Collectors.toList()); if (!keyMatches.isEmpty()) { + if (serverKey instanceof OpenSshCertificate) { + // Also check whether the certified key has been revoked. + PublicKey certifiedKey = ((OpenSshCertificate) serverKey).getCertPubKey(); + String certKeyType = KeyUtils.getKeyType(certifiedKey); + if (hostMatches.stream() // + .filter(entry -> certKeyType.equals(entry.getHostEntry().getKeyEntry().getKeyType())) + .filter(k -> KeyUtils.compareKeys(k.getServerKey(), certifiedKey)) + .anyMatch(entry -> "revoked".equals(entry.getHostEntry().getMarker()))) { + handleRevokedKey(clientSession, remoteAddress, certifiedKey); + return false; + } + } return true; } @@ -306,7 +318,8 @@ public class KnownHostsServerKeyVerifier } Optional<HostEntryPair> anyNonRevokedMatch = hostMatches.stream() - .filter(k -> !"revoked".equals(k.getHostEntry().getMarker())) + .filter(k -> !"revoked".equals(k.getHostEntry().getMarker()) + && !"cert-authority".equals(k.getHostEntry().getMarker())) .findAny(); if (!anyNonRevokedMatch.isPresent()) { diff --git a/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java b/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java index 9e0b6a5db..d6b47a407 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/signature/KnownHostsCertificateTest.java @@ -20,6 +20,7 @@ package org.apache.sshd.common.signature; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.security.KeyPair; import java.util.Arrays; import java.util.Collections; @@ -199,4 +200,24 @@ class KnownHostsCertificateTest extends BaseTestSupport { s.auth().verify(AUTH_TIMEOUT); } } + + @Test + void testHostCertificateWithRejectedHostKeyFails() throws Exception { + initKeys(KeyUtils.EC_ALGORITHM, 256, KeyUtils.EC_ALGORITHM, 256, "ecdsa-sha2-nistp256", "cert-authority"); + Path knownHosts = tmp.resolve("known_hosts"); + StringBuilder line = new StringBuilder(); + line.append("@revoked "); + line.append("[localhost]:").append(port).append(",[127.0.0.1]:").append(port).append(' '); + line.append(PublicKeyEntry.toString(hostKey.getPublic())); + line.append('\n'); + Files.write(knownHosts, Collections.singletonList(line.toString()), StandardOpenOption.APPEND); + client.setServerKeyVerifier(new KnownHostsServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE, knownHosts)); + assertThrows(SshException.class, () -> { + try (ClientSession s = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(CONNECT_TIMEOUT) + .getSession()) { + s.addPasswordIdentity(getCurrentTestName()); + s.auth().verify(AUTH_TIMEOUT); + } + }); + } }
