如何将 16 位值的数组转换为 base64?
Posted
技术标签:
【中文标题】如何将 16 位值的数组转换为 base64?【英文标题】:How to convert an array of 16-bit values to base64? 【发布时间】:2014-07-28 22:27:51 【问题描述】:所以我现在正在研究 C++ 中的加密/解密方法。它将std::string
作为输入(加上将用于加密消息的“密钥”),并产生一个std::string
输出,表示加密的字符串。
在加密过程中,我将std::string
转换为uint16_t
的数组,并在加密过程中对其进行一些计算。原因很简单,因为 uint_16_t
值提供了更多的空间来通过算法加密原始值,而不是 char
。
问题是,为了将加密消息作为std::string
返还,我需要以某种方式将uint_16_t
值的数组转换为可读的(即适合char
数组而不会溢出的东西) .为此,我认为我可以使用 base64,但我发现的所有 base64 实现都只将std::string
或char*
作为输入(8 位/元素)。显然,如果我将我的 uint16_t
数组提供给它,我将永远无法恢复我的原始值,因为 base64 函数在转换之前将其转换为 8 位。
所以这是我的问题:有人知道将uint16_t
数组编码为可打印字符串(如base64)并返回而不会丢失任何数据的方法吗?
我知道我必须获取数据的字节才能使用 base64,但我不知道该怎么做。
提前感谢您的帮助!
【问题讨论】:
提示:N
16 位值的数组也恰好是2*N
8 位值的数组。
“原因很简单,因为 uint_16_t 值通过算法提供了比 char 更大的空间来加密原始值。”这是没有意义的。几乎每一种现代加密算法都是在字节序列上定义的。
【参考方案1】:
您可以使用base-n 迷你库,它提供通用的、基于迭代器的 I/O。
以下代码按预期将“1 2 3 4 65535”输出到标准输出:
uint16_t arr[] 1, 2, 3, 4, 65535 ;
const int len = sizeof(arr)/sizeof(arr[0]);
std::string encoded;
bn::encode_b64(arr, arr + len, std::back_inserter(encoded));
uint16_t out[len] 0 ;
bn::decode_b64(encoded.begin(), encoded.end(), out);
for (auto c : out)
std::cout << c << " ";
强制披露:我是 lib 的作者
【讨论】:
这听起来真不错!但由于这将是加密/解密方法的一部分,更复杂的解决方案会增加更多的安全性,所以我将使用我发布的字节拆分方法。不过这个库很酷,我可能会用一段时间。谢谢! 无论您使用哪种库,我相信“更复杂的解决方案会增加更多的安全性”声明会被许多人认为是有争议的。您可能想了解人们对“通过默默无闻的安全”的看法。 好吧,如果有人试图解码加密数据,简单地通过 base-64 解码器运行它对他们来说没什么大不了的,因此不那么安全。我的方法有多层加密,所以这不会暴露数据本身,但是黑客在继续破解加密之前肯定需要更多的努力才能弄清楚这个字节组合的东西。至少我希望如此。 通常建议遵循一个完善的加密协议,而不是实施一个 ad-hoc。我不相信字节切换的一次迭代对严肃的自动密码分析的弹性增加了很多。我假设您的目标不是关键任务级别的安全性,所以我不会再让您感到厌烦了;) 我写这个加密方法只是为了好玩,仅此而已。如果我正在设计其他人会使用的东西,我肯定会使用 RSA 之类的东西。【参考方案2】:所以我终于解决了。我发布它以防其他人需要这样的东西。
基本上我将uint16_t
值分成两个uint8_t
,每个都是8 位值,因此它们可以与任何base64 实现一起使用。这是我的方法:
#include <iostream>
using namespace std;
#define BYTE_T uint8_t
#define TWOBYTE_T uint16_t
#define LOWBYTE(x) ((BYTE_T)x)
#define HIGHBYTE(x) ((TWOBYTE_T)x >> 0x8)
#define BYTE_COMBINE(h, l) (((BYTE_T)h << 0x8) + (BYTE_T)l)
int main()
// an array with 16-bit integers
uint16_t values[5] = 1, 2, 3, 4, 65535;
// split the 16-bit integers into an array of 8-bit ones
uint8_t split_values[10]; // notice that you need an array twice as big (16/8 = 2)
int val_count = 0;
for (int i=0; i<10; i+=2)
split_values[i] = HIGHBYTE(values[val_count]);
split_values[i+1] = LOWBYTE(values[val_count]);
val_count++;
// base64 encode the 8-bit values, then decode them back
// or do whatever you want with them that requires 8-bit numbers
// then reunite the 8-bit integers to the original array of 16-bit ones
uint16_t restored[5];
int rest_count = 0;
for (int i=0; i<10; i+=2)
restored[rest_count] = BYTE_COMBINE(split_values[i], split_values[i+1]);
rest_count++;
for (const auto &i : restored) cout << i << " ";
cout << endl;
return 0;
当然,相同的方法适用于任何长度。您只需要更改 for 循环的位移。可以轻松修改此代码以将 32 位整数拆分为 16 位整数,或者其他任何真正的整数。
【讨论】:
【参考方案3】:在此处查看上一个问题:base64 decode snippet in c++
将uint16_t*
转换为unsigned const char*
并进行编码,如下所示:
// Data to base64 encode
std::vector<uint16_t> some_data;
// Populate some_data...
// ...
// base64 encode it
std::string base64_data = base64_encode((unsigned char const*)&some_data[0], some_data.size()*2 );
【讨论】:
【参考方案4】:假设 uint16_t 值的范围是从 0 到 63,并且您使用的是 ASCII,只需将 0x21(十六进制 21)添加到每个值并输出即可。这将创建一个可打印的字符串,但出于显示目的,您可能还希望在一些字符后打印一个新行,而不是显示一个非常长的字符串。任何解码器都必须从从文件中读取的每个字符中减去 0x21(如果文件中有换行符,请忽略这些(在减去 0x21 之前进行检查)。
【讨论】:
其实不是这样的。uint_16_t
值的范围从 1 到 UINT16_MAX
(即 65535)。虽然它们确实代表 ASCII 字符,但它们是经过编码的。加密字符的算法采用char
并输出一个更大的数字uint_16_t
,它以编码形式表示该字符。
无论如何,我想我可以将 16 位值分别拆分为两个 8 位值,然后我可以使用任何 base64 函数。我会试一试。
某些 8 位值可能会导致问题。您可以将 16 位值的数组“解包”为 6 位值(如果需要填充,则使用零位进行填充),然后将 0x21 添加到每个 6 位值并将其作为 8 位值输出。解码器将从每个 8 位值中减去 0x21 以生成 6 位值,然后将 6 位值打包以创建 16 位值的数组。您可以使用特殊的 8 位值,例如 0x61(十进制 64 + 十六进制 21)来指示编码字符串的结尾。
@GregS - 压缩的 6 位值在可显示的 ASCII 字符范围内通过添加一个值扩展为 8 位值。 Wiki 示例将 0x41 ('A') 添加到 6 位值以生成 8 位 ASCII 可打印字符:wiki base64。在 wiki 示例中,'=' (hex 3d) 用于在编码字符串的末尾进行填充。
@GregS - 我应该澄清一下。请注意,原始帖子要求类似 base64 的东西,而不是 base64,只是可打印的。使用连续值将允许使用加减法执行编码和解码,而不是使用查找/映射表。以上是关于如何将 16 位值的数组转换为 base64?的主要内容,如果未能解决你的问题,请参考以下文章
MFC如何将一个unsigned int数组转换为bitmap?