jsq/psr7-stream-encryption

用于加密和解密任意大小的数据流。

0.4.0 2020-02-29 21:40 UTC

This package is auto-updated.

Last update: 2024-08-29 03:36:47 UTC


README

Build Status Total Downloads Author

PHP的内置OpenSSL绑定提供了加密和解密数据的便捷方式。然而,由ext-openssl提供的接口仅对字符串进行操作,因此解密大型密文需要将整个密文加载到内存中,并接收包含全部解码明文的字符串。

本包旨在允许加密和解密任意大小的数据流。它支持使用AES-CBC、AES-CTR和AES-ECB进行流式加密和解密。

不推荐在新的系统中使用AES-ECB。它包括在内是为了与旧系统兼容。有关ECB的缺点,请参考维基百科上的讨论。

用法

使用加密装饰器装饰一个Psr\Http\Message\StreamInterface实例,以便在调用装饰流上的read时逐步加密装饰流的內容

$iv = random_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$cipherMethod = new Cbc($iv);
$key = 'some-secret-password-here';

$inStream = new Stream(fopen('some-input-file', 'r')); // Any PSR-7 stream will be fine here
$cipherTextStream = new AesEncryptingStream($inStream, $key, $cipherMethod); // Wrap the stream in an EncryptingStream
$cipherTextFile = Psr7\stream_for(fopen('encrypted.file', 'w'));
Psr7\copy_to_stream($cipherTextStream, $cipherTextFile); // When you read from the encrypting stream, the data will be encrypted.

// You'll also need to store the IV somewhere, because we'll need it later to decrypt the data.
// In this case, I'll base64 encode it and stick it in a file (but we could put it anywhere where we can retrieve it later, like a database column)
file_put_contents('encrypted.iv', base64_encode($iv));

在调用加密流上的read之前不会执行加密。

要计算密文的HMAC,将装饰流包装在一个HashingStream实例中

$hash = null;
$ciphertext = new Jsq\EncryptionStreams\AesEncryptingStream(
    $plaintext,
    $key,
    $cipherMethod
);
$hashingDecorator = new Jsq\EncryptionStreams\HashingStream(
    $ciphertext,
    $key,
    function ($calculatedHash) use (&$hash) {
        $hash = $calculatedHash;
    }
);

while (!$ciphertext->eof()) {
    $ciphertext->read(1024 * 1024);
}

assert('$hash === $hashingDecorator->getHash()');

在解密密文时,在将其作为参数传递给解密流之前,将密文包装在一个哈希装饰器中

$key = 'secret key';
$iv = random_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$plainText = 'Super secret text';
$cipherText = openssl_encrypt(
    $plainText,
    'aes-256-cbc',
    $key,
    OPENSSL_RAW_DATA
    $iv
);
$expectedHash = hash('sha256', $cipherText);

$hashingDecorator = new Jsq\EncryptingStreams\HashingStream(
    GuzzleHttp\Psr7\stream_for($cipherText),
    $key,
    function ($hash) use ($expectedHash) {
        if ($hash !== $expectedHash) {
            throw new DomainException('Cipher text mac does not match expected value!');
        }
    }
);

$decrypted = new Jsq\EncryptionStreams\AesEncryptingStream(
    $cipherText,
    $key,
    $cipherMethod
);
while (!$decrypted->eof()) {
    $decrypted->read(1024 * 1024);
}

与加密装饰器一样,HashingStream是延迟执行的,并且只有在读取底层流时才会对底层流进行哈希处理。在上面的示例中,只有在读取了整个密文(并且所有但最后一个块已解密)后才会抛出异常。

HashingStream不可定位,因此您需要将其包装在一个GuzzleHttp\Psr7\CachingStream中,以支持随机访问。