使用cryptset保护所有数据

cryptsetup可以进行整个磁盘的加密,对于磁盘而言,除非是在线已经挂载好了,在可读写的时候数据可以被复制走,一旦发生断电,重启,断开,除非使用密钥等挂载,都无法正常读取出数据。 想象一下下面的情景:

你的电脑被物理改变了所有权, 这个一般都会发生断电操作,当重新恢复供电,系统启动后,没有密钥或者密码,将无法读取数据。 如果你不幸被胁迫了,需要配合挂载这些硬盘,而cryptsetup允许将磁盘配置的头文件单独存放,也允许将真实的数据从磁盘的某个位置开始存放。

这样,你可以在真实的数据之外,存放一个虚假的数据。当被需要配合的时候,可以加载虚假的数据,从而保护自己的人身安全。

如果虚假的数据在真实的数据之前,在此基础上不断的追加,终会覆盖掉真实的数据,这样谁也找不会真实的数据了。

最好的密码保护就是自己也不知道密码是什么。幸运的是,cryptsetup支持使用密钥文件。

配合上面的情景,如果我们使用密钥文件指定某个偏移位置开始来作为真实的密钥文件,整个密钥文件作为虚假位置的密钥文件的话,从而达到不需要记住密码,隐藏真实密钥。不知道密钥偏移,就无法去解密数据。

所以,最后所要记住的可能就仅仅是密钥偏移了。

问我要密码,没有密码,要密钥文件,给你。要偏移,没有偏移(窃喜)。要数据,没门。。。。 如果不幸,密钥文件中的某个位置更改了,很不幸,所有数据丢失了。 如果将密钥文件和磁盘头文件合并成一个,不知道密钥文件大小,偏移,想要这个数据就非常困难了。

总之,你可以不断的组合上面的逻辑,对你的数据进行全面的保护。多一层保护,多一层安全。

但cryptset的命令真的很长。..我写了个简单的命令行来缩短下。

#!/bin/bash
KEYFILE=""
HEADFILE=""
KEYFILEOFFSET=0
KEYFILESIZE=512
DEVICE=""
S_POS=0
PAYLOAD=0
ACTION=""
CIPHER="serpent-xts-plain64"
CRYPTSETUP="cryptsetup"
EXTEND=""
BLOCKSIZE=4194304
MOUNT=""
N=1
DEVICENAME=""

function convert() {
local ret=`echo $1 | awk '/[0-9]$/{print $1;next};/[gG]$/{printf "%u\n", $1*(1024*1024*1024);next};/[mM]$/{printf "%u\n", $1*(1024*1024);next};/[kK]$/{printf "%u\n", $1*1024;next}' `
echo "$ret"
}

function get_args() {
  while getopts "k:h:d:p:s:c:eb:n:m:" arg
    do 
      case $arg in
        k)
        KEYFILE=$OPTARG
        ;;
    h)
      HEADFILE=$OPTARG
      ;;
    d)
      DEVICE=$OPTARG
      ;;
    p)
      PAYLOAD=$(($(convert $OPTARG) / 512))
      ;;
    s)
      S_POS=$(convert $OPTARG)
      ;;
    c)
      CIPHER=$OPTARG
      ;;
    e)
      EXTEND="extend"
      ;;
    b)
      BLOCKSIZE=$(convert $OPTARG)
      ;;
    n)
      N=$OPTARG
      ;;
    m)	
      DEVICENAME=$OPTARG
      ;;
    ?)
      echo "unknow argument"
      exit 1
      ;;
    esac
      done
}


function open() {
  dname=${DEVICENAME:-`basename $DEVICE`}
  $CRYPTSETUP open --keyfile-size $KEYSIZE  --key-file $KEYFILE --keyfile-offset $KEYOFFSET --header $HEADFILE $DEVICE $dname
}

function close() {
  dname=${DEVICENAME:-`basename $DEVICE`}
  $CRYPTSETUP close $dname
}

function format() {
  $CRYPTSETUP luksFormat --cipher $CIPHER --key-size 512 --keyfile-size $KEYSIZE --hash sha512  --key-file $KEYFILE --keyfile-offset $KEYOFFSET --header $HEADFILE --align-payload $PAYLOAD $DEVICE
}

function dump() {
  $CRYPTSETUP luksDump --keyfile-size $KEYSIZE  --key-file $KEYFILE --keyfile-offset $KEYOFFSET $HEADFILE 
}

function extend() {
  dd if=$KEYFILE of=/dev/shm/`basename $KEYFILE` bs=$BLOCKSIZE count=1 skip=1
    HEADFILE=/dev/shm/`basename $KEYFILE`
}

function quit() {
return 0
}

function extendquit() {
rm -rf /dev/shm/`basename $KEYFILE`
}


ACTION=$1
shift
get_args $@
$EXTEND
echo $KEYFILE
echo $HEADFILE
KEYOFFSET=$((`od $KEYFILE -N $N -tu4 -j $S_POS | grep [^0$N] | awk '{print $2}'` * 8))
KEYSIZE=$((BLOCKSIZE - KEYOFFSET))

$ACTION

${EXTEND}quit

脚本的简单用法:

格式化

./cryptsetup.sh format -d disk.img -k keyfile -s 5 -p 0 -h header
参数 含义
-d 目标磁盘,可以为磁盘镜像文件
-k 密钥文件
-s 密钥偏移的位置
-p 数据在磁盘上的偏移位置,为字节数字
-h 用于磁盘头文件保存的文件位置

一般情况下,keyfile文件和header文件都是通过dd if=/dev/urandom生成的随机内容,这样真实的header数据被写入后,将不会出现明显的特征。 -s 这个数字首先从keyfile中读取一个字节的数字,然后将这个数字*8作为真实密钥文件的起始偏移。

一般情况下, 做完上述步骤后,可以将keyfile和header连成一个文件。隐藏header文件和keyfile文件

cat keyfile header > disk

打开

./cryptsetup.sh open -d disk.img -k disk -s 5 -m test -e
参数 含义
-m mapper名称, 之后可以从/dev/mapper/参数的位置去挂载
-e 在打开之前需要进行的额外操作

如果将密钥文件和磁盘头文件合并成一个,则必须要使用-e, 这个在操作之前,将会先进行文件分割,割离出正确的文件,然后才能进行后续的操作。因此,format一定不要带这个参数

之后,可以通过/dev/mapper/挂载位置来进行后续的磁盘操作,比如格式化,挂载,读取写入等。

关闭

./cryptsetup.sh close -d disk.img -k disk -s 5 -m test -e

关闭之前如果进行了mount记得先umount

查看header数据(dump)

./cryptsetup.sh dump -d disk.img -k disk -s 5 -m test -e

cryptsetup 小脚本

过于复杂的密码很容易丢失,即使使用密钥管理器也容易发生误操作或者被迫暴露。而密钥文件其实也并非那么的可靠。

cryptsetup的命令真的很长。我写了个小脚本(crypt.sh)简化下。

#!/bin/bash

KEYFILE=""
HEADFILE=""
KEYFILEOFFSET=0
DEVICE=""
S_POS=0
PAYLOAD=0
ACTION=""
CIPHER="serpent-xts-plain64"
CRYPTSETUP="cryptsetup"
EXTEND=""
BLOCKSIZE=4194304
N=1

function convert() {
local ret=`echo $1 | awk '/[0-9]$/{print $1;next};/[gG]$/{printf "%u\n", $1*(1024*1024*1024);next};/[mM]$/{printf "%u\n", $1*(1024*1024);next};/[kK]$/{printf "%u\n", $1*1024;next}' `
echo "$ret"
}

function get_args() {
  while getopts "k:h:d:p:s:c:eb:n:m:" arg
    do 
      case $arg in
        k)
        KEYFILE=$OPTARG
        ;;
    h)
      HEADFILE=$OPTARG
      ;;
    d)
      DEVICE=$OPTARG
      ;;
    p)
      PAYLOAD=$(($(convert $OPTARG) / 512))
      ;;
    s)
      S_POS=$(convert $OPTARG)
      ;;
    c)
      CIPHER=$OPTARG
      ;;
    e)
      EXTEND="extend"
      ;;
    b)
      BLOCKSIZE=$(convert $OPTARG)
      ;;
    n)
      N=$OPTARG
      ;;
    ?)
      echo "unknow argument"
      exit 1
      ;;
    esac
      done
}

function open() {
  $CRYPTSETUP open --keyfile-size $KEYSIZE  --key-file $KEYFILE --keyfile-offset $KEYOFFSET --header $HEADFILE $DEVICE `basename $DEVICE`
}

function close() {
  $CRYPTSETUP close `basename $DEVICE`
}

function format() {
  $CRYPTSETUP luksFormat --cipher $CIPHER --key-size 512 --keyfile-size $KEYSIZE --hash sha512  --key-file $KEYFILE --keyfile-offset $KEYOFFSET --header $HEADFILE --align-payload $PAYLOAD $DEVICE
}

function dump() {
    $CRYPTSETUP luksDump --keyfile-size $KEYSIZE  --key-file $KEYFILE --keyfile-offset $KEYOFFSET $HEADFILE
}

function extend() {
  dd if=$KEYFILE of=/dev/shm/`basename $KEYFILE` bs=$BLOCKSIZE count=1 skip=1
    HEADFILE=/dev/shm/`basename $KEYFILE`
}

function quit() {
return 0
}

function extendquit() {
rm -rf /dev/shm/`basename $KEYFILE`
}

ACTION=$1
shift
get_args $@
$EXTEND
echo $KEYFILE
echo $HEADFILE
KEYOFFSET=$((`od $KEYFILE -N $N -tu4 -j $S_POS | grep [^0$N] | awk '{print $2}'` * 8))
KEYSIZE=$((BLOCKSIZE - KEYOFFSET))
$ACTION
${EXTEND}quit

使用方法如下

crypt.sh action -k key -h header -d /dev/sdx -p xG -s offset -c cipher -b blocksize -e -n N

脚本只支持key文件的方式。脚本使用key文件的offset* 8位置的数值作为在key文件中的偏移,默认为1个字节,可以通过设置N的值来修改字节的大小,比如N为2就是两个字节的值*8作为最终的偏移,N越大,则密钥文件需要的也越大,因为表示偏移位置的数字范围也越大。我比较喜欢使用随机生成的文件,因此,这个最终的偏移是多少,除了打开文件,具体计算,我也不清楚,但只需要记住初始的值offset就可以了。

blocksize为密钥文件的大小, 单独可能没太大用,但如果将key文件和header文件直接拼接起来,带上参数-e, 则后续的操作(format除外), 就不需要使用header文件了。脚本会将header文件解出来,放到/dev/shm下面,使用完之后,再删除掉它, 这个时候, blocksize就很有用了。

-p 对应的是align-payload 参数,支持偏移,这个值和blocksize, offset 一样, 支持K,M,G这样人类可看的格式。 action可以为 open, close, format, dump 对应的使cryptsetup的 open, close, format,luksDump

这样一来, 主要只用记住密钥文件的偏移即可。