diff --git a/core/java/src/net/i2p/data/Certificate.java b/core/java/src/net/i2p/data/Certificate.java
index 88b1f9d59f654b4b0b0ec15d47793fbc2c2424dd..afcfbf1b04e8099837c9be48b07876861e97798e 100644
--- a/core/java/src/net/i2p/data/Certificate.java
+++ b/core/java/src/net/i2p/data/Certificate.java
@@ -103,6 +103,8 @@ public class Certificate extends DataStructureImpl {
             throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
         if (type == CERTIFICATE_TYPE_KEY) {
             if (length == 4) {
+                if (Arrays.equals(payload, KeyCertificate.X25519_Ed25519_PAYLOAD))
+                    return KeyCertificate.X25519_Ed25519_CERT;
                 if (Arrays.equals(payload, KeyCertificate.Ed25519_PAYLOAD))
                     return KeyCertificate.ELG_Ed25519_CERT;
                 if (Arrays.equals(payload, KeyCertificate.ECDSA256_PAYLOAD))
diff --git a/core/java/src/net/i2p/data/KeyCertificate.java b/core/java/src/net/i2p/data/KeyCertificate.java
index 0aabb2c17f9c375e87b935ba44168888c13e1db5..c599d0f5433384f3a0c5d30b3f9b52a42799ddd9 100644
--- a/core/java/src/net/i2p/data/KeyCertificate.java
+++ b/core/java/src/net/i2p/data/KeyCertificate.java
@@ -20,16 +20,33 @@ public class KeyCertificate extends Certificate {
 
     public static final int HEADER_LENGTH = 4;
 
-    /** @since 0.9.22 pkg private for Certificate.create() */
+    /**
+     * ElG + Ed25519
+     *
+     * @since 0.9.22 pkg private for Certificate.create()
+     */
     static final byte[] Ed25519_PAYLOAD = new byte[] {
         0, (byte) (SigType.EdDSA_SHA512_Ed25519.getCode()), 0, 0
     };
 
-    /** @since 0.9.22 pkg private for Certificate.create() */
+    /**
+     * ElG + P256
+     *
+     * @since 0.9.22 pkg private for Certificate.create()
+     */
     static final byte[] ECDSA256_PAYLOAD = new byte[] {
         0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
     };
 
+    /**
+     * X25519 + Ed25519
+     *
+     * @since 0.9.54
+     */
+    static final byte[] X25519_Ed25519_PAYLOAD = new byte[] {
+        0, (byte) (SigType.EdDSA_SHA512_Ed25519.getCode()), 0, (byte) (EncType.ECIES_X25519.getCode())
+    };
+
     /**
      *  An immutable ElG/ECDSA-P256 certificate.
      */
@@ -41,6 +58,12 @@ public class KeyCertificate extends Certificate {
      */
     public static final KeyCertificate ELG_Ed25519_CERT;
 
+    /**
+     *  An immutable X25519/Ed25519 certificate.
+     *  @since 0.9.54
+     */
+    public static final KeyCertificate X25519_Ed25519_CERT;
+
     static {
         KeyCertificate kc;
         try {
@@ -55,6 +78,12 @@ public class KeyCertificate extends Certificate {
             throw new RuntimeException(dfe);  // won't happen
         }
         ELG_Ed25519_CERT = kc;
+        try {
+            kc = new X25519_Ed25519Cert();
+        } catch (DataFormatException dfe) {
+            throw new RuntimeException(dfe);  // won't happen
+        }
+        X25519_Ed25519_CERT = kc;
     }
 
     /**
@@ -401,4 +430,72 @@ public class KeyCertificate extends Certificate {
             return _hashcode;
         }
     }
+
+    /**
+     *  An immutable X25519/Ed25519 certificate.
+     *  @since 0.9.54
+     */
+    private static final class X25519_Ed25519Cert extends KeyCertificate {
+        private static final byte[] ED_DATA = new byte[] { CERTIFICATE_TYPE_KEY,
+                                                           0, HEADER_LENGTH,
+                                                           0, (byte) SigType.EdDSA_SHA512_Ed25519.getCode(),
+                                                           0, (byte) EncType.ECIES_X25519.getCode()
+        };
+        private static final int ED_LENGTH = ED_DATA.length;
+        private final int _hashcode;
+
+        public X25519_Ed25519Cert() throws DataFormatException {
+            super(X25519_Ed25519_PAYLOAD);
+            _hashcode = super.hashCode();
+        }
+
+        /** @throws RuntimeException always */
+        @Override
+        public void setCertificateType(int type) {
+            throw new RuntimeException("Data already set");
+        }
+
+        /** @throws RuntimeException always */
+        @Override
+        public void setPayload(byte[] payload) {
+            throw new RuntimeException("Data already set");
+        }
+    
+        /** @throws RuntimeException always */
+        @Override
+        public void readBytes(InputStream in) throws DataFormatException, IOException {
+            throw new RuntimeException("Data already set");
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public void writeBytes(OutputStream out) throws IOException {
+            out.write(ED_DATA);
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public int writeBytes(byte target[], int offset) {
+            System.arraycopy(ED_DATA, 0, target, offset, ED_LENGTH);
+            return ED_LENGTH;
+        }
+    
+        /** @throws RuntimeException always */
+        @Override
+        public int readBytes(byte source[], int offset) throws DataFormatException {
+            throw new RuntimeException("Data already set");
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public int size() {
+            return ED_LENGTH;
+        }
+    
+        /** Overridden for efficiency */
+        @Override
+        public int hashCode() {
+            return _hashcode;
+        }
+    }
 }