diff --git a/playwright/e2e/crypto/device-verification.spec.ts b/playwright/e2e/crypto/device-verification.spec.ts
index 9be79452c4571d0c861cb009caa3d342f8b34abd..64846ac86dc90d7ed3b9a8055ed76b6fcf1c906d 100644
--- a/playwright/e2e/crypto/device-verification.spec.ts
+++ b/playwright/e2e/crypto/device-verification.spec.ts
@@ -169,8 +169,8 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
 
         // Fill the passphrase
         const dialog = page.locator(".mx_Dialog");
-        await dialog.locator("input").fill("new passphrase");
-        await dialog.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
+        await dialog.locator("textarea").fill("new passphrase");
+        await dialog.getByRole("button", { name: "Continue", disabled: false }).click();
 
         await page.locator(".mx_AuthPage").getByRole("button", { name: "Done" }).click();
 
@@ -190,10 +190,9 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
 
         // Fill the recovery key
         const dialog = page.locator(".mx_Dialog");
-        await dialog.getByRole("button", { name: "use your Recovery Key" }).click();
         const aliceRecoveryKey = await aliceBotClient.getRecoveryKey();
-        await dialog.locator("#mx_securityKey").fill(aliceRecoveryKey.encodedPrivateKey);
-        await dialog.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
+        await dialog.locator("textarea").fill(aliceRecoveryKey.encodedPrivateKey);
+        await dialog.getByRole("button", { name: "Continue", disabled: false }).click();
 
         await page.locator(".mx_AuthPage").getByRole("button", { name: "Done" }).click();
 
diff --git a/playwright/e2e/crypto/utils.ts b/playwright/e2e/crypto/utils.ts
index c31ccb130d609878e039a0b02b9b47c71627f66a..289b123e86e7c38abad0bf32c40c9b32ca2eb515 100644
--- a/playwright/e2e/crypto/utils.ts
+++ b/playwright/e2e/crypto/utils.ts
@@ -228,8 +228,8 @@ export async function logIntoElement(page: Page, credentials: Credentials, secur
             await useSecurityKey.click();
         }
         // Fill in the recovery key
-        await page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey);
-        await page.locator(".mx_Dialog_primary:not([disabled])", { hasText: "Continue" }).click();
+        await page.locator(".mx_Dialog").locator("textarea").fill(securityKey);
+        await page.getByRole("button", { name: "Continue", disabled: false }).click();
         await page.getByRole("button", { name: "Done" }).click();
     }
 }
@@ -263,7 +263,7 @@ export async function verifySession(app: ElementAppPage, securityKey: string) {
     const settings = await app.settings.openUserSettings("Encryption");
     await settings.getByRole("button", { name: "Verify this device" }).click();
     await app.page.getByRole("button", { name: "Verify with Recovery Key" }).click();
-    await app.page.locator(".mx_Dialog").locator('input[type="password"]').fill(securityKey);
+    await app.page.locator(".mx_Dialog").locator("textarea").fill(securityKey);
     await app.page.getByRole("button", { name: "Continue", disabled: false }).click();
     await app.page.getByRole("button", { name: "Done" }).click();
     await app.settings.closeDialog();
diff --git a/res/css/_common.pcss b/res/css/_common.pcss
index 98b66aec2b4bba2a6315e75185188b88630f7e5e..3eed8c93c603e12f670c16f68a6327b436aad12d 100644
--- a/res/css/_common.pcss
+++ b/res/css/_common.pcss
@@ -601,6 +601,7 @@ legend {
         .mx_Dialog_nonDialogButton,
         .mx_AccessibleButton,
         .mx_IdentityServerPicker button,
+        .mx_AccessSecretStorageDialog button,
         [class|="maplibregl"]
     ),
 .mx_Dialog_buttons button:not(.mx_Dialog_nonDialogButton, .mx_AccessibleButton),
diff --git a/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss b/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss
index 83b9fe96b45825114d2955c248aa55c6d49ae935..943ec3a41fe73449d125374180d203de82282e69 100644
--- a/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss
+++ b/res/css/views/dialogs/security/_AccessSecretStorageDialog.pcss
@@ -7,62 +7,14 @@ Please see LICENSE files in the repository root for full details.
 */
 
 .mx_AccessSecretStorageDialog {
-    .mx_AccessSecretStorageDialog_titleWithIcon {
-        &::before {
-            content: "";
-            display: inline-block;
-            width: 24px;
-            height: 24px;
-            margin-inline-end: $spacing-8;
-            position: relative;
-            top: 5px;
-            background-color: $primary-content;
-        }
-
-        &.mx_AccessSecretStorageDialog_resetBadge::before {
-            /* The image isn't capable of masking, so we use a background instead. */
-            background-image: url("@vector-im/compound-design-tokens/icons/error-solid.svg");
-            background-size: 24px;
-            background-color: transparent;
-        }
-
-        &.mx_AccessSecretStorageDialog_secureBackupTitle::before {
-            mask-image: url("$(res)/img/feather-customised/secure-backup.svg");
-        }
-
-        &.mx_AccessSecretStorageDialog_securePhraseTitle::before {
-            mask-image: url("$(res)/img/feather-customised/secure-phrase.svg");
-        }
+    &.mx_EncryptionCard {
+        /* override some styles that we don't need */
+        border: 0px none;
+        box-shadow: none;
+        padding: 0px;
     }
 
     .mx_AccessSecretStorageDialog_primaryContainer {
-        .mx_AccessSecretStorageDialog_passPhraseInput {
-            width: 300px;
-            border: 1px solid $accent;
-            border-radius: 5px;
-        }
-
-        .mx_AccessSecretStorageDialog_keyStatus {
-            height: 30px;
-        }
-
-        .mx_AccessSecretStorageDialog_recoveryKeyEntry {
-            display: flex;
-            align-items: center;
-
-            .mx_AccessSecretStorageDialog_recoveryKeyEntry_textInput {
-                flex-grow: 1;
-            }
-
-            .mx_AccessSecretStorageDialog_recoveryKeyEntry_entryControlSeparatorText {
-                margin: $spacing-16;
-            }
-
-            .mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput {
-                display: none;
-            }
-        }
-
         .mx_AccessSecretStorageDialog_recoveryKeyFeedback {
             &::before {
                 content: "";
@@ -76,15 +28,6 @@ Please see LICENSE files in the repository root for full details.
                 margin-inline-end: 5px;
             }
 
-            &.mx_AccessSecretStorageDialog_recoveryKeyFeedback--valid {
-                color: $accent;
-
-                &::before {
-                    mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
-                    background-color: $accent;
-                }
-            }
-
             &.mx_AccessSecretStorageDialog_recoveryKeyFeedback--invalid {
                 color: $alert;
 
@@ -94,46 +37,9 @@ Please see LICENSE files in the repository root for full details.
                 }
             }
         }
+    }
 
-        .mx_Dialog_buttons {
-            $spacingStart: $spacing-24; /* 16px icon + 8px padding */
-
-            text-align: initial;
-            display: flex;
-            flex-flow: column;
-            gap: 14px;
-
-            .mx_Dialog_buttons_additive {
-                float: none;
-
-                .mx_AccessSecretStorageDialog_reset {
-                    position: relative;
-                    padding-inline-start: $spacingStart;
-                    /* To avoid bold styling inherent with <strong> elements */
-                    font-weight: inherit;
-
-                    &::before {
-                        content: "";
-                        display: inline-block;
-                        position: absolute;
-                        height: 16px;
-                        width: 16px;
-                        left: 0;
-                        top: 2px; /* alignment */
-                        background-image: url("@vector-im/compound-design-tokens/icons/error-solid.svg");
-                        background-size: contain;
-                    }
-
-                    .mx_AccessSecretStorageDialog_reset_link {
-                        color: $alert;
-                    }
-                }
-            }
-
-            .mx_Dialog_buttons_row {
-                gap: $spacing-16; /* TODO: needs normalization */
-                padding-inline-start: $spacingStart;
-            }
-        }
+    .mx_EncryptionCard_buttons {
+        margin-top: var(--cpd-space-20x);
     }
 }
diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
index 01d7203b68c5c934a6eea4a41b66abfb2905160c..5b1fb3e142de5bfec6b86151f9c6794dc23cc29a 100644
--- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
+++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
@@ -6,27 +6,17 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
 Please see LICENSE files in the repository root for full details.
 */
 
+import { Button } from "@vector-im/compound-web";
+import LockSolidIcon from "@vector-im/compound-design-tokens/assets/web/icons/lock-solid";
 import { debounce } from "lodash";
 import classNames from "classnames";
 import React, { type ChangeEvent, type FormEvent } from "react";
-import { logger } from "matrix-js-sdk/src/logger";
-import { decodeRecoveryKey } from "matrix-js-sdk/src/crypto-api";
 import { type SecretStorage } from "matrix-js-sdk/src/matrix";
 
-import { MatrixClientPeg } from "../../../../MatrixClientPeg";
 import Field from "../../elements/Field";
-import AccessibleButton, { type ButtonEvent } from "../../elements/AccessibleButton";
 import { _t } from "../../../../languageHandler";
-import { accessSecretStorage } from "../../../../SecurityManager";
-import Modal from "../../../../Modal";
-import DialogButtons from "../../elements/DialogButtons";
-import BaseDialog from "../BaseDialog";
-import { chromeFileInputFix } from "../../../../utils/BrowserWorkarounds";
-
-// Maximum acceptable size of a key file. It's 59 characters including the spaces we encode,
-// so this should be plenty and allow for people putting extra whitespace in the file because
-// maybe that's a thing people would do?
-const KEY_FILE_MAX_SIZE = 128;
+import { EncryptionCard } from "../../settings/encryption/EncryptionCard";
+import { EncryptionCardButtons } from "../../settings/encryption/EncryptionCardButtons";
 
 // Don't shout at the user that their key is invalid every time they type a key: wait a short time
 const VALIDATION_THROTTLE_MS = 200;
@@ -34,401 +24,196 @@ const VALIDATION_THROTTLE_MS = 200;
 export type KeyParams = { passphrase?: string; recoveryKey?: string };
 
 interface IProps {
+    /**
+     * Information about the Secret Storage key that we want to get.
+     */
     keyInfo: SecretStorage.SecretStorageKeyDescription;
+    /**
+     * Callback to check whether the given key is correct.
+     */
     checkPrivateKey: (k: KeyParams) => Promise<boolean>;
+    /**
+     * Callback for when the user is done with this dialog.  `result` will
+     * contain information about the key that was entered, or will be `false` if
+     * the user cancelled.
+     */
     onFinished(result?: false | KeyParams): void;
 }
 
 interface IState {
+    //! The recovery key/phrase that the user entered
     recoveryKey: string;
-    recoveryKeyValid: boolean | null;
+    //! Is the recovery key/phrase correct?  `null` means no key/phrase has been entered
     recoveryKeyCorrect: boolean | null;
-    recoveryKeyFileError: boolean | null;
-    forceRecoveryKey: boolean;
-    passPhrase: string;
-    keyMatches: boolean | null;
-    resetting: boolean;
 }
 
 /*
  * Access Secure Secret Storage by requesting the user's passphrase.
  */
 export default class AccessSecretStorageDialog extends React.PureComponent<IProps, IState> {
-    private fileUpload = React.createRef<HTMLInputElement>();
-    private inputRef = React.createRef<HTMLInputElement>();
+    private inputRef = React.createRef<HTMLTextAreaElement>();
 
     public constructor(props: IProps) {
         super(props);
 
         this.state = {
             recoveryKey: "",
-            recoveryKeyValid: null,
             recoveryKeyCorrect: null,
-            recoveryKeyFileError: null,
-            forceRecoveryKey: false,
-            passPhrase: "",
-            keyMatches: null,
-            resetting: false,
         };
     }
 
     private onCancel = (): void => {
-        if (this.state.resetting) {
-            this.setState({ resetting: false });
-        }
         this.props.onFinished(false);
     };
 
-    private onUseRecoveryKeyClick = (): void => {
-        this.setState({
-            forceRecoveryKey: true,
-        });
-    };
-
     private validateRecoveryKeyOnChange = debounce(async (): Promise<void> => {
         await this.validateRecoveryKey(this.state.recoveryKey);
     }, VALIDATION_THROTTLE_MS);
 
-    private async validateRecoveryKey(recoveryKey: string): Promise<void> {
+    /**
+     * Checks whether the security key/phrase is correct.
+     *
+     * Sets `state.recoveryKeyCorrect` accordingly, and if the key/phrase is
+     * correct, returns a `KeyParams` structure.
+     */
+    private async validateRecoveryKey(recoveryKey: string): Promise<KeyParams | undefined> {
+        recoveryKey = recoveryKey.trim();
+
         if (recoveryKey === "") {
             this.setState({
-                recoveryKeyValid: null,
                 recoveryKeyCorrect: null,
             });
-            return;
         }
 
+        const hasPassphrase = this.props.keyInfo?.passphrase?.salt && this.props.keyInfo?.passphrase?.iterations;
+
+        // If the user has a passphrase, we want to try validating it both as a
+        // key and as a passphrase.  We first try to validate it as a key, since
+        // that check is faster.
+
         try {
-            const cli = MatrixClientPeg.safeGet();
-            const decodedKey = decodeRecoveryKey(recoveryKey);
-            const correct = await cli.secretStorage.checkKey(decodedKey, this.props.keyInfo);
-            this.setState({
-                recoveryKeyValid: true,
-                recoveryKeyCorrect: correct,
-            });
-        } catch {
-            this.setState({
-                recoveryKeyValid: false,
-                recoveryKeyCorrect: false,
-            });
+            const input = { recoveryKey };
+            const recoveryKeyCorrect = await this.props.checkPrivateKey(input);
+            if (recoveryKeyCorrect) {
+                this.setState({ recoveryKeyCorrect });
+                return input;
+            }
+        } catch {}
+
+        if (hasPassphrase) {
+            try {
+                const input = { passphrase: recoveryKey };
+                const recoveryKeyCorrect = await this.props.checkPrivateKey(input);
+                if (recoveryKeyCorrect) {
+                    this.setState({ recoveryKeyCorrect });
+                    return input;
+                }
+            } catch {}
         }
+
+        this.setState({
+            recoveryKeyCorrect: false,
+        });
     }
 
-    private onRecoveryKeyChange = (ev: ChangeEvent<HTMLInputElement>): void => {
+    private onRecoveryKeyChange = (ev: ChangeEvent<HTMLTextAreaElement>): void => {
         this.setState({
             recoveryKey: ev.target.value,
-            recoveryKeyFileError: null,
         });
 
-        // also clear the file upload control so that the user can upload the same file
-        // the did before (otherwise the onchange wouldn't fire)
-        if (this.fileUpload.current) this.fileUpload.current.value = "";
-
-        // We don't use Field's validation here because a) we want it in a separate place rather
-        // than in a tooltip and b) we want it to display feedback based on the uploaded file
-        // as well as the text box. Ideally we would refactor Field's validation logic so we could
+        // We don't use Field's validation here because we want it in a separate place rather
+        // than in a tooltip. Ideally we would refactor Field's validation logic so we could
         // re-use some of it.
         this.validateRecoveryKeyOnChange();
     };
 
-    private onRecoveryKeyFileChange = async (ev: ChangeEvent<HTMLInputElement>): Promise<void> => {
-        if (!ev.target.files?.length) return;
-
-        const f = ev.target.files[0];
-
-        if (f.size > KEY_FILE_MAX_SIZE) {
-            this.setState({
-                recoveryKeyFileError: true,
-                recoveryKeyCorrect: false,
-                recoveryKeyValid: false,
-            });
-        } else {
-            const contents = await f.text();
-            // test it's within the base58 alphabet. We could be more strict here, eg. require the
-            // right number of characters, but it's really just to make sure that what we're reading is
-            // text because we'll put it in the text field.
-            if (/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\s]+$/.test(contents)) {
-                const recoveryKey = contents.trim();
-                this.setState({
-                    recoveryKeyFileError: null,
-                    recoveryKey,
-                });
-                await this.validateRecoveryKey(recoveryKey);
-            } else {
-                this.setState({
-                    recoveryKeyFileError: true,
-                    recoveryKeyCorrect: false,
-                    recoveryKeyValid: false,
-                    recoveryKey: "",
-                });
-            }
-        }
-    };
-
-    private onRecoveryKeyFileUploadClick = (): void => {
-        this.fileUpload.current?.click();
-    };
-
-    private onPassPhraseNext = async (ev: FormEvent<HTMLFormElement> | React.MouseEvent): Promise<void> => {
-        ev.preventDefault();
-
-        if (this.state.passPhrase.length <= 0) {
-            this.inputRef.current?.focus();
-            return;
-        }
-
-        this.setState({ keyMatches: null });
-        const input = { passphrase: this.state.passPhrase };
-        const keyMatches = await this.props.checkPrivateKey(input);
-        if (keyMatches) {
-            this.props.onFinished(input);
-        } else {
-            this.setState({ keyMatches });
-            this.inputRef.current?.focus();
-        }
-    };
-
     private onRecoveryKeyNext = async (ev: FormEvent<HTMLFormElement> | React.MouseEvent): Promise<void> => {
         ev.preventDefault();
 
-        if (!this.state.recoveryKeyValid) return;
+        const keyParams = await this.validateRecoveryKey(this.state.recoveryKey);
 
-        this.setState({ keyMatches: null });
-        const input = { recoveryKey: this.state.recoveryKey };
-        const keyMatches = await this.props.checkPrivateKey(input);
-        if (keyMatches) {
-            this.props.onFinished(input);
+        if (keyParams !== undefined) {
+            this.props.onFinished(keyParams);
         } else {
-            this.setState({ keyMatches });
+            this.inputRef.current?.focus();
         }
     };
 
-    private onPassPhraseChange = (ev: ChangeEvent<HTMLInputElement>): void => {
-        this.setState({
-            passPhrase: ev.target.value,
-            keyMatches: null,
+    private getKeyValidationClasses(): string {
+        return classNames({
+            "mx_AccessSecretStorageDialog_recoveryKeyFeedback": this.state.recoveryKeyCorrect !== null,
+            "mx_AccessSecretStorageDialog_recoveryKeyFeedback--invalid": this.state.recoveryKeyCorrect === false,
         });
-    };
-
-    private onResetAllClick = (ev: ButtonEvent): void => {
-        ev.preventDefault();
-        this.setState({ resetting: true });
-    };
-
-    private onConfirmResetAllClick = async (): Promise<void> => {
-        // Hide ourselves so the user can interact with the reset dialogs.
-        // We don't conclude the promise chain (onFinished) yet to avoid confusing
-        // any upstream code flows.
-        //
-        // Note: this will unmount us, so don't call `setState` or anything in the
-        // rest of this function.
-        Modal.toggleCurrentDialogVisibility();
+    }
 
-        try {
-            // Force reset secret storage (which resets the key backup)
-            await accessSecretStorage(
-                async (): Promise<void> => {
-                    // Now we can indicate that the user is done pressing buttons, finally.
-                    // Upstream flows will detect the new secret storage, key backup, etc and use it.
-                    this.props.onFinished({});
-                },
-                { forceReset: true, resetCrossSigning: true },
-            );
-        } catch (e) {
-            logger.error(e);
-            this.props.onFinished(false);
+    private getKeyValidationText(): string | null {
+        if (this.state.recoveryKeyCorrect) {
+            return null;
+        } else if (this.state.recoveryKeyCorrect === null) {
+            return _t("encryption|access_secret_storage_dialog|alternatives");
+        } else {
+            return _t("encryption|access_secret_storage_dialog|key_validation_text|wrong_security_key");
         }
-    };
+    }
 
-    private getKeyValidationText(): string {
-        if (this.state.recoveryKeyFileError) {
-            return _t("encryption|access_secret_storage_dialog|key_validation_text|wrong_file_type");
-        } else if (this.state.recoveryKeyCorrect) {
-            return _t("encryption|access_secret_storage_dialog|key_validation_text|recovery_key_is_correct");
-        } else if (this.state.recoveryKeyValid) {
-            return _t("encryption|access_secret_storage_dialog|key_validation_text|wrong_security_key");
-        } else if (this.state.recoveryKeyValid === null) {
-            return "";
+    private getRecoveryKeyFeedback(): React.ReactNode | null {
+        const validationText = this.getKeyValidationText();
+        if (validationText === null) {
+            return null;
         } else {
-            return _t("encryption|access_secret_storage_dialog|key_validation_text|invalid_security_key");
+            return <div className={this.getKeyValidationClasses()}>{validationText}</div>;
         }
     }
 
     public render(): React.ReactNode {
-        const hasPassphrase = this.props.keyInfo?.passphrase?.salt && this.props.keyInfo?.passphrase?.iterations;
-
-        const resetLine = (
-            <strong className="mx_AccessSecretStorageDialog_reset">
-                {_t("encryption|reset_all_button", undefined, {
-                    a: (sub) => (
-                        <AccessibleButton
-                            kind="link_inline"
-                            onClick={this.onResetAllClick}
-                            className="mx_AccessSecretStorageDialog_reset_link"
-                        >
-                            {sub}
-                        </AccessibleButton>
-                    ),
-                })}
-            </strong>
-        );
-
-        let content;
-        let title;
-        let titleClass;
-        if (this.state.resetting) {
-            title = _t("encryption|access_secret_storage_dialog|reset_title");
-            titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_resetBadge"];
-            content = (
-                <div>
-                    <p>{_t("encryption|access_secret_storage_dialog|reset_warning_1")}</p>
-                    <p>{_t("encryption|access_secret_storage_dialog|reset_warning_2")}</p>
-                    <DialogButtons
-                        primaryButton={_t("action|reset")}
-                        onPrimaryButtonClick={this.onConfirmResetAllClick}
-                        hasCancel={true}
-                        onCancel={this.onCancel}
-                        focus={false}
-                        primaryButtonClass="danger"
-                    />
-                </div>
-            );
-        } else if (hasPassphrase && !this.state.forceRecoveryKey) {
-            title = _t("encryption|access_secret_storage_dialog|security_phrase_title");
-            titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_securePhraseTitle"];
-
-            let keyStatus;
-            if (this.state.keyMatches === false) {
-                keyStatus = (
-                    <div className="mx_AccessSecretStorageDialog_keyStatus">
-                        {"\uD83D\uDC4E "}
-                        {_t("encryption|access_secret_storage_dialog|security_phrase_incorrect_error")}
-                    </div>
-                );
-            } else {
-                keyStatus = <div className="mx_AccessSecretStorageDialog_keyStatus" />;
-            }
-
-            content = (
-                <div>
-                    <p>
-                        {_t(
-                            "encryption|access_secret_storage_dialog|enter_phrase_or_key_prompt",
-                            {},
-                            {
-                                button: (s) => (
-                                    <AccessibleButton kind="link_inline" onClick={this.onUseRecoveryKeyClick}>
-                                        {s}
-                                    </AccessibleButton>
-                                ),
-                            },
-                        )}
-                    </p>
-
-                    <form className="mx_AccessSecretStorageDialog_primaryContainer" onSubmit={this.onPassPhraseNext}>
+        const title = _t("encryption|access_secret_storage_dialog|security_key_title");
+
+        const recoveryKeyFeedback = this.getRecoveryKeyFeedback();
+        const content = (
+            <div>
+                <form
+                    className="mx_AccessSecretStorageDialog_primaryContainer"
+                    onSubmit={this.onRecoveryKeyNext}
+                    spellCheck={false}
+                    autoComplete="off"
+                >
+                    <div className="mx_AccessSecretStorageDialog_recoveryKeyEntry">
                         <Field
                             inputRef={this.inputRef}
-                            id="mx_passPhraseInput"
-                            className="mx_AccessSecretStorageDialog_passPhraseInput"
-                            type="password"
-                            label={_t("encryption|access_secret_storage_dialog|security_phrase_title")}
-                            value={this.state.passPhrase}
-                            onChange={this.onPassPhraseChange}
+                            element="textarea"
+                            rows={2}
+                            cols={45}
+                            id="mx_securityKey"
+                            label={_t("encryption|access_secret_storage_dialog|security_key_title")}
+                            value={this.state.recoveryKey}
+                            onChange={this.onRecoveryKeyChange}
                             autoFocus={true}
-                            autoComplete="new-password"
-                        />
-                        {keyStatus}
-                        <DialogButtons
-                            primaryButton={_t("action|continue")}
-                            onPrimaryButtonClick={this.onPassPhraseNext}
-                            hasCancel={true}
-                            onCancel={this.onCancel}
-                            focus={false}
-                            primaryDisabled={this.state.passPhrase.length === 0}
-                            additive={resetLine}
+                            forceValidity={this.state.recoveryKeyCorrect ?? undefined}
+                            autoComplete="off"
                         />
-                    </form>
-                </div>
-            );
-        } else {
-            title = _t("encryption|access_secret_storage_dialog|security_key_title");
-            titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_secureBackupTitle"];
-
-            const feedbackClasses = classNames({
-                "mx_AccessSecretStorageDialog_recoveryKeyFeedback": true,
-                "mx_AccessSecretStorageDialog_recoveryKeyFeedback--valid": this.state.recoveryKeyCorrect === true,
-                "mx_AccessSecretStorageDialog_recoveryKeyFeedback--invalid": this.state.recoveryKeyCorrect === false,
-            });
-            const recoveryKeyFeedback = <div className={feedbackClasses}>{this.getKeyValidationText()}</div>;
-
-            content = (
-                <div>
-                    <p>{_t("encryption|access_secret_storage_dialog|use_security_key_prompt")}</p>
-
-                    <form
-                        className="mx_AccessSecretStorageDialog_primaryContainer"
-                        onSubmit={this.onRecoveryKeyNext}
-                        spellCheck={false}
-                        autoComplete="off"
-                    >
-                        <div className="mx_AccessSecretStorageDialog_recoveryKeyEntry">
-                            <div className="mx_AccessSecretStorageDialog_recoveryKeyEntry_textInput">
-                                <Field
-                                    type="password"
-                                    id="mx_securityKey"
-                                    label={_t("encryption|access_secret_storage_dialog|security_key_title")}
-                                    value={this.state.recoveryKey}
-                                    onChange={this.onRecoveryKeyChange}
-                                    autoFocus={true}
-                                    forceValidity={this.state.recoveryKeyCorrect ?? undefined}
-                                    autoComplete="off"
-                                />
-                            </div>
-                            <span className="mx_AccessSecretStorageDialog_recoveryKeyEntry_entryControlSeparatorText">
-                                {_t("encryption|access_secret_storage_dialog|separator", {
-                                    recoveryFile: "",
-                                    securityKey: "",
-                                })}
-                            </span>
-                            <div>
-                                <input
-                                    type="file"
-                                    className="mx_AccessSecretStorageDialog_recoveryKeyEntry_fileInput"
-                                    ref={this.fileUpload}
-                                    onClick={chromeFileInputFix}
-                                    onChange={this.onRecoveryKeyFileChange}
-                                />
-                                <AccessibleButton kind="primary" onClick={this.onRecoveryKeyFileUploadClick}>
-                                    {_t("action|upload")}
-                                </AccessibleButton>
-                            </div>
-                        </div>
-                        {recoveryKeyFeedback}
-                        <DialogButtons
-                            primaryButton={_t("action|continue")}
-                            onPrimaryButtonClick={this.onRecoveryKeyNext}
-                            hasCancel={true}
-                            cancelButton={_t("action|go_back")}
-                            cancelButtonClass="warning"
-                            onCancel={this.onCancel}
-                            focus={false}
-                            primaryDisabled={!this.state.recoveryKeyValid}
-                            additive={resetLine}
-                        />
-                    </form>
-                </div>
-            );
-        }
+                    </div>
+                    {recoveryKeyFeedback}
+                    <EncryptionCardButtons>
+                        <Button disabled={!this.state.recoveryKeyCorrect} onClick={this.onRecoveryKeyNext}>
+                            {_t("action|continue")}
+                        </Button>
+                        <Button kind="tertiary" onClick={this.onCancel}>
+                            {_t("action|cancel")}
+                        </Button>
+                    </EncryptionCardButtons>
+                </form>
+            </div>
+        );
 
         return (
-            <BaseDialog
+            <EncryptionCard
+                Icon={LockSolidIcon}
                 className="mx_AccessSecretStorageDialog"
-                onFinished={this.props.onFinished}
                 title={title}
-                titleClass={titleClass}
+                description={_t("encryption|access_secret_storage_dialog|privacy_warning")}
             >
-                <div>{content}</div>
-            </BaseDialog>
+                {content}
+            </EncryptionCard>
         );
     }
 }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 9adb5bc14a354feb038dff277ce0262740437178..d848f839a53ea063bc57d958ca6a352ffa3afdfc 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -911,22 +911,13 @@
     "empty_room_was_name": "Empty room (was %(oldName)s)",
     "encryption": {
         "access_secret_storage_dialog": {
-            "enter_phrase_or_key_prompt": "Enter your Security Phrase or <button>use your Recovery Key</button> to continue.",
+            "alternatives": "If you have a security key or security phrase, this will work too.",
             "key_validation_text": {
-                "invalid_security_key": "Invalid Recovery Key",
-                "recovery_key_is_correct": "Looks good!",
-                "wrong_file_type": "Wrong file type",
-                "wrong_security_key": "Wrong Recovery Key"
+                "wrong_security_key": "The recovery key you entered is not correct."
             },
-            "reset_title": "Reset everything",
-            "reset_warning_1": "Only do this if you have no other device to complete verification with.",
-            "reset_warning_2": "If you reset everything, you will restart with no trusted sessions, no trusted users, and might not be able to see past messages.",
+            "privacy_warning": "Make sure nobody can see this screen!",
             "restoring": "Restoring keys from backup",
-            "security_key_title": "Recovery Key",
-            "security_phrase_incorrect_error": "Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
-            "security_phrase_title": "Security Phrase",
-            "separator": "%(securityKey)s or %(recoveryFile)s",
-            "use_security_key_prompt": "Use your Recovery Key to continue."
+            "security_key_title": "Recovery key"
         },
         "bootstrap_title": "Setting up keys",
         "cancel_entering_passphrase_description": "Are you sure you want to cancel entering passphrase?",
diff --git a/test/unit-tests/components/views/dialogs/AccessSecretStorageDialog-test.tsx b/test/unit-tests/components/views/dialogs/AccessSecretStorageDialog-test.tsx
index e0320b290861fb07863decc1502e8842999c3d3c..2d597f8e18f7f869f7fec8bc951fbe34aabcbed8 100644
--- a/test/unit-tests/components/views/dialogs/AccessSecretStorageDialog-test.tsx
+++ b/test/unit-tests/components/views/dialogs/AccessSecretStorageDialog-test.tsx
@@ -29,9 +29,9 @@ describe("AccessSecretStorageDialog", () => {
         render(<AccessSecretStorageDialog {...defaultProps} {...props} />);
     };
 
-    const enterRecoveryKey = (placeholder = "Recovery Key"): void => {
+    const enterRecoveryKey = (): void => {
         act(() => {
-            fireEvent.change(screen.getByPlaceholderText(placeholder), {
+            fireEvent.change(screen.getByRole("textbox"), {
                 target: {
                     value: recoveryKey,
                 },
@@ -67,19 +67,19 @@ describe("AccessSecretStorageDialog", () => {
         renderComponent({ onFinished, checkPrivateKey });
 
         // check that the input field is focused
-        expect(screen.getByPlaceholderText("Recovery Key")).toHaveFocus();
+        expect(screen.getByRole("textbox")).toHaveFocus();
 
         await enterRecoveryKey();
         await submitDialog();
 
-        expect(screen.getByText("Looks good!")).toBeInTheDocument();
+        expect(screen.getByText("Continue")).not.toHaveAttribute("aria-disabled", "true");
         expect(checkPrivateKey).toHaveBeenCalledWith({ recoveryKey });
         expect(onFinished).toHaveBeenCalledWith({ recoveryKey });
     });
 
     it("Notifies the user if they input an invalid Recovery Key", async () => {
         const onFinished = jest.fn();
-        const checkPrivateKey = jest.fn().mockResolvedValue(true);
+        const checkPrivateKey = jest.fn().mockResolvedValue(false);
         renderComponent({ onFinished, checkPrivateKey });
 
         jest.spyOn(mockClient.secretStorage, "checkKey").mockImplementation(() => {
@@ -89,8 +89,8 @@ describe("AccessSecretStorageDialog", () => {
         await enterRecoveryKey();
         await submitDialog();
 
-        expect(screen.getByText("Continue")).toBeDisabled();
-        expect(screen.getByText("Invalid Recovery Key")).toBeInTheDocument();
+        expect(screen.getByText("The recovery key you entered is not correct.")).toBeInTheDocument();
+        expect(screen.getByText("Continue")).toHaveAttribute("aria-disabled", "true");
     });
 
     it("Notifies the user if they input an invalid passphrase", async function () {
@@ -110,46 +110,10 @@ describe("AccessSecretStorageDialog", () => {
         const checkPrivateKey = jest.fn().mockResolvedValue(false);
         renderComponent({ checkPrivateKey, keyInfo });
 
-        await enterRecoveryKey("Security Phrase");
-        expect(screen.getByPlaceholderText("Security Phrase")).toHaveValue(recoveryKey);
-        await submitDialog();
-
-        await expect(
-            screen.findByText(
-                "👎 Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
-            ),
-        ).resolves.toBeInTheDocument();
-
-        expect(screen.getByPlaceholderText("Security Phrase")).toHaveFocus();
-    });
-
-    it("Can reset secret storage", async () => {
-        jest.spyOn(mockClient.secretStorage, "checkKey").mockResolvedValue(true);
-
-        const onFinished = jest.fn();
-        const checkPrivateKey = jest.fn().mockResolvedValue(true);
-        renderComponent({ onFinished, checkPrivateKey });
-
-        await userEvent.click(screen.getByText("Reset all"), { delay: null });
-
-        // It will prompt the user to confirm resetting
-        expect(screen.getByText("Reset everything")).toBeInTheDocument();
-        await userEvent.click(screen.getByText("Reset"), { delay: null });
-
-        // Then it will prompt the user to create a key/passphrase
-        await screen.findByText("Set up Secure Backup");
-        document.execCommand = jest.fn().mockReturnValue(true);
-        jest.spyOn(mockClient.getCrypto()!, "createRecoveryKeyFromPassphrase").mockResolvedValue({
-            privateKey: new Uint8Array(),
-            encodedPrivateKey: recoveryKey,
-        });
-        screen.getByRole("button", { name: "Continue" }).click();
-
-        await screen.findByText(/Save your Recovery Key/);
-        screen.getByRole("button", { name: "Copy" }).click();
-        await screen.findByText("Copied!");
-        screen.getByRole("button", { name: "Continue" }).click();
+        await enterRecoveryKey();
+        expect(screen.getByRole("textbox")).toHaveValue(recoveryKey);
 
-        await screen.findByText("Secure Backup successful");
+        await expect(screen.findByText("The recovery key you entered is not correct.")).resolves.toBeInTheDocument();
+        expect(screen.getByText("Continue")).toHaveAttribute("aria-disabled", "true");
     });
 });