dm-crypt (简体中文)/Device encryption (简体中文) - ArchWiki

Jump to content
< Dm-crypt (简体中文)

翻译状态：本文是 Dm-crypt/Device encryption 的翻译。上次翻译日期：2022-07-29。如果英文版本有所更改，则您可以帮助同步翻译。

本节介绍如何从命令行手动使用 dm-crypt 来加密系统。

## 准备

在使用 cryptsetup 之前，请始终确保已加载`dm_crypt`内核模块（kernel module）。

## Cryptsetup 用法

Cryptsetup 是与 dm-crypt 交互的命令行工具，用于创建、访问和管理加密设备。该工具后来被扩展为支持依赖 Linux 内核设备映射器和加密模块（Linux kernel device-mapper and the cryptographic modules）的不同加密类型。最显著的扩展是 Linux 统一密钥设置 (LUKS) 扩展，它将 dm-crypt 所需的所有设置信息存储在磁盘本身上，并抽象分区和密钥管理以提高易用性。通过设备映射器访问的设备称为块设备。有关详细信息，请参阅静态数据加密#块设备加密（Data-at-rest encryption#Block device encryption）。

该工具的使用如下：

    # cryptsetup OPTIONS action action-specific-options device dmname

它具有选项和加密模式的编译默认值，如果在命令行上未指定其他值，则将使用这些默认值。看看

    $ cryptsetup --help

它按顺序列出了加密模式的选项、操作和默认参数。可以在手册页上找到完整的选项列表。由于需要或可选不同的参数，根据加密模式和操作，以下部分将进一步指出差异。块设备加密速度很快，但速度也很重要。由于在设置后更改块设备的加密密码很困难，因此提前检查各个参数的 dm-crypt 性能很重要：

    $ cryptsetup benchmark

可以指导在安装之前决定算法和密钥大小。如果某些 AES 密码以相当高的吞吐量表现出色，那么这些密码可能是 CPU中具有硬件支持的密码。

提示： 您可能想在学习时练习在虚拟机（virtual machine）中加密虚拟硬盘。

### Cryptsetup 密码和密钥

加密的块设备受密钥保护。密钥可能是以下两者之一：

- 密码: 参见 Security#Passwords。
- 密钥文件, 参见 #密钥文件。

两种密钥类型都有默认的最大大小：密码最多可包含 512 个字符，密钥文件最多可包含 8192KiB。

LUKS 在这一点上需要注意的一个重要区别是，该密钥用于解锁 LUKS 加密设备的主密钥，并且可以通过 root 访问权限进行更改。其他加密模式不支持在设置后更改密钥，因为它们不使用主密钥进行加密。有关详细信息，请参阅静态数据加密#块设备加密（Data-at-rest encryption#Block device encryption）。

## 使用 dm-crypt 的加密选项

Cryptsetup 支持与 dm-crypt 一起使用的不同加密操作模式：

- `--type luks` 使用默认的 LUKS 格式版本（cryptsetup<2.1.0为LUKS1，cryptsetup≥2.1.0为LUKS2）；
- `--type luks1` 用于使用 LUKS1，最常见的LUKS 版本；
- `--type luks2` 用于使用 LUKS2，LUKS的最新可用版本，允许附加扩展；
- `--type plain` 使用 dm-crypt plain模式；
- `--type loopaes` 用于 loopaes传统模式；
- `--type tcrypt` 用于TrueCrypt 兼容模式。
- `--type bitlk` 用于 BitLocker 兼容模式。请参阅cryptsetup(8) § BITLK (Windows BitLocker-compatible) EXTENSION (EXPERIMENTAL)。

可用的加密密码和散列的基本加密选项可用于所有模式，并依赖于内核加密后端功能。所有已加载并可在运行时用作选项的内容都可以通过以下方式查看：

    $ less /proc/crypto

提示： 如果列表很短，请执行`$ cryptsetup benchmark`触发加载可用模块。

下面介绍 `luks`、 `luks1`、 `luks2` 和 `plain` 模式的加密选项。请注意，表格列出了本文各个示例中使用的选项，而不是所有可用的选项。

### LUKS 模式的加密选项

在 LUKS 加密模式下设置新的 dm-crypt 设备的 cryptsetup 操作是`luksFormat`。与名称所暗示的不同，它不会格式化设备，而是设置 LUKS设备标头并使用所需的加密选项加密主密钥。

为了使用`cryptsetup --help`列出的编译默认值创建一个新的 LUKS 容器，只需执行：

    # cryptsetup luksFormat device

从 cryptsetup 2.4.0 开始，这相当于：

    # cryptsetup --type luks2 --cipher aes-xts-plain64 --hash sha256 --iter-time 2000 --key-size 256 --pbkdf argon2id --use-urandom --verify-passphrase luksFormat device

默认值与下表中更高规格的加密示例进行了比较，并附有注释：

选项 | Cryptsetup 2.1.0默认值 | 示例 | 注释
--- | --- | --- | ---
--cipher -c | `aes-xts-plain64` | `aes-xts-plain64` | 1.6.0 版 改为XTS 模式下的 AES 密码 （参阅 FAQ的第 5.16 项）。建议不要使用以前的默认值 `--cipher aes-cbc-essiv`，因为它的已知问题和针对它们的实际攻击。
--key-size -s | `256` (XTS为`512`) | `512` | 默认情况下，XTS密码使用512 位密钥大小。但是请注意，XTS将提供的密钥分成两半，因此这会导致使用 AES-256。
--hash -h | `sha256` | `sha512` | 用于密钥派生的哈希算法。版本 1.7.0 将默认值从 `sha1` 更改为 `sha256` “不是出于安全原因【而】主要是为了确保它可以在SHA1不可用的系统上运行”[1]。 以前默认的`sha1`仍然可以用于与旧版本的 cryptsetup 兼容，因为它被认为是安全的(参阅第5.20 条)。
--iter-time -i | `2000` | `5000` | 用于 PBKDF2 密码处理的毫秒数。版本 1.7.0 将默认值从 `1000` 更改为`2000`，以“尽量保持 PBKDF2 迭代计数仍然足够高，并且用户仍然可以接受。”[2]。此选项仅与设置或更改密码的 LUKS 操作相关，例如 `luksFormat` 或 `luksAddKey` 。指定 0作为参数选择编译的默认值。
--use-urandom | `--use-urandom` | `--use-random` | 选择要使用的随机数生成器（random number generator）。请注意，/dev/random 阻塞池已被删除。因此，`--use-random`标志现在等同于`--use-urandom`。
--verify-passphrase -y | Yes | - | 在 Arch Linux 中默认为 `luksFormat` 和 `luksAddKey` 启用。

LUKS 功能和选项的属性在 LUKS1(pdf) 和 LUKS2(pdf) 规范中进行了描述。

提示： 项目开发者的 devconfcz2016(pdf)演示文稿总结了 LUKS2 主要规范更新的动机。

#### 迭代时间

来自 cryptsetup FAQ§2.1 和 §3.4：

  设置密码时计算密钥槽 [……] 的解锁时间。默认为 1 秒（LUKS2 为 2 秒）。 [……]
  密码迭代计数基于时间，因此安全级别取决于创建 LUKS 容器的系统的 CPU 能力。 [……]
  如果在快速机器上设置密码，然后在慢速机器上解锁，解锁时间可能会更长。

因此，最好始终在最常访问它的机器上创建一个容器。

阅读其余部分，了解如何在需要时正确调整迭代次数。

#### 扇区大小

请参阅Advanced Format#dm-crypt.

### plain模式的加密选项

在dm-crypt plain模式下，设备上没有主密钥，因此，不需要设置它。相反，要使用的加密选项被直接用来创建加密硬盘和命名设备之间的映射。映射可以针对一个分区或一个完整的设备创建。在后一种情况下，甚至不需要一个分区表。

使用cryptsetup 的默认参数创建plain模式映射：

    # cryptsetup options open --type plain device dmname

执行它将提示输入密码，该密码应该具有非常高的熵。下面是默认参数与dm-crypt/Encrypting an entire system#Plain dm-crypt中的示例的比较。

选项 | Cryptsetup 2.1.0默认值 | 示例 | 注释
--- | --- | --- | ---
--hash -h | `ripemd160` | - | 哈希（hash）用于从密码创建密钥；它不用于密钥文件。
--cipher -c | `aes-cbc-essiv:sha256` | `aes-xts-plain64` | 密码由三部分组成：cipher-chainmode-IV generator。请参阅 Data-at-rest encryption#Ciphers and modes of operation 了解这些设置的说明，以及DMCrypt 文档了解一些可用选项。
--key-size -s | `256` | `512` | 密钥大小（以位（bit）为单位）。其大小将取决于使用的密码以及使用的链模式。 Xts 模式需要两倍于 cbc 的密钥大小。
--size -b | 目标硬盘的实际大小 | `2048` (映射设备为 512B×2048=1MiB) | 限制设备的最大大小（以 512字节扇区为单位）。
--offset -o | `0` | `0` | 从目标硬盘起始位置（the beginning of the target disk）的偏移量（以512 字节扇区为单位）开始映射。
--skip -p | `0` | `2048` (512B×2048=1MiB将被跳过) | 开始时要跳过的 512字节加密数据扇区的数量。
--key-file -d | 默认使用密码 | `/dev/sdZ` (或例如 `/boot/keyfile.enc`) | 用作密钥的设备或文件。有关详细信息，请参阅#Keyfiles[损坏的链接：无效的章节]。
--keyfile-offset | `0` | `0` | 从密钥开始的文件开头（the beginning of the file）的偏移量（以字节为单位）。从cryptsetup 1.6.7 开始支持此选项。
--keyfile-size -l | `8192kB` | - (默认应用) | 限制从密钥文件中读取的字节数。从 cryptsetup 1.6.7 开始支持此选项。

使用设备`/dev/sdX`, 上述右栏示例的结果是：

    # cryptsetup --cipher=aes-xts-plain64 --offset=0 --key-file=/dev/sdZ --key-size=512 open --type=plain /dev/sdX enc

与使用 LUKS 加密不同，上述命令在需要重新建立映射时必须完整执行，因此记住密码、哈希和密钥文件的详细信息很重要。我们现在可以检查是否已进行映射：

    # fdisk -l

现在一个条目应当存在于`/dev/mapper/enc`。

## 使用 cryptsetup 加密设备

本节介绍如何使用选项来创建新的加密块设备并手动访问它们。

警告： GRUB 对 LUKS2 的支持有限；有关详细信息，请参阅GRUB#Encrypted /boot。对 GRUB 需要解锁的分区使用 LUKS1 (`cryptsetup luksFormat --type luks1`)。

### 使用 LUKS 模式加密设备

#### 格式化 LUKS 分区

要将分区设置为加密的 LUKS分区，请执行：

    # cryptsetup luksFormat device

然后将提示您输入密码并进行验证。

有关命令行选项，请参阅#Encryption options for LUKS mode[损坏的链接：无效的章节]。

您可以通过以下方式检查结果：

    # cryptsetup luksDump device

您会注意到，dump 不仅显示密码头信息，还显示用于 LUKS 分区的密钥槽。

以下示例将使用 XTS 模式下的默认 AES 密码和有效的 256 位加密在 `/dev/sda1`上创建一个加密的根分区

    # cryptsetup -s 512 luksFormat /dev/sda1

##### 使用LUKS来格式化带有密钥文件的分区

在创建新的 LUKS 加密分区时，密钥文件可能会在创建时与分区相关联，使用：

    # cryptsetup luksFormat device /path/to/mykeyfile

有关如何生成和管理密钥文件的说明，请参阅#密钥文件。

#### 使用设备映射器解锁/映射 LUKS分区

一旦创建了 LUKS 分区，就可以解锁它们。

解锁过程将使用设备映射器将分区映射到新的设备名称。这提醒内核，`device`实际上是一个加密设备，应该通过 LUKS 使用 `/dev/mapper/dm_name` 寻址，以免覆盖加密数据。为防止意外覆盖，为了防止意外覆盖，请阅读在完成设置后备份cryptheader的可能性。

为了打开一个加密的LUKS分区，执行：

    # cryptsetup open device dm_name

然后将提示您输入密码以解锁该分区。通常，设备映射名称描述了被映射的分区的功能。例如，以下内容解锁了根 luks 分区`/dev/sda1`并将其映射到名为`root`的设备映射器：

    # cryptsetup open /dev/sda1 root

打开后，根分区设备地址将是`/dev/mapper/root` 而不是分区（例如 `/dev/sda1`）。

为了在加密层上设置 LVM，解密卷组的设备文件将类似于`/dev/mapper/root`而不是`/dev/sda1`。然后LVM 将为创建的所有逻辑卷提供附加名称如`/dev/lvmpool/root` 与`/dev/lvmpool/swap`。

为了将加密数据写入分区，必须通过设备映射名称对其进行访问。访问的第一步通常是创建文件系统。例如：

    # mkfs -t ext4 /dev/mapper/root

然后，设备`/dev/mapper/root`可以像其他分区一样被挂载(mount)。

要关闭 LUKS 容器，请卸载分区并执行以下操作：

    # cryptsetup close root

#### 使用 TPM 存储密钥

请参阅 Trusted Platform Module#Data-at-rest encryption with LUKS.

### 使用plain模式加密设备

创建和后续访问dm-crypt plain模式加密只需要使用cryptsetup `open`操作，并使用正确的参数。下面的例子展示了两个非根设备的例子，但通过将两个设备堆叠起来增加了一个quirk(即，第二个是在第一个设备内部创建的)。显然，叠加加密会使开销加倍。这里的用例只是为了演示使用cipher选项的另一个示例。

第一个映射器是使用 cryptsetup 的plain模式默认值创建的，如上文的表中左列所述

    # cryptsetup --type plain -v open /dev/sdaX plain1

    Enter passphrase:
    Command successful.

现在我们在其中添加第二个块设备，使用不同的加密参数和（可选的）偏移量，创建一个文件系统并挂载它

    # cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10  open /dev/mapper/plain1 plain2

    Enter passphrase:

    # lsblk -p

     NAME
     /dev/sda
     ├─/dev/sdaX
     │ └─/dev/mapper/plain1
     │   └─/dev/mapper/plain2
     ...

    # mkfs -t ext2 /dev/mapper/plain2
    # mount -t ext2 /dev/mapper/plain2 /mnt
    # echo "This is stacked. one passphrase per foot to shoot." > /mnt/stacked.txt

我们关闭堆栈（stack），以检查访问是否有效

    # cryptsetup close plain2
    # cryptsetup close plain1

首先，让我们尝试直接打开文件系统：

    # cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10 open /dev/sdaX plain2

    # mount -t ext2 /dev/mapper/plain2 /mnt

    mount: wrong fs type, bad option, bad superblock on /dev/mapper/plain2,
          missing codepage or helper program, or other error

为什么那行不通？因为“plain2”起始块（`10`）仍然使用“plain1”中的密码加密。它只能通过堆叠映射器访问。不过这个错误是任意的，尝试错误的口令或错误的选项也会产生同样的结果。对于 dm-crypt plain 模式，`open`操作本身不会出错。

以正确的顺序重试：

    # cryptsetup close plain2    # 上一次尝试的功能失调的映射器

    # cryptsetup --type plain open /dev/sdaX plain1

    Enter passphrase:

    # cryptsetup --type plain --cipher=serpent-xts-plain64 --hash=sha256 --key-size=256 --offset=10 open /dev/mapper/plain1 plain2

    Enter passphrase:

    # mount /dev/mapper/plain2 /mnt && cat /mnt/stacked.txt

    This is stacked. one passphrase per foot to shoot.

dm-crypt也会处理一些混合模式的叠加加密。例如，LUKS模式可以在 “plain1”映射器上叠加。当它被关闭时，它的头会在 “plain1”中被加密。

仅可用于plain模式的是选项`--shared`。有了它，可以将单个设备分割成不同的非重叠映射器。我们在下一个示例中就是这样做的，这次为“plain2”使用与 loopaes兼容的密码模式：

    # cryptsetup --type plain --offset 0 --size 1000 open /dev/sdaX plain1

    Enter passphrase:

    # cryptsetup --type plain --offset 1000 --size 1000 --shared --cipher=aes-cbc-lmk --hash=sha256 open /dev/sdaX plain2

    Enter passphrase:

    # lsblk -p

    NAME
    dev/sdaX
    ├─/dev/sdaX
    │ ├─/dev/mapper/plain1
    │ └─/dev/mapper/plain2
    ...

正如设备树所示，两者都位于同一级别，即没有堆叠，“plain2”可以单独打开。

## 专门针对LUKS的Cryptsetup操作

### 密钥管理

可以为 LUKS 分区定义附加密钥。这使用户能够创建访问密钥以进行安全的备份存储。在所谓的密钥托管中，一个密钥用于日常使用，另一个保存在托管中以获取对分区的访问权限，以防忘记日常密码或密钥文件丢失/损坏。 也可以使用不同的密钥槽向用户授予对分区的访问权限，方法是发出第二个密钥，然后再将其撤销。

一旦创建了加密分区，就会创建初始密钥槽0。（如果没有手动指定其他密钥槽）。额外的密钥槽从1到7进行编号。可以通过发出以下命令查看哪些密钥槽被使用

    # cryptsetup luksDump /dev/device

其中`device`是包含LUKS头的块设备。此命令和以下本节中的所有命令也适用于头备份文件。

#### 添加 LUKS 密钥

添加新的密钥槽是通过cryptsetup的`luksAddKey`动作完成的。为了安全起见，在输入新密钥之前，它总是会要求输入一个有效的现有密钥（"any passphrase"），对于已经解锁的设备也是如此。

    # cryptsetup luksAddKey /dev/device (/path/to/additionalkeyfile)

    Enter any passphrase:
    Enter new passphrase for key slot:
    Verify passphrase:

如果给出了`/path/to/additionalkeyfile`，cryptsetup 将为 `additionalkeyfile` 添加一个新的密钥槽。否则将提示输入两次新密码。为了使用现有的密钥文件来授权操作，`--key-file`或 `-d` 选项后跟“旧的” `keyfile`将尝试解锁所有可用的密钥文件密钥槽：

    # cryptsetup luksAddKey /dev/device (/path/to/additionalkeyfile) -d /path/to/keyfile

如果打算使用多个密钥并更改或撤销它们，则可以使用 `--key-slot`或 `-S` 选项来指定密钥槽：

    # cryptsetup luksAddKey /dev/device -S 6

    Enter any passphrase:
    Enter new passphrase for key slot:
    Verify passphrase:

    # cryptsetup luksDump /dev/sda8 | grep 'Slot 6'

    Key Slot 6: ENABLED

为了在此示例中显示关联的操作，我们决定在继续删除之前立即更改密钥：

    # cryptsetup luksChangeKey /dev/device -S 6

    Enter LUKS passphrase to be changed:
    Enter new LUKS passphrase:

#### 移除 LUKS 密钥

从头中删除密钥有三种不同的操作：

- `luksRemoveKey` 用于通过指定其密码/密钥文件来删除密钥。
- `luksKillSlot` 可用于从特定密钥槽中删除密钥（使用另一个密钥）。显然，如果您忘记了密码、丢失了密钥文件或无法访问它，这将非常有用。
- `luksErase` 用于快速删除所有活动密钥。

警告：

- 以上所有操作都可用于不可撤销地删除加密设备的最后一个活动密钥！
- 在1.6.4版本中添加了`luksErase`命令，以快速关闭对设备的访问。此操作不会提示输入有效密码！它不会擦除 LUKS 头，而是一次擦除所有密钥槽，因此，除非您拥有 LUKS 头的有效备份，否则您将无法重新获得访问权限。

对于上述警告，最好知道我们要保留的密钥是有效的。一个简单的检查是使用 `-v` 选项解锁设备，该选项将指出它占用的密钥槽：

    # cryptsetup --test-passphrase -v open /dev/device

    Enter passphrase for /dev/device:
    Key slot 1 unlocked.
    Command successful.

现在我们可以使用其密码删除在上一小节中添加的密钥：

    # cryptsetup luksRemoveKey /dev/device

    Enter LUKS passphrase to be deleted:

如果我们对两个密钥槽使用相同的密码，那么现在将擦除第一个槽。只有再次执行它才会删除第二个。

或者，我们可以指定密钥槽：

    # cryptsetup luksKillSlot /dev/device 6

    Enter any remaining LUKS passphrase:

请注意，在这两种情况下，都不需要确认。

    # cryptsetup luksDump /dev/sda8 | grep 'Slot 6'

    Key Slot 6: DISABLED

重申上面的警告：如果密钥槽 1 和 6 使用了相同的密码，那么现在两者都将消失。

### 备份与恢复

如果 LUKS 加密分区的头被破坏，您将无法解密您的数据。这就像忘记密码或损坏用于解锁分区的密钥文件一样，都是一种困境。损坏可能是由于您在稍后重新分区硬盘时自己的错误或第三方程序误解了分区表而造成的。因此，备份（LUKS 加密分区的）头并将其存储在另一个硬盘上可能是个好主意。

注意： 如果其中一个LUKS加密分区的密码被泄露，你必须在每一个加密头（cryptheader）的副本上撤销它，即使是那些你已经备份的副本。否则，使用被泄露的密码短语的备份加密头的副本可被用来确定主密钥，该主密钥可以用来解密关联的分区（甚至是实际的分区，而不仅仅是备份的版本）。另一方面，如果主密钥被泄露，您必须重新加密整个分区。有关详细信息，请参阅 LUKS FAQ。

#### 使用 cryptsetup 进行备份

Cryptsetup 的`luksHeaderBackup` 操作存储LUKS 头和密钥槽区域的二进制备份：

    # cryptsetup luksHeaderBackup /dev/device --header-backup-file /mnt/backup/file.img

其中`device`是包含LUKS 卷的分区。

你也可以将纯文本头备份到ramfs中，并在将其写入持久性存储之前用例如GPG进行加密：

    # mount --mkdir -t ramfs ramfs /root/tmp
    # cryptsetup luksHeaderBackup /dev/device --header-backup-file /root/tmp/file.img
    # gpg2 --recipient User_ID --encrypt /root/tmp/file.img
    # cp /root/tmp/file.img.gpg /mnt/backup/
    # umount /root/tmp

警告： tmpfs 可以在内存不足的情况下交换到硬盘，所以这里不推荐。

#### 使用 cryptsetup 恢复

警告： 恢复不正确的头或恢复到未加密的分区将导致数据丢失!该操作无法检查头是否实际上是该特定设备的正确头。

为了避免恢复错误的头，你可以先使用`--header` 远程连接它，来确保它真的没错。

    # cryptsetup -v --header /mnt/backup/file.img open /dev/device test

    Key slot 0 unlocked.
    Command successful.

    # mount /dev/mapper/test /mnt/test && ls /mnt/test
    # umount /mnt/test
    # cryptsetup close test

现在检查成功，可以执行恢复：

    # cryptsetup luksHeaderRestore /dev/device --header-backup-file ./mnt/backup/file.img

现在所有的密钥槽区域都被重写了；发出命令后，只有备份文件中活跃的密钥槽可用。

#### 手动备份和恢复

头（header）始终位于设备的开头，并且无需访问 cryptsetup 也可以执行备份。首先，您必须找出加密分区的有效负载偏移量：

    # cryptsetup luksDump /dev/device | grep "Payload offset"

    Payload offset:	4040

其次检查驱动器的扇区大小

    # fdisk -l /dev/device | grep "Sector size"

    Sector size (logical/physical): 512 bytes / 512 bytes

现在您知道了这些值，您可以使用简单的 dd 命令备份头：

    # dd if=/dev/device of=/path/to/file.img bs=512 count=4040

并安全存储。

然后可以使用与备份时相同的值执行还原：

    # dd if=./file.img of=/dev/device bs=512 count=4040

### 重新加密设备

This article or section needs expansion.

Reason: 使用 LUKS2（带有 16 MiB 头）的 cryptsetup 2.2 支持在线加密/解密/重新加密。[3] (Discuss in Talk:Dm-crypt (简体中文)/Device encryption (简体中文))

This article or section is out of date.

Reason: cryptsetup-reencrypt 在 cryptsetup 2.5.0 中被移除。[4] (Discuss in Talk:Dm-crypt (简体中文)/Device encryption (简体中文))

cryptsetup包具有两个重新加密选项。

cryptsetup reencrypt
  `cryptsetup`本身的参数：首选方法。目前仅限 LUKS2 设备。可以在线执行操作。支持多个并行重新加密作业。对系统故障具有弹性。有关详细信息，请参阅 cryptsetup(8)。

cryptsetup-reencrypt
  旧版工具，除 LUKS2 外，还支持 LUKS1。只能在未安装的设备上执行操作。一次单个进程。对系统故障敏感。有关详细信息，请参阅cryptsetup-reencrypt(8)。

两者都可用于将现有的未加密文件系统转换为 LUKS 加密文件系统或从设备中永久删除 LUKS 加密（使用`--decrypt`）。顾名思义，它也可用于重新加密现有的 LUKS 加密设备，但是，无法对分离的 LUKS 头或其他加密模式（例如plain模式）进行重新加密。对于重新加密，可以更改#LUKS 模式的加密选项。

重新加密的一种应用可能是在密码或#密钥文件被泄露并且不能确定（对方）没有获得 LUKS 头的副本后再次保护数据。例如，如果只有一个口令被窃取，但没有发生对设备的物理/逻辑访问，那么只需改变相应的口令/密钥即可（#密钥管理）。

警告： 始终确保有可靠的备份可用，并在使用该工具之前仔细检查您指定的选项！

下面展示了一个对未加密的文件系统分区进行加密以及对现有LUKS设备进行重新加密的例子。

#### 加密现有的未加密文件系统

提示： 如果您尝试加密现有的根分区，您可能需要创建一个将被挂载到`/boot`的单独的且未加密的引导分区（请参阅Dm-crypt/Encrypting an entire system#Preparing the boot partition）。这不是绝对必要的，但有许多优点：

- 如果`/boot` 位于加密的根分区内，系统将在机器开机时要求输入两次密码。第一次发生在引导加载程序尝试读取位于加密`/boot`中的文件时，第二次发生在内核尝试挂载加密分区 [5]。这可能不是所需的行为，可以通过使用单独且未加密的引导分区来防止。
- 如果`/boot`位于加密分区，某些系统还原应用程序（例如，timeshiftAUR）将无法运行[6]。

简而言之，如果需要，创建一个大小至少为 260 MiB 的分区。请参阅Partitioning#/boot。

LUKS 加密头始终存储在设备的起始（the beginning of the device）。由于现有文件系统通常会分配所有分区扇区，因此第一步是缩小它以给 LUKS头腾出空间。

This article or section needs expansion.

Reason: cryptsetup手册建议使用两倍于LUKS2头的大小，也就是32MB，并使用`--reduce-device-size 32M`。 (Discuss in Talk:Dm-crypt (简体中文)/Device encryption (简体中文))

默认的LUKS2头需要16兆字节。如果当前的文件系统占据了所有的可用空间，那么我们至少要缩减这么多。要将`ext4`上现有的`/dev/sdaX`文件系统缩小到目前可能的最小值:

    # umount /mnt

    # e2fsck -f /dev/sdaX

    e2fsck 1.43-WIP (18-May-2015)
    Pass 1: Checking inodes, blocks, and sizes
    ...
    /dev/sda6: 12/166320 files (0.0% non-contiguous), 28783/665062 blocks

    # resize2fs -p -M /dev/sdaX

    resize2fs 1.43-WIP (18-May-2015)
    Resizing the filesystem on /dev/sdaX to 26347 (4k) blocks.
    The filesystem on /dev/sdaX is now 26347 (4k) blocks long.

提示： 使用`-M` 缩小到最小尺寸可能需要很长时间。您可能想要计算比当前大小仅小32 MiB 的大小，而不是使用 `-M` 。
警告： 文件系统应被缩小，而底层设备（比如说一个分区）应保持其原始大小。一些图形工具（例如GParted）可能会同时调整文件系统和分区的大小，加密后可能会发生数据丢失。

现在我们使用默认密码对其进行加密，我们不必明确指定它：

    # cryptsetup reencrypt --encrypt --reduce-device-size 16M /dev/sdaX

    WARNING!

    ========

    This will overwrite data on LUKS2-temp-12345678-9012-3456-7890-123456789012.new irrevocably.

    Are you sure? (Type 'yes' in capital letters): YES
    Enter passphrase for LUKS2-temp-12345678-9012-3456-7890-123456789012.new:
    Verify passphrase:

完成后，整个`/dev/sdaX` 分区都被加密，而不仅仅是文件系统被压缩的空间。作为最后一步，我们将原来的`ext4`文件系统扩展到现在加密的分区上，再次占用所有可用的空间：

    # cryptsetup open /dev/sdaX recrypt

    Enter passphrase for /dev/sdaX:
    ...

    # resize2fs /dev/mapper/recrypt

    resize2fs 1.43-WIP (18-May-2015)
    Resizing the filesystem on /dev/mapper/recrypt to 664807 (4k) blocks.
    The filesystem on /dev/mapper/recrypt is now 664807 (4k) blocks long.

    # mount /dev/mapper/recrypt /mnt

文件系统现在可以使用了。您可能希望将其添加到您的crypttab。

提示： 如果刚刚加密了根分区，可能需要做许多加密后的调整。 参阅 Dm-crypt/Encrypting an entire system#Configuring mkinitcpio, Dm-crypt/Encrypting an entire system#Configuring the boot loader和Dm-crypt/Encrypting an entire system#Configuring GRUB.

#### 重新加密现有的 LUKS 分区

在此示例中，现有的 LUKS 设备被重新加密。

警告： 请仔细检查您是否正确指定了加密选项，并且在没有可靠备份的情况下永远不要重新加密！

为了使用现有加密选项重新加密设备，不需要指定它们：

    # cryptsetup reencrypt /dev/sdaX

注意： 对于LUKS1，我们需要使用旧版工具：

    # cryptsetup-reencrypt /dev/sdaX

使用不同的密码和/或散列重新加密设备时，会保留现有密钥。另一个用例是重新加密具有非当前加密选项的 LUKS 设备。除了上述关于正确指定选项的警告外，更改 LUKS 头的能力也可能受到其大小的限制。例如，如果设备最初使用 CBC 模式密码和 128 位密钥大小加密，则 LUKS 头将是上述`4096`个扇区大小的一半：

    # cryptsetup luksDump /dev/sdaX | grep -e "mode" -e "Payload" -e "MK bits"

    Cipher mode:   	cbc-essiv:sha256
    Payload offset:	2048
    MK bits:       	128

虽然可以升级这种设备的加密，但目前只能分两步进行。首先，使用相同的加密选项重新加密，但使用`--reduce-device-size` 选项为更大的 LUKS 头腾出更多空间。其次，使用所需的密码再次重新加密整个设备。由于这个原因以及在任何情况下都应该创建备份的事实，创建一个新的、全新的加密设备来恢复始终是更快的选择。

### 从LUKS1到LUKS2的转换和回退

cryptsetup包具有`convert`选项，用于在 LUKS1 和 LUKS2 容器类型之间进行转换。参数 `--type` 是必需的。

从 LUKS1 迁移到 LUKS2：

    # cryptsetup convert --type luks2 /dev/sdaX

注意： LUKS 头大小将为 2 MiB 而不是 16MiB。

回滚到 LUKS1（例如，从带有加密 /boot 的GRUB 启动）：

    # cryptsetup convert --type luks1 /dev/sdaX

Note: 从 LUKS2 到 LUKS1 的转换并非总是可行的。您可能会收到以下错误：

    Cannot convert to LUKS1 format - keyslot 0 is not LUKS1 compatible.

## 调整加密设备的大小

This article or section needs expansion.

Reason: 应该重写本节以更通用地介绍调整大小。也许与 Resizing LVM-on-LUKS 同步工作。 (Discuss in Talk:Dm-crypt (简体中文)/Device encryption (简体中文))

如果将使用 dm-crypt 加密的存储设备（使用dd 之类的工具）克隆到另一个更大的设备，则必须调整底层 dm-crypt 设备的大小以使用整个空间。

在此示例中，目标设备是 /dev/sdX2，将使用与分区相邻的整个可用空间：

    # cryptsetup luksOpen /dev/sdX2 sdX2
    # cryptsetup resize sdX2

然后必须调整底层文件系统的大小。

### 回送文件系统

假设一个加密的回送文件系统存储在文件`/bigsecret`中，循环到`/dev/loop0`，映射到`secret`并挂载在`/mnt/secret`上，如 dm-crypt/Encrypting a non-root file system#File container中的例子。

如果容器文件当前已映射和/或挂载，请卸载 并且/或着 关闭它：

    # umount /mnt/secret
    # cryptsetup close secret
    # losetup -d /dev/loop0

接下来，使用要添加的数据大小扩展容器文件。在本例中，文件将扩展为 1M * 1024，即1G。

警告： 一定确保使用两个`>`，而不是只有一个，否则你会覆盖文件，而不是追加到它。强烈建议在这一步骤之前做一个备份。

    # dd if=/dev/urandom bs=1M count=1024 | cat - >> /bigsecret

现在将容器映射到回送设备：

    # losetup /dev/loop0 /bigsecret
    # cryptsetup open /dev/loop0 secret

在此之后，将容器的加密部分的大小调整为容器文件的新的最大尺寸：

    # cryptsetup resize secret

最后，执行文件系统检查，如果没问题，调整它的大小（例如ext2/3/4）：

    # e2fsck -f /dev/mapper/secret
    # resize2fs /dev/mapper/secret

您现在可以再次挂载容器：

    # mount /dev/mapper/secret /mnt/secret

### 完整性保护设备

如果设备是在支持完整性的情况下格式化的 (例如 `--integrity hmac-sha256`) 并且支持块设备被缩小，则无法打开它，并出现以下错误： `device-mapper: reload ioctl on failed: Invalid argument`.

为了解决这个问题而不再次擦除设备，可以用以前的主密钥进行格式化（保持每个扇区的标签有效）。

    # cryptsetup luksDump /dev/sdX2 --dump-master-key --master-key-file=/tmp/masterkey-in-tmpfs.key
    # cryptsetup luksFormat /dev/sdX2 --type luks2 --integrity hmac-sha256 --master-key-file=/tmp/masterkey-in-tmpfs.key --integrity-no-wipe
    # rm /tmp/masterkey-in-tmpfs.key

## 密钥文件

注意： 本节描述使用明文密钥文件。如果你想加密你的密钥文件给你双重身份验证，请参阅使用 GPG 或OpenSSL 加密密钥文件了解详细信息，但仍请阅读本节。

什么是密钥文件？

密钥文件是将其数据用作解锁加密卷的密码的文件。 这意味着如果此类文件丢失或更改，则可能无法再解密该卷。

提示： 在密钥文件之外定义一个密码，以便在定义的密钥文件丢失或改变的情况下对加密卷进行备份访问。

为什么要使用密钥文件？

密钥文件有很多种。所使用的每种类型的密钥文件都有以下总结的优点和缺点:

### 密钥文件的类型

#### 密码

这是一个包含简单密码的密钥文件。这种类型的密钥文件的好处是，如果文件丢失，它包含的数据是已知的，并且很容易被加密卷的所有者记住。然而，缺点是这不会增加在初始系统启动期间输入密码的任何安全性。

示例： `1234`

注意： 包含密码短语的密钥文件中不得有换行符。一种选择是使用以下方法来创建它：

    # echo -n 'your_passphrase' > /path/to/keyfile
    # chown root:root /path/to/keyfile; chmod 400 /path/to/keyfile

如果文件包含特殊字符，例如反斜杠，而不是转义这些字符，建议直接编辑密钥文件，直接输入或粘贴密码，然后用方便的perl命令一行删除结尾换行：

    # perl -pi -e 'chomp if eof' /path/to/keyfile

#### 随机文本

这是一个包含随机字符块的密钥文件。这种类型的密钥文件的好处是它比简单的密码更能抵抗字典攻击。在这种情况下，可以利用密钥文件的另一个优势，即所用数据的长度。由于这不是要由人记住以供输入的字符串，因此创建包含数千个随机字符作为密钥的文件是微不足道的。缺点是如果此文件丢失或更改，则很可能在没有备份密码的情况下无法访问加密卷。

示例: `fjqweifj830149-57 819y4my1-38t1934yt8-91m 34co3;t8y;9p3y-`

#### 二进制

这是一个已被定义为密钥文件的二进制文件。在将文件识别为密钥文件的候选文件时，建议选择相对静态的文件，例如照片、音乐、视频剪辑。这些文件的好处是它们具有双重功能，使它们更难被识别为密钥文件。与具有大量随机文本的文本文件不同，这种密钥文件对于旁观者来说看起来就像一个普通的图像文件或音乐剪辑。缺点是如果此文件丢失或更改，则很可能在没有备份密码的情况下无法访问加密卷。此外，与随机生成的文本文件相比，理论上存在随机性损失。这是因为图像、视频和音乐在相邻数据位之间具有某种内在关系，而随机文本文件不存在这种关系。然而，这是有争议的，从未被公开利用。

示例： 图片、文本、视频……

### 使用随机字符创建密钥文件

#### 将密钥文件存储在文件系统上

密钥文件可以是任意内容和大小。

这里 dd 用于生成 2048 个随机字节的密钥文件，将其存储在文件`/etc/mykeyfile` 中：

    # dd bs=512 count=4 if=/dev/random of=/etc/mykeyfile iflag=fullblock

如果你计划将密钥文件存储在一个外部设备上，也可以轻松将输出（outputfile）更改为对应的目录：

    # dd bs=512 count=4 if=/dev/random of=/media/usbstick/mykeyfile iflag=fullblock

要拒绝除`root` 以外的其他用户的任何访问：

    # chmod 600 /etc/mykeyfile

##### 安全地覆盖存储的密钥文件

如果你把你的临时密钥文件存储在一个物理存储设备上，并想删除它，记住以后不要只是删除密钥文件，而是使用类似于

    # shred --remove --zero mykeyfile

来安全地覆盖它。对于 FAT 或 ext2等过时的文件系统，这已经足够了，而在日志文件系统、闪存硬件和其他情况下，强烈建议擦除整个设备。

#### 将密钥文件存储在 ramfs 中

另外，您可以挂载一个 ramfs 来临时存储密钥文件：

    # mount --mkdir -t ramfs ramfs /root/myramfs
    # cd /root/myramfs

优点是它驻留在 RAM 中而不是物理磁盘上，因此卸载 ramfs 后无法恢复。将密钥文件复制到另一个安全且持久的文件系统后，再次卸载 ramfs

    # umount /root/myramfs

### 配置 LUKS 以使用密钥文件

将密钥文件的密钥槽添加到 LUKS 头：

    # cryptsetup luksAddKey /dev/sda2 /etc/mykeyfile

    Enter any LUKS passphrase:
    key slot 0 unlocked.
    Command successful.

### 使用密钥文件手动解锁分区

打开 LUKS设备时使用`--key-file` 选项：

    # cryptsetup open /dev/sda2 dm_name --key-file /etc/mykeyfile

### 在启动时解锁根分区

这只是配置mkinitcpio以包括必要的模块或文件，并配置cryptkey内核参数以知道在哪里找到密钥文件。

下面介绍两种情况：

1. 使用存储在外部介质（例如 U盘）上的密钥文件
2. 使用嵌入在initramfs 中的密钥文件

#### 使用存储在外部介质上的密钥文件

##### 配置mkinitcpio

您必须将驱动器文件系统的内核模块添加到 `/etc/mkinitcpio.conf`中的 MODULES array中。例如，如果文件系统是Ext4，则添加`ext4`；如果是FAT，则添加`vfat`：

    MODULES=(vfat)

如果在启动时有关于坏的超级块和坏的码表的信息，那么你需要加载一个额外的码表模块。例如，你可能需要`nls_iso8859-1`模块来加载`iso8859-1`编码页。

重新生成 initramfs。

##### 配置内核参数

- 对于使用encrypt钩子的基于busybox的initramfs，参阅dm-crypt/System configuration#cryptkey。
- 对于使用sd-encrypt钩子的基于systemd的initramfs，参阅dm-crypt/System configuration#rd.luks.key。

#### 在 initramfs 中嵌入密钥文件

警告： 只有你通过以下方式充分保护密钥文件时，才使用嵌入式密钥文件：

- 在启动过程的早期使用某种形式的身份验证。否则会发生自动解密，完全违背块设备加密的目的。
- `/boot`已加密。否则，不同安装（包括实时环境（live environment））上的 root 可以从 initramfs 中提取您的密钥，并在没有任何其他身份验证的情况下解锁设备。

此方法允许使用一个特殊命名的密钥文件，该密钥文件将嵌入到 initramfs 中并由 `encrypt`钩子拾取以自动解锁根文件系统（ `cryptdevice` ）。在使用 GRUB 早期加密硬盘功能时应用它可能很有用，以避免在引导期间输入两个密码。

`encrypt`钩子允许用户使用`cryptkey` 内核参数指定密钥文件：在 initramfs 的情况下，语法是`rootfs:/path/to/keyfile`。请参阅dm-crypt/System configuration#cryptkey。此外，这个内核参数默认使用`/crypto_keyfile.bin`，如果 initramfs 包含具有此名称的有效密钥，将自动进行解密，无需配置`cryptkey`参数。

如果使用`sd-encrypt`而不是`encrypt`,请使用`rd.luks.key`内核参数指定密钥文件的位置：在 initramfs的情况下，语法为`/path/to/keyfile` 。请参阅 dm-crypt/System configuration#rd.luks.key。这个内核参数默认使用`/etc/cryptsetup-keys.d/name.key`（其中 `name`是在#Encrypting devices with cryptsetup[损坏的链接：无效的章节] 中用于解密的 `dm_name`），如果 initramfs 包含此路径的有效密钥，则可以省略。

生成密钥文件，为其赋予适当的权限并将其添加为 LUKS 密钥：

    # dd bs=512 count=4 if=/dev/random of=/crypto_keyfile.bin iflag=fullblock
    # chmod 600 /crypto_keyfile.bin
    # cryptsetup luksAddKey /dev/sdX# /crypto_keyfile.bin

注意： 注意：initramfs 由 mkinitcpio 生成，默认权限为`600`，因此普通用户无法通过生成的 initramfs 读取密钥文件。

在 mkinitcpio 的 FILES array中括入密钥：

    /etc/mkinitcpio.conf

    FILES=(/crypto_keyfile.bin)

最后重新生成initramfs。

在下一次重新启动时，您应该只需要输入一次容器解密密码。

(资料来源)

Category:

- Data-at-rest encryption (简体中文)

Hidden categories:

- Pages with broken section links
- Pages or sections flagged with Template:Expansion
- Pages or sections flagged with Template:Out of date