diff --git a/openssh-8.7p1-minrsabits.patch b/openssh-8.7p1-minrsabits.patch
new file mode 100644
index 0000000..57019b6
--- /dev/null
+++ b/openssh-8.7p1-minrsabits.patch
@@ -0,0 +1,424 @@
+diff --git a/auth2-hostbased.c b/auth2-hostbased.c
+index 2ab222ed6..4e9437912 100644
+--- a/auth2-hostbased.c
++++ b/auth2-hostbased.c
+@@ -118,6 +118,10 @@ userauth_hostbased(struct ssh *ssh, const char *method)
+ 		    "(null)" : key->cert->signature_type);
+ 		goto done;
+ 	}
++	if ((r = sshkey_check_rsa_length(key, options.rsa_min_size)) != 0) {
++		logit("refusing %s key", sshkey_type(key));
++		goto done;
++	}
+ 
+ 	if (!authctxt->valid || authctxt->user == NULL) {
+ 		debug2_f("disabled because of invalid user");
+diff --git a/auth2-pubkey.c b/auth2-pubkey.c
+index daa756a01..68e7dea1f 100644
+--- a/auth2-pubkey.c
++++ b/auth2-pubkey.c
+@@ -172,6 +172,10 @@ userauth_pubkey(struct ssh *ssh, const char *method)
+ 		    "(null)" : key->cert->signature_type);
+ 		goto done;
+ 	}
++	if ((r = sshkey_check_rsa_length(key, options.rsa_min_size)) != 0) {
++		logit("refusing %s key", sshkey_type(key));
++		goto done;
++	}
+ 	key_s = format_key(key);
+ 	if (sshkey_is_cert(key))
+ 		ca_s = format_key(key->cert->signature_key);
+diff --git a/readconf.c b/readconf.c
+index 5b5afa8e3..5e17abd41 100644
+--- a/readconf.c
++++ b/readconf.c
+@@ -160,7 +160,7 @@ typedef enum {
+ 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
+ 	oFingerprintHash, oUpdateHostkeys, oHostbasedAcceptedAlgorithms,
+ 	oPubkeyAcceptedAlgorithms, oCASignatureAlgorithms, oProxyJump,
+-	oSecurityKeyProvider, oKnownHostsCommand,
++	oSecurityKeyProvider, oKnownHostsCommand, oRSAMinSize,
+ 	oIgnore, oIgnoredUnknownOption, oDeprecated, oUnsupported
+ } OpCodes;
+ 
+@@ -306,6 +306,7 @@ static struct {
+ 	{ "proxyjump", oProxyJump },
+ 	{ "securitykeyprovider", oSecurityKeyProvider },
+ 	{ "knownhostscommand", oKnownHostsCommand },
++	{ "rsaminsize", oRSAMinSize },
+ 
+ 	{ NULL, oBadOption }
+ };
+@@ -2162,6 +2163,10 @@ process_config_line_depth(Options *options, struct passwd *pw, const char *host,
+ 			*charptr = xstrdup(arg);
+ 		break;
+ 
++	case oRSAMinSize:
++		intptr = &options->rsa_min_size;
++		goto parse_int;
++
+ 	case oDeprecated:
+ 		debug("%s line %d: Deprecated option \"%s\"",
+ 		    filename, linenum, keyword);
+@@ -2409,6 +2414,7 @@ initialize_options(Options * options)
+ 	options->hostbased_accepted_algos = NULL;
+ 	options->pubkey_accepted_algos = NULL;
+ 	options->known_hosts_command = NULL;
++	options->rsa_min_size = -1;
+ }
+ 
+ /*
+@@ -2598,6 +2604,8 @@ fill_default_options(Options * options)
+ 	if (options->sk_provider == NULL)
+ 		options->sk_provider = xstrdup("$SSH_SK_PROVIDER");
+ #endif
++	if (options->rsa_min_size == -1)
++		options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
+ 
+ 	/* Expand KEX name lists */
+ 	all_cipher = cipher_alg_list(',', 0);
+@@ -3287,6 +3295,7 @@ dump_client_config(Options *o, const char *host)
+ 	dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts);
+ 	dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max);
+ 	dump_cfg_int(oServerAliveInterval, o->server_alive_interval);
++	dump_cfg_int(oRSAMinSize, o->rsa_min_size);
+ 
+ 	/* String options */
+ 	dump_cfg_string(oBindAddress, o->bind_address);
+diff --git a/readconf.h b/readconf.h
+index f647bd42a..29db353ab 100644
+--- a/readconf.h
++++ b/readconf.h
+@@ -176,6 +176,8 @@ typedef struct {
+ 
+ 	char   *known_hosts_command;
+ 
++	int	rsa_min_size;	/* minimum size of RSA keys */
++
+ 	char	*ignored_unknown; /* Pattern list of unknown tokens to ignore */
+ }       Options;
+ 
+diff --git a/servconf.c b/servconf.c
+index f7317a5cb..362ff5b67 100644
+--- a/servconf.c
++++ b/servconf.c
+@@ -177,6 +177,7 @@ initialize_server_options(ServerOptions *options)
+ 	options->fingerprint_hash = -1;
+ 	options->disable_forwarding = -1;
+ 	options->expose_userauth_info = -1;
++	options->rsa_min_size = -1;
+ }
+ 
+ /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
+@@ -416,6 +417,8 @@ fill_default_server_options(ServerOptions *options)
+ 		options->expose_userauth_info = 0;
+ 	if (options->sk_provider == NULL)
+ 		options->sk_provider = xstrdup("internal");
++	if (options->rsa_min_size == -1)
++		options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
+ 
+ 	assemble_algorithms(options);
+ 
+@@ -489,6 +492,7 @@ typedef enum {
+ 	sStreamLocalBindMask, sStreamLocalBindUnlink,
+ 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
+ 	sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
++	sRSAMinSize,
+ 	sDeprecated, sIgnore, sUnsupported
+ } ServerOpCodes;
+ 
+@@ -632,6 +636,7 @@ static struct {
+ 	{ "rdomain", sRDomain, SSHCFG_ALL },
+ 	{ "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
+ 	{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
++	{ "rsaminsize", sRSAMinSize, SSHCFG_ALL },
+ 	{ NULL, sBadOption, 0 }
+ };
+ 
+@@ -2377,6 +2382,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
+ 			*charptr = xstrdup(arg);
+ 		break;
+ 
++	case sRSAMinSize:
++		intptr = &options->rsa_min_size;
++		goto parse_int;
++
+ 	case sDeprecated:
+ 	case sIgnore:
+ 	case sUnsupported:
+@@ -2549,6 +2558,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
+ 	M_CP_INTOPT(rekey_limit);
+ 	M_CP_INTOPT(rekey_interval);
+ 	M_CP_INTOPT(log_level);
++	M_CP_INTOPT(rsa_min_size);
+ 
+ 	/*
+ 	 * The bind_mask is a mode_t that may be unsigned, so we can't use
+@@ -2810,6 +2820,7 @@ dump_config(ServerOptions *o)
+ 	dump_cfg_int(sMaxSessions, o->max_sessions);
+ 	dump_cfg_int(sClientAliveInterval, o->client_alive_interval);
+ 	dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max);
++	dump_cfg_int(sRSAMinSize, o->rsa_min_size);
+ 	dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask);
+ 
+ 	/* formatted integer arguments */
+diff --git a/servconf.h b/servconf.h
+index 115db1e79..2e3486906 100644
+--- a/servconf.h
++++ b/servconf.h
+@@ -227,6 +227,7 @@ typedef struct {
+ 	int	expose_userauth_info;
+ 	u_int64_t timing_secret;
+ 	char   *sk_provider;
++	int	rsa_min_size;	/* minimum size of RSA keys */
+ }       ServerOptions;
+ 
+ /* Information about the incoming connection as used by Match */
+diff --git a/ssh.c b/ssh.c
+index a926cc007..cd13fb879 100644
+--- a/ssh.c
++++ b/ssh.c
+@@ -500,14 +500,22 @@ resolve_canonicalize(char **hostp, int port)
+ }
+ 
+ /*
+- * Check the result of hostkey loading, ignoring some errors and
+- * fatal()ing for others.
++ * Check the result of hostkey loading, ignoring some errors and either
++ * discarding the key or fatal()ing for others.
+  */
+ static void
+-check_load(int r, const char *path, const char *message)
++check_load(int r, struct sshkey **k, const char *path, const char *message)
+ {
+ 	switch (r) {
+ 	case 0:
++		/* Check RSA keys size and discard if undersized */
++		if (k != NULL && *k != NULL &&
++		    (r = sshkey_check_rsa_length(*k,
++		    options.rsa_min_size)) != 0) {
++			error_r(r, "load %s \"%s\"", message, path);
++			free(*k);
++			*k = NULL;
++		}
+ 		break;
+ 	case SSH_ERR_INTERNAL_ERROR:
+ 	case SSH_ERR_ALLOC_FAIL:
+@@ -1557,12 +1565,13 @@ main(int ac, char **av)
+ 	if ((o) >= sensitive_data.nkeys) \
+ 		fatal_f("pubkey out of array bounds"); \
+ 	check_load(sshkey_load_public(p, &(sensitive_data.keys[o]), NULL), \
+-	    p, "pubkey"); \
++	    &(sensitive_data.keys[o]), p, "pubkey"); \
+ } while (0)
+ #define L_CERT(p,o) do { \
+ 	if ((o) >= sensitive_data.nkeys) \
+ 		fatal_f("cert out of array bounds"); \
+-	check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), p, "cert"); \
++	check_load(sshkey_load_cert(p, &(sensitive_data.keys[o])), \
++	    &(sensitive_data.keys[o]), p, "cert"); \
+ } while (0)
+ 
+ 		if (options.hostbased_authentication == 1) {
+@@ -2244,7 +2253,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
+ 		filename = default_client_percent_dollar_expand(cp, cinfo);
+ 		free(cp);
+ 		check_load(sshkey_load_public(filename, &public, NULL),
+-		    filename, "pubkey");
++		    &public, filename, "pubkey");
+ 		debug("identity file %s type %d", filename,
+ 		    public ? public->type : -1);
+ 		free(options.identity_files[i]);
+@@ -2263,7 +2272,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
+ 			continue;
+ 		xasprintf(&cp, "%s-cert", filename);
+ 		check_load(sshkey_load_public(cp, &public, NULL),
+-		    filename, "pubkey");
++		    &public, filename, "pubkey");
+ 		debug("identity file %s type %d", cp,
+ 		    public ? public->type : -1);
+ 		if (public == NULL) {
+@@ -2294,7 +2303,7 @@ load_public_identity_files(const struct ssh_conn_info *cinfo)
+ 		free(cp);
+ 
+ 		check_load(sshkey_load_public(filename, &public, NULL),
+-		    filename, "certificate");
++		    &public, filename, "certificate");
+ 		debug("certificate file %s type %d", filename,
+ 		    public ? public->type : -1);
+ 		free(options.certificate_files[i]);
+diff --git a/sshconnect2.c b/sshconnect2.c
+index 67f8e0309..d050c1656 100644
+--- a/sshconnect2.c
++++ b/sshconnect2.c
+@@ -91,6 +91,10 @@ static const struct ssh_conn_info *xxx_conn_info;
+ static int
+ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
+ {
++	int r;
++
++	if ((r = sshkey_check_rsa_length(hostkey, options.rsa_min_size)) != 0)
++		fatal_r(r, "Bad server host key");
+ 	if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
+ 	    xxx_conn_info) == -1)
+ 		fatal("Host key verification failed.");
+@@ -1747,6 +1751,12 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
+		close(agent_fd);
+	} else {
+ 		for (j = 0; j < idlist->nkeys; j++) {
++			if ((r = sshkey_check_rsa_length(idlist->keys[j],
++			    options.rsa_min_size)) != 0) {
++				debug_fr(r, "ignoring %s agent key",
++				    sshkey_ssh_name(idlist->keys[j]));
++				continue;
++			}
+ 			found = 0;
+ 			TAILQ_FOREACH(id, &files, next) {
+ 				/*
+diff --git a/sshd.c b/sshd.c
+index d26eb86ae..5f36905a1 100644
+--- a/sshd.c
++++ b/sshd.c
+@@ -1746,6 +1746,13 @@ main(int ac, char **av)
+ 				fatal_r(r, "Could not demote key: \"%s\"",
+ 				    options.host_key_files[i]);
+ 		}
++		if (pubkey != NULL && (r = sshkey_check_rsa_length(pubkey,
++		    options.rsa_min_size)) != 0) {
++			error_fr(r, "Host key %s", options.host_key_files[i]);
++			sshkey_free(pubkey);
++			sshkey_free(key);
++			continue;
++		}
+ 		sensitive_data.host_keys[i] = key;
+ 		sensitive_data.host_pubkeys[i] = pubkey;
+ 
+diff --git a/sshkey.c b/sshkey.c
+index 47864e6d8..8bad6bd99 100644
+--- a/sshkey.c
++++ b/sshkey.c
+@@ -2319,18 +2319,24 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
+ 	return ret;
+ }
+ 
+-#ifdef WITH_OPENSSL
+-static int
+-check_rsa_length(const RSA *rsa)
++int
++sshkey_check_rsa_length(const struct sshkey *k, int min_size)
+ {
++#ifdef WITH_OPENSSL
+ 	const BIGNUM *rsa_n;
++	int nbits;
+ 
+-	RSA_get0_key(rsa, &rsa_n, NULL, NULL);
+-	if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
++	if (k == NULL || k->rsa == NULL ||
++	    (k->type != KEY_RSA && k->type != KEY_RSA_CERT))
++		return 0;
++	RSA_get0_key(k->rsa, &rsa_n, NULL, NULL);
++	nbits = BN_num_bits(rsa_n);
++	if (nbits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
++	    (min_size > 0 && nbits < min_size))
+ 		return SSH_ERR_KEY_LENGTH;
++#endif /* WITH_OPENSSL */
+ 	return 0;
+ }
+-#endif
+ 
+ static int
+ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
+@@ -2391,7 +2397,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
+ 			goto out;
+ 		}
+ 		rsa_n = rsa_e = NULL; /* transferred */
+-		if ((ret = check_rsa_length(key->rsa)) != 0)
++		if ((ret = sshkey_check_rsa_length(key, 0)) != 0)
+ 			goto out;
+ #ifdef DEBUG_PK
+ 		RSA_print_fp(stderr, key->rsa, 8);
+@@ -3580,7 +3586,7 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
+ 			goto out;
+ 		}
+ 		rsa_p = rsa_q = NULL; /* transferred */
+-		if ((r = check_rsa_length(k->rsa)) != 0)
++		if ((r = sshkey_check_rsa_length(k, 0)) != 0)
+ 			goto out;
+ 		if ((r = ssh_rsa_complete_crt_parameters(k, rsa_iqmp)) != 0)
+ 			goto out;
+@@ -4566,7 +4572,7 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
+ 			r = SSH_ERR_LIBCRYPTO_ERROR;
+ 			goto out;
+ 		}
+-		if ((r = check_rsa_length(prv->rsa)) != 0)
++		if ((r = sshkey_check_rsa_length(prv, 0)) != 0)
+ 			goto out;
+ 	} else if (EVP_PKEY_base_id(pk) == EVP_PKEY_DSA &&
+ 	    (type == KEY_UNSPEC || type == KEY_DSA)) {
+diff --git a/sshkey.h b/sshkey.h
+index 125cadb64..52e879456 100644
+--- a/sshkey.h
++++ b/sshkey.h
+@@ -267,6 +267,7 @@ int	sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
+ int	sshkey_parse_pubkey_from_private_fileblob_type(struct sshbuf *blob,
+     int type, struct sshkey **pubkeyp);
+ 
++int sshkey_check_rsa_length(const struct sshkey *, int);
+ /* XXX should be internal, but used by ssh-keygen */
+ int ssh_rsa_complete_crt_parameters(struct sshkey *, const BIGNUM *);
+ 
+diff --git a/ssh.1 b/ssh.1
+index b4956aec..b1a40ebd 100644
+--- a/ssh.1
++++ b/ssh.1
+@@ -554,6 +554,7 @@ For full details of the options listed below, and their possible values, see
+ .It LogLevel
+ .It MACs
+ .It Match
++.It RSAMinSize
+ .It NoHostAuthenticationForLocalhost
+ .It NumberOfPasswordPrompts
+ .It PasswordAuthentication
+diff --git a/ssh_config.5 b/ssh_config.5
+index 24a46460..68771e4b 100644
+--- a/ssh_config.5
++++ b/ssh_config.5
+@@ -1322,6 +1322,10 @@ The argument to this keyword must be
+ or
+ .Cm no
+ (the default).
++.It Cm RSAMinSize
++Provides a minimal bits requirement for RSA keys when used for signature and
++verification but not for the key generation. The default value is 1024 and
++can't be reduced.
+ .It Cm NumberOfPasswordPrompts
+ Specifies the number of password prompts before giving up.
+ The argument to this keyword must be an integer.
+diff --git a/sshd_config.5 b/sshd_config.5
+index 867a747d..e08811ca 100644
+--- a/sshd_config.5
++++ b/sshd_config.5
+@@ -1266,6 +1266,10 @@ will refuse connection attempts with a probability of rate/100 (30%)
+ if there are currently start (10) unauthenticated connections.
+ The probability increases linearly and all connection attempts
+ are refused if the number of unauthenticated connections reaches full (60).
++.It Cm RSAMinSize
++Provides a minimal bits requirement for RSA keys when used for signature and
++verification but not for the key generation. The default value is 1024 and
++can't be reduced.
+ .It Cm ModuliFile
+ Specifies the
+ .Xr moduli 5
+diff --git a/sshkey.h b/sshkey.h
+index 094815e0..2bb8cb90 100644
+--- a/sshkey.h
++++ b/sshkey.h
+@@ -286,6 +286,8 @@ int	 sshkey_private_serialize_maxsign(struct sshkey *key,
+ 
+ void	 sshkey_sig_details_free(struct sshkey_sig_details *);
+ 
++int ssh_set_rsa_min_bits(int minbits);
++
+ #ifdef SSHKEY_INTERNAL
+ int ssh_rsa_sign(const struct sshkey *key,
+     u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
diff --git a/openssh.spec b/openssh.spec
index feee273..eb5904b 100644
--- a/openssh.spec
+++ b/openssh.spec
@@ -51,7 +51,7 @@
 
 # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1
 %global openssh_ver 8.7p1
-%global openssh_rel 9
+%global openssh_rel 10
 %global pam_ssh_agent_ver 0.10.4
 %global pam_ssh_agent_rel 4
 
@@ -212,6 +212,9 @@ Patch980: openssh-8.7p1-sftpscp-dir-create.patch
 # https://github.com/openssh/openssh-portable/pull/299
 # downstream only
 Patch981: openssh-8.7p1-recursive-scp.patch
+# https://github.com/djmdjm/openssh-wip/pull/13
+Patch982: openssh-8.7p1-minrsabits.patch
+
 # Minimize the use of SHA1 as a proof of possession for RSA key (#2031868)
 # upstream commits:
 # 291721bc7c840d113a49518f3fca70e86248b8e8
@@ -398,6 +401,7 @@ popd
 %patch979 -p1 -b .find-principals
 %patch980 -p1 -b .sftpdirs
 %patch981 -p1 -b .scp-sftpdirs
+%patch982 -p1 -b .minrsabits
 
 %patch200 -p1 -b .audit
 %patch201 -p1 -b .audit-race
@@ -684,6 +688,10 @@ test -f %{sysconfig_anaconda} && \
 %endif
 
 %changelog
+* Wed Jun 29 2022 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-10
+- Set minimal value of RSA key length via configuration option
+  Related: rhbz#2066882
+
 * Wed Jun 29 2022 Zoltan Fridrich <zfridric@redhat.com> - 8.7p1-9
 - Update minimize-sha1-use.patch to use upstream code
   Related: rhbz#2031868