iqb / ecryptfs
用PHP编写的Userland EcryptFS库。
Requires
- php: >=7.0
- ext-openssl: *
Requires (Dev)
- phpunit/phpunit: 4.*
This package is not auto-updated.
Last update: 2024-09-21 00:09:14 UTC
README
EcryptFS是一种Linux文件系统,允许您加密文件(和文件名)。它是Linux内核的一部分,例如,由Ubuntu用于加密用户主目录。
EcryptFS使用两个(可能不同的)密钥进行加密
- FNEK(文件名加密密钥)用于加密/解密文件名
- FEKEK(文件加密密钥加密密钥)用于加密/解密文件内容所使用的特定随机密钥
默认情况下,这两个密钥是从口令短语派生的。
加密/解密文件名
加密文件名以前缀ECRYPTFS_FNEK_ENCRYPTED.
开头,后跟加密后的原始文件名。例如,使用口令短语test
解密ECRYPTFS_FNEK_ENCRYPTED.FWayVrRYlN446EY.WUc7GBFqG9GB6qF3eRmJZ7NYS7ANeS4Gfi9c34ZDTU--
将解密为loremipsum.txt
。
加密和解密文件名的代码如下所示
<?php require_once(__DIR__ . '/vendor/autoload.php'); $passphrase = 'test'; // We need to derive the File Name Encryption key from the passphrase $fnek = \Iqb\Ecryptfs\Util::deriveFNEK($passphrase); // We need a crypto engine to do the work (currently only OpenSSL) $cryptoEngine = new \Iqb\Ecryptfs\OpenSslCryptoEngine(); $filename = 'loremipsum.txt'; $encryptedFilename = \Iqb\Ecryptfs\Util::encryptFilename($cryptoEngine, $filename, $fnek); // Should output 'ECRYPTFS_FNEK_ENCRYPTED.FWayVrRYlN446EY.WUc7GBFqG9GB6qF3eRmJZ7NYS7ANeS4Gfi9c34ZDTU--' echo $encryptedFilename, PHP_EOL; // And the reverse operation should return the original file name if (\Iqb\Ecryptfs\Util::decryptFilename($cryptoEngine, $filename, $fnek) !== $filename) { throw new \RuntimeException("Decryption error"); } // You can test whether a file name is encrypted or not by using the isEncryptedFilename method. // But this method will just check the prefix of the filename (but works even if the file name contains a directory): if (\Iqb\Ecryptfs\Util::isEncryptedFilename(\realpath($encryptedFilename))) { echo $encryptedFilename, " is an encrypted filename", PHP_EOL; } if (!\Iqb\Ecryptfs\Util::isEncryptedFilename($filename)) { echo $filename, " is not an encrypted filename", PHP_EOL; }
解密文件内容
目前文件解密只有基本支持。解密通过流包装器ecryptfs://
处理。文件加密密钥加密密钥(FEKEK)是从提供的口令短语派生的。
<?php require_once(__DIR__ . '/vendor/autoload.php'); // The passphrase to use $passphrase = 'test'; // We must pass it as a stream context $context = \stream_context_create([ 'ecryptfs' => [ 'passphrase' => $passphrase, ] ]); // alternatively we could use constants to avoid typos: $context = \stream_context_create([ \Iqb\Ecryptfs\StreamWrapper::STREAM_NAME => [ \Iqb\Ecryptfs\StreamWrapper::CONTEXT_PASSPHRASE => $passphrase, ] ]); // This will print some lorem ipsum text echo \file_get_contents('ecryptfs://' . __DIR__ . '/tests/data/encrypted/ECRYPTFS_FNEK_ENCRYPTED.FWayVrRYlN446EY.WUc7GBFqG9GB6qF3eRmJZ7NYS7ANeS4Gfi9c34ZDTU--', null, $context), PHP_EOL;
ecryptfs://
之后的所有内容以及流上下文都传递给fopen()
,因此您可以使用PHP中所有可用的流包装器访问加密文件。
如果您没有文件,可以将一个打开的资源(例如通过Guzzle通过HTTP打开的文件)通过流上下文传递
<?php require_once(__DIR__ . '/vendor/autoload.php'); // The passphrase to use $passphrase = 'test'; // Open the file directly or use a handle from somewhere else: $stream_resource = \fopen(__DIR__ . '/tests/data/encrypted/ECRYPTFS_FNEK_ENCRYPTED.FWayVrRYlN446EY.WUc7GBFqG9GB6qF3eRmJZ7NYS7ANeS4Gfi9c34ZDTU--', 'r'); // And pass the stream resource via the stream context $context = \stream_context_create([ \Iqb\Ecryptfs\StreamWrapper::STREAM_NAME => [ \Iqb\Ecryptfs\StreamWrapper::CONTEXT_PASSPHRASE => $passphrase, \Iqb\Ecryptfs\StreamWrapper::CONTEXT_STREAM => $stream_resource, ] ]); // This will print some lorem ipsum text // Everything after the 'ecryptfs://' is ignored echo \file_get_contents('ecryptfs://', null, $context), PHP_EOL;
限制
- 目前不支持在解密文件内容中进行查找
- 目前无法加密文件
- 目前仅完全支持AES(128位和256位)
- 192位AES仅适用于文件名(由于原始EcryptFS内核实现中的限制)
- 如果随机生成的文件加密密钥(FEK)可用,并且可以与多个FEKEK一起解密(理论上在EcryptFS文件头中是可能的,但据我所知并未使用),则仅尝试第一个包。如果它使用另一个FEKEK加密,则解密将失败。
兼容性
要测试与您特定版本的EcryptFS的兼容性,只需运行PHPUnit测试套件。IntegrationTest类创建真实的EcryptFS挂载并向挂载写入文件以验证功能。这需要安装EcryptFS实用工具包(例如Debian/Ubuntu中的ecryptfs-utils),并且测试由root或sudo(无需密码)执行时是可执行的。
库是在Debian Stretch和内核4.9上开发的,但至少与Debian Jessie中的EcryptFS版本兼容,据我所知CI测试在Ubuntu上运行。磁盘上的EcryptFS格式似乎相当稳定,与未来内核版本的兼容性风险很小。