我在unsigned char变量中有二进制数据.我需要将它们转换为c中的PEM base64.我查看了openssl库,但我找不到任何功能.有没有人有任何想法?
这是我正在使用的那个:
#include#include static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; static char *decoding_table = NULL; static int mod_table[] = {0, 2, 1}; char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length) { *output_length = 4 * ((input_length + 2) / 3); char *encoded_data = malloc(*output_length); if (encoded_data == NULL) return NULL; for (int i = 0, j = 0; i < input_length;) { uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; } for (int i = 0; i < mod_table[input_length % 3]; i++) encoded_data[*output_length - 1 - i] = '='; return encoded_data; } unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length) { if (decoding_table == NULL) build_decoding_table(); if (input_length % 4 != 0) return NULL; *output_length = input_length / 4 * 3; if (data[input_length - 1] == '=') (*output_length)--; if (data[input_length - 2] == '=') (*output_length)--; unsigned char *decoded_data = malloc(*output_length); if (decoded_data == NULL) return NULL; for (int i = 0, j = 0; i < input_length;) { uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF; if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF; } return decoded_data; } void build_decoding_table() { decoding_table = malloc(256); for (int i = 0; i < 64; i++) decoding_table[(unsigned char) encoding_table[i]] = i; } void base64_cleanup() { free(decoding_table); }
请记住,这在解码时不会进行任何错误检查 - 非基本64位编码数据将被处理.
我知道这个问题很老了,但我对提供的解决方案数量感到困惑 - 他们每个人都声称自己更快更好.我在github上放了一个项目来比较base64编码器和解码器:https://github.com/gaspardpetit/base64/
在这一点上,我并不局限于C算法 - 如果一个实现在C++中表现良好,它可以很容易地向后移植到C.还使用Visual Studio 2015进行测试.如果有人想用clang /的结果更新这个答案gcc,做我的客人.
最快的编码器:我发现的两个最快的编码器实现是Jouni Malinen的http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c和Apache的https://opensource.apple .com/source/QuickTimeStreamingServer/QuickTimeStreamingServer-452/CommonUtilitiesLib/base64.c.
以下是使用我到目前为止测试的不同算法编码32K数据的时间(以微秒为单位):
jounimalinen 25.1544 apache 25.5309 NibbleAndAHalf 38.4165 internetsoftwareconsortium 48.2879 polfosol 48.7955 wikibooks_org_c 51.9659 gnome 74.8188 elegantdice 118.899 libb64 120.601 manuelmartinez 120.801 arduino 126.262 daedalusalpha 126.473 CppCodec 151.866 wikibooks_org_cpp 343.2 adp_gmbh 381.523 LihO 406.693 libcurl 3246.39 user152949 4828.21
(RenéNyffenegger的解决方案,在此问题的另一个答案中记入,在此列为adp_gmbh).
这是来自Jouni Malinen的一个我稍微修改后返回一个std :: string:
/* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005-2011, Jouni Malinen* * This software may be distributed under the terms of the BSD license. * See README for more details. */ // 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string // instead of a buffer allocated with malloc. #include static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** * base64_encode - Base64 encode * @src: Data to be encoded * @len: Length of the data to be encoded * @out_len: Pointer to output length variable, or %NULL if not used * Returns: Allocated buffer of out_len bytes of encoded data, * or empty string on failure */ std::string base64_encode(const unsigned char *src, size_t len) { unsigned char *out, *pos; const unsigned char *end, *in; size_t olen; olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */ if (olen < len) return std::string(); /* integer overflow */ std::string outStr; outStr.resize(olen); out = (unsigned char*)&outStr[0]; end = src + len; in = src; pos = out; while (end - in >= 3) { *pos++ = base64_table[in[0] >> 2]; *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; } if (end - in) { *pos++ = base64_table[in[0] >> 2]; if (end - in == 1) { *pos++ = base64_table[(in[0] & 0x03) << 4]; *pos++ = '='; } else { *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = base64_table[(in[1] & 0x0f) << 2]; } *pos++ = '='; } return outStr; }
最快的解码器:这是解码结果,我必须承认我有点惊讶:
polfosol 45.2335 wikibooks_org_c 74.7347 apache 77.1438 libb64 100.332 gnome 114.511 manuelmartinez 126.579 elegantdice 138.514 daedalusalpha 151.561 jounimalinen 206.163 arduino 335.95 wikibooks_org_cpp 350.437 CppCodec 526.187 internetsoftwareconsortium 862.833 libcurl 1280.27 LihO 1852.4 adp_gmbh 1934.43 user152949 5332.87
来自c ++中base64解码片段的 Polfosol片段速度最快,几乎是2倍.
以下是为了完整性而编写的代码:
static const int B64index[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; std::string b64decode(const void* data, const size_t len) { unsigned char* p = (unsigned char*)data; int pad = len > 0 && (len % 4 || p[len - 1] == '='); const size_t L = ((len + 3) / 4 - pad) * 4; std::string str(L / 4 * 3 + pad, '\0'); for (size_t i = 0, j = 0; i < L; i += 4) { int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]]; str[j++] = n >> 16; str[j++] = n >> 8 & 0xFF; str[j++] = n & 0xFF; } if (pad) { int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12; str[str.size() - 1] = n >> 16; if (len > L + 2 && p[L + 2] != '=') { n |= B64index[p[L + 2]] << 6; str.push_back(n >> 8 & 0xFF); } } return str; }
但你也可以在openssl中执行它(openssl enc
命令做它....),看看BIO_f_base64()
函数
这是我使用OpenSSL的解决方案.
/* A BASE-64 ENCODER AND DECODER USING OPENSSL */ #include#include //Only needed for strlen(). char *base64encode (const void *b64_encode_this, int encode_this_many_bytes){ BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO. BUF_MEM *mem_bio_mem_ptr; //Pointer to a "memory BIO" structure holding our base64 data. b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO. mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory sink BIO. BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-sink BIO chain. BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less. BIO_write(b64_bio, b64_encode_this, encode_this_many_bytes); //Records base64 encoded data. BIO_flush(b64_bio); //Flush data. Necessary for b64 encoding, because of pad characters. BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr); //Store address of mem_bio's memory structure. BIO_set_close(mem_bio, BIO_NOCLOSE); //Permit access to mem_ptr after BIOs are destroyed. BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one). BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1); //Makes space for end null. (*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0'; //Adds null-terminator to tail. return (*mem_bio_mem_ptr).data; //Returns base-64 encoded data. (See: "buf_mem_st" struct). } char *base64decode (const void *b64_decode_this, int decode_this_many_bytes){ BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO. char *base64_decoded = calloc( (decode_this_many_bytes*3)/4+1, sizeof(char) ); //+1 = null. b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO. mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory source BIO. BIO_write(mem_bio, b64_decode_this, decode_this_many_bytes); //Base64 data saved in source. BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-source BIO chain. BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines. int decoded_byte_index = 0; //Index where the next base64_decoded byte should be written. while ( 0 < BIO_read(b64_bio, base64_decoded+decoded_byte_index, 1) ){ //Read byte-by-byte. decoded_byte_index++; //Increment the index until read of BIO decoded data is complete. } //Once we're done reading decoded data, BIO_read returns -1 even though there's no error. BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (i.e. the 1st one). return base64_decoded; //Returns base-64 decoded data with trailing null terminator. } /*Here's one way to base64 encode/decode using the base64encode() and base64decode functions.*/ int main(void){ char data_to_encode[] = "Base64 encode this string!"; //The string we will base-64 encode. int bytes_to_encode = strlen(data_to_encode); //Number of bytes in string to base64 encode. char *base64_encoded = base64encode(data_to_encode, bytes_to_encode); //Base-64 encoding. int bytes_to_decode = strlen(base64_encoded); //Number of bytes in string to base64 decode. char *base64_decoded = base64decode(base64_encoded, bytes_to_decode); //Base-64 decoding. printf("Original character string is: %s\n", data_to_encode); //Prints our initial string. printf("Base-64 encoded string is: %s\n", base64_encoded); //Prints base64 encoded string. printf("Base-64 decoded string is: %s\n", base64_decoded); //Prints base64 decoded string. free(base64_encoded); //Frees up the memory holding our base64 encoded data. free(base64_decoded); //Frees up the memory holding our base64 decoded data. }
libb64同时具有C和C++ API.它是轻量级的,也许是最快的公开实现.它也是一个专用的独立base64编码库,如果你不需要使用更大的库(如OpenSSL或glib)所带来的所有其他东西,这可能会很好.
glib具有base64编码功能:https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html
我需要在std :: string上使用C++实现.没有一个答案满足我的需求,我需要简单的双功能解码编码和解码,但我懒得编写自己的代码,所以我发现了这个:
http://www.adp-gmbh.ch/cpp/common/base64.html
代码的归功于RenéNyffenegger.
如果网站出现故障,请将代码放在下面:
base64.cpp
/* base64.cpp and base64.h Copyright (C) 2004-2008 René Nyffenegger This source code is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this source code must not be misrepresented; you must not claim that you wrote the original source code. If you use this source code in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original source code. 3. This notice may not be removed or altered from any source distribution. René Nyffenegger rene.nyffenegger@adp-gmbh.ch */ #include "base64.h" #includestatic const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; static inline bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { std::string ret; int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; while (in_len--) { char_array_3[i++] = *(bytes_to_encode++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) ret += base64_chars[char_array_4[i]]; i = 0; } } if (i) { for(j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; while((i++ < 3)) ret += '='; } return ret; } std::string base64_decode(std::string const& encoded_string) { int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; std::string ret; while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) char_array_4[i] = base64_chars.find(char_array_4[i]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (i = 0; (i < 3); i++) ret += char_array_3[i]; i = 0; } } if (i) { for (j = i; j <4; j++) char_array_4[j] = 0; for (j = 0; j <4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; } return ret; }
base64.h
#includestd::string base64_encode(unsigned char const* , unsigned int len); std::string base64_decode(std::string const& s);
用法
const std::string s = "test"; std::string encoded = base64_encode(reinterpret_cast(s.c_str()), s.length()); std::string decoded = base64_decode(encoded);
GNU coreutils在lib/base64中有它.它有点膨胀,但处理像EBCDIC这样的东西.您也可以自己玩,例如,
char base64_digit (n) unsigned n; { if (n < 10) return n - '0'; else if (n < 10 + 26) return n - 'a'; else if (n < 10 + 26 + 26) return n - 'A'; else assert(0); return 0; } unsigned char base64_decode_digit(char c) { switch (c) { case '=' : return 62; case '.' : return 63; default : if (isdigit(c)) return c - '0'; else if (islower(c)) return c - 'a' + 10; else if (isupper(c)) return c - 'A' + 10 + 26; else assert(0); } return 0xff; } unsigned base64_decode(char *s) { char *p; unsigned n = 0; for (p = s; *p; p++) n = 64 * n + base64_decode_digit(*p); return n; }
通过这些礼物向所有人了解,你不应该混淆"自己玩耍"和"实施标准".Yeesh.
这是我多年来一直使用的解码器......
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const int BASE64_INPUT_SIZE = 57; BOOL isbase64(char c) { return c && strchr(table, c) != NULL; } inline char value(char c) { const char *p = strchr(table, c); if(p) { return p-table; } else { return 0; } } int UnBase64(unsigned char *dest, const unsigned char *src, int srclen) { *dest = 0; if(*src == 0) { return 0; } unsigned char *p = dest; do { char a = value(src[0]); char b = value(src[1]); char c = value(src[2]); char d = value(src[3]); *p++ = (a << 2) | (b >> 4); *p++ = (b << 4) | (c >> 2); *p++ = (c << 6) | d; if(!isbase64(src[1])) { p -= 2; break; } else if(!isbase64(src[2])) { p -= 2; break; } else if(!isbase64(src[3])) { p--; break; } src += 4; while(*src && (*src == 13 || *src == 10)) src++; } while(srclen-= 4); *p = 0; return p-dest; }