diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst
index b45dabbf69d6..f80f243e227e 100644
--- a/Documentation/crypto/userspace-if.rst
+++ b/Documentation/crypto/userspace-if.rst
@@ -131,9 +131,9 @@ from the kernel crypto API. If the buffer is too small for the message
 digest, the flag MSG_TRUNC is set by the kernel.
 
 In order to set a message digest key, the calling application must use
-the setsockopt() option of ALG_SET_KEY. If the key is not set the HMAC
-operation is performed without the initial HMAC state change caused by
-the key.
+the setsockopt() option of ALG_SET_KEY or ALG_SET_KEY_BY_KEY_SERIAL. If the
+key is not set the HMAC operation is performed without the initial HMAC state
+change caused by the key.
 
 Symmetric Cipher API
 --------------------
@@ -382,6 +382,15 @@ mentioned optname:
 
    -  the RNG cipher type to provide the seed
 
+- ALG_SET_KEY_BY_KEY_SERIAL -- Setting the key via keyring key_serial_t.
+   This operation behaves the same as ALG_SET_KEY. The decrypted
+   data is copied from a keyring key, and uses that data as the
+   key for symmetric encryption.
+
+   The passed in key_serial_t must have the KEY_(POS|USR|GRP|OTH)_SEARCH
+   permission set, otherwise -EPERM is returned. Supports key types: user,
+   logon, encrypted, and trusted.
+
 -  ALG_SET_AEAD_AUTHSIZE -- Setting the authentication tag size for
    AEAD ciphers. For a encryption operation, the authentication tag of
    the given size will be generated. For a decryption operation, the
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index e893c0f6c879..da949089def2 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -12,6 +12,8 @@
 #include <linux/crypto.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/key.h>
+#include <linux/key-type.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/net.h>
@@ -19,6 +21,10 @@
 #include <linux/sched.h>
 #include <linux/sched/signal.h>
 #include <linux/security.h>
+#include <linux/string.h>
+#include <keys/user-type.h>
+#include <keys/trusted-type.h>
+#include <keys/encrypted-type.h>
 
 struct alg_type_list {
 	const struct af_alg_type *type;
@@ -222,6 +228,136 @@ static int alg_setkey(struct sock *sk, sockptr_t ukey, unsigned int keylen)
 	return err;
 }
 
+#ifdef CONFIG_KEYS
+
+static int read_key_type_user(const struct key *key, u8 **dest, u16 *dest_len)
+{
+	const struct user_key_payload *ukp;
+
+	ukp = user_key_payload_locked(key);
+	if (IS_ERR_OR_NULL(ukp))
+		return -EKEYREVOKED;
+
+	*dest_len = key->datalen;
+	*dest = kmalloc(*dest_len, GFP_KERNEL);
+	if (!*dest)
+		return -ENOMEM;
+
+	memcpy(*dest, ukp->data, *dest_len);
+	return 0;
+}
+
+static int read_key_type_encrypted(const struct key *key, u8 **dest, u16 *dest_len)
+{
+	const struct encrypted_key_payload *ekp;
+
+	ekp = dereference_key_locked(key);
+	if (IS_ERR_OR_NULL(ekp))
+		return -EKEYREVOKED;
+
+	*dest_len = ekp->decrypted_datalen;
+	*dest = kmalloc(*dest_len, GFP_KERNEL);
+	if (!*dest)
+		return -ENOMEM;
+
+	memcpy(*dest, ekp->decrypted_data, *dest_len);
+
+	return 0;
+}
+
+static int read_key_type_trusted(const struct key *key, u8 **dest, u16 *dest_len)
+{
+	const struct trusted_key_payload *tkp;
+
+	tkp = dereference_key_locked(key);
+	if (IS_ERR_OR_NULL(tkp))
+		return -EKEYREVOKED;
+
+	*dest_len = tkp->key_len;
+	*dest = kmalloc(*dest_len, GFP_KERNEL);
+	if (!*dest)
+		return -ENOMEM;
+
+	memcpy(*dest, tkp->key, *dest_len);
+	return 0;
+}
+
+static int alg_setkey_by_key_serial(struct sock *sk, sockptr_t ukey, unsigned int keylen)
+{
+	u8 *ukey_serial;
+	int err;
+	u8 *key_data;
+	u16 key_data_len;
+	struct key *key;
+	key_ref_t key_ref;
+	key_serial_t *key_serial;
+	int (*read_key)(const struct key *key, u8 **dest, u16 *dest_len);
+
+	struct alg_sock *ask = alg_sk(sk);
+	const struct af_alg_type *type = ask->type;
+
+	ukey_serial = sock_kmalloc(sk, keylen, GFP_KERNEL);
+	if (!ukey_serial)
+		return -ENOMEM;
+
+	err = -EFAULT;
+	if (copy_from_sockptr(ukey_serial, ukey, keylen))
+		goto out;
+
+	key_serial = (key_serial_t *)ukey_serial;
+	key_ref = lookup_user_key(*key_serial, 0, KEY_NEED_SEARCH);
+	if (IS_ERR(key_ref)) {
+		err = PTR_ERR(key_ref);
+		goto out;
+	}
+
+	key = key_ref_to_ptr(key_ref);
+
+	down_read(&key->sem);
+
+	err = -ENOPROTOOPT;
+	if (!strcmp(key->type->name, "user") ||
+	    !strcmp(key->type->name, "logon")) {
+		read_key = &read_key_type_user;
+	} else if (IS_ENABLED(CONFIG_ENCRYPTED_KEYS) &&
+			   !strcmp(key->type->name, "encrypted")) {
+		read_key = &read_key_type_encrypted;
+	} else if (IS_ENABLED(CONFIG_TRUSTED_KEYS) &&
+			   !strcmp(key->type->name, "trusted")) {
+		read_key = &read_key_type_trusted;
+	} else {
+		up_read(&key->sem);
+		goto out;
+	}
+
+	err = read_key(key, &key_data, &key_data_len);
+	if (err) {
+		up_read(&key->sem);
+		kfree_sensitive(key_data);
+		goto out;
+	}
+
+	up_read(&key->sem);
+
+	err = type->setkey(ask->private, key_data, key_data_len);
+
+	kfree_sensitive(key_data);
+
+out:
+	sock_kzfree_s(sk, ukey_serial, keylen);
+
+	return err;
+}
+
+#else
+
+static inline int alg_setkey_by_key_serial(struct sock *sk, sockptr_t ukey, unsigned int keylen)
+{
+	return -ENOPROTOOPT;
+}
+
+#endif
+
 static int alg_setsockopt(struct socket *sock, int level, int optname,
 			  sockptr_t optval, unsigned int optlen)
 {
@@ -242,12 +378,16 @@ static int alg_setsockopt(struct socket *sock, int level, int optname,
 
 	switch (optname) {
 	case ALG_SET_KEY:
+	case ALG_SET_KEY_BY_KEY_SERIAL:
 		if (sock->state == SS_CONNECTED)
 			goto unlock;
 		if (!type->setkey)
 			goto unlock;
 
-		err = alg_setkey(sk, optval, optlen);
+		if (optname == ALG_SET_KEY_BY_KEY_SERIAL)
+			err = alg_setkey_by_key_serial(sk, optval, optlen);
+		else
+			err = alg_setkey(sk, optval, optlen);
 		break;
 	case ALG_SET_AEAD_AUTHSIZE:
 		if (sock->state == SS_CONNECTED)
diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h
index 578b18aab821..0824fbc026a1 100644
--- a/include/uapi/linux/if_alg.h
+++ b/include/uapi/linux/if_alg.h
@@ -52,6 +52,7 @@ struct af_alg_iv {
 #define ALG_SET_AEAD_ASSOCLEN		4
 #define ALG_SET_AEAD_AUTHSIZE		5
 #define ALG_SET_DRBG_ENTROPY		6
+#define ALG_SET_KEY_BY_KEY_SERIAL	7
 
 /* Operations */
 #define ALG_OP_DECRYPT			0
