That didn't work, but the following did work:
diff --git a/certbot/certbot/_internal/cli/__init__.py b/certbot/certbot/_internal/cli/__init__.py
index 4212c353b..38f9674a4 100644
--- a/certbot/certbot/_internal/cli/__init__.py
+++ b/certbot/certbot/_internal/cli/__init__.py
@@ -279,7 +279,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False):
"security", "--rsa-key-size", type=int, metavar="N",
default=flag_default("rsa_key_size"), help=config_help("rsa_key_size"))
helpful.add(
- "security", "--key-type", choices=['rsa', 'ecdsa'], type=str,
+ "security", "--key-type", choices=['rsa', 'ecdsa', 'ed25519'], type=str,
default=flag_default("key_type"), help=config_help("key_type"))
helpful.add(
"security", "--elliptic-curve", type=str, choices=[
diff --git a/certbot/certbot/crypto_util.py b/certbot/certbot/crypto_util.py
index e620d43e0..be903e866 100644
--- a/certbot/certbot/crypto_util.py
+++ b/certbot/certbot/crypto_util.py
@@ -19,6 +19,7 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
+from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from cryptography.hazmat.primitives.serialization import Encoding
@@ -289,6 +290,15 @@ def make_key(bits=1024, key_type="rsa", elliptic_curve=None):
encryption_algorithm=NoEncryption()
)
key = crypto.load_privatekey(crypto.FILETYPE_PEM, _key_pem)
+ elif key_type == 'ed25519':
+ _key = Ed25519PrivateKey.generate()
+ _key_pem = _key.private_bytes(
+ encoding=Encoding.PEM,
+ format=PrivateFormat.PKCS8,
+ encryption_algorithm=NoEncryption()
+ )
+ key = crypto.load_privatekey(crypto.FILETYPE_PEM, _key_pem)
+
else:
raise errors.Error("Invalid key_type specified: {}. Use [rsa|ecdsa]".format(key_type))
return crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
But ALSO:
diff --git a/acme/acme/crypto_util.py b/acme/acme/crypto_util.py
index 749478bf5..52435970e 100644
--- a/acme/acme/crypto_util.py
+++ b/acme/acme/crypto_util.py
@@ -214,7 +214,7 @@ def make_csr(private_key_pem, domains, must_staple=False):
csr.add_extensions(extensions)
csr.set_pubkey(private_key)
csr.set_version(2)
- csr.sign(private_key, 'sha256')
+ csr.sign(private_key, 'NULL')
return crypto.dump_certificate_request(
crypto.FILETYPE_PEM, csr)
AND modify OpenSSLs crypt
!:
diff -Naur a/lib64/python3.9/site-packages/OpenSSL/crypto.py b/lib64/python3.9/site-packages/OpenSSL/crypto.py
--- a/lib64/python3.9/site-packages/OpenSSL/crypto.py 2021-08-13 20:35:44.284389392 +0200
+++ b/lib64/python3.9/site-packages/OpenSSL/crypto.py 2021-08-13 20:36:24.206705683 +0200
@@ -1052,8 +1052,8 @@
raise ValueError("Key is uninitialized")
digest_obj = _lib.EVP_get_digestbyname(_byte_string(digest))
- if digest_obj == _ffi.NULL:
- raise ValueError("No such digest method")
+ #if digest_obj == _ffi.NULL:
+ # raise ValueError("No such digest method")
sign_result = _lib.X509_REQ_sign(self._req, pkey._pkey, digest_obj)
_openssl_assert(sign_result > 0)
Without that latter modification, OpenSSLs crypt
doesn't accept the NULL
parameter..
This is of course just a proof of concept modification and shouldn't be used. 
Also note that the following actually works too, for Ed25519:
diff --git a/acme/acme/crypto_util.py b/acme/acme/crypto_util.py
index 749478bf5..56c16f7d9 100644
--- a/acme/acme/crypto_util.py
+++ b/acme/acme/crypto_util.py
@@ -214,7 +214,7 @@ def make_csr(private_key_pem, domains, must_staple=False):
csr.add_extensions(extensions)
csr.set_pubkey(private_key)
csr.set_version(2)
- csr.sign(private_key, 'sha256')
+ csr.sign(private_key, 'FOOBAR')
return crypto.dump_certificate_request(
crypto.FILETYPE_PEM, csr)
... as long as you also modify the OpenSSL crypt part to ignore any non-existing digest
But an existing digest does not work 
