iqb/ecryptfs

用PHP编写的Userland EcryptFS库。

dev-master 2018-02-05 09:29 UTC

This package is not auto-updated.

Last update: 2024-09-21 00:09:14 UTC


README

Build Status Scrutinizer Score Code Coverage Software License

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格式似乎相当稳定,与未来内核版本的兼容性风险很小。