在 Linux 上使用加密磁盘


一、前言

Windows 有属于自己的磁盘加密方案 BitLocker,macOS 也有属于自己的磁盘加密方案 FileVault,那么 Linux 有没有属于自己的磁盘加密方案呢?

当然有的,名字叫 dm-crypt。

本文将介绍 Linux 磁盘加密方案 dm-crypt 的使用方法。

⚠️ 注意
• 在操作磁盘之前,请务必确认磁盘上的重要数据已经妥善备份!


二、基础知识

2.1 设备映射器

设备映射器(Device Mapper,简称 DM)是 Linux 内核中的一种块设备映射机制,适用于 2.6 及以上版本的内核。

下面我用相对通俗的语言来解释下这个机制吧。

其实就是在系统上创建一个虚拟块设备。在对该虚拟块设备进行 I/O 操作的时候,系统会先对 I/O 的数据进行一些变换,然后再使用变换后的数据对物理块设备进行 I/O 操作。

这个变换过程对用户来说是完全透明的,用户不必理会。至于数据是怎么变换的,这个取决于相关的驱动程序,这里就不深入研究了。

LVM、软 Raid 以及接下来要讲的 dm-crypt 都使用了这个机制。

当然了,DM 是支持嵌套使用的。想要 LVM over dm-crypt?想要 dm-crypt over dm-crypt?没问题!

DM 生成的虚拟块设备在系统的 /dev/mapper/ 目录下。

2.2 dm-crypt

dm-crypt 是 Linux 内核的一个子系统,负责对块设备进行加解密操作,通常以模块的形式存在。

dm-crypt 有四种工作模式,下面做下简单介绍。

  1. LUKS(Linux Unified Key Setup):这是最常用的模式。该模式为加密块设备提供强大的密钥管理功能,身份认证方式可以基于密码或者某个文件(最大 8 MB)。每个使用该模式加密的块设备的都含有一个 LUKS 头,记录了块设备所使用的加密算法、摘要算法以及认证信息等。需要注意的是,LUKS 头一旦损毁,块设备上的数据将全部丢失。
  2. PLAIN:不提供密钥管理功能,直接使用给定的认证信息(密码或文件)通过密钥派生函数生成固定长度的密钥对块设备进行加解密操作。该模式下系统只管埋头加解密,不会验证认证信息是否正确。这样一来,只要认证信息或者配置稍微弄错了一点就会导致数据损坏,风险略高,所以一般不使用该模式。
  3. TCRYPT:支持打开 TrueCrypt/VeraCrypt 软件创建的加密块设备,并使用部分功能。TrueCrypt/VeraCrypt 是跨平台的磁盘加密软件。
  4. loop-AES:支持打开 loop-AES 软件创建的加密块设备。loop-AES 是一款很旧的 Linux 磁盘加密软件。

下文将使用 LUKS 模式来演示加密磁盘的相关操作。

由于 dm-crypt 使用了 DM 机制,因此可以自由地选择要加密整个磁盘还是仅加密某一分区。可以将解锁之后映射出来的虚拟块设备看作是一个真实的磁盘,想怎么分区就怎么分区,想用什么文件系统就用什么文件系统。

当然了,使用 DM 机制也有一个缺点,那就是块设备无法无损地开关加解密。也就是说,如果一个加了密的磁盘想要取消加密,就必须把解锁后映射出来的虚拟块设备上的数据拷到另外一个磁盘上,然后取消 DM 映射,再把数据拷回来。反过来也一样,这点应该不难理解。

说明
• 块设备可以是真实存在的(物理磁盘),也可以是虚拟的(Loop 设备)。下文统一将块设备称为磁盘。

2.3 cryptsetup

cryptsetup 是 dm-crypt 的前端程序,提供命令行接口来让用户方便地对磁盘进行加密相关的操作。

各大 Linux 发行版的包管理器都可以很方便地安装 cryptsetup。


三、操作环境

  • 操作系统:CentOS 7.8.2003
  • 磁盘:/dev/sda

四、操作方法

4.1 安装 cryptsetup

执行以下命令:

yum -y install cryptsetup

4.2 创建加密磁盘

命令用法

cryptsetup luksFormat [选项] <磁盘> [认证文件]

常用选项

选项 功能
-c <加密算法> 指定加密算法,默认为 aes-xts-plain64
默认的加密算法已经足够安全,一般不需要设置。
-h <散列算法> 指定散列算法,默认为 sha1
想要更安全的话可以设置为 sha512
-s <密钥长度(bit)> 指定加密算法使用的密钥长度,默认值取决于用了哪个加密算法。
如果用了默认的加密算法则默认值为 256,想要更安全的话可以设置为 512
-i <迭代时间(ms)> 指定密钥派生函数迭代的时间,默认为 10002000
想要更安全的话可以调高些,但不宜过高(建议不超过 50000),过高可能会对性能造成影响。

加密磁盘默认会使用基于密码的身份认证方式,并且在设置密码的时候会有一个密码复杂度检测功能,弱密码不让设置。

说明
• 如果想要关闭密码复杂度检测功能,可以在命令后面加上 -

如果需要使用基于文件的身份认证方式,可以在命令后面指定认证所用文件的路径。

例如

加密 /dev/sda,使用基于密码的身份认证方式。

执行命令:

cryptsetup luksFormat /dev/sda

运行结果:

WARNING!
========
这将覆盖 /dev/sda 上的数据,该动作不可取消。

Are you sure? (Type uppercase yes): YES ← 输入大写的 YES 然后回车来确认操作
输入 /dev/sda 的口令: ← 输入要设置的密码然后回车
确认密码: ← 再输一次密码然后回车
[[email protected]:~]# 

4.3 解锁加密磁盘

命令用法

cryptsetup luksOpen [选项] <磁盘> <DM 设备名称>

常用选项

选项 功能
--key-file <认证文件> 如果磁盘使用了基于文件的身份认证方式,可以用该参数指定认证所用文件的路径。
--allow-discards 开启 SSD TRIM 支持,但会稍稍降低安全性。

例如

解锁 /dev/sda,并将解锁后的磁盘映射到 /dev/mapper/sda-unlock

执行命令:

cryptsetup luksOpen /dev/sda sda-unlock

运行结果:

输入 /dev/sda 的口令: ← 输入密码然后回车
[[email protected]:~]# 

解锁磁盘之后就可以把 /dev/mapper/sda-unlock 当作一个普通的磁盘来用了。分区以及创建文件系统的方法这里就不再赘述了。

4.4 关闭加密磁盘

如果不想再使用加密磁盘了,可以将解锁后映射出来的虚拟块设备删掉。

说明
• 删掉虚拟块设备之前要先把上面的文件系统卸载掉。

命令用法

cryptsetup luksClose <DM 设备名称>

例如

关闭刚才解锁的 /dev/sda

执行命令:

cryptsetup luksClose sda-unlock

4.5 查看 LUKS 头信息

命令用法

cryptsetup luksDump <磁盘>

例如

查看 /dev/sda 的 LUKS 头信息。

执行命令:

cryptsetup luksDump /dev/sda

运行结果:

LUKS header information for /dev/sda

Version:        1 ← LUKS 版本
Cipher name:    aes ← 加密算法
Cipher mode:    xts-plain64 ← 加密算法的工作模式
Hash spec:      sha256 ← 散列算法
Payload offset: 4096 ← LUKS 头偏移量(这里的数字是块数,一个块为 512 Byte,换算成容量就是 2 MB。也就是说,磁盘的前 2 MB 是用来存放 LUKS 头的)
MK bits:        256 ← 主密钥长度
MK digest:      d5 e0 01 e2 19 18 b7 52 a8 eb cd e6 04 10 7c b4 0b 56 37 f3 ← 主密钥散列值
MK salt:        7a ba f5 0c ae 3f ed 85 6a b9 20 ac 20 e0 34 98 ← 生成主密钥的盐值
                05 cb 90 4e 85 5b 0d 76 92 84 a8 76 4b ea aa f1 
MK iterations:  50961 ← 密钥派生函数的迭代次数
UUID:           86894947-bdbf-47cf-a84d-6a1fd6809cc3 ← 加密盘的唯一标识符
↓ 下面是密钥槽信息,下文会详细说
Key Slot 0: ENABLED
    Iterations:             795580
    Salt:                   1c b9 39 f2 c3 cb 9b 08 51 a1 ee 7f 96 b5 c8 2c 
                            3d 2c be 12 69 96 1c 04 98 bb 3b 71 87 6c cf 7c 
    Key material offset:    8
    AF stripes:             4000
Key Slot 1: DISABLED
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

4.6 身份认证方式管理

LUKS 支持最多 8 个身份认证方式。

只要其中一个可以认证成功,磁盘就可以解锁。

身份认证方式在 LUKS 中叫做密钥槽(Key Slot),编号为 0-7。

说明
• 密钥槽编号一般无需特别关注,系统会自动管理。

密钥槽状态可通过上面讲的 cryptsetup luksDump <磁盘> 命令来查看。

4.6.1 添加身份认证方式

命令用法

cryptsetup luksAddKey [选项] <磁盘> [新的认证文件]

常用选项

选项 功能
--key-file <认证文件> 如果磁盘目前使用了基于文件的身份认证方式,可以用该参数指定认证所用文件的路径。

执行该命令默认会添加一个基于密码的身份认证方式,并且在设置密码的时候会有一个密码复杂度检测功能,弱密码不让设置。

说明
• 如果要关闭密码复杂度检测功能,可以在命令后面加上 -

如果需要添加基于文件的身份认证方式,可以在命令后面指定认证所用文件的路径。

例如

/etc/ 目录下生成一个名字为 sda.key,大小为 1 MB 的随机内容文件,然后将这个文件作为 /dev/sda 的认证方式。

执行命令:

dd if=/dev/urandom of=/etc/sda.key bs=1M count=1 && \
cryptsetup luksAddKey /dev/sda /etc/sda.key

运行结果:

记录了1+0 的读入
记录了1+0 的写出
1048576字节(1.0 MB)已复制,0.00715355 秒,147 MB/秒
输入任意已存在的口令: ← 输入密码然后回车
[[email protected]:~]# 

4.6.2 更改身份认证方式

可用于更换某个认证方式所用的密码或文件,但前提是可以使用该认证方式来解锁磁盘(记得密码或认证所用文件可访问)。

命令用法

cryptsetup luksChangeKey [选项] <磁盘> [新的认证文件]

常用选项

选项 功能
--key-file <认证文件> 如果需要修改的认证方式是基于文件的,可以用该参数指定认证所用文件的路径。

默认情况下系统会将认证方式修改为基于密码的,并且在设置密码的时候会有一个密码复杂度检测,弱密码不让设置。

说明
• 如果要关闭密码复杂度检测功能,可以在命令后面加上 -

如果需要将认证方式修改为基于文件的或者更换认证所用的文件,可以在命令后面指定认证所用文件的路径。

说明
• 如果有重复的身份认证方式(密码或认证所用文件相同),需重复执行命令。

例如

修改 /dev/sda 之前设置的密码。

执行命令:

cryptsetup luksChangeKey /dev/sda

运行结果:

输入要更改的口令: ← 输入旧密码然后回车
输入新口令: ← 输入新密码然后回车
确认密码: ← 再输一次新密码然后回车
[[email protected]:~]# 

4.6.3 删除身份认证方式

⚠️ 注意
• 需保留至少一个身份认证方式,否则磁盘将无法解锁!

以下分两种情况处理。

如果可以使用要删除的认证方式来解锁磁盘(记得密码或认证所用文件可访问):

命令用法

cryptsetup luksRemoveKey [选项] <磁盘>

常用选项

选项 功能
--key-file <认证文件> 如果需要删除的认证方式是基于文件的,可以用该参数指定认证所用文件的路径。

说明
• 如果有重复的身份认证方式(密码或认证所用文件相同),需重复执行命令。

如果无法使用要删除的认证方式来解锁磁盘(忘记密码或认证所用文件无法访问):

需要通过强制删除密钥槽的方式来移除身份认证方式。

先执行以下命令,记录一下可以解锁磁盘的认证方式属于哪个密钥槽:

命令用法

cryptsetup -v luksAddKey [选项] <磁盘>

常用选项

选项 功能
--key-file <认证文件> 如果可解锁磁盘的认证方式是基于文件的,可以用该参数指定认证所用文件的路径。

执行完命令之后输入密码(基于文件的认证方式不用),看下提示哪个密钥槽已解锁,把密钥槽编号记下来,然后按 Ctrl + C 取消操作。请记住,后续千万不要删除那个编号的密钥槽!

然后执行 cryptsetup luksDump <磁盘> 命令来查看密钥槽的状态。ENABLED 的密钥槽就是正在使用的密钥槽。

最后执行以下命令来将除上面记下编号之外的其他密钥槽删掉即可:

命令用法

cryptsetup luksKillSlot [选项] <磁盘> <密钥槽编号>

常用选项

选项 功能
--key-file <认证文件> 如果可解锁磁盘的认证方式是基于文件的,可以用该参数指定认证所用文件的路径。

例如

假设 /dev/sda 之前设置的密码忘记了,要删掉对应的密钥槽。

操作方法及结果如下:

[[email protected]:~]# cryptsetup -v luksAddKey --key-file /etc/sda.key /dev/sda ← 执行该命令确定下可解锁磁盘的认证方式属于哪一个密钥槽
密钥槽 1 已解锁。 ← 可以看到,该认证方式属于密钥槽 1。下面千万不要删除这个密钥槽!
输入密钥槽的新口令: 从终端读取口令时出错。 ← 这里按 Ctrl + C 取消操作
命令失败,代码 -1(错误或缺失的参数)。
[[email protected]:~]# cryptsetup luksDump /dev/sda ← 执行该命令查看密钥槽信息
LUKS header information for /dev/sda

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha256
Payload offset: 4096
MK bits:        256
MK digest:      d5 e0 01 e2 19 18 b7 52 a8 eb cd e6 04 10 7c b4 0b 56 37 f3 
MK salt:        7a ba f5 0c ae 3f ed 85 6a b9 20 ac 20 e0 34 98 
                05 cb 90 4e 85 5b 0d 76 92 84 a8 76 4b ea aa f1 
MK iterations:  50961
UUID:           86894947-bdbf-47cf-a84d-6a1fd6809cc3

Key Slot 0: ENABLED ← 忘记的密码正是对应这个密钥槽
    Iterations:             730206
    Salt:                   1a 98 6c 22 55 9c 27 e5 c7 5f e0 81 15 ec e3 aa 
                            43 a3 77 1f cb 27 6b 4e da 7b 11 7a ed 9d f3 07 
    Key material offset:    8
    AF stripes:             4000
Key Slot 1: ENABLED ← 刚才确定的千万不要删除的密钥槽
    Iterations:             772146
    Salt:                   16 ac 7a 38 3a f6 f8 89 ce 5f 68 67 3a 5f 0d 91 
                            b0 dd 77 99 30 c0 55 fc 13 0c d6 db 10 12 76 f8 
    Key material offset:    264
    AF stripes:             4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED
[[email protected]:~]# cryptsetup luksKillSlot --key-file /etc/sda.key /dev/sda 0 ← 执行该命令删除忘记的密码所属的密钥槽
[[email protected]:~]# cryptsetup luksDump /dev/sda ← 执行该命令查看密钥槽信息,确认下忘记的密码所属的密钥槽是否已经删除
LUKS header information for /dev/sda

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha256
Payload offset: 4096
MK bits:        256
MK digest:      d5 e0 01 e2 19 18 b7 52 a8 eb cd e6 04 10 7c b4 0b 56 37 f3 
MK salt:        7a ba f5 0c ae 3f ed 85 6a b9 20 ac 20 e0 34 98 
                05 cb 90 4e 85 5b 0d 76 92 84 a8 76 4b ea aa f1 
MK iterations:  50961
UUID:           86894947-bdbf-47cf-a84d-6a1fd6809cc3

Key Slot 0: DISABLED ← 可以看到,忘记的密码所属的密钥槽已经被删除了
Key Slot 1: ENABLED
    Iterations:             772146
    Salt:                   16 ac 7a 38 3a f6 f8 89 ce 5f 68 67 3a 5f 0d 91 
                            b0 dd 77 99 30 c0 55 fc 13 0c d6 db 10 12 76 f8 
    Key material offset:    264
    AF stripes:             4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

4.6.4 删除所有身份认证方式(快速抹掉磁盘数据)

命令用法

cryptsetup luksErase [选项] <磁盘>

常用选项

选项 功能
--key-file <认证文件> 如果磁盘目前使用了基于文件的身份认证方式,可以用该参数指定认证所用文件的路径。

⚠️ 注意
• 执行该操作之后磁盘将再也无法解锁,请谨慎操作!

4.7 设置开机自动解锁磁盘

要设置开机自动解锁磁盘有一个前提,那就是必须有一个身份认证方式是基于文件的,否则每次开机都会被要求在 Console 上输入磁盘密码才能解锁磁盘(输错三次会自动跳过解锁流程,不会导致系统无法启动)。

操作方法

请使用 vim 之类的文本编辑器打开 /etc/crypttab,然后按照以下格式配置(一行一个加密盘):

<DM 设备名称> <磁盘> <身份认证文件> luks[,<选项>][,<选项>]...

常用选项

选项 功能
discard 开启 SSD TRIM 支持,但会稍稍降低安全性。

说明
• 如果要设置每次开机都输入磁盘密码,可在 <身份认证文件> 这一列填入 -

修改完成后记得保存文件。

例如

设置开机自动用 /etc/sda.key 解锁 /dev/sda,并将解锁后的磁盘映射到 /dev/mapper/sda-unlock

操作方法:

/etc/crypttab 中添加这样一行:

sda-unlock /dev/sda /etc/sda.key luks

五、参考资料

  1. Linux 内核中的 Device Mapper 机制 – IBM
  2. 磁盘加密用户使用指南 – Fedora Project Wiki
  3. dm-crypt – ArchWiki
  4. 扫盲 dm-crypt——多功能 Linux 磁盘加密工具(兼容 TrueCrypt 和 VeraCrypt)- 编程随想的博客

发表评论

电子邮件地址不会被公开。