【问题标题】:encrypting and serializing stl string and other containers加密和序列化 stl 字符串和其他容器
【发布时间】:2012-09-24 21:10:57
【问题描述】:

我在 stl 容器(向量)中有数据。向量中的每个节点都是一个结构,其中也包含 stl 字符串。

struct record
{
string name;
string location;
int salary;
}

vector< record > employees;

我想对员工进行序列化,但我也想在序列化之前对其进行加密。

我的加密函数是这样的:

Encode(const char * inBfr, const int in_size, char ** outBfr, int& out_size )

通过搜索,stl 标准似乎不需要我的结构的内存是连续的,所以我不能只获取 employees 变量的内存。有没有其他智能方法可以将此编码功能与基于 stl 的结构/容器一起使用? Encode 函数在普通的 char * 缓冲区中工作对我有好处,所以我确切地知道进出什么,但 stl 结构不是,我正在努力寻找一种好方法,以便我可以将 stl 与这个函数一起使用。

如果有帮助,我也愿意使用任何其他 stl 容器。

【问题讨论】:

  • std::vector的内存必须是连续的(所以你可以使用&amp;employees[0],或者用C++11,employees.data())。但是,结构的布局可能包括填充,这取决于编译器的突发奇想。假设您以后需要能够反序列化,那么依赖结构的布局可能是个坏主意。
  • Encode 函数看起来是个糟糕的主意。 ** 表示您可能做错了什么。
  • @KevinBallard 我在之前的评论中说了同样的话,然后意识到这行不通,因为std::string(存在于每条记录中)只包含指向数据的指针(忘记 SSO),所以你不能简单地对向量的底层缓冲区进行操作。
  • @MooingDuck ** 有什么问题?它只是意味着该函数正在分配所需的任何内存,并且大小在 out_size 变量中给出。我认为这没有任何问题。
  • @zadane:这意味着您可能有内存泄漏。 “正确”的 C++ 可能是 std::vector&lt;char&gt;&amp;,或者根本不传入 outbuffer 参数。

标签: c++ serialization encryption stl


【解决方案1】:

虽然std::vector&lt;T&gt; 中的元素保证连续布局,但这并没有真正的帮助:您拥有的记录可能包括填充,更重要的是,会将std::string 的内容存储在std::string 对象(如果使用小字符串优化,该值可能嵌入在 std::string 中,但它也将包含几个不属于 std::strings 值的字节)。因此,您最好的选择是格式化您的记录并加密格式化的字符串。

格式很简单,但我个人会将编码函数封装成一个简单的std::streambuf,以便可以通过过滤流缓冲区完成加密。鉴于您给出的签名,这可能看起来像这样:

class encryptbuf
    : public std::streambuf {
    std::streambuf* d_sbuf;
    char            d_buffer[1024];
public:
    encryptbuf(std::streambuf* sbuf)
        : d_sbuf(sbuf) {
        this->setp(this->d_buffer, this->d_buffer + sizeof(this->d_buffer) - 1);
    }
    int overflow(int c) {
        if (c != std::char_traits<char>::eof()) {
            *this->pptr() = std::char_traits<char>::to_char_type(c);
            this->pbump(1);
        }
        return this->pubsync()? std::char_traits<char>::eof(): std::char_traits<char>::not_eof(c);
    }
    int sync() {
        char* out(0);
        int   size(0);
        Encode(this->pbase(), this->pptr() - this->pbase(), &out, size);
        this->d_sbuf->sputn(out, size);
        delete[] out; // dunno: it seems the output buffer is allocated but how?
        this->setp(this->pbase(), this->epptr());
        return this->d_sbuf->pubsync();
    }
};

int main() {
    encryptbuf    sbuf(std::cout.rdbuf());
    std::ostream eout(&sbuf);
    eout << "print something encoded to standard output\n" << std::flush;
}

现在,为您的记录创建一个输出运算符,只需打印到 std::ostream 即可用于创建编码

【讨论】:

  • 有一刻我震惊地遇到一个知道流如何运作良好的用户,只需发布​​这样的答案。然后我看到了名字:D
  • 感谢您提供的精彩示例,我需要更多时间来完全理解这一点:)
  • 像现在这样,是否应该一遍又一遍地打印相同的(编码的)信息? 编辑哈,我找到了,并修复了它。 (我可能错过了一些细微差别,但它似乎修复了无限输出并且也适用于更长的输出测试)
  • 是的,它应该调用底层流缓冲区的pubsync()。此外,我发布的原始版本没有重置缓冲区(缺少对 setp() 的调用)。我在移动设备上输入了这段代码,但无法测试...
  • @DietmarKühl 我独立提出了setp(this-&gt;pbase(), this-&gt;epptr()); :)(我很自豪)。因为它在我的测试平台 proggy 中运行良好,所以你知道。不过,测试用例的变化不大。 (例如,不要尝试一次写入 >1024)
【解决方案2】:

将结构序列化为字符串,然后加密字符串可能是最简单的方法。例如:

std::ostringstream buffer;

buffer << a_record.name << "\n" << a_record.location << "\n" << a_record.salary;

encode(buffer.str().c_str(), buffer.str().length(), /* ... */);

如果是我,我可能会写 encode(或者至少是它的包装器)来获取向量、字符串或流中的输入(并可能产生输出)。

如果你想雄心勃勃,还有其他可能性。首先,@MooingDuck 提出了一个很好的观点,即通常值得为班级重载operator&lt;&lt;,而不是一直处理单个项目。这通常是一个类似于上面的小函数:

std::ostream &operator<<(std::ostream &os, record const &r) { 
    return os << r.name << "\n" << r.location << "\n" << r.salary;
}

使用它,您只需:

std::ostringstream os;
os << a_record;

encode(os.str().c_str(), os.str().length(), /* ... */);

其次,如果你想变得更加雄心勃勃,你可以将加密放入(例如)codecvt facet,这样你就可以在将所有数据写入流时自动加密所有数据,并将其解密为你读回来。另一种可能性是将加密构建到过滤streambuf对象中。 codecvt facet 可能是理论上应该首选的方法,但streambuf 几乎可以肯定更容易实现,涉及的不相关“东西”更少。

【讨论】:

  • 我可能还会提到如何为班级重载&lt;&lt;,也许可以制作一个自动加密的流?
  • 我看到的问题是,在以这种方式(分别)对每个记录进行编码之后,我会在文件中写入什么?可能是encoded_string_size,然后是encoded_string,以此类推整个向量!?我正在编写的每个节点的大小仍然不会被编码,可能需要另一个级别的加密。如果我可以一次性对整个向量进行编码并将生成的缓冲区写入文件,这似乎更容易实现。对此有什么想法吗?
  • @zadane:是的:它不起作用,因为 std::string 的按位复制不起作用。杰里给出这个建议并得到支持是有原因的。
  • @zadane:您几乎需要先序列化它们,然后再加密。您可以单独加密记录,也可以将所有记录序列化为一个大字符串,然后对其进行加密——但您几乎需要在加密之前将其“展平”。
  • @zadane:不,没有这样的保证,因为字符串编码、不同的类型大小、字节序……对不起,伙计。序列化很难。
猜你喜欢
  • 1970-01-01
  • 2020-06-25
  • 2018-07-08
  • 1970-01-01
  • 2012-07-01
  • 2014-02-07
  • 2017-01-02
相关资源
最近更新 更多