From a4489456b6f65281e172380cc4826cee5e851dbb Mon Sep 17 00:00:00 2001
From: Jeff Forcier <jeff@bitprophet.org>
Date: Sun, 15 Feb 2026 16:11:44 -0500
Subject: [PATCH] Remove SHA1 support from RSA key handling

---
 paramiko/auth_handler.py |  6 ++++
 paramiko/client.py       | 26 ++++++++++++++++
 paramiko/config.py       |  1 +
 paramiko/rsakey.py       | 15 ++++++----
 paramiko/sftp_server.py  |  4 +++
 paramiko/transport.py    | 34 +++++++++++++++------
 sites/www/changelog.rst  | 12 ++++++++
 tests/_util.py           | 39 ++----------------------
 tests/auth.py            | 41 +++++++++++++++----------
 tests/test_client.py     | 65 +++++++++++++---------------------------
 tests/test_pkey.py       |  8 ++---
 tests/test_transport.py  | 28 ++++++-----------
 12 files changed, 143 insertions(+), 136 deletions(-)

Index: paramiko-3.5.1/paramiko/auth_handler.py
===================================================================
--- paramiko-3.5.1.orig/paramiko/auth_handler.py
+++ paramiko-3.5.1/paramiko/auth_handler.py
@@ -614,7 +614,13 @@ Error Message: {}
             # NOTE: server never wants to guess a client's algo, they're
             # telling us directly. No need for _finalize_pubkey_algorithm
             # anywhere in this flow.
+            # TODO: ok is this a spot where it can say a SHA2 dealie in some
+            # fields but still ssh-rsa within the pubkey blob part?
+            # TODO: ok so this would be rsa-sha2-256 or w/e, if this field says
+            # ssh-rsa the request can get stuffed.
             algorithm = m.get_text()
+            # TODO: This part would, if deconstructed, still be allowed to have
+            # "ssh-rsa" in its first field.
             keyblob = m.get_binary()
             try:
                 key = self._generate_key_from_request(algorithm, keyblob)
Index: paramiko-3.5.1/paramiko/client.py
===================================================================
--- paramiko-3.5.1.orig/paramiko/client.py
+++ paramiko-3.5.1/paramiko/client.py
@@ -441,10 +441,32 @@ class SSHClient(ClosingContextManager):
 
         our_server_keys = self._system_host_keys.get(server_hostkey_name)
         if our_server_keys is None:
+            # TODO: this is getting us the test suite _test_connection host key
+            # setup by virtue of that code doing tc.get_host_keys().add() (that
+            # method wraps self._host_keys)
+            # TODO: it should be analogous to running paramiko w/ a
+            # ~/.ssh/known_hosts file
             our_server_keys = self._host_keys.get(server_hostkey_name)
         if our_server_keys is not None:
+            # TODO: keytype needs to turn into one of the rsa-sha2 keys if it's
+            # an RSAKey.
+            # TODO: where is the equivalent on our server-side? hopefully the
+            # tests exercise that lol
+            # TODO: also, is it just me or does this [0] mean we literally do
+            # not actually implement real HostKeyAlgorithms agreement like
+            # ssh.c does?! eesh
             keytype = our_server_keys.keys()[0]
             sec_opts = t.get_security_options()
+            # TODO: clean this up a bit, but it does help some tests pass!
+            if keytype == "ssh-rsa":
+                if "rsa-sha2-512" in sec_opts.key_types:
+                    keytype = "rsa-sha2-512"
+                elif "rsa-sha2-256" in sec_opts.key_types:
+                    keytype = "rsa-sha2-256"
+                else:
+                    raise Exception(
+                        "TODO: REPLACEME with appropriate exception for 'what even is this key type in your known_hosts files?'"
+                    )
             other_types = [x for x in sec_opts.key_types if x != keytype]
             sec_opts.key_types = [keytype] + other_types
 
@@ -461,6 +483,10 @@ class SSHClient(ClosingContextManager):
                     self, server_hostkey_name, server_key
                 )
             else:
+                # TODO: this should 'just work' but dblcheck (HostKeys will be
+                # offering an ssh-rsa-via-sha2 key as "ssh-rsa" still, and the
+                # key would be RSAKey whose .get_name would still say
+                # "ssh-rsa")
                 our_key = our_server_keys.get(server_key.get_name())
                 if our_key != server_key:
                     if our_key is None:
Index: paramiko-3.5.1/paramiko/config.py
===================================================================
--- paramiko-3.5.1.orig/paramiko/config.py
+++ paramiko-3.5.1/paramiko/config.py
@@ -446,6 +446,7 @@ class SSHConfig:
         # The actual tokens!
         replacements = {
             # TODO: %%???
+            # TODO: sha1 bad / this is offspec from rfc/openssh
             "%C": sha1(tohash.encode()).hexdigest(),
             "%d": homedir,
             "%h": configured_hostname,
Index: paramiko-3.5.1/paramiko/rsakey.py
===================================================================
--- paramiko-3.5.1.orig/paramiko/rsakey.py
+++ paramiko-3.5.1/paramiko/rsakey.py
@@ -38,8 +38,6 @@ class RSAKey(PKey):
 
     name = "ssh-rsa"
     HASHES = {
-        "ssh-rsa": hashes.SHA1,
-        "ssh-rsa-cert-v01@openssh.com": hashes.SHA1,
         "rsa-sha2-256": hashes.SHA256,
         "rsa-sha2-256-cert-v01@openssh.com": hashes.SHA256,
         "rsa-sha2-512": hashes.SHA512,
@@ -81,7 +79,14 @@ class RSAKey(PKey):
 
     @classmethod
     def identifiers(cls):
-        return list(cls.HASHES.keys())
+        # NOTE: we no longer want to have ssh-rsa+SHA1 in HASHES but we still
+        # need to advertise we can be used to read ssh-rsa keys (w/ assumption
+        # other parts of system will enforce the use of SHA2 signing algos).
+        # Thus, just say so here.
+        return list(cls.HASHES.keys()) + [
+            "ssh-rsa",
+            "ssh-rsa-cert-v01@openssh.com",
+        ]
 
     @property
     def size(self):
@@ -125,8 +130,8 @@ class RSAKey(PKey):
         sig = self.key.sign(
             data,
             padding=padding.PKCS1v15(),
-            # HASHES being just a map from long identifier to either SHA1 or
-            # SHA256 - cert'ness is not truly relevant.
+            # HASHES being just a map from long identifier to algo; cert'ness
+            # is not truly relevant.
             algorithm=self.HASHES[algorithm](),
         )
         m = Message()
Index: paramiko-3.5.1/paramiko/sftp_server.py
===================================================================
--- paramiko-3.5.1.orig/paramiko/sftp_server.py
+++ paramiko-3.5.1/paramiko/sftp_server.py
@@ -82,6 +82,7 @@ from paramiko.sftp import (
     SFTP_OP_UNSUPPORTED,
 )
 
+# TODO: nuke or update w/ newer algs, see below
 _hash_class = {"sha1": sha1, "md5": md5}
 
 
@@ -305,6 +306,9 @@ class SFTPServer(BaseSFTP, SubsystemHand
             return
         f = self.file_table[handle]
         for x in alg_list:
+            # TODO: this only contains sha1 and md5 so uh, is this extension
+            # actually supported anymore? do we need to update the map for
+            # newer algos instead? other?
             if x in _hash_class:
                 algname = x
                 alg = _hash_class[x]
Index: paramiko-3.5.1/paramiko/transport.py
===================================================================
--- paramiko-3.5.1.orig/paramiko/transport.py
+++ paramiko-3.5.1/paramiko/transport.py
@@ -205,7 +205,6 @@ class Transport(threading.Thread, Closin
         "ecdsa-sha2-nistp521",
         "rsa-sha2-512",
         "rsa-sha2-256",
-        "ssh-rsa",
         "ssh-dss",
     )
     # ~= PubKeyAcceptedAlgorithms
@@ -216,7 +215,6 @@ class Transport(threading.Thread, Closin
         "ecdsa-sha2-nistp521",
         "rsa-sha2-512",
         "rsa-sha2-256",
-        "ssh-rsa",
         "ssh-dss",
     )
     _preferred_kex = (
@@ -310,12 +308,18 @@ class Transport(threading.Thread, Closin
     }
 
     _key_info = {
-        # TODO: at some point we will want to drop this as it's no longer
-        # considered secure due to using SHA-1 for signatures. OpenSSH 8.8 no
-        # longer supports it. Question becomes at what point do we want to
-        # prevent users with older setups from using this?
-        "ssh-rsa": RSAKey,
-        "ssh-rsa-cert-v01@openssh.com": RSAKey,
+        # TODO: do some downstream uses of this need to be able to 'see'
+        # ssh-rsa in not-using-SHA1 contexts?
+        # TODO: NO!!! good.
+        # TODO: it's used in:
+        # - Transport._verify_key - verification - do not want ssh-rsa
+        # - SecurityOptions - only really uses this as a filter for what's
+        # allowed to be overwritten into its .key_types (which ==
+        # transport._preferred_keys), and since the latter doesn't want ssh-rsa
+        # in it, this use case doesn't require that string in here either.
+        # - AuthHandler._generate_key_from_request - server-side auth
+        # support - is looking at the 'algorithm' field in the request when it
+        # references this structure, so yup, do not want ssh-rsa
         "rsa-sha2-256": RSAKey,
         "rsa-sha2-256-cert-v01@openssh.com": RSAKey,
         "rsa-sha2-512": RSAKey,
@@ -1402,11 +1406,13 @@ class Transport(threading.Thread, Closin
             # TODO: a more robust implementation would be to ask each key class
             # for its nameS plural, and just use that.
             # TODO: that could be used in a bunch of other spots too
+            # TODO: don't we have that now, lol
+            # TODO: either way this is ~= like using SecurityOptions.key_types
+            # = xxx, but different, which sucks sigh
             if isinstance(hostkey, RSAKey):
                 self._preferred_keys = [
                     "rsa-sha2-512",
                     "rsa-sha2-256",
-                    "ssh-rsa",
                 ]
             else:
                 self._preferred_keys = [hostkey.get_name()]
@@ -2008,6 +2014,8 @@ class Transport(threading.Thread, Closin
         key = self._key_info[self.host_key_type](Message(host_key))
         if key is None:
             raise SSHException("Unknown host key type")
+        # TODO: like, here, can a host offer "ssh-rsa" but request SHA2, or are
+        # those baked in?
         if not key.verify_ssh_sig(self.H, Message(sig)):
             raise SSHException(
                 "Signature verification ({}) failed.".format(
@@ -3238,6 +3246,14 @@ class SecurityOptions:
 
     @key_types.setter
     def key_types(self, x):
+        # TODO: so this reads Transport._key_info.keys(), yells if any values
+        # in `x` /aren't/ in that list, then overwrites
+        # Transport._preferred_keys with `x`...
+        # TODO: so you can read this pretty simply as "replace
+        # transport._preferred_keys with x".
+        # TODO: which is...bad...in cases where SSHClient is trying to simply
+        # load up known_hosts or system known hosts, and use those to determine
+        # which hostkey /algorithms/ it is willing to accept
         self._set("_preferred_keys", "_key_info", x)
 
     @property
Index: paramiko-3.5.1/tests/_util.py
===================================================================
--- paramiko-3.5.1.orig/tests/_util.py
+++ paramiko-3.5.1/tests/_util.py
@@ -168,42 +168,9 @@ def is_low_entropy():
     return is_32bit and os.environ.get("PYTHONHASHSEED", None) == "0"
 
 
-def sha1_signing_unsupported():
-    """
-    This is used to skip tests in environments where SHA-1 signing is
-    not supported by the backend.
-    """
-    private_key = rsa.generate_private_key(
-        public_exponent=65537, key_size=2048, backend=default_backend()
-    )
-    message = b"Some dummy text"
-    try:
-        private_key.sign(
-            message,
-            padding.PSS(
-                mgf=padding.MGF1(hashes.SHA1()),
-                salt_length=padding.PSS.MAX_LENGTH,
-            ),
-            hashes.SHA1(),
-        )
-        return False
-    except UnsupportedAlgorithm as e:
-        return e._reason == _Reasons.UNSUPPORTED_HASH
-
-
-requires_sha1_signing = unittest.skipIf(
-    sha1_signing_unsupported(), "SHA-1 signing not supported"
-)
-
-_disable_sha2 = dict(
-    disabled_algorithms=dict(keys=["rsa-sha2-256", "rsa-sha2-512"])
-)
-_disable_sha1 = dict(disabled_algorithms=dict(keys=["ssh-rsa"]))
-_disable_sha2_pubkey = dict(
-    disabled_algorithms=dict(pubkeys=["rsa-sha2-256", "rsa-sha2-512"])
-)
-_disable_sha1_pubkey = dict(disabled_algorithms=dict(pubkeys=["ssh-rsa"]))
-
+# TODO: doublecheck where _disable_xxx helpers were in use; disabling sha1 no
+# longer makes any sense, and presumably neither does en/dis abling sha2 but
+# that's the question innit
 
 unicodey = "\u2022"
 
Index: paramiko-3.5.1/tests/auth.py
===================================================================
--- paramiko-3.5.1.orig/tests/auth.py
+++ paramiko-3.5.1/tests/auth.py
@@ -31,11 +31,7 @@ from paramiko import (
 )
 
 from ._util import (
-    _disable_sha1_pubkey,
-    _disable_sha2,
-    _disable_sha2_pubkey,
     _support,
-    requires_sha1_signing,
     server,
     unicodey,
 )
@@ -134,8 +130,9 @@ class AuthHandler_:
         verify that we catch a server disconnecting during auth, and report
         it as an auth failure.
         """
-        with server(defer=True, skip_verify=True) as (tc, ts), raises(
-            AuthenticationException
+        with (
+            server(defer=True, skip_verify=True) as (tc, ts),
+            raises(AuthenticationException),
         ):
             tc.auth_password("bad-server", "hello")
 
@@ -144,9 +141,10 @@ class AuthHandler_:
         verify that authentication times out if server takes to long to
         respond (or never responds).
         """
-        with server(defer=True, skip_verify=True) as (tc, ts), raises(
-            AuthenticationException
-        ) as info:
+        with (
+            server(defer=True, skip_verify=True) as (tc, ts),
+            raises(AuthenticationException) as info,
+        ):
             tc.auth_timeout = 1  # 1 second, to speed up test
             tc.auth_password("unresponsive-server", "hello")
             assert "Authentication timeout" in str(info.value)
@@ -158,7 +156,6 @@ class AuthOnlyHandler_:
         return server(*args, **kwargs)
 
     class fallback_pubkey_algorithm:
-        @requires_sha1_signing
         def key_type_algo_selected_when_no_server_sig_algs(self):
             privkey = RSAKey.from_private_key_file(_support("rsa.key"))
             # Server pretending to be an apparently common setup:
@@ -167,6 +164,10 @@ class AuthOnlyHandler_:
             # This is the scenario in which Paramiko has to guess-the-algo, and
             # where servers that don't support sha2 or server-sig-algs can give
             # us trouble.
+            # TODO: ok maybe I need to reinstate _disable_sha2_pubkey, but it
+            # _might_ make sense to choose some other key type to disable, if
+            # it's more sensible now - surely the modern version of this
+            # scenario would be disabling like, ecdsa or something?
             server_init = dict(_disable_sha2_pubkey, server_sig_algs=False)
             with self._server(
                 pubkeys=[privkey],
@@ -177,9 +178,11 @@ class AuthOnlyHandler_:
                 # Auth did work
                 assert tc.is_authenticated()
                 # Selected ssh-rsa, instead of first-in-the-list (rsa-sha2-512)
+                # TODO: this needs to be selecting some other fallback now,
+                # presumably whatever is now first in one of our attribute
+                # lists
                 assert tc._agreed_pubkey_algorithm == "ssh-rsa"
 
-        @requires_sha1_signing
         def key_type_algo_selection_is_cert_suffix_aware(self):
             # This key has a cert next to it, which should trigger cert-aware
             # loading within key classes.
@@ -197,10 +200,10 @@ class AuthOnlyHandler_:
                 # Selected expected cert type
                 assert (
                     tc._agreed_pubkey_algorithm
+                    # TODO: this needs to be choosing a sha2 or w/e now
                     == "ssh-rsa-cert-v01@openssh.com"
                 )
 
-        @requires_sha1_signing
         def uses_first_preferred_algo_if_key_type_not_in_list(self):
             # This is functionally the same as legacy AuthHandler, just
             # arriving at the same place in a different manner.
@@ -210,6 +213,8 @@ class AuthOnlyHandler_:
                 pubkeys=[privkey],
                 connect=dict(pkey=privkey),
                 server_init=server_init,
+                # TODO: this is outdated now, disable something else, probably
+                # whatever is /now/ first in the list instead of ssh-rsa
                 client_init=_disable_sha1_pubkey,  # no ssh-rsa
                 catch_error=True,
             ) as (tc, ts, err):
@@ -226,7 +231,7 @@ class SHA2SignaturePubkeys:
             connect=dict(pkey=privkey),
             init=dict(
                 disabled_algorithms=dict(
-                    pubkeys=["ssh-rsa", "rsa-sha2-256", "rsa-sha2-512"]
+                    pubkeys=["rsa-sha2-256", "rsa-sha2-512"]
                 )
             ),
             catch_error=True,
@@ -234,68 +239,43 @@ class SHA2SignaturePubkeys:
             assert isinstance(err, SSHException)
             assert "no RSA pubkey algorithms" in str(err)
 
-    def client_sha2_disabled_server_sha1_disabled_no_match(self):
-        privkey = RSAKey.from_private_key_file(_support("rsa.key"))
-        with server(
-            pubkeys=[privkey],
-            connect=dict(pkey=privkey),
-            client_init=_disable_sha2_pubkey,
-            server_init=_disable_sha1_pubkey,
-            catch_error=True,
-        ) as (tc, ts, err):
-            assert isinstance(err, AuthenticationException)
-
-    def client_sha1_disabled_server_sha2_disabled_no_match(self):
-        privkey = RSAKey.from_private_key_file(_support("rsa.key"))
-        with server(
-            pubkeys=[privkey],
-            connect=dict(pkey=privkey),
-            client_init=_disable_sha1_pubkey,
-            server_init=_disable_sha2_pubkey,
-            catch_error=True,
-        ) as (tc, ts, err):
-            assert isinstance(err, AuthenticationException)
-
-    @requires_sha1_signing
-    def ssh_rsa_still_used_when_sha2_disabled(self):
-        privkey = RSAKey.from_private_key_file(_support("rsa.key"))
-        # NOTE: this works because key obj comparison uses public bytes
-        # TODO: would be nice for PKey to grow a legit "give me another obj of
-        # same class but just the public bits" using asbytes()
-        with server(
-            pubkeys=[privkey], connect=dict(pkey=privkey), init=_disable_sha2
-        ) as (tc, _):
-            assert tc.is_authenticated()
-
-    @requires_sha1_signing
+    # TODO: isn't this duplicating some of the earlier tests? if not, update it
     def first_client_preferred_algo_used_when_no_server_sig_algs(self):
         privkey = RSAKey.from_private_key_file(_support("rsa.key"))
-        # Server pretending to be an apparently common setup:
-        # - doesn't support (or have enabled) sha2
-        # - also doesn't support (or have enabled) server-sig-algs/ext-info
-        # This is the scenario in which Paramiko has to guess-the-algo, and
-        # where servers that don't support sha2 or server-sig-algs give us
-        # trouble.
-        server_init = dict(_disable_sha2_pubkey, server_sig_algs=False)
         with server(
             pubkeys=[privkey],
             connect=dict(username="slowdive", pkey=privkey),
-            server_init=server_init,
+            server_init=dict(
+                disabled_algorithms=dict(
+                    # Disable both SHA2 just to keep previous theme of this
+                    # test alive (auth failure but w/ proof we tried offering
+                    # something)
+                    pubkeys=["rsa-sha2-256", "rsa-sha2-512"]
+                ),
+                # Don't publish server-sig-algs, forcing use of fallback on the
+                # client side
+                server_sig_algs=False,
+            ),
+            # Incidentally prove we're using the filtered algorithm list on the
+            # client side - taking out 512 means we should offer 256
+            client_init=dict(
+                disabled_algorithms=dict(pubkeys=["rsa-sha2-512"])
+            ),
             catch_error=True,
         ) as (tc, ts, err):
+            # There was no agreement, we threw an exception...
             assert not tc.is_authenticated()
             assert isinstance(err, AuthenticationException)
-            # Oh no! this isn't ssh-rsa, and our server doesn't support sha2!
-            assert tc._agreed_pubkey_algorithm == "rsa-sha2-512"
+            # ...but we can observe the client did their best guess at what an
+            # agreement /could/ have been, and it was 256
+            assert tc._agreed_pubkey_algorithm == "rsa-sha2-256"
 
     def sha2_512(self):
         privkey = RSAKey.from_private_key_file(_support("rsa.key"))
         with server(
             pubkeys=[privkey],
             connect=dict(pkey=privkey),
-            init=dict(
-                disabled_algorithms=dict(pubkeys=["ssh-rsa", "rsa-sha2-256"])
-            ),
+            init=dict(disabled_algorithms=dict(pubkeys=["rsa-sha2-256"])),
         ) as (tc, ts):
             assert tc.is_authenticated()
             assert tc._agreed_pubkey_algorithm == "rsa-sha2-512"
@@ -305,22 +285,7 @@ class SHA2SignaturePubkeys:
         with server(
             pubkeys=[privkey],
             connect=dict(pkey=privkey),
-            init=dict(
-                disabled_algorithms=dict(pubkeys=["ssh-rsa", "rsa-sha2-512"])
-            ),
-        ) as (tc, ts):
-            assert tc.is_authenticated()
-            assert tc._agreed_pubkey_algorithm == "rsa-sha2-256"
-
-    def sha2_256_when_client_only_enables_256(self):
-        privkey = RSAKey.from_private_key_file(_support("rsa.key"))
-        with server(
-            pubkeys=[privkey],
-            connect=dict(pkey=privkey),
-            # Client-side only; server still accepts all 3.
-            client_init=dict(
-                disabled_algorithms=dict(pubkeys=["ssh-rsa", "rsa-sha2-512"])
-            ),
+            init=dict(disabled_algorithms=dict(pubkeys=["rsa-sha2-512"])),
         ) as (tc, ts):
             assert tc.is_authenticated()
             assert tc._agreed_pubkey_algorithm == "rsa-sha2-256"
Index: paramiko-3.5.1/tests/test_client.py
===================================================================
--- paramiko-3.5.1.orig/tests/test_client.py
+++ paramiko-3.5.1/tests/test_client.py
@@ -41,7 +41,7 @@ from paramiko import SSHClient
 from paramiko.pkey import PublicBlob
 from paramiko.ssh_exception import SSHException, AuthenticationException
 
-from ._util import _support, requires_sha1_signing, slow
+from ._util import _support, slow
 
 
 requires_gss_auth = unittest.skipUnless(
@@ -49,6 +49,8 @@ requires_gss_auth = unittest.skipUnless(
 )
 
 FINGERPRINTS = {
+    # TODO: this should still be ok as it's specifically re: key
+    # type/identity.
     "ssh-dss": b"\x44\x78\xf0\xb9\xa2\x3c\xc5\x18\x20\x09\xff\x75\x5b\xc1\xd2\x6c",  # noqa
     "ssh-rsa": b"\x60\x73\x38\x44\xcb\x51\x86\x65\x7f\xde\xda\xa2\x2b\x5a\x57\xd5",  # noqa
     "ecdsa-sha2-nistp256": b"\x25\x19\xeb\x55\xe6\xa1\x47\xff\x4f\x38\xd2\x75\x6f\xa5\xd5\x60",  # noqa
@@ -200,7 +202,13 @@ class ClientTest(unittest.TestCase):
         # Client setup
         self.tc = SSHClient()
         self.tc.get_host_keys().add(
-            f"[{self.addr}]:{self.port}", "ssh-rsa", public_host_key
+            f"[{self.addr}]:{self.port}",
+            # TODO: so, this is 'keytype', which is still allowed to be
+            # ssh-rsa, as it "comes from the key itself" per `man sshd`.
+            # TODO: but q is, is part of our pipeline still conflating keytype
+            # with signature algorithm? seems likely?
+            "ssh-rsa",
+            public_host_key,
         )
 
         # Actual connection
@@ -242,45 +250,40 @@ class ClientTest(unittest.TestCase):
 
 
 class SSHClientTest(ClientTest):
-    @requires_sha1_signing
     def test_client(self):
         """
         verify that the SSHClient stuff works too.
         """
         self._test_connection(password="pygmalion")
 
-    @requires_sha1_signing
     def test_client_dsa(self):
         """
         verify that SSHClient works with a DSA key.
         """
         self._test_connection(key_filename=_support("dss.key"))
 
-    @requires_sha1_signing
     def test_client_rsa(self):
         """
         verify that SSHClient works with an RSA key.
         """
         self._test_connection(key_filename=_support("rsa.key"))
 
-    @requires_sha1_signing
     def test_client_ecdsa(self):
         """
         verify that SSHClient works with an ECDSA key.
         """
         self._test_connection(key_filename=_support("ecdsa-256.key"))
 
-    @requires_sha1_signing
     def test_client_ed25519(self):
         self._test_connection(key_filename=_support("ed25519.key"))
 
-    @requires_sha1_signing
     def test_multiple_key_files(self):
         """
         verify that SSHClient accepts and tries multiple key files.
         """
         # This is dumb :(
         types_ = {
+            # TODO: this is just about key identity so should be ok
             "rsa": "ssh-rsa",
             "dss": "ssh-dss",
             "ecdsa": "ecdsa-sha2-nistp256",
@@ -307,7 +310,6 @@ class SSHClientTest(ClientTest):
                 self.tearDown()
                 self.setUp()
 
-    @requires_sha1_signing
     def test_multiple_key_files_failure(self):
         """
         Expect failure when multiple keys in play and none are accepted
@@ -321,7 +323,6 @@ class SSHClientTest(ClientTest):
             allowed_keys=["ecdsa-sha2-nistp256"],
         )
 
-    @requires_sha1_signing
     def test_certs_allowed_as_key_filename_values(self):
         # NOTE: giving cert path here, not key path. (Key path test is below.
         # They're similar except for which path is given; the expected auth and
@@ -334,7 +335,6 @@ class SSHClientTest(ClientTest):
                 public_blob=PublicBlob.from_file(f"{key_path}-cert.pub"),
             )
 
-    @requires_sha1_signing
     def test_certs_implicitly_loaded_alongside_key_filename_keys(self):
         # NOTE: a regular test_connection() w/ rsa.key would incidentally
         # test this (because test_xxx.key-cert.pub exists) but incidental tests
@@ -349,29 +349,6 @@ class SSHClientTest(ClientTest):
                 public_blob=PublicBlob.from_file(f"{key_path}-cert.pub"),
             )
 
-    def _cert_algo_test(self, ver, alg):
-        # Issue #2017; see auth_handler.py
-        self.connect_kwargs["username"] = "somecertuser"  # neuter pw auth
-        self._test_connection(
-            # NOTE: SSHClient is able to take either the key or the cert & will
-            # set up its internals as needed
-            key_filename=_support("rsa.key-cert.pub"),
-            server_name="SSH-2.0-OpenSSH_{}".format(ver),
-        )
-        assert (
-            self.tc._transport._agreed_pubkey_algorithm
-            == "{}-cert-v01@openssh.com".format(alg)
-        )
-
-    @requires_sha1_signing
-    def test_old_openssh_needs_ssh_rsa_for_certs_not_rsa_sha2(self):
-        self._cert_algo_test(ver="7.7", alg="ssh-rsa")
-
-    @requires_sha1_signing
-    def test_newer_openssh_uses_rsa_sha2_for_certs_not_ssh_rsa(self):
-        # NOTE: 512 happens to be first in our list and is thus chosen
-        self._cert_algo_test(ver="7.8", alg="rsa-sha2-512")
-
     def test_default_key_locations_trigger_cert_loads_if_found(self):
         # TODO: what it says on the tin: ~/.ssh/id_rsa tries to load
         # ~/.ssh/id_rsa-cert.pub. Right now no other tests actually test that
@@ -417,6 +394,8 @@ class SSHClientTest(ClientTest):
 
         host_id = f"[{self.addr}]:{self.port}"
 
+        # TODO: this should still be okay, host keys -> keytype -> is not
+        # directly tied to algo -> but need to see if guts do that
         client.get_host_keys().add(host_id, "ssh-rsa", public_host_key)
         assert len(client.get_host_keys()) == 1
         assert public_host_key == client.get_host_keys()[host_id]["ssh-rsa"]
@@ -512,13 +491,15 @@ class SSHClientTest(ClientTest):
 
         self.tc = SSHClient()
         self.tc.get_host_keys().add(
-            f"[{self.addr}]:{self.port}", "ssh-rsa", public_host_key
+            f"[{self.addr}]:{self.port}",
+            # TODO: hostkey keytype == ok-ish
+            "ssh-rsa",
+            public_host_key,
         )
         # Connect with a half second banner timeout.
         kwargs = dict(self.connect_kwargs, banner_timeout=0.5)
         self.assertRaises(paramiko.SSHException, self.tc.connect, **kwargs)
 
-    @requires_sha1_signing
     def test_auth_trickledown(self):
         """
         Failed key auth doesn't prevent subsequent pw auth from succeeding
@@ -539,7 +520,6 @@ class SSHClientTest(ClientTest):
         )
         self._test_connection(**kwargs)
 
-    @requires_sha1_signing
     @slow
     def test_auth_timeout(self):
         """
@@ -666,15 +646,14 @@ class SSHClientTest(ClientTest):
         host_key = paramiko.ECDSAKey.generate()
         self._client_host_key_bad(host_key)
 
-    @requires_sha1_signing
     def test_host_key_negotiation_2(self):
+        # TODO: this may now be too small for audit recco, dlbcheck
         host_key = paramiko.RSAKey.generate(2048)
         self._client_host_key_bad(host_key)
 
     def test_host_key_negotiation_3(self):
         self._client_host_key_good(paramiko.ECDSAKey, "ecdsa-256.key")
 
-    @requires_sha1_signing
     def test_host_key_negotiation_4(self):
         self._client_host_key_good(paramiko.RSAKey, "rsa.key")
 
@@ -746,10 +725,10 @@ class SSHClientTest(ClientTest):
             "host",
             sock=Mock(),
             password="no",
-            disabled_algorithms={"keys": ["ssh-dss"]},
+            disabled_algorithms={"keys": ["ed25519"]},
         )
         call_arg = Transport.call_args[1]["disabled_algorithms"]
-        assert call_arg == {"keys": ["ssh-dss"]}
+        assert call_arg == {"keys": ["ed25519"]}
 
     @patch("paramiko.client.Transport")
     def test_transport_factory_defaults_to_Transport(self, Transport):
@@ -792,7 +771,6 @@ class PasswordPassphraseTests(ClientTest
     # instead of suffering a real connection cycle.
     # TODO: in that case, move the below to be part of an integration suite?
 
-    @requires_sha1_signing
     def test_password_kwarg_works_for_password_auth(self):
         # Straightforward / duplicate of earlier basic password test.
         self._test_connection(password="pygmalion")
@@ -800,12 +778,10 @@ class PasswordPassphraseTests(ClientTest
     # TODO: more granular exception pending #387; should be signaling "no auth
     # methods available" because no key and no password
     @raises(SSHException)
-    @requires_sha1_signing
     def test_passphrase_kwarg_not_used_for_password_auth(self):
         # Using the "right" password in the "wrong" field shouldn't work.
         self._test_connection(passphrase="pygmalion")
 
-    @requires_sha1_signing
     def test_passphrase_kwarg_used_for_key_passphrase(self):
         # Straightforward again, with new passphrase kwarg.
         self._test_connection(
@@ -813,7 +789,6 @@ class PasswordPassphraseTests(ClientTest
             passphrase="television",
         )
 
-    @requires_sha1_signing
     def test_password_kwarg_used_for_passphrase_when_no_passphrase_kwarg_given(
         self,
     ):  # noqa
@@ -824,7 +799,6 @@ class PasswordPassphraseTests(ClientTest
         )
 
     @raises(AuthenticationException)  # TODO: more granular
-    @requires_sha1_signing
     def test_password_kwarg_not_used_for_passphrase_when_passphrase_kwarg_given(  # noqa
         self,
     ):
Index: paramiko-3.5.1/tests/test_pkey.py
===================================================================
--- paramiko-3.5.1.orig/tests/test_pkey.py
+++ paramiko-3.5.1/tests/test_pkey.py
@@ -45,7 +45,7 @@ from cryptography.hazmat.primitives.asym
 from unittest.mock import patch, Mock
 import pytest
 
-from ._util import _support, is_low_entropy, requires_sha1_signing
+from ._util import _support, is_low_entropy
 
 
 # from openssh's ssh-keygen
@@ -255,10 +255,6 @@ class KeyTest(unittest.TestCase):
         pub = RSAKey(data=key.asbytes())
         self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
 
-    @requires_sha1_signing
-    def test_sign_and_verify_ssh_rsa(self):
-        self._sign_and_verify_rsa("ssh-rsa", SIGNED_RSA)
-
     def test_sign_and_verify_rsa_sha2_512(self):
         self._sign_and_verify_rsa("rsa-sha2-512", SIGNED_RSA_512)
 
@@ -280,10 +276,10 @@ class KeyTest(unittest.TestCase):
         pub = DSSKey(data=key.asbytes())
         self.assertTrue(pub.verify_ssh_sig(b"ice weasels", msg))
 
-    @requires_sha1_signing
     def test_generate_rsa(self):
+        # TODO: this probs needs to be larger number now
         key = RSAKey.generate(1024)
-        msg = key.sign_ssh_data(b"jerri blank")
+        msg = key.sign_ssh_data(b"jerri blank", algorithm="rsa-sha2-256")
         msg.rewind()
         self.assertTrue(key.verify_ssh_sig(b"jerri blank", msg))
 
Index: paramiko-3.5.1/tests/test_transport.py
===================================================================
--- paramiko-3.5.1.orig/tests/test_transport.py
+++ paramiko-3.5.1/tests/test_transport.py
@@ -66,11 +66,8 @@ from paramiko.message import Message
 from ._util import (
     needs_builtin,
     _support,
-    requires_sha1_signing,
     slow,
     server,
-    _disable_sha2,
-    _disable_sha1,
     TestServer as NullServer,
 )
 from ._loop import LoopSocket
@@ -1108,7 +1105,7 @@ class AlgorithmDisablingTests(unittest.T
             disabled_algorithms={
                 "ciphers": ["aes128-cbc"],
                 "macs": ["hmac-md5"],
-                "keys": ["ssh-dss"],
+                "keys": ["rsa-sha2-512"],
                 "kex": ["diffie-hellman-group14-sha256"],
             },
         )
@@ -1116,9 +1113,10 @@ class AlgorithmDisablingTests(unittest.T
         assert "aes128-cbc" not in t.preferred_ciphers
         assert "hmac-md5" in t._preferred_macs
         assert "hmac-md5" not in t.preferred_macs
-        assert "ssh-dss" in t._preferred_keys
-        assert "ssh-dss" not in t.preferred_keys
-        assert "ssh-dss-cert-v01@openssh.com" not in t.preferred_keys
+        assert "rsa-sha2-512" in t._preferred_keys
+        assert "rsa-sha2-512" not in t.preferred_keys
+        # Filtering also accounts for cert forms
+        assert "rsa-sha2-512-cert-v01@openssh.com" not in t.preferred_keys
         assert "diffie-hellman-group14-sha256" in t._preferred_kex
         assert "diffie-hellman-group14-sha256" not in t.preferred_kex
 
@@ -1128,7 +1126,7 @@ class AlgorithmDisablingTests(unittest.T
             disabled_algorithms={
                 "ciphers": ["aes128-cbc"],
                 "macs": ["hmac-md5"],
-                "keys": ["ssh-dss"],
+                "keys": ["rsa-sha2-256"],
                 "kex": ["diffie-hellman-group14-sha256"],
                 "compression": ["zlib"],
             },
@@ -1157,7 +1155,7 @@ class AlgorithmDisablingTests(unittest.T
         # included (as this message includes the full lists)
         assert "aes128-cbc" not in ciphers
         assert "hmac-md5" not in macs
-        assert "ssh-dss" not in server_keys
+        assert "rsa-sha2-256" not in server_keys
         assert "diffie-hellman-group14-sha256" not in kexen
         assert "zlib" not in compressions
 
@@ -1170,14 +1168,6 @@ class TestSHA2SignatureKeyExchange(unitt
     # are new tests in test_pkey.py which use known signature blobs to prove
     # the SHA2 family was in fact used!
 
-    @requires_sha1_signing
-    def test_base_case_ssh_rsa_still_used_as_fallback(self):
-        # Prove that ssh-rsa is used if either, or both, participants have SHA2
-        # algorithms disabled
-        for which in ("init", "client_init", "server_init"):
-            with server(**{which: _disable_sha2}) as (tc, _):
-                assert tc.host_key_type == "ssh-rsa"
-
     def test_kex_with_sha2_512(self):
         # It's the default!
         with server() as (tc, _):
@@ -1209,16 +1199,6 @@ class TestSHA2SignatureKeyExchange(unitt
             else:
                 raise err
 
-    def test_client_sha2_disabled_server_sha1_disabled_no_match(self):
-        self._incompatible_peers(
-            client_init=_disable_sha2, server_init=_disable_sha1
-        )
-
-    def test_client_sha1_disabled_server_sha2_disabled_no_match(self):
-        self._incompatible_peers(
-            client_init=_disable_sha1, server_init=_disable_sha2
-        )
-
     def test_explicit_client_hostkey_not_limited(self):
         # Be very explicit about the hostkey on BOTH ends,
         # and ensure it still ends up choosing sha2-512.
@@ -1243,7 +1223,7 @@ class TestExtInfo(unittest.TestCase):
             # data stored on Transport after hearing back from a compatible
             # server (such as ourselves in server mode)
             assert tc.server_extensions == {
-                "server-sig-algs": b"ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss"  # noqa
+                "server-sig-algs": b"ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256,ssh-dss" # noqa
             }
 
     def test_client_uses_server_sig_algs_for_pubkey_auth(self):
Index: paramiko-3.5.1/tests/pkey.py
===================================================================
--- paramiko-3.5.1.orig/tests/pkey.py
+++ paramiko-3.5.1/tests/pkey.py
@@ -207,12 +207,14 @@ class PKey_:
 
         def rsa_is_all_combos_of_cert_and_sha_type(self):
             assert RSAKey.identifiers() == [
-                "ssh-rsa",
-                "ssh-rsa-cert-v01@openssh.com",
                 "rsa-sha2-256",
                 "rsa-sha2-256-cert-v01@openssh.com",
                 "rsa-sha2-512",
                 "rsa-sha2-512-cert-v01@openssh.com",
+                # Still required for identifying keys-not-algorithms! But now
+                # they come last.
+                "ssh-rsa",
+                "ssh-rsa-cert-v01@openssh.com",
             ]
 
         def dss_is_protocol_name(self):
