This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 47f779f06cb345c7cb706cb81f1214c37dab1fda
Author: FliegenKLATSCH <ch...@koras.de>
AuthorDate: Wed Apr 8 21:18:40 2020 +0200

    [SSHD-660] Add support for serer side openssh host certkeys
---
 .../org/apache/sshd/cli/server/SshServerMain.java  |  15 ++
 .../apache/sshd/common/config/keys/KeyUtils.java   |   8 +
 .../common/config/keys/OpenSshCertificate.java     |  63 +++++++
 .../common/config/keys/OpenSshCertificateImpl.java | 210 +++++++++++++++++++++
 .../keys/impl/OpenSSHCertificateDecoder.java       | 118 ++++++++++++
 .../FileHostKeyCertificateProvider.java            | 102 ++++++++++
 .../keyprovider/HostKeyCertificateProvider.java    |  32 ++++
 .../sshd/common/keyprovider/KeyPairProvider.java   |  14 +-
 .../sshd/common/signature/BuiltinSignatures.java   |  88 +++++++++
 .../sshd/common/signature/SignatureFactory.java    |   2 +-
 .../apache/sshd/common/signature/SignatureRSA.java |   4 +-
 .../org/apache/sshd/common/util/buffer/Buffer.java |  34 +++-
 .../sshd/common/util/buffer/ByteArrayBuffer.java   |   7 +
 .../util/buffer/keys/BufferPublicKeyParser.java    |   1 +
 .../buffer/keys/OpenSSHCertPublicKeyParser.java    |  89 +++++++++
 .../security/eddsa/EdDSASecurityProviderUtils.java |   2 -
 .../util/security/eddsa/EDDSAProviderTest.java     |   4 +-
 .../java/org/apache/sshd/client/ClientBuilder.java |   6 +
 .../apache/sshd/client/ClientFactoryManager.java   |   6 +
 .../java/org/apache/sshd/client/kex/DHGClient.java | 105 ++++++++++-
 .../sshd/client/session/AbstractClientSession.java |  27 ++-
 .../java/org/apache/sshd/server/ServerBuilder.java |   8 +
 .../apache/sshd/server/ServerFactoryManager.java   |   6 +
 .../java/org/apache/sshd/server/SshServer.java     |  11 ++
 .../sshd/server/config/keys/ServerIdentity.java    |  23 ++-
 .../java/org/apache/sshd/server/kex/DHGServer.java |   1 +
 .../sshd/server/session/AbstractServerSession.java |  43 ++++-
 .../common/signature/OpenSSHCertificateTest.java   | 161 ++++++++++++++++
 .../org/apache/sshd/common/signature/example-ca    |  49 +++++
 .../apache/sshd/common/signature/example-ca.pub    |   1 +
 .../apache/sshd/common/signature/ssh_host_rsa_key  |  38 ++++
 .../common/signature/ssh_host_rsa_key-cert.pub     |   1 +
 .../sshd/common/signature/ssh_host_rsa_key.pub     |   1 +
 .../signature/ssh_host_rsa_key_sha1-cert.pub       |   1 +
 34 files changed, 1259 insertions(+), 22 deletions(-)

diff --git 
a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java 
b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
index 321ae1e..c0f42ac 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
@@ -19,6 +19,7 @@
 
 package org.apache.sshd.cli.server;
 
+import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
@@ -26,12 +27,15 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.logging.Level;
+import java.util.stream.Collectors;
 
 import org.apache.sshd.common.NamedResource;
 import org.apache.sshd.common.PropertyResolver;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.config.ConfigFileReaderSupport;
 import org.apache.sshd.common.config.SshConfigFileReader;
+import org.apache.sshd.common.keyprovider.FileHostKeyCertificateProvider;
+import org.apache.sshd.common.keyprovider.HostKeyCertificateProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.server.SshServer;
@@ -62,6 +66,7 @@ public class SshServerMain extends SshServerCliSupport {
         String hostKeyType = 
AbstractGeneratorHostKeyProvider.DEFAULT_ALGORITHM;
         int hostKeySize = 0;
         Collection<String> keyFiles = null;
+        Collection<String> certFiles = null;
         Map<String, Object> options = new 
TreeMap<>(String.CASE_INSENSITIVE_ORDER);
 
         int numArgs = GenericUtils.length(args);
@@ -140,6 +145,11 @@ public class SshServerMain extends SshServerCliSupport {
                         keyFiles = new LinkedList<>();
                     }
                     keyFiles.add(optValue);
+                } else if 
(ServerIdentity.HOST_CERT_CONFIG_PROP.equals(optName)) {
+                    if (certFiles == null) {
+                        certFiles = new LinkedList<>();
+                    }
+                    certFiles.add(optValue);
                 } else if 
(ConfigFileReaderSupport.PORT_CONFIG_PROP.equals(optName)) {
                     port = Integer.parseInt(optValue);
                 } else {
@@ -171,6 +181,11 @@ public class SshServerMain extends SshServerCliSupport {
         KeyPairProvider hostKeyProvider =
             resolveServerKeys(System.err, hostKeyType, hostKeySize, keyFiles);
         sshd.setKeyPairProvider(hostKeyProvider);
+        if (certFiles != null) {
+            HostKeyCertificateProvider certProvider = new 
FileHostKeyCertificateProvider(
+                    
certFiles.stream().map(Paths::get).collect(Collectors.toList()));
+            sshd.setHostKeyCertificateProvider(certProvider);
+        }
         // Should come AFTER key pair provider setup so auto-welcome can be 
generated if needed
         setupServerBanner(sshd, resolver);
         sshd.setPort(port);
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
index 37f4051..fd89f9e 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/KeyUtils.java
@@ -68,6 +68,7 @@ import org.apache.sshd.common.Factory;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.impl.DSSPublicKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
+import org.apache.sshd.common.config.keys.impl.OpenSSHCertificateDecoder;
 import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
 import org.apache.sshd.common.config.keys.impl.SkECDSAPublicKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
@@ -140,6 +141,8 @@ public final class KeyUtils {
     /** @see <A HREF="">https://tools.ietf.org/html/rfc8332#section-3</A> */
     public static final String RSA_SHA256_KEY_TYPE_ALIAS = "rsa-sha2-256";
     public static final String RSA_SHA512_KEY_TYPE_ALIAS = "rsa-sha2-512";
+    public static final String RSA_SHA256_CERT_TYPE_ALIAS = 
"rsa-sha2-256-cert-...@openssh.com";
+    public static final String RSA_SHA512_CERT_TYPE_ALIAS = 
"rsa-sha2-512-cert-...@openssh.com";
 
     private static final AtomicReference<DigestFactory> DEFAULT_DIGEST_HOLDER 
= new AtomicReference<>();
 
@@ -153,9 +156,12 @@ public final class KeyUtils {
         NavigableMapBuilder.<String, 
String>builder(String.CASE_INSENSITIVE_ORDER)
             .put(RSA_SHA256_KEY_TYPE_ALIAS, KeyPairProvider.SSH_RSA)
             .put(RSA_SHA512_KEY_TYPE_ALIAS, KeyPairProvider.SSH_RSA)
+            .put(RSA_SHA256_CERT_TYPE_ALIAS, KeyPairProvider.SSH_RSA_CERT)
+            .put(RSA_SHA512_CERT_TYPE_ALIAS, KeyPairProvider.SSH_RSA_CERT)
             .build();
 
     static {
+        registerPublicKeyEntryDecoder(OpenSSHCertificateDecoder.INSTANCE);
         registerPublicKeyEntryDecoder(RSAPublicKeyDecoder.INSTANCE);
         registerPublicKeyEntryDecoder(DSSPublicKeyEntryDecoder.INSTANCE);
 
@@ -800,6 +806,8 @@ public final class KeyUtils {
             }
         } else if (SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
             return KeyPairProvider.SSH_ED25519;
+        } else if (key instanceof OpenSshCertificate) {
+            return ((OpenSshCertificate) key).getKeyType();
         }
 
         return null;
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java
new file mode 100644
index 0000000..9fa7d5a
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificate.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.config.keys;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.List;
+
+public interface OpenSshCertificate extends PublicKey, PrivateKey {
+    int SSH_CERT_TYPE_USER = 1;
+    int SSH_CERT_TYPE_HOST = 2;
+
+    String getRawKeyType();
+
+    byte[] getNonce();
+
+    String getKeyType();
+
+    PublicKey getServerHostKey();
+
+    long getSerial();
+
+    int getType();
+
+    String getId();
+
+    Collection<String> getPrincipals();
+
+    long getValidAfter();
+
+    long getValidBefore();
+
+    List<String> getCriticalOptions();
+
+    List<String> getExtensions();
+
+    String getReserved();
+
+    PublicKey getCaPubKey();
+
+    byte[] getMessage();
+
+    byte[] getSignature();
+
+    String getSignatureAlg();
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificateImpl.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificateImpl.java
new file mode 100644
index 0000000..3af92ea
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/OpenSshCertificateImpl.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.config.keys;
+
+import java.security.PublicKey;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+public class OpenSshCertificateImpl implements OpenSshCertificate {
+
+    private static final long serialVersionUID = -3592634724148744943L;
+
+    private String keyType;
+    private byte[] nonce;
+    private PublicKey serverHostKey;
+    private long serial;
+    private int type;
+    private String id;
+    private Collection<String> principals;
+    private long validAfter;
+    private long validBefore;
+    private List<String> criticalOptions;
+    private List<String> extensions;
+    private String reserved;
+    private PublicKey caPubKey;
+    private byte[] message;
+    private byte[] signature;
+
+    public OpenSshCertificateImpl() {
+        super();
+    }
+
+    @Override
+    public String getRawKeyType() {
+        return keyType.split("@")[0].substring(0, keyType.indexOf("-cert"));
+    }
+
+    @Override
+    public byte[] getNonce() {
+        return nonce;
+    }
+
+    @Override
+    public String getKeyType() {
+        return keyType;
+    }
+
+    @Override
+    public PublicKey getServerHostKey() {
+        return serverHostKey;
+    }
+
+    @Override
+    public long getSerial() {
+        return serial;
+    }
+
+    @Override
+    public int getType() {
+        return type;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public Collection<String> getPrincipals() {
+        return principals;
+    }
+
+    @Override
+    public long getValidAfter() {
+        return validAfter;
+    }
+
+    @Override
+    public long getValidBefore() {
+        return validBefore;
+    }
+
+    @Override
+    public List<String> getCriticalOptions() {
+        return criticalOptions;
+    }
+
+    @Override
+    public List<String> getExtensions() {
+        return extensions;
+    }
+
+    @Override
+    public String getReserved() {
+        return reserved;
+    }
+
+    @Override
+    public PublicKey getCaPubKey() {
+        return caPubKey;
+    }
+
+    @Override
+    public byte[] getMessage() {
+        return message;
+    }
+
+    @Override
+    public byte[] getSignature() {
+        return signature;
+    }
+
+    @Override
+    public String getSignatureAlg() {
+        return new ByteArrayBuffer(signature).getString();
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return null;
+    }
+
+    @Override
+    public String getFormat() {
+        return null;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return new byte[0];
+    }
+
+    public void setKeyType(String keyType) {
+        this.keyType = keyType;
+    }
+
+    public void setNonce(byte[] nonce) {
+        this.nonce = nonce;
+    }
+
+    public void setServerHostKey(PublicKey serverHostKey) {
+        this.serverHostKey = serverHostKey;
+    }
+
+    public void setSerial(long serial) {
+        this.serial = serial;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public void setPrincipals(Collection<String> principals) {
+        this.principals = principals;
+    }
+
+    public void setValidAfter(long validAfter) {
+        this.validAfter = validAfter;
+    }
+
+    public void setValidBefore(long validBefore) {
+        this.validBefore = validBefore;
+    }
+
+    public void setCriticalOptions(List<String> criticalOptions) {
+        this.criticalOptions = criticalOptions;
+    }
+
+    public void setExtensions(List<String> extensions) {
+        this.extensions = extensions;
+    }
+
+    public void setReserved(String reserved) {
+        this.reserved = reserved;
+    }
+
+    public void setCaPubKey(PublicKey caPubKey) {
+        this.caPubKey = caPubKey;
+    }
+
+    public void setMessage(byte[] message) {
+        this.message = message;
+    }
+
+    public void setSignature(byte[] signature) {
+        this.signature = signature;
+    }
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java
new file mode 100644
index 0000000..1e26aa8
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/impl/OpenSSHCertificateDecoder.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.config.keys.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.buffer.keys.OpenSSHCertPublicKeyParser;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class OpenSSHCertificateDecoder extends 
AbstractPublicKeyEntryDecoder<OpenSshCertificate, OpenSshCertificate> {
+    public static final OpenSSHCertificateDecoder INSTANCE = new 
OpenSSHCertificateDecoder();
+
+    public OpenSSHCertificateDecoder() {
+        super(OpenSshCertificate.class, OpenSshCertificate.class,
+                Collections.unmodifiableList(Arrays.asList(
+                        KeyUtils.RSA_SHA256_CERT_TYPE_ALIAS,
+                        KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS,
+                        KeyPairProvider.SSH_RSA_CERT,
+                        KeyPairProvider.SSH_DSS_CERT,
+                        KeyPairProvider.SSH_ED25519_CERT,
+                        KeyPairProvider.SSH_ECDSA_SHA2_NISTP256_CERT,
+                        KeyPairProvider.SSH_ECDSA_SHA2_NISTP384_CERT,
+                        KeyPairProvider.SSH_ECDSA_SHA2_NISTP521_CERT
+                )));
+    }
+
+    @Override
+    public OpenSshCertificate decodePublicKey(
+            SessionContext session, String keyType, InputStream keyData, 
Map<String, String> headers)
+            throws IOException, GeneralSecurityException {
+
+        byte[] bytes = IoUtils.toByteArray(keyData);
+
+        ByteArrayBuffer buffer = new ByteArrayBuffer(bytes);
+
+        OpenSshCertificate cert = (OpenSshCertificate) 
OpenSSHCertPublicKeyParser.INSTANCE.getRawPublicKey(keyType, buffer);
+
+        if (cert.getType() != OpenSshCertificate.SSH_CERT_TYPE_HOST) {
+            throw new GeneralSecurityException("The provided certificate is 
not a Host certificate.");
+        }
+
+        return cert;
+    }
+
+    @Override
+    public String encodePublicKey(OutputStream s, OpenSshCertificate key) 
throws IOException {
+        Objects.requireNonNull(key, "No public key provided");
+
+        ByteArrayBuffer buffer = new ByteArrayBuffer();
+        buffer.putRawPublicKeyBytes(key);
+        s.write(buffer.getCompactData());
+
+        return key.getKeyType();
+    }
+
+    @Override
+    public OpenSshCertificate clonePublicKey(OpenSshCertificate key) throws 
GeneralSecurityException {
+        try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
+            String keyType = encodePublicKey(outStream, key);
+            try (InputStream inStream = new 
ByteArrayInputStream(outStream.toByteArray())) {
+                return decodePublicKey(null, keyType, inStream, null);
+            }
+        } catch (IOException e) {
+            throw new GeneralSecurityException("Unable to clone key.", e);
+        }
+    }
+
+    @Override
+    public OpenSshCertificate clonePrivateKey(OpenSshCertificate key) throws 
GeneralSecurityException {
+        return clonePublicKey(key);
+    }
+
+    @Override
+    public KeyPairGenerator getKeyPairGenerator() throws 
GeneralSecurityException {
+        return null;
+    }
+
+    @Override
+    public KeyFactory getKeyFactoryInstance() throws GeneralSecurityException {
+        return null;
+    }
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProvider.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProvider.java
new file mode 100644
index 0000000..0c9b8b1
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/FileHostKeyCertificateProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.keyprovider;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.StreamSupport;
+
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.apache.sshd.common.session.SessionContext;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.logging.AbstractLoggingBean;
+
+public class FileHostKeyCertificateProvider extends AbstractLoggingBean 
implements HostKeyCertificateProvider {
+    private Collection<? extends Path> files;
+
+    public FileHostKeyCertificateProvider() {
+        super();
+    }
+
+    public FileHostKeyCertificateProvider(Path path) {
+        this(Collections.singletonList(Objects.requireNonNull(path, "No path 
provided")));
+    }
+
+    public FileHostKeyCertificateProvider(Path... files) {
+        this(Arrays.asList(ValidateUtils.checkNotNullAndNotEmpty(files, "No 
path provided")));
+    }
+
+    public FileHostKeyCertificateProvider(Collection<? extends Path> files) {
+        this.files = files;
+    }
+
+    public Collection<? extends Path> getPaths() {
+        return files;
+    }
+
+    @Override
+    public Iterable<OpenSshCertificate> loadCertificates(SessionContext 
session) throws IOException, GeneralSecurityException {
+
+        List<OpenSshCertificate> certificates = new ArrayList<>();
+        for (Path file : files) {
+            List<String> lines = Files.readAllLines(file, 
StandardCharsets.UTF_8);
+            for (String line : lines) {
+                line = GenericUtils.replaceWhitespaceAndTrim(line);
+                if (line.isEmpty() || line.startsWith("#")) {
+                    continue;
+                }
+
+                PublicKeyEntry publicKeyEntry = 
PublicKeyEntry.parsePublicKeyEntry(line);
+                if (publicKeyEntry == null) {
+                    continue;
+                }
+                PublicKey publicKey = publicKeyEntry.resolvePublicKey(session, 
null, null);
+                if (publicKey == null) {
+                    continue;
+                }
+                if (!(publicKey instanceof OpenSshCertificate)) {
+                    throw new InvalidKeyException("Got unexpected key type in 
" + file + ". Expected OpenSSHCertificate.");
+                }
+                certificates.add((OpenSshCertificate) publicKey);
+            }
+        }
+
+        return certificates;
+    }
+
+    @Override
+    public OpenSshCertificate loadCertificate(SessionContext session, String 
keyType) throws IOException, GeneralSecurityException {
+        return StreamSupport.stream(loadCertificates(session).spliterator(), 
false)
+            .filter(pubKey -> Objects.equals(pubKey.getKeyType(), keyType))
+            .findFirst().orElse(null);
+    }
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/HostKeyCertificateProvider.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/HostKeyCertificateProvider.java
new file mode 100644
index 0000000..ab8eba0
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/HostKeyCertificateProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.keyprovider;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
+import org.apache.sshd.common.session.SessionContext;
+
+public interface HostKeyCertificateProvider {
+
+    Iterable<OpenSshCertificate> loadCertificates(SessionContext session) 
throws IOException, GeneralSecurityException;
+
+    OpenSshCertificate loadCertificate(SessionContext session, String keyType) 
throws IOException, GeneralSecurityException;
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
index 237b61f..2bc6114 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/KeyPairProvider.java
@@ -73,6 +73,16 @@ public interface KeyPairProvider extends KeyIdentityProvider 
{
     String ECDSA_SHA2_NISTP521 = ECCurves.nistp521.getKeyType();
 
     /**
+     * SSH identifier for openssh cert keys
+     */
+    String SSH_RSA_CERT = "ssh-rsa-cert-...@openssh.com";
+    String SSH_DSS_CERT = "ssh-dss-cert-...@openssh.com";
+    String SSH_ED25519_CERT = "ssh-ed25519-cert-...@openssh.com";
+    String SSH_ECDSA_SHA2_NISTP256_CERT = 
"ecdsa-sha2-nistp256-cert-...@openssh.com";
+    String SSH_ECDSA_SHA2_NISTP384_CERT = 
"ecdsa-sha2-nistp384-cert-...@openssh.com";
+    String SSH_ECDSA_SHA2_NISTP521_CERT = 
"ecdsa-sha2-nistp521-cert-...@openssh.com";
+
+    /**
      * A {@link KeyPairProvider} that has no keys
      */
     KeyPairProvider EMPTY_KEYPAIR_PROVIDER =
@@ -84,7 +94,7 @@ public interface KeyPairProvider extends KeyIdentityProvider {
 
             @Override
             public Iterable<String> getKeyTypes(SessionContext session) {
-                return Collections.emptyList();
+                return Collections.emptySet();
             }
 
             @Override
@@ -122,7 +132,7 @@ public interface KeyPairProvider extends 
KeyIdentityProvider {
     /**
      * @param session The {@link SessionContext} for invoking this load 
command - may
      * be {@code null} if not invoked within a session context (e.g., offline 
tool).
-     * @return The available {@link Iterable} key types in preferred order - 
never {@code null}
+     * @return The available {@link Iterable} key types - never {@code null}
      * @throws IOException If failed to read/parse the keys data
      * @throws GeneralSecurityException If failed to generate the keys
      */
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
index bedf5de..695e26a 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/BuiltinSignatures.java
@@ -57,18 +57,36 @@ public enum BuiltinSignatures implements SignatureFactory {
             return new SignatureDSA();
         }
     },
+    dsa_cert(KeyPairProvider.SSH_DSS_CERT) {
+        @Override
+        public Signature create() {
+            return new SignatureDSA();
+        }
+    },
     rsa(KeyPairProvider.SSH_RSA) {
         @Override
         public Signature create() {
             return new SignatureRSASHA1();
         }
     },
+    rsa_cert(KeyPairProvider.SSH_RSA_CERT) {
+        @Override
+        public Signature create() {
+            return new SignatureRSASHA1();
+        }
+    },
     rsaSHA256(KeyUtils.RSA_SHA256_KEY_TYPE_ALIAS) {
         @Override
         public Signature create() {
             return new SignatureRSASHA256();
         }
     },
+    rsaSHA256_cert(KeyUtils.RSA_SHA256_CERT_TYPE_ALIAS) {
+        @Override
+        public Signature create() {
+            return new SignatureRSASHA256();
+        }
+    },
     rsaSHA512(KeyUtils.RSA_SHA512_KEY_TYPE_ALIAS) {
         private final AtomicReference<Boolean> supportHolder = new 
AtomicReference<>();
 
@@ -95,6 +113,32 @@ public enum BuiltinSignatures implements SignatureFactory {
             return supported;
         }
     },
+    rsaSHA512_cert(KeyUtils.RSA_SHA512_CERT_TYPE_ALIAS) {
+        private final AtomicReference<Boolean> supportHolder = new 
AtomicReference<>();
+
+        @Override
+        public Signature create() {
+            return new SignatureRSASHA512();
+        }
+
+        @Override
+        public boolean isSupported() {
+            Boolean supported = supportHolder.get();
+            if (supported == null) {
+                try {
+                    java.security.Signature sig =
+                        
SecurityUtils.getSignature(SignatureRSASHA512.ALGORITHM);
+                    supported = sig != null;
+                } catch (Exception e) {
+                    supported = Boolean.FALSE;
+                }
+
+                supportHolder.set(supported);
+            }
+
+            return supported;
+        }
+    },
     nistp256(KeyPairProvider.ECDSA_SHA2_NISTP256) {
         @Override
         public Signature create() {
@@ -106,6 +150,17 @@ public enum BuiltinSignatures implements SignatureFactory {
             return SecurityUtils.isECCSupported();
         }
     },
+    nistp256_cert(KeyPairProvider.SSH_ECDSA_SHA2_NISTP256_CERT) {
+        @Override
+        public Signature create() {
+            return new SignatureECDSA.SignatureECDSA256();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
     nistp384(KeyPairProvider.ECDSA_SHA2_NISTP384) {
         @Override
         public Signature create() {
@@ -117,6 +172,17 @@ public enum BuiltinSignatures implements SignatureFactory {
             return SecurityUtils.isECCSupported();
         }
     },
+    nistp384_cert(KeyPairProvider.SSH_ECDSA_SHA2_NISTP384_CERT) {
+        @Override
+        public Signature create() {
+            return new SignatureECDSA.SignatureECDSA384();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
     nistp521(KeyPairProvider.ECDSA_SHA2_NISTP521) {
         @Override
         public Signature create() {
@@ -128,6 +194,17 @@ public enum BuiltinSignatures implements SignatureFactory {
             return SecurityUtils.isECCSupported();
         }
     },
+    nistp521_cert(KeyPairProvider.SSH_ECDSA_SHA2_NISTP521_CERT) {
+        @Override
+        public Signature create() {
+            return new SignatureECDSA.SignatureECDSA521();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isECCSupported();
+        }
+    },
     sk_ecdsa_sha2_nistp256(SkECDSAPublicKeyEntryDecoder.KEY_TYPE) {
         @Override
         public Signature create() {
@@ -150,6 +227,17 @@ public enum BuiltinSignatures implements SignatureFactory {
             return SecurityUtils.isEDDSACurveSupported();
         }
     },
+    ed25519_cert(KeyPairProvider.SSH_ED25519_CERT) {
+        @Override
+        public Signature create() {
+            return SecurityUtils.getEDDSASigner();
+        }
+
+        @Override
+        public boolean isSupported() {
+            return SecurityUtils.isEDDSACurveSupported();
+        }
+    },
     sk_ssh_ed25519(SkED25519PublicKeyEntryDecoder.KEY_TYPE) {
         @Override
         public Signature create() {
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
index 25296d0..363859c 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java
@@ -90,7 +90,6 @@ public interface SignatureFactory extends 
BuiltinFactory<Signature> {
             return Collections.emptyList();
         }
 
-        // We want to preserve the original available order as it indicates 
the preference
         Set<String> providedKeys = new HashSet<>();
         for (String providedType : provided) {
             Collection<String> equivTypes =
@@ -102,6 +101,7 @@ public interface SignatureFactory extends 
BuiltinFactory<Signature> {
             return Collections.emptyList();
         }
 
+        // We want to preserve the original available order as it indicates 
the preference
         List<String> supported = new ArrayList<>(available);
         for (int index = 0; index < supported.size(); index++) {
             String kt = supported.get(index);
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
index b0fb2fe..ff84404 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureRSA.java
@@ -78,7 +78,9 @@ public abstract class SignatureRSA extends AbstractSignature {
              *      corresponds to a good-faith implementation and is 
considered safe to accept.
              */
             String canonicalName = KeyUtils.getCanonicalKeyType(keyType);
-            
ValidateUtils.checkTrue(KeyPairProvider.SSH_RSA.equals(canonicalName), 
"Mismatched key type: %s", keyType);
+            if (!KeyPairProvider.SSH_RSA.equals(canonicalName) && 
!KeyPairProvider.SSH_RSA_CERT.equals(canonicalName)) {
+                throw new IllegalArgumentException("Mismatched key type: " + 
keyType);
+            }
             data = encoding.getValue();
         }
 
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
index 2482fb0..db65ac9 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -60,6 +60,7 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.cipher.ECCurves;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.NumberUtils;
@@ -114,6 +115,11 @@ public abstract class Buffer implements Readable {
     public abstract byte[] array();
 
     /**
+     * @return The bytes consumed so far
+     */
+    public abstract byte[] getBytesConsumed();
+
+    /**
      * @param pos A position in the <U>raw</U> underlying data bytes
      * @return The byte at the specified position without changing the
      * current {@link #rpos() read position}. <B>Note:</B> no validation
@@ -893,18 +899,21 @@ public abstract class Buffer implements Readable {
     }
 
     public void putRawPublicKey(PublicKey key) {
+        putString(KeyUtils.getKeyType(key));
+        putRawPublicKeyBytes(key);
+    }
+
+    public void putRawPublicKeyBytes(PublicKey key) {
         Objects.requireNonNull(key, "No key");
         if (key instanceof RSAPublicKey) {
             RSAPublicKey rsaPub = (RSAPublicKey) key;
 
-            putString(KeyPairProvider.SSH_RSA);
             putMPInt(rsaPub.getPublicExponent());
             putMPInt(rsaPub.getModulus());
         } else if (key instanceof DSAPublicKey) {
             DSAPublicKey dsaPub = (DSAPublicKey) key;
             DSAParams dsaParams = dsaPub.getParams();
 
-            putString(KeyPairProvider.SSH_DSS);
             putMPInt(dsaParams.getP());
             putMPInt(dsaParams.getQ());
             putMPInt(dsaParams.getG());
@@ -918,11 +927,30 @@ public abstract class Buffer implements Readable {
             }
 
             byte[] ecPoint = ECCurves.encodeECPoint(ecKey.getW(), ecParams);
-            putString(curve.getKeyType());
             putString(curve.getName());
             putBytes(ecPoint);
         } else if (SecurityUtils.EDDSA.equals(key.getAlgorithm())) {
             SecurityUtils.putRawEDDSAPublicKey(this, key);
+        } else if (key instanceof OpenSshCertificate) {
+            OpenSshCertificate cert = (OpenSshCertificate) key;
+
+            putBytes(cert.getNonce());
+            putRawPublicKeyBytes(cert.getServerHostKey());
+            putLong(cert.getSerial());
+            putInt(cert.getType());
+            putString(cert.getId());
+            ByteArrayBuffer tmpBuffer = new ByteArrayBuffer();
+            tmpBuffer.putStringList(cert.getPrincipals(), false);
+            putBytes(tmpBuffer.getCompactData());
+            putLong(cert.getValidAfter());
+            putLong(cert.getValidBefore());
+            putNameList(cert.getCriticalOptions());
+            putNameList(cert.getExtensions());
+            putString(cert.getReserved());
+            tmpBuffer = new ByteArrayBuffer();
+            tmpBuffer.putRawPublicKey(cert.getCaPubKey());
+            putBytes(tmpBuffer.getCompactData());
+            putBytes(cert.getSignature());
         } else {
             throw new BufferException("Unsupported raw public key algorithm: " 
+ key.getAlgorithm());
         }
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
index 65fcb0b..2a98434 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
@@ -158,6 +158,13 @@ public class ByteArrayBuffer extends Buffer {
     }
 
     @Override
+    public byte[] getBytesConsumed() {
+        byte[] consumed = new byte[rpos];
+        System.arraycopy(data, 0, consumed, 0, rpos);
+        return consumed;
+    }
+
+    @Override
     public byte rawByte(int pos) {
         return data[pos];
     }
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
index 6b9bae2..2b788e5 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/BufferPublicKeyParser.java
@@ -60,6 +60,7 @@ public interface BufferPublicKeyParser<PUB extends PublicKey> 
{
                     ECBufferPublicKeyParser.INSTANCE,
                     SkECBufferPublicKeyParser.INSTANCE,
                     ED25519BufferPublicKeyParser.INSTANCE,
+                    OpenSSHCertPublicKeyParser.INSTANCE,
                     SkED25519BufferPublicKeyParser.INSTANCE));
 
     /**
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/OpenSSHCertPublicKeyParser.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/OpenSSHCertPublicKeyParser.java
new file mode 100644
index 0000000..1c9823a
--- /dev/null
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/keys/OpenSSHCertPublicKeyParser.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.util.buffer.keys;
+
+import java.security.GeneralSecurityException;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.config.keys.OpenSshCertificateImpl;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+
+public class OpenSSHCertPublicKeyParser extends 
AbstractBufferPublicKeyParser<PublicKey> {
+
+    public static final OpenSSHCertPublicKeyParser INSTANCE = new 
OpenSSHCertPublicKeyParser(PublicKey.class,
+        Arrays.asList(
+            KeyPairProvider.SSH_RSA_CERT,
+            KeyPairProvider.SSH_DSS_CERT,
+            KeyPairProvider.SSH_ECDSA_SHA2_NISTP256_CERT,
+            KeyPairProvider.SSH_ECDSA_SHA2_NISTP384_CERT,
+            KeyPairProvider.SSH_ECDSA_SHA2_NISTP521_CERT,
+            KeyPairProvider.SSH_ED25519_CERT
+        ));
+
+    public OpenSSHCertPublicKeyParser(Class<PublicKey> keyClass, 
Collection<String> supported) {
+        super(keyClass, supported);
+    }
+
+    @Override
+    public PublicKey getRawPublicKey(String keyType, Buffer buffer) throws 
GeneralSecurityException {
+
+        OpenSshCertificateImpl certificate = new OpenSshCertificateImpl();
+        certificate.setKeyType(keyType);
+
+        certificate.setNonce(buffer.getBytes());
+
+        String rawKeyType = certificate.getRawKeyType();
+        certificate.setServerHostKey(DEFAULT.getRawPublicKey(rawKeyType, 
buffer));
+
+        certificate.setSerial(buffer.getLong());
+        certificate.setType(buffer.getInt());
+
+        certificate.setId(buffer.getString());
+
+        certificate.setPrincipals(new 
ByteArrayBuffer(buffer.getBytes()).getStringList(false));
+        certificate.setValidAfter(buffer.getLong());
+        certificate.setValidBefore(buffer.getLong());
+
+        certificate.setCriticalOptions(buffer.getNameList());
+        certificate.setExtensions(buffer.getNameList());
+
+        certificate.setReserved(buffer.getString());
+
+        try {
+            certificate.setCaPubKey(buffer.getPublicKey());
+        } catch (SshException ex) {
+            throw new GeneralSecurityException("Could not parse public CA key 
with ID: " + certificate.getId(), ex);
+        }
+
+        certificate.setMessage(buffer.getBytesConsumed());
+        certificate.setSignature(buffer.getBytes());
+
+        if (buffer.rpos() != buffer.wpos()) {
+            throw new GeneralSecurityException("KeyExchange signature 
verification failed, got more data than expected: "
+                + buffer.rpos() + ", actual: " + buffer.wpos() + ". ID of the 
ca certificate: " + certificate.getId());
+        }
+
+        return certificate;
+    }
+}
diff --git 
a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
 
b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
index 242f550..e002d4b 100644
--- 
a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
+++ 
b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java
@@ -30,7 +30,6 @@ import java.util.Objects;
 
 import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder;
 import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
-import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.security.SecurityUtils;
@@ -187,7 +186,6 @@ public final class EdDSASecurityProviderUtils {
         EdDSAPublicKey edKey = ValidateUtils.checkInstanceOf(key, 
EdDSAPublicKey.class, "Not an EDDSA public key: %s", key);
         byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(edKey);
         ValidateUtils.checkNotNull(seed, "No seed extracted from key: %s", 
edKey.getA());
-        buffer.putString(KeyPairProvider.SSH_ED25519);
         buffer.putBytes(seed);
         return buffer;
     }
diff --git 
a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
 
b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
index 3ad00be..51ffd0b 100644
--- 
a/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
+++ 
b/sshd-common/src/test/java/org/apache/sshd/common/util/security/eddsa/EDDSAProviderTest.java
@@ -30,7 +30,6 @@ import java.security.Signature;
 import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
-import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
 import org.apache.sshd.common.util.security.SecurityUtils;
 import org.apache.sshd.util.test.JUnitTestSupport;
@@ -118,7 +117,8 @@ public class EDDSAProviderTest extends JUnitTestSupport {
         assertNotNull("No public key generated", pubKey);
         assertEquals("Mismatched public key algorithm", SecurityUtils.EDDSA, 
pubKey.getAlgorithm());
 
-        Buffer buf = SecurityUtils.putRawEDDSAPublicKey(new ByteArrayBuffer(), 
pubKey);
+        ByteArrayBuffer buf = new ByteArrayBuffer();
+        buf.putRawPublicKey(pubKey);
         PublicKey actual = buf.getRawPublicKey();
         assertEquals("Mismatched key algorithm", pubKey.getAlgorithm(), 
actual.getAlgorithm());
         assertEquals("Mismatched recovered key", pubKey, actual);
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java 
b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
index 46d7ba9..36d1e4c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java
@@ -76,6 +76,12 @@ public class ClientBuilder extends BaseBuilder<SshClient, 
ClientBuilder> {
          */
         Collections.unmodifiableList(
             Arrays.asList(
+                BuiltinSignatures.nistp256_cert,
+                BuiltinSignatures.nistp384_cert,
+                BuiltinSignatures.nistp521_cert,
+                BuiltinSignatures.ed25519_cert,
+                BuiltinSignatures.rsa_cert,
+                BuiltinSignatures.dsa_cert,
                 BuiltinSignatures.nistp256,
                 BuiltinSignatures.nistp384,
                 BuiltinSignatures.nistp521,
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java 
b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
index 5c8694b..017cf7a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientFactoryManager.java
@@ -114,6 +114,12 @@ public interface ClientFactoryManager
     boolean DEFAULT_IGNORE_INVALID_IDENTITIES = true;
 
     /**
+     * Defines if we should abort in case we encounter an invalid (e.g. 
expired) openssh certificate.
+     * The default is to ignore the certificate and proceed with the plain 
host key.
+     */
+    String ABORT_ON_INVALID_CERTIFICATE = "abort-on-invalid-certificate";
+
+    /**
      * @return The {@link HostConfigEntryResolver} to use in order to resolve 
the
      * effective session parameters - never {@code null}
      */
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java 
b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
index e2694ee..4f1a4f0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
@@ -18,22 +18,31 @@
  */
 package org.apache.sshd.client.kex;
 
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.security.PublicKey;
+import java.util.Collection;
 import java.util.Objects;
 
+import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
 import org.apache.sshd.common.kex.AbstractDH;
 import org.apache.sshd.common.kex.DHFactory;
+import org.apache.sshd.common.kex.KexProposalOption;
 import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.kex.KeyExchangeFactory;
+import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
 
 /**
  * Base class for DHG key exchange algorithms.
@@ -124,10 +133,29 @@ public class DHGClient extends 
AbstractDHClientKeyExchange {
 
         buffer = new ByteArrayBuffer(k_s);
         serverKey = buffer.getRawPublicKey();
-        String keyAlg = KeyUtils.getKeyType(serverKey);
+        PublicKey serverPublicHostKey = serverKey;
+
+        if (serverKey instanceof OpenSshCertificate) {
+            OpenSshCertificate openSshKey = (OpenSshCertificate) serverKey;
+            serverPublicHostKey = openSshKey.getServerHostKey();
+
+            try {
+                verifyCertificate(session, openSshKey);
+            } catch (SshException e) {
+                if 
(session.getBooleanProperty(ClientFactoryManager.ABORT_ON_INVALID_CERTIFICATE, 
false)) {
+                    throw e;
+                } else {
+                    // ignore certificate
+                    serverKey = openSshKey.getServerHostKey();
+                    log.info("Ignoring invalid certificate {}", 
openSshKey.getId(), e);
+                }
+            }
+        }
+
+        String keyAlg = 
session.getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
         if (GenericUtils.isEmpty(keyAlg)) {
-            throw new SshException("Unsupported server key type: " + 
serverKey.getAlgorithm()
-                + "[" + serverKey.getFormat() + "]");
+            throw new SshException("Unsupported server key type: " + 
serverPublicHostKey.getAlgorithm()
+                + "[" + serverPublicHostKey.getFormat() + "]");
         }
 
         buffer = new ByteArrayBuffer();
@@ -145,7 +173,7 @@ public class DHGClient extends AbstractDHClientKeyExchange {
         Signature verif = ValidateUtils.checkNotNull(
             NamedFactory.create(session.getSignatureFactories(), keyAlg),
             "No verifier located for algorithm=%s", keyAlg);
-        verif.initVerifier(session, serverKey);
+        verif.initVerifier(session, serverPublicHostKey);
         verif.update(session, h);
         if (!verif.verify(session, sig)) {
             throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
@@ -154,4 +182,73 @@ public class DHGClient extends AbstractDHClientKeyExchange 
{
 
         return true;
     }
+
+    protected void verifyCertificate(Session session, OpenSshCertificate 
openSshKey) throws Exception {
+        PublicKey signatureKey = openSshKey.getCaPubKey();
+        String keyAlg = KeyUtils.getKeyType(signatureKey);
+
+        if (KeyPairProvider.SSH_RSA_CERT.equals(openSshKey.getKeyType())) {
+            // allow sha2 signatures for legacy reasons
+            String variant = openSshKey.getSignatureAlg();
+            if (!GenericUtils.isEmpty(variant) && 
KeyPairProvider.SSH_RSA.equals(KeyUtils.getCanonicalKeyType(variant))) {
+                log.debug("Allowing to use variant {} instead of {}", variant, 
keyAlg);
+                keyAlg = variant;
+            } else {
+                throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                    "Found invalid signature alg " + variant);
+            }
+        }
+
+        Signature verif = ValidateUtils.checkNotNull(
+                NamedFactory.create(session.getSignatureFactories(), keyAlg),
+                "No verifier located for algorithm=%s", keyAlg);
+        verif.initVerifier(session, signatureKey);
+        verif.update(session, openSshKey.getMessage());
+        if (!verif.verify(session, openSshKey.getSignature())) {
+            throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                    "KeyExchange CA signature verification failed for key 
type=" + keyAlg);
+        }
+
+        if (openSshKey.getType() != OpenSshCertificate.SSH_CERT_TYPE_HOST) {
+            throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                    "KeyExchange signature verification failed, not a host key 
(2): "
+                            + openSshKey.getType());
+        }
+
+        long now = System.currentTimeMillis() / 1000;
+        // valid after <= current time < valid before
+        if (!(openSshKey.getValidAfter() <= now && now < 
openSshKey.getValidBefore())) {
+            throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                    "KeyExchange signature verification failed, CA expired: "
+                            + openSshKey.getValidAfter() + "-" + 
openSshKey.getValidBefore());
+        }
+
+        /*
+         * We compare only the connect address against the principals and do 
not do any reverse DNS lookups.
+         * If one wants to connect with the IP it has to be included in the 
principals list of the certificate.
+         */
+        SocketAddress connectSocketAddress = 
getClientSession().getConnectAddress();
+        if (connectSocketAddress instanceof SshdSocketAddress) {
+            connectSocketAddress = ((SshdSocketAddress) 
connectSocketAddress).toInetSocketAddress();
+        }
+        if (connectSocketAddress instanceof InetSocketAddress) {
+            String hostName = ((InetSocketAddress) 
connectSocketAddress).getHostString();
+            Collection<String> principals = openSshKey.getPrincipals();
+            if (GenericUtils.isEmpty(principals) || 
!principals.contains(hostName)) {
+                throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                        "KeyExchange signature verification failed, invalid 
principal: "
+                                + principals);
+            }
+        } else {
+            throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                    "KeyExchange signature verification failed, could not 
determine connect host.");
+        }
+
+        if (!GenericUtils.isEmpty(openSshKey.getCriticalOptions())) {
+            // no critical option defined for host keys yet
+            throw new 
SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
+                    "KeyExchange signature verification failed, unrecognized 
critical option: "
+                            + openSshKey.getCriticalOptions());
+        }
+    }
 }
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
index 558266c..b36dfb6 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/session/AbstractClientSession.java
@@ -51,6 +51,7 @@ import 
org.apache.sshd.common.channel.PtyChannelConfigurationHolder;
 import org.apache.sshd.common.cipher.BuiltinCiphers;
 import org.apache.sshd.common.cipher.CipherNone;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
 import org.apache.sshd.common.forward.ForwardingFilter;
 import org.apache.sshd.common.future.DefaultKeyExchangeFuture;
 import org.apache.sshd.common.future.KeyExchangeFuture;
@@ -546,10 +547,28 @@ public abstract class AbstractClientSession extends 
AbstractSession implements C
         IoSession networkSession = getIoSession();
         SocketAddress remoteAddress = networkSession.getRemoteAddress();
         PublicKey serverKey = kex.getServerKey();
-        boolean verified = serverKeyVerifier.verifyServerKey(this, 
remoteAddress, serverKey);
-        if (log.isDebugEnabled()) {
-            log.debug("checkKeys({}) key={}-{}, verified={}",
-                this, KeyUtils.getKeyType(serverKey), 
KeyUtils.getFingerPrint(serverKey), verified);
+
+        boolean verified = false;
+        if (serverKey instanceof OpenSshCertificate) {
+            // check if we trust the CA
+            verified = serverKeyVerifier.verifyServerKey(this, remoteAddress, 
((OpenSshCertificate) serverKey).getCaPubKey());
+            if (log.isDebugEnabled()) {
+                log.debug("checkCA({}) key={}-{}, verified={}",
+                    this, KeyUtils.getKeyType(serverKey), 
KeyUtils.getFingerPrint(serverKey), verified);
+            }
+
+            if (!verified) {
+                // fallback to actual public host key
+                serverKey = ((OpenSshCertificate) 
serverKey).getServerHostKey();
+            }
+        }
+
+        if (!verified) {
+            verified = serverKeyVerifier.verifyServerKey(this, remoteAddress, 
serverKey);
+            if (log.isDebugEnabled()) {
+                log.debug("checkKeys({}) key={}-{}, verified={}",
+                    this, KeyUtils.getKeyType(serverKey), 
KeyUtils.getFingerPrint(serverKey), verified);
+            }
         }
 
         if (!verified) {
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java 
b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
index 0f34f80..cbcebc3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java
@@ -98,6 +98,14 @@ public class ServerBuilder extends BaseBuilder<SshServer, 
ServerBuilder> {
     public static final List<BuiltinSignatures> DEFAULT_SIGNATURE_PREFERENCE =
         Collections.unmodifiableList(
             Arrays.asList(
+                BuiltinSignatures.nistp256_cert,
+                BuiltinSignatures.nistp384_cert,
+                BuiltinSignatures.nistp521_cert,
+                BuiltinSignatures.ed25519_cert,
+                BuiltinSignatures.rsaSHA512_cert,
+                BuiltinSignatures.rsaSHA256_cert,
+                BuiltinSignatures.rsa_cert,
+                BuiltinSignatures.dsa_cert,
                 BuiltinSignatures.nistp256,
                 BuiltinSignatures.nistp384,
                 BuiltinSignatures.nistp521,
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java 
b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
index 10e23b0..83f7770 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.common.keyprovider.HostKeyCertificateProvider;
 import org.apache.sshd.server.command.CommandFactory;
 import org.apache.sshd.server.session.ServerProxyAcceptorHolder;
 import org.apache.sshd.server.shell.ShellFactory;
@@ -110,4 +111,9 @@ public interface ServerFactoryManager
      * or {@code null}/empty if subsystems are not supported on this server
      */
     List<SubsystemFactory> getSubsystemFactories();
+
+    /**
+     * @return a {@link HostKeyCertificateProvider} if available, null as 
default
+     */
+    HostKeyCertificateProvider getHostKeyCertificateProvider();
 }
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java 
b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index fcc676f..be5d946 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -39,6 +39,7 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager;
 import org.apache.sshd.common.io.IoAcceptor;
 import org.apache.sshd.common.io.IoServiceFactory;
 import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.keyprovider.HostKeyCertificateProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.helpers.AbstractSession;
 import org.apache.sshd.common.util.GenericUtils;
@@ -105,6 +106,7 @@ public class SshServer extends AbstractFactoryManager 
implements ServerFactoryMa
     private List<SubsystemFactory> subsystemFactories;
     private List<UserAuthFactory> userAuthFactories;
     private KeyPairProvider keyPairProvider;
+    private HostKeyCertificateProvider hostKeyCertificateProvider;
     private PasswordAuthenticator passwordAuthenticator;
     private PublickeyAuthenticator publickeyAuthenticator;
     private KeyboardInteractiveAuthenticator interactiveAuthenticator;
@@ -261,6 +263,15 @@ public class SshServer extends AbstractFactoryManager 
implements ServerFactoryMa
     }
 
     @Override
+    public HostKeyCertificateProvider getHostKeyCertificateProvider() {
+        return hostKeyCertificateProvider;
+    }
+
+    public void setHostKeyCertificateProvider(HostKeyCertificateProvider 
hostKeyCertificateProvider) {
+        this.hostKeyCertificateProvider = hostKeyCertificateProvider;
+    }
+
+    @Override
     protected void checkConfig() {
         super.checkConfig();
 
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
index 7cc5010..5e3ffc5 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/config/keys/ServerIdentity.java
@@ -55,6 +55,7 @@ public final class ServerIdentity {
      * The server's keys configuration multi-value
      */
     public static final String HOST_KEY_CONFIG_PROP = "HostKey";
+    public static final String HOST_CERT_CONFIG_PROP = "HostCertificate";
 
     public static final Function<String, String> ID_GENERATOR =
             ServerIdentity::getIdentityFileName;
@@ -137,11 +138,31 @@ public final class ServerIdentity {
      * @see 
org.apache.sshd.common.config.ConfigFileReaderSupport#readConfigFile(Path, 
java.nio.file.OpenOption...)
      */
     public static Map<String, Path> findIdentities(Properties props, 
LinkOption... options) throws IOException {
+        return getLocations(HOST_KEY_CONFIG_PROP, props, options);
+    }
+
+    /**
+     * @param props   The {@link Properties} holding the server's 
configuration - ignored
+     *                if {@code null}/empty
+     * @param options The {@link LinkOption}s to use when checking files 
existence
+     * @return A {@link Map} of the found certificates where key=the identity 
type
+     * (case <U>insensitive</U>) and value=the {@link Path} of the file holding
+     * the specific type key
+     * @throws IOException If failed to access the file system
+     * @see #getIdentityType(String)
+     * @see #HOST_CERT_CONFIG_PROP
+     * @see 
org.apache.sshd.common.config.ConfigFileReaderSupport#readConfigFile(Path, 
java.nio.file.OpenOption...)
+     */
+    public static Map<String, Path> findCertificates(Properties props, 
LinkOption... options) throws IOException {
+        return getLocations(HOST_CERT_CONFIG_PROP, props, options);
+    }
+
+    private static Map<String, Path> getLocations(String configPropKey, 
Properties props, LinkOption... options) throws IOException {
         if (GenericUtils.isEmpty(props)) {
             return Collections.emptyMap();
         }
 
-        String keyList = props.getProperty(HOST_KEY_CONFIG_PROP);
+        String keyList = props.getProperty(configPropKey);
         String[] paths = GenericUtils.split(keyList, ',');
         if (GenericUtils.isEmpty(paths)) {
             return Collections.emptyMap();
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java 
b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
index c8ee45e..7e96045 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
@@ -104,6 +104,7 @@ public class DHGServer extends AbstractDHServerKeyExchange {
 
         KeyPair kp = Objects.requireNonNull(session.getHostKey(), "No server 
key pair available");
         String algo = 
session.getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
+
         Signature sig = ValidateUtils.checkNotNull(
             NamedFactory.create(session.getSignatureFactories(), algo),
             "Unknown negotiated server keys: %s", algo);
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 7232935..5f4ddcf 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -28,6 +28,8 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.NamedResource;
@@ -37,6 +39,7 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.auth.AbstractUserAuthServiceFactory;
 import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.OpenSshCertificate;
 import org.apache.sshd.common.io.IoService;
 import org.apache.sshd.common.io.IoSession;
 import org.apache.sshd.common.io.IoWriteFuture;
@@ -46,6 +49,7 @@ import org.apache.sshd.common.kex.KexState;
 import org.apache.sshd.common.kex.extension.KexExtensionHandler;
 import 
org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase;
 import org.apache.sshd.common.kex.extension.KexExtensionHandler.KexPhase;
+import org.apache.sshd.common.keyprovider.HostKeyCertificateProvider;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.SessionContext;
@@ -81,6 +85,7 @@ public abstract class AbstractServerSession extends 
AbstractSession implements S
     private HostBasedAuthenticator hostBasedAuthenticator;
     private List<UserAuthFactory> userAuthFactories;
     private KeyPairProvider keyPairProvider;
+    private HostKeyCertificateProvider hostKeyCertificateProvider;
 
     protected AbstractServerSession(ServerFactoryManager factoryManager, 
IoSession ioSession) {
         super(true, factoryManager, ioSession);
@@ -189,6 +194,15 @@ public abstract class AbstractServerSession extends 
AbstractSession implements S
             (parent == null) ? null : ((ServerAuthenticationManager) 
parent).getKeyPairProvider());
     }
 
+    public HostKeyCertificateProvider getHostKeyCertificateProvider() {
+        ServerFactoryManager manager = getFactoryManager();
+        return resolveEffectiveProvider(HostKeyCertificateProvider.class, 
hostKeyCertificateProvider, manager.getHostKeyCertificateProvider());
+    }
+
+    public void setHostKeyCertificateProvider(HostKeyCertificateProvider 
hostKeyCertificateProvider) {
+        this.hostKeyCertificateProvider = hostKeyCertificateProvider;
+    }
+
     @Override
     public void setKeyPairProvider(KeyPairProvider keyPairProvider) {
         this.keyPairProvider = keyPairProvider;
@@ -367,9 +381,25 @@ public abstract class AbstractServerSession extends 
AbstractSession implements S
 
         KeyPairProvider kpp = getKeyPairProvider();
         boolean debugEnabled = log.isDebugEnabled();
-        Iterable<String> provided;
+        Set<String> provided = null;
         try {
-            provided = (kpp == null) ? null : kpp.getKeyTypes(this);
+            if (kpp != null) {
+                provided = 
GenericUtils.stream(kpp.getKeyTypes(this)).collect(Collectors.toSet());
+
+                HostKeyCertificateProvider hostKeyCertificateProvider = 
getHostKeyCertificateProvider();
+                if (hostKeyCertificateProvider != null) {
+                    Iterable<OpenSshCertificate> certificates = 
hostKeyCertificateProvider.loadCertificates(this);
+                    for (OpenSshCertificate certificate : certificates) {
+                        // Add the certificate alg only if the corresponding 
keyPair type is available
+                        if (provided.contains(certificate.getRawKeyType())) {
+                            provided.add(certificate.getKeyType());
+                        } else {
+                            log.info("No private key for provided certificate 
available. Missing private key type: {}",
+                                certificate.getRawKeyType());
+                        }
+                    }
+                }
+            }
         } catch (Error e) {
             log.warn("resolveAvailableSignaturesProposal({}) failed ({}) to 
get key types: {}",
                  this, e.getClass().getSimpleName(), e.getMessage());
@@ -498,7 +528,16 @@ public abstract class AbstractServerSession extends 
AbstractSession implements S
         KeyPairProvider provider =
             Objects.requireNonNull(getKeyPairProvider(), "No host keys 
provider");
         try {
+            HostKeyCertificateProvider hostKeyCertificateProvider = 
getHostKeyCertificateProvider();
+            if (hostKeyCertificateProvider != null) {
+                OpenSshCertificate publicKey = 
hostKeyCertificateProvider.loadCertificate(this, keyType);
+                if (publicKey != null) {
+                    KeyPair keyPair = provider.loadKey(this, 
publicKey.getRawKeyType());
+                    return new KeyPair(publicKey, keyPair.getPrivate());
+                }
+            }
             return provider.loadKey(this, keyType);
+
         } catch (IOException | GeneralSecurityException | Error e) {
             log.warn("getHostKey({}) failed ({}) to load key of type={}[{}]: 
{}",
                  this, e.getClass().getSimpleName(), proposedKey, keyType, 
e.getMessage());
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java
new file mode 100644
index 0000000..50bd39d
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/signature/OpenSSHCertificateTest.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.sshd.common.signature;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.client.ClientFactoryManager;
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.keyprovider.FileHostKeyCertificateProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.CoreTestSupportUtils;
+import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class)   // see 
https://github.com/junit-team/junit/wiki/Parameterized-tests
+@Parameterized.UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class)
+public class OpenSSHCertificateTest extends BaseTestSupport {
+    private static SshServer sshd;
+    private static SshClient client;
+    private static int port;
+
+    private final FileHostKeyCertificateProvider certificateProvider;
+    private final FileKeyPairProvider keyPairProvider;
+    private final List<NamedFactory<Signature>> signatureFactory;
+
+    public OpenSSHCertificateTest(String keyPath, String certPath, 
List<NamedFactory<Signature>> signatureFactory) {
+        this.keyPairProvider = new 
FileKeyPairProvider(getTestResourcesFolder().resolve(keyPath));
+        this.certificateProvider = new 
FileHostKeyCertificateProvider(getTestResourcesFolder().resolve(certPath));
+        this.signatureFactory = signatureFactory;
+    }
+
+    @BeforeClass
+    public static void setupClientAndServer() throws Exception {
+        sshd = 
CoreTestSupportUtils.setupTestServer(OpenSSHCertificateTest.class);
+        sshd.start();
+        port = sshd.getPort();
+
+        client = 
CoreTestSupportUtils.setupTestClient(OpenSSHCertificateTest.class);
+        client.start();
+    }
+
+    @AfterClass
+    public static void tearDownClientAndServer() throws Exception {
+        if (sshd != null) {
+            try {
+                sshd.stop(true);
+            } finally {
+                sshd = null;
+            }
+        }
+
+        if (client != null) {
+            try {
+                client.stop();
+            } finally {
+                client = null;
+            }
+        }
+    }
+
+    @Parameterized.Parameters(name = "type={2}")
+    public static List<Object[]> parameters() {
+        List<Object[]> list = new ArrayList<>();
+
+        String key = "ssh_host_rsa_key";
+        String certificate = "ssh_host_rsa_key_sha1-cert.pub";
+        String certificateSha512 = "ssh_host_rsa_key-cert.pub";
+
+        // default client
+        list.add(new Object[]{key, certificate, null});
+        list.add(new Object[]{key, certificate, 
Arrays.asList(BuiltinSignatures.rsa_cert, BuiltinSignatures.rsa)});
+        // client does not support cert
+        list.add(new Object[]{key, certificate, 
Collections.singletonList(BuiltinSignatures.rsa)});
+        // rsa variant
+        list.add(new Object[]{key, certificateSha512, 
Arrays.asList(BuiltinSignatures.rsaSHA512_cert, BuiltinSignatures.rsaSHA512)});
+        list.add(new Object[]{key, certificateSha512, 
Arrays.asList(BuiltinSignatures.rsa_cert, BuiltinSignatures.rsaSHA512)});
+
+        return Collections.unmodifiableList(list);
+    }
+
+    @Test
+    public void testOpenSshCertificates() throws Exception {
+        sshd.setKeyPairProvider(keyPairProvider);
+        sshd.setHostKeyCertificateProvider(certificateProvider);
+        if (signatureFactory != null) {
+            client.setSignatureFactories(signatureFactory);
+        }
+
+        // default client
+        try (ClientSession s = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
+            .verify(CONNECT_TIMEOUT).getSession()) {
+            s.addPasswordIdentity(getCurrentTestName());
+            s.auth().verify(AUTH_TIMEOUT);
+        }
+    }
+
+    @Test
+    public void testPrincipal() throws Exception {
+        sshd.setKeyPairProvider(keyPairProvider);
+        sshd.setHostKeyCertificateProvider(certificateProvider);
+        if (signatureFactory != null) {
+            client.setSignatureFactories(signatureFactory);
+        }
+
+        // invalid principal, but continue
+        PropertyResolverUtils.updateProperty(client, 
ClientFactoryManager.ABORT_ON_INVALID_CERTIFICATE, false);
+        try (ClientSession s = client.connect(getCurrentTestName(), 
"localhost", port)
+            .verify(CONNECT_TIMEOUT).getSession()) {
+            s.addPasswordIdentity(getCurrentTestName());
+            s.auth().verify(AUTH_TIMEOUT);
+        }
+
+        // invalid principal, abort
+        PropertyResolverUtils.updateProperty(client, 
ClientFactoryManager.ABORT_ON_INVALID_CERTIFICATE, true);
+        try (ClientSession s = client.connect(getCurrentTestName(), 
"localhost", port)
+            .verify(CONNECT_TIMEOUT).getSession()) {
+            s.addPasswordIdentity(getCurrentTestName());
+            s.auth().verify(AUTH_TIMEOUT);
+
+            // in case client does not support cert, no exception should be 
thrown
+            
Assert.assertFalse(client.getSignatureFactories().contains(BuiltinSignatures.rsa_cert));
+        } catch (SshException e) {
+            
Assert.assertEquals(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED, 
e.getDisconnectCode());
+        }
+    }
+}
diff --git 
a/sshd-core/src/test/resources/org/apache/sshd/common/signature/example-ca 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/example-ca
new file mode 100644
index 0000000..5c1f3cd
--- /dev/null
+++ b/sshd-core/src/test/resources/org/apache/sshd/common/signature/example-ca
@@ -0,0 +1,49 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+-----END OPENSSH PRIVATE KEY-----
diff --git 
a/sshd-core/src/test/resources/org/apache/sshd/common/signature/example-ca.pub 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/example-ca.pub
new file mode 100644
index 0000000..c5776fc
--- /dev/null
+++ 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/example-ca.pub
@@ -0,0 +1 @@
+ssh-rsa 
AAAAB3NzaC1yc2EAAAADAQABAAACAQDC9ueamUQ2HJDZvS9L6fpb4umROUrPrRjb6TaIuH3K8h9uT8+8oJIM4Pqn7mlSMvX0Vi8VUCpMQ2fZnqukFrB5W8jNdGG7a/IEDF/Yg3dON1D7I43GRo/I7Qk9EGSeDayvIeh0g6dqalx7dyIg83GvsuqC+dZlQpLdFML1PciaHur1NHF9zDyLKjb+Qfxi738Y3tmfSFOd37r0r2dWLqRQ0uDk1uY2fF0YYnQonvpW3Sc6pS8lxJXAOPRYs0a3IG0AQKvzXxjg3U4NYKU0rJ8LLpoeEYbgSYwCPg6RWYg618Yu6NU7OjdQ0i3ZfoCh4O/xuW+oz/RoEEkM8bzDr7GuLeluX7u5jUXUYoeBCbgdp21xTIlCezmzYhZYb6X5Vuk481xXCaEvHAJz++Qz8M3K7YWGilgynFFm6UUHCefgGirei10X4x24FpnTxKmLpxgGWlW1
 [...]
diff --git 
a/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key
 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key
new file mode 100644
index 0000000..1db1976
--- /dev/null
+++ 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key
@@ -0,0 +1,38 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+-----END OPENSSH PRIVATE KEY-----
diff --git 
a/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key-cert.pub
 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key-cert.pub
new file mode 100644
index 0000000..c570d82
--- /dev/null
+++ 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key-cert.pub
@@ -0,0 +1 @@
+ssh-rsa-cert-...@openssh.com 
AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgaLndJbPMJA6TJ5Gd1R+vScUdZaKELX/nEdWsfMmmOZ8AAAADAQABAAABgQDq0+az6bCKulks7E/RQBGUNlo8Pxe8Ylwc7KF2PxTrlGtD2qEjNyLMkBGAJMOME5PKiAoUNpmgxA/2oDbNQUEQpHOISnqz58SrkYwtlbRPntiu1bfZKQBxgs8oP0nVZU30ck9CmUVWdOY3R+sy85lL/g1vEwgGQPJHmnARg76TlX1xaDaR2RsHR+dz5MfPgZUn3C1SSIWSEoiuTNoh9uthHulybAhnHccTCt+90HN7nUXGSuoiu6P+Wg0p3oqClMk548MOBtSNyPFsSKFEOoivPHOfIuP1j5Of+j9iYhTp6bIrqKkUeX/y6s9iuw/d/ZlKYp2/h913iCHQ+VlJVJa/C96FXzudZ67pmuJ
 [...]
diff --git 
a/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key.pub
 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key.pub
new file mode 100644
index 0000000..65bea39
--- /dev/null
+++ 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa 
AAAAB3NzaC1yc2EAAAADAQABAAABgQDq0+az6bCKulks7E/RQBGUNlo8Pxe8Ylwc7KF2PxTrlGtD2qEjNyLMkBGAJMOME5PKiAoUNpmgxA/2oDbNQUEQpHOISnqz58SrkYwtlbRPntiu1bfZKQBxgs8oP0nVZU30ck9CmUVWdOY3R+sy85lL/g1vEwgGQPJHmnARg76TlX1xaDaR2RsHR+dz5MfPgZUn3C1SSIWSEoiuTNoh9uthHulybAhnHccTCt+90HN7nUXGSuoiu6P+Wg0p3oqClMk548MOBtSNyPFsSKFEOoivPHOfIuP1j5Of+j9iYhTp6bIrqKkUeX/y6s9iuw/d/ZlKYp2/h913iCHQ+VlJVJa/C96FXzudZ67pmuJKIesGlYfQLz96Q/Tq5F+QuJm0wG0qUuq3Aa+LZcobOSw+Th/WEmQ9j9O6stZ4Pfh/BPUwPa12u19pLgKH8m25DYLjBLYZO+Zu
 [...]
diff --git 
a/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key_sha1-cert.pub
 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key_sha1-cert.pub
new file mode 100644
index 0000000..67116fa
--- /dev/null
+++ 
b/sshd-core/src/test/resources/org/apache/sshd/common/signature/ssh_host_rsa_key_sha1-cert.pub
@@ -0,0 +1 @@
+ssh-rsa-cert-...@openssh.com 
AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgH+fKXukuzsdEZJ/q+yMiHyxAjVOMR3ZNBfiwxjfKVc0AAAADAQABAAABgQDq0+az6bCKulks7E/RQBGUNlo8Pxe8Ylwc7KF2PxTrlGtD2qEjNyLMkBGAJMOME5PKiAoUNpmgxA/2oDbNQUEQpHOISnqz58SrkYwtlbRPntiu1bfZKQBxgs8oP0nVZU30ck9CmUVWdOY3R+sy85lL/g1vEwgGQPJHmnARg76TlX1xaDaR2RsHR+dz5MfPgZUn3C1SSIWSEoiuTNoh9uthHulybAhnHccTCt+90HN7nUXGSuoiu6P+Wg0p3oqClMk548MOBtSNyPFsSKFEOoivPHOfIuP1j5Of+j9iYhTp6bIrqKkUeX/y6s9iuw/d/ZlKYp2/h913iCHQ+VlJVJa/C96FXzudZ67pmuJ
 [...]

Reply via email to