c++ - 如果使用 Gzip 类压缩,如何将文件名添加到存档?

标签 c++ gzip crypto++

我在加密前使用 Gzip 压缩数据。

Gzip gz;
gz.Put(file,size)
gz.MessageEnd();
gz.Get(file,gz.MaxRetrievable());

我希望创建的 gzip 文件包含原始文件名作为元数据。我如何通过 Crypto++ 界面执行此操作?

最佳答案

I want the created gzip file to include the original filename as metadata. How do i do this through the Crypto++ interface?

你不能开箱即用。这似乎是 Crypto++ 的限制。 (但见下文)。

来自 RFC 1952显然有一个字段:

  (if FLG.FNAME set)

     +=========================================+
     |...original file name, zero-terminated...| (more-->)
     +=========================================+

但是 Crypto++ 不允许你设置它(来自 gzip.c source code ):

void Gzip::WritePrestreamHeader()
{
    m_totalLen = 0;
    m_crc.Restart();

    AttachedTransformation()->Put(MAGIC1);
    AttachedTransformation()->Put(MAGIC2);
    AttachedTransformation()->Put(DEFLATED);
    AttachedTransformation()->Put(0);       // general flag
    AttachedTransformation()->PutWord32(0); // time stamp
    byte extra = (GetDeflateLevel() == 1) ? FAST : ((GetDeflateLevel() == 9) ? SLOW : 0);
    AttachedTransformation()->Put(extra);
    AttachedTransformation()->Put(GZIP_OS_CODE);
}

Crypto++ 如果在解压缩时存在它(Gunzip 是 GZIP 解压缩器)(来自 gzip.c source code),则静默丢弃它:

void Gunzip::ProcessPrestreamHeader()
{
    ...

    if (flags & EXTRA_FIELDS)   // skip extra fields
    {
        word16 length;
        if (m_inQueue.GetWord16(length, LITTLE_ENDIAN_ORDER) != 2) throw HeaderErr();
        if (m_inQueue.Skip(length)!=length) throw HeaderErr();
    }

    if (flags & FILENAME)   // skip filename
        do
            if(!m_inQueue.Get(b)) throw HeaderErr();
        while (b);

    if (flags & COMMENTS)   // skip comments
        do
            if(!m_inQueue.Get(b)) throw HeaderErr();
        while (b);
}

你是我记得大约 15 年来第一个提出要求的人,所以这不是一个受欢迎的要求:)

以下是修改源代码以添加文件时间、文件名和注释的方法。您可以在 Crypto++ wiki 页面上找到 Gzip 的补丁。和 Gunzip .您可以在 Pastebin 上的 Diff for Crypto++ gzip for filename and comment processing 找到 SVN 差异。 .文件大部分相同,但 wiki 更新,因为它在 Gzip::WritePoststreamTail 中有一些重置代码。 .

首先,将以下 protected 成员添加到GzipGunzip类:

word32 m_filetime;
std::string m_filename;
std::string m_comment;

其次,为 m_filetime 添加一个初始化器对于每个构造函数(GzipGunzip )。例如,这是 Gzip 中的一个。 Actor :

Gzip(const NameValuePairs &parameters, BufferedTransformation *attachment=NULL)
    : Deflator(parameters, attachment), m_filetime(0) {}

第三,将公共(public) setter 添加到Gzip类:

void SetFiletime(word32 filetime) { m_filetime = filetime; }
void SetFilename(const std::string& filename) { m_filename = filename; }
void SetComment(const std::string& comment) { m_comment = comment; }

第四,给Gunzip添加public getters类:

word32 GetFiletime() const { return m_filetime; }
const std::string& GetFilename() const { return m_filename; }
const std::string& GetComment() const { return m_comment; }

五、改Gzip::WritePrestreamHeader到 cpp 文件中的以下内容:

void Gzip::WritePrestreamHeader()
{
    m_totalLen = 0;
    m_crc.Restart();

    int flags = 0;
    if(!m_filename.empty())
        flags |= FILENAME;
    if(!m_comment.empty())
        flags |= COMMENTS;

    AttachedTransformation()->Put(MAGIC1);
    AttachedTransformation()->Put(MAGIC2);
    AttachedTransformation()->Put(DEFLATED);
    AttachedTransformation()->Put((byte)flags);    // general flag
    AttachedTransformation()->PutWord32(m_filetime, LITTLE_ENDIAN_ORDER);    // time stamp

    byte extra = (GetDeflateLevel() == 1) ? FAST : ((GetDeflateLevel() == 9) ? SLOW : 0);
    AttachedTransformation()->Put(extra);        
    AttachedTransformation()->Put(GZIP_OS_CODE);

    if(!m_filename.empty())
        AttachedTransformation()->Put((const unsigned char*)m_filename.data(), m_filename.size() +1);

    if(!m_comment.empty())
        AttachedTransformation()->Put((const unsigned char*)m_comment.data(), m_comment.size() +1);
}

六、改Gzip::WritePoststreamTail到 cpp 文件中的以下内容:

void Gzip::WritePoststreamTail()
{
    SecByteBlock crc(4);
    m_crc.Final(crc);
    AttachedTransformation()->Put(crc, 4);
    AttachedTransformation()->PutWord32(m_totalLen, LITTLE_ENDIAN_ORDER);

    m_filetime = 0;

    m_filename.erase(0);
    m_filename.reserve(16);

    m_comment.erase(0);
    m_comment.reserve(32);
}

七、改Gunzip::ProcessPrestreamHeader到 cpp 文件中的以下内容:

void Gunzip::ProcessPrestreamHeader()
{
    m_length = 0;
    m_crc.Restart();

    m_filetime = 0;

    m_filename.erase(0);
    m_filename.reserve(16);

    m_comment.erase(0);
    m_comment.reserve(32);

    byte buf[6];
    byte b, flags;

    if (m_inQueue.Get(buf, 2)!=2) throw HeaderErr();
    if (buf[0] != MAGIC1 || buf[1] != MAGIC2) throw HeaderErr();
    if (!m_inQueue.Get(b) || (b != DEFLATED)) throw HeaderErr();     // skip CM flag
    if (!m_inQueue.Get(flags)) throw HeaderErr();
    if (flags & (ENCRYPTED | CONTINUED)) throw HeaderErr();
    if (m_inQueue.GetWord32(m_filetime, LITTLE_ENDIAN_ORDER) != 4) throw HeaderErr();
    if (m_inQueue.Skip(2)!=2) throw HeaderErr();    // Skip extra flags and OS type

    if (flags & EXTRA_FIELDS)   // skip extra fields
    {
        word16 length;
        if (m_inQueue.GetWord16(length, LITTLE_ENDIAN_ORDER) != 2) throw HeaderErr();
        if (m_inQueue.Skip(length)!=length) throw HeaderErr();
    }

    if (flags & FILENAME)   // extract filename
    {
        do
        {
            if(!m_inQueue.Get(b)) throw HeaderErr();
            if(b) m_filename.append( 1, (char)b );
        }
        while (b);
    }

    if (flags & COMMENTS)   // extract comments
    {
        do
        {
            if(!m_inQueue.Get(b)) throw HeaderErr();
            if(b) m_comment.append( 1, (char)b );
        }
        while (b);
    }
}

使用方法如下:

try {

    Gzip zipper(new FileSink("gzip-test.gz", true));
    zipper.SetFilename("test-filename.txt");
    zipper.SetComment("This is a test of filenames and comments");

    string data = "abcdefghijklmnopqrstuvwxyz";
    zipper.Put( (unsigned char*) data.c_str(), data.size());
    zipper.MessageEnd();        
}
catch(CryptoPP::Exception& ex)
{
    cerr << ex.what() << endl;
}

压缩文件名为gzip-test.gz . header 中的原始文件名字段是 test-filename.txt , 标题中的注释是 This is a test of filenames and comments .

您可以使用十六进制编辑器查看它的运行情况:

enter image description here


以下是 gzip -d 在实践中的工作原理和 Mac OS X 上的默认存档提取器:忽略嵌入的文件名,并使用存档文件名减去 gz 来保存文件。扩展名:

enter image description here

我不知道这是否是预期的行为,因为文件名成员与在不支持长文件名的文件系统上保留长文件名有关。

关于 super 用户的行为有一个悬而未决的问题:Is Gzip supposed to honor original filename for decompress? .

编辑:感谢 super 用户 Simon,必须使用 gunzip -N <gz-file>使用隐藏在 header 中的原始文件名进行提取。


为了完整起见,以下是将存档写入内存中的字符串而不是磁盘文件时的往返行程。

string s1, s2;
string data = "abcdefghijklmnopqrstuvwxyz";

Gzip zipper(new StringSink(s1));
zipper.SetFilename("test-filename.txt");
zipper.SetComment("This is a test of filenames and comments");

zipper.Put( (unsigned char*) data.c_str(), data.size());
zipper.MessageEnd();

Gunzip unzipper(new StringSink(s2));
unzipper.Put( (unsigned char*) s1.data(), s1.size());
unzipper.MessageEnd();

cout << "Filename: " << unzipper.GetFilename() << endl;
cout << "Comment: " << unzipper.GetComment() << endl;
cout << "Data: " << s2 << endl;

它产生了预期的结果。


上面的修改有一个重要的警告。当存档包含单个文件时,mods 运行良好。但是如果流包含多个文件,则只会为最后一个文件流提供文件时间、文件名和注释。如果它们在最后一个文件流中丢失,那么它们在 getter 中将为空。

限制的原因与 Crypto++ 管道的架构方式有关。在 Crypto++ 中,每个流都被视为一条消息。您可以调用NextMessage()并取回流中的字节。但是流是解压缩字节的集合,而不是可以容纳额外字段的更高级别的结构。

我很确定它的修复非常重要。我相信这意味着 Gzip压缩机和 Gunzip解压缩器将需要添加 channel ,以便您可以检索其他数据,例如从经过身份验证的加密过滤器检索密文 (DEFAULT_CHANNEL) 或身份验证标签 (AAD_CHANNEL) 的方式。

关于c++ - 如果使用 Gzip 类压缩,如何将文件名添加到存档?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27739140/

相关文章:

c++ - glCreateShader 给出相同的 ID

python - 如何使用 zcat 在 Python 中测试 gzip 文件目录并解压缩 gzip 文件?

PHP输出缓冲,由ob_gzhandler引起的内容编码错误?

c++ - Crypto++ 无法构建 Qt 应用程序

c++ - 将 Integer 转换回 SecByteBlock?

c++ - 为什么这个 openmp 给 SIGSEGV?

c++ - Nvidia NPP nppiFilter 在与 2d 内核卷积时产生垃圾

webpack - 我们是否需要手动设置 Nuxt 发送 gzipped 或 brotli-compressed 文件?

c++ - 如何在 Crypto++ 中更改接收器

c++ - 将函数从一个类发送到另一个类并在 C++ 中执行