ฉันจะเข้ารหัส base64 (ถอดรหัส) ใน C ได้อย่างไร


132

ฉันมีข้อมูลไบนารีในตัวแปรถ่านที่ไม่ได้ลงชื่อ ฉันต้องการแปลงเป็น PEM base64 ใน c ฉันดูในไลบรารี openssl แต่ไม่พบฟังก์ชันใด ๆ ร่างกายมีความคิดบ้างไหม?

c  base64 

11
ฉันมีที่เก็บ github ที่ทดสอบฟังก์ชัน base64 และ unbase64 ส่วนหัวเดียวที่คุณต้องการคือbase64.h
bobobobo

น่าเสียดายที่คำตอบส่วนใหญ่ที่นี่ไม่ตรงประเด็นโดยสิ้นเชิง C ++ ไม่ใช่ C.
Joe Coder

คำตอบ:


106

นี่คือสิ่งที่ฉันใช้:

#include <stdint.h>
#include <stdlib.h>


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 ที่เข้ารหัสจะได้รับการประมวลผล


7
มันไม่สมเหตุสมผลเลยที่จะใช้สิ่งนี้หากมีห้องสมุด
Diego Woitasen

11
คุณสามารถข้าม libm และ math.h "dependency" รวมทั้งความจำเป็นในการดำเนินการทศนิยม (ซึ่งช้าในฮาร์ดแวร์บางตัว) โดยใช้ *output_length = ((input_length - 1) / 3) * 4 + 4;ในส่วนเริ่มต้นของ base64_encode
Fabian Henze

9
ฉันรู้ว่ามัน "ไม่มีการตรวจสอบข้อผิดพลาด" แต่โดยเฉพาะอย่างยิ่งสังเกตว่าแม้ว่าตารางการถอดรหัสในตัวถอดรหัสจะเป็นอาร์เรย์ 256 เนื่องจาก char ถูกลงนามในสถาปัตยกรรมส่วนใหญ่คุณจึงจัดทำดัชนีตั้งแต่ -128 ถึง 127 อักขระใด ๆ ที่มีค่าสูง ชุดบิตจะทำให้คุณอ่านนอกหน่วยความจำที่จัดสรร การบังคับให้การค้นหาข้อมูลเป็นถ่านที่ไม่ได้ลงชื่อจะเป็นการล้างข้อมูลนั้น คุณยังคงเอาขยะมาทิ้ง แต่คุณจะไม่ผิดพลาด
bitmusher

1
คุณมีปัญหาอาร์เรย์นอกขอบเขตในbuild_decoding_table. encoding_table[64]ที่encoding_table[255]จะไม่มีอยู่
bobobobo

3
การถอดรหัสยังไม่สามารถจัดการกับสถานการณ์ที่ส่วนขยาย "=" หายไป ร่วมกับข้อผิดพลาดอื่น ๆ ทำให้การใช้งานค่อนข้างแย่
Lothar

57

ฉันรู้ว่าคำถามนี้ค่อนข้างเก่า แต่ฉันเริ่มสับสนกับจำนวนวิธีแก้ปัญหาที่มีให้ - แต่ละคำถามอ้างว่าเร็วและดีกว่า ฉันรวบรวมโปรเจ็กต์บน 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

นี่คือเวลา (ในหน่วยไมโครวินาที) ในการเข้ารหัสข้อมูล 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 <j@w1.fi>
*
* 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 <string>

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

ข้อมูลโค้ดของ Polfosol จากbase64 ถอดรหัส snippet ใน c ++นั้นเร็วที่สุดโดยมีค่าเกือบ 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;
}

19
ฉันไม่คิดว่า std :: string และฟังก์ชันอื่น ๆ ที่คุณใช้เป็นส่วนหนึ่งของ ANSI C คำถามที่ถามหารหัส C และติดแท็ก C จะได้รับคำตอบที่ได้รับการโหวตมากที่สุดใน C ++
SF.

4
อ้างถึงตัวเองว่า "ฉันไม่ได้ จำกัด ตัวเองไว้ที่อัลกอริทึม C - หากการใช้งานหนึ่งทำงานได้ดีใน C ++ ก็สามารถย้อนกลับไปที่ C ได้อย่างง่ายดาย" เพิ่มchar* outStrพารามิเตอร์อื่นและเขียนลงในบัฟเฟอร์นั้นแทนที่จะส่งคืนstd::stringหากคุณต้องการมันเป็นเรื่องเล็กน้อยที่จะทำ ก่อนที่ฉันจะโพสต์สิ่งนี้มีคำตอบ C ++ สองคำพร้อมการโหวตที่นี่
GaspardP

หากต้องการโซลูชันที่ใช้งานได้ดีสำหรับทั้งการถอดรหัสและการเข้ารหัสโดยไม่ต้องใช้รหัสจากสองที่ฉันจะเลือกเวอร์ชัน apache สำหรับ C และโซลูชันของ polfosol สำหรับ C ++
DaedalusAlpha

@GaspardP การถอดรหัสของ Polfosol สามารถใช้กับการเข้ารหัสของ Jouni ได้หรือไม่?
Sam Thomas

33

แต่คุณสามารถทำได้ใน openssl ( openssl encคำสั่งทำ .... ) ดูที่BIO_f_base64()ฟังก์ชัน


ดูเหมือนว่า OP จะใช้ OpenSSL อยู่แล้วด้วยเหตุผลอื่นดังนั้นนี่อาจเป็นวิธีที่ดีที่สุดในการดำเนินการนี้
joshk0

18

นี่คือวิธีแก้ปัญหาของฉันโดยใช้ OpenSSL

/* A BASE-64 ENCODER AND DECODER USING OPENSSL */
#include <openssl/pem.h>
#include <string.h> //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.
}

2
ในบรรทัด "Adds a null-terminator" ฉันได้รับข้อผิดพลาด AddressSanitizer ว่าการเขียนล้นฮีปทีละ 1 ไบต์
bparker

ขอบคุณฉันได้แก้ไขข้อผิดพลาดแล้วนอกเหนือจากการทำการทดสอบอย่างละเอียดด้วยสตริงสุ่มขนาดของไบต์แบบสุ่มเพื่อให้แน่ใจว่าโค้ดทำงานตามที่โฆษณาไว้ :)
schulwitz

1
NICE! ฉันรวบรวมมันด้วย cc -o base base.c -lssl -lcrypto . ไม่มีข้อผิดพลาด มันสร้างผลลัพธ์นี้: Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
clearlight

@schulwitz ฉันมีไฟล์ที่เข้ารหัสเป็นสตริงโดยใช้ python แต่เมื่อฉันถอดรหัสสตริงโดยใช้ฟังก์ชันของคุณและพยายามเขียนผลลัพธ์ที่ถอดรหัสไปยังไฟล์ (ใน C) ฉันไม่ได้รับไฟล์เดียวกันกลับมา สตริงที่เข้ารหัสถูกต้อง `` const ไม่ได้ลงนาม char * jarFile = "<ไฟล์ที่เข้ารหัส>"; int หลัก () {print_version (); ไฟล์ * fp; ถ่าน * ออก = base64decode (jarFile, strlen (jarFile)); fp = fopen ("file.jar", "wb"); ถ้า (fp == NULL) {printf ("File open failed"); กลับ 1; } fwrite (ออก, sizeof (ออก), 1, fp); fclose (FP); ฟรี (ออก); กลับ 0; } ``
Sam Thomas

1
@SamThomas การใช้ strlen ทำงานในตัวอย่างของฉันเพราะฉันสร้างสตริงที่มีตัวเทอร์มิเนเตอร์ว่างเพียงตัวเดียว (และอยู่ท้ายสตริง) ดู: tutorialspoint.com/cprogramming/c_strings.htm การ อ่านใน jarFile ด้วย strlen จะล้มเหลวเนื่องจากเทอร์มิเนเตอร์ว่างอาจอยู่ตรงกลางของไฟล์ไบนารีของคุณทำให้ค่า bytes_to_decode ยุ่งเหยิง ดู: stackoverflow.com/questions/24596189/… ค้นหาขนาดไฟล์ของคุณด้วยวิธีอื่น: stackoverflow.com/questions/238603/…
schulwitz

18

glib มีฟังก์ชันสำหรับการเข้ารหัส base64: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html


7
แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป
พนักงานทุกคนมีความสำคัญ

16

libb64มีทั้ง C และ C ++ API มีน้ำหนักเบาและอาจเป็นการนำไปใช้งานต่อสาธารณะที่เร็วที่สุด นอกจากนี้ยังเป็นไลบรารีการเข้ารหัส base64 แบบสแตนด์อะโลนโดยเฉพาะซึ่งอาจดีถ้าคุณไม่ต้องการสิ่งอื่น ๆ ทั้งหมดที่มาจากการใช้ไลบรารีขนาดใหญ่เช่น OpenSSL หรือ glib


5
หมายเหตุเกี่ยวกับ libb64: BUFFERSIZE ถูกกำหนดไว้ในไฟล์ make ดังนั้นหากคุณไม่ได้ใช้ make / cmake คุณจะต้องกำหนดด้วยตนเองในไฟล์ส่วนหัวเพื่อที่จะคอมไพล์ได้ ผลงาน / ทดสอบสั้น ๆ VS2012
ทอม

3
ดังที่ทอมกล่าว: #define BUFFERSIZE 16777216คุณสามารถเปลี่ยนเป็น 65536 ได้หากต้องการบัฟเฟอร์ที่เล็กกว่า
jyz

1
ระวัง! หลังจากหนึ่งชั่วโมงของการดีบักฉันพบว่า libb64 ถือว่าcharมีการเซ็นชื่อบนระบบเป้าหมาย ... นี่เป็นปัญหาเนื่องจากbase64_decode_valueสามารถส่งคืนจำนวนลบซึ่งจะถูกโยนไปยังถ่าน
Noir

หมายเหตุการใช้งาน sourceforge จะเพิ่มบรรทัดใหม่ซึ่งไม่ได้รับการสนับสนุนในระดับสากล ส้อมโดย BuLogics บน GitHubขจัดพวกเขาและฉันได้สร้างคำขอดึงขึ้นอยู่กับการหาประโยชน์อย่างมากของคุณ @Noir
ความเป็นด่าง

แม้ว่าลิงก์นี้อาจตอบคำถามได้ แต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์เพื่อการอ้างอิง คำตอบแบบลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าที่เชื่อมโยงเปลี่ยนไป
พนักงานทุกคนมีความจำเป็น

14

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


3
นอกจากนี้ยัง'+'เป็น 62 และ'/'63 ใน PEM base64 ตามที่ OP ขอ นี่คือรายการของbase64 เข้ารหัสสายพันธุ์ ฉันไม่เห็นตัวแปรการเข้ารหัส base64 ที่มีลำดับอักขระที่คุณใช้ แต่คณิตศาสตร์ที่อยู่เบื้องหลังอัลกอริทึมนั้นถูกต้อง
Patrick

2
ดังที่ได้กล่าวไปแล้ว: ระวังอัลกอริทึมนี้ไม่สามารถใช้งานร่วมกับbase64 ทั่วไปได้
Cerber

แล้วการเข้ารหัสล่ะ?
Geremia

14

ผมจำเป็นต้องมี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"
#include <iostream>

static 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

#include <string>

std::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<const unsigned char*>(s.c_str()), s.length());
  std::string decoded = base64_decode(encoded);

9

นี่คือตัวถอดรหัสที่ฉันใช้มาหลายปีแล้ว ...

    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;
    }

* dest = 0 คืออะไร เริ่มต้นเพื่อ?
ทิม

1
มันเป็นเพียงการดำเนินการที่ง่ายมากที่ทำให้แน่ใจว่าบัฟเฟอร์ปลายทางถูกตั้งค่าเป็น NULL ในกรณีที่ผู้โทรไม่ได้ทำเช่นนั้นก่อนการโทรและถ้าบางทีการถอดรหัสล้มเหลวบัฟเฟอร์ที่ส่งคืนจะมีความยาวเป็นศูนย์ ฉันไม่ได้บอกว่าฉันแก้จุดบกพร่องติดตามและทำโปรไฟล์กิจวัตรนี้มันเป็นเพียงสิ่งเดียวที่ฉันใช้มาหลายปี :) เมื่อฉันดูตอนนี้มันไม่จำเป็นต้องมีจริงๆแล้วทำไมเราไม่เรียกมันว่า "แบบฝึกหัดสำหรับผู้อ่าน"? ฮิฮิ .. บางทีฉันจะแก้ไขให้ ขอบคุณที่ชี้ให้ดู!
LarryF

3
UnBase64ฟังก์ชันของคุณอาจทำลายหน่วยความจำหลังจากบัฟเฟอร์ปลายทางหากบัฟเฟอร์นั้นมีขนาดที่แน่นอนที่จำเป็นในการถอดรหัสสตริงที่เข้ารหัส 64 ฐาน ยกตัวอย่างกรณีง่ายๆที่คุณพยายามถอดรหัสสตริงที่เข้ารหัส 64 ฐานต่อไปนี้ "BQ ==" ให้เป็น BYTE เดียวเช่นunsigned char Result = 0; UnBase64(&Result, "BQ==", 4); มันจะทำให้สแตกเสียหาย!
Mike Dinescu

3
ใช่ทำให้เกิดข้อผิดพลาดที่น่ารังเกียจในแอปของเรา ไม่แนะนำ.
Harald Maassen

สวัสดี Larry ขอบคุณสำหรับการแบ่งปันรหัส มันมีประโยชน์มาก!
Federico

4

ในกรณีที่ผู้คนต้องการโซลูชัน c ++ ฉันใส่โซลูชัน OpenSSL นี้เข้าด้วยกัน (สำหรับทั้งการเข้ารหัสและถอดรหัส) คุณจะต้องเชื่อมโยงกับไลบรารี "crypto" (ซึ่งก็คือ OpenSSL) สิ่งนี้ได้รับการตรวจสอบการรั่วไหลด้วย valgrind (แม้ว่าคุณสามารถเพิ่มรหัสตรวจสอบข้อผิดพลาดเพิ่มเติมเพื่อให้ดีขึ้นเล็กน้อย - ฉันรู้ว่าอย่างน้อยฟังก์ชันการเขียนควรตรวจสอบค่าส่งคืน)

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <stdlib.h>

string base64_encode( const string &str ){

    BIO *base64_filter = BIO_new( BIO_f_base64() );
    BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL );

    BIO *bio = BIO_new( BIO_s_mem() );
    BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL );

    bio = BIO_push( base64_filter, bio );

    BIO_write( bio, str.c_str(), str.length() );

    BIO_flush( bio );

    char *new_data;

    long bytes_written = BIO_get_mem_data( bio, &new_data );

    string result( new_data, bytes_written );
    BIO_free_all( bio );

    return result;

}



string base64_decode( const string &str ){

    BIO *bio, *base64_filter, *bio_out;
    char inbuf[512];
    int inlen;
    base64_filter = BIO_new( BIO_f_base64() );
    BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL );

    bio = BIO_new_mem_buf( (void*)str.c_str(), str.length() );

    bio = BIO_push( base64_filter, bio );

    bio_out = BIO_new( BIO_s_mem() );

    while( (inlen = BIO_read(bio, inbuf, 512)) > 0 ){
        BIO_write( bio_out, inbuf, inlen );
    }

    BIO_flush( bio_out );

    char *new_data;
    long bytes_written = BIO_get_mem_data( bio_out, &new_data );

    string result( new_data, bytes_written );

    BIO_free_all( bio );
    BIO_free_all( bio_out );

    return result;

}

BIO_free_all จำเป็นต้องระบุส่วนหัวไม่ใช่ส่วนท้าย - ของห่วงโซ่ชีวภาพของคุณ (เช่น base64_filter) การใช้งานปัจจุบันของคุณมีหน่วยความจำรั่ว
schulwitz

@schulwitz สายไหนรั่ว? Bio_free_all ปลดปล่อยห่วงโซ่ทั้งหมด
Homer6

4

ฉันเขียนหนึ่งเพื่อใช้กับ C ++ มันเร็วมากทำงานกับสตรีมฟรีและโอเพ่นซอร์ส:

https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/

อย่าลังเลที่จะใช้มันหากตรงกับวัตถุประสงค์ของคุณ

แก้ไข: เพิ่มรหัสแบบอินไลน์ตามคำขอ

การเพิ่มประสิทธิภาพทำได้โดยใช้ตารางการค้นหาสำหรับการเข้ารหัสและถอดรหัส _UINT8เป็นunsigned charระบบปฏิบัติการส่วนใหญ่

/** Static Base64 character encoding lookup table */
const char CBase64::encodeCharacterTable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/** Static Base64 character decoding lookup table */
const char CBase64::decodeCharacterTable[256] = {
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
    ,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,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,-1,-1,-1,-1,-1,-1,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,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
    ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1}; 

/*!
\brief Encodes binary data to base 64 character data
\param in The data to encode
\param out The encoded data as characters
*/
void CBase64::Encode(std::istream &in, std::ostringstream &out)
{
    char buff1[3];
    char buff2[4];
    _UINT8 i=0, j;
    while(in.readsome(&buff1[i++], 1))
        if (i==3)
        {
            out << encodeCharacterTable[(buff1[0] & 0xfc) >> 2];
            out << encodeCharacterTable[((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4)];
            out << encodeCharacterTable[((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6)];
            out << encodeCharacterTable[buff1[2] & 0x3f];
            i=0;
        }

    if (--i)
    {
        for(j=i;j<3;j++) buff1[j] = '\0';

        buff2[0] = (buff1[0] & 0xfc) >> 2;
        buff2[1] = ((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4);
        buff2[2] = ((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6);
        buff2[3] = buff1[2] & 0x3f;

        for (j=0;j<(i+1);j++) out << encodeCharacterTable[buff2[j]];

        while(i++<3) out << '=';
    }

}

/*!
\brief Decodes base 64 character data to binary data
\param in The character data to decode
\param out The decoded data
*/
void CBase64::Decode(std::istringstream &in, std::ostream &out)
{
    char buff1[4];
    char buff2[4];
    _UINT8 i=0, j;

    while(in.readsome(&buff2[i], 1) && buff2[i] != '=')
    {
        if (++i==4)
        {
            for (i=0;i!=4;i++)
                buff2[i] = decodeCharacterTable[buff2[i]];

            out << (char)((buff2[0] << 2) + ((buff2[1] & 0x30) >> 4));
            out << (char)(((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2));
            out << (char)(((buff2[2] & 0x3) << 6) + buff2[3]);

            i=0;
        }
    }

    if (i) 
    {
        for (j=i;j<4;j++) buff2[j] = '\0';
        for (j=0;j<4;j++) buff2[j] = decodeCharacterTable[buff2[j]];

        buff1[0] = (buff2[0] << 2) + ((buff2[1] & 0x30) >> 4);
        buff1[1] = ((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2);
        buff1[2] = ((buff2[2] & 0x3) << 6) + buff2[3];

        for (j=0;j<(i-1); j++) out << (char)buff1[j];
    }
}

1
ดูเหมือนว่าบล็อกที่เชื่อมโยงจะไม่มีอยู่ใน URL นั้นอีกต่อไป
HulkHolden

@HulkHolden มันยังมีอยู่ที่นี่tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src

@cpburnz ฉันได้เพิ่มตัวอย่างอินไลน์แล้วตอนนี้และแสดงความคิดเห็นว่าทำไมมันถึงเร็วขอบคุณ

2

การปรับปรุงโค้ดเล็กน้อยจาก ryyst (ผู้ที่ได้รับคะแนนโหวตมากที่สุด) คือการไม่ใช้ตารางถอดรหัสที่จัดสรรแบบไดนามิก แต่เป็นตารางที่คำนวณล่วงหน้า const คงที่ สิ่งนี้ช่วยลดการใช้ตัวชี้และการเริ่มต้นตารางและยังหลีกเลี่ยงการรั่วไหลของหน่วยความจำหากลืมล้างตารางการถอดรหัสด้วย base64_cleanup () (โดยวิธีการใน base64_cleanup () หลังจากเรียกฟรี (decoding_table) ควรมี decoding_table = NULL มิฉะนั้นการเรียก base64_decode โดยไม่ได้ตั้งใจหลังจาก base64_cleanup () จะหยุดทำงานหรือทำให้เกิดพฤติกรรมที่ไม่กำหนด) อีกวิธีหนึ่งคือการใช้ std :: unique_ptr ... แต่ฉันพอใจแค่มี const char [256] บนสแต็กและหลีกเลี่ยงการใช้พอยน์เตอร์พร้อมกัน - โค้ดดูสะอาดและสั้นลงด้วยวิธีนี้

ตารางการถอดรหัสคำนวณได้ดังนี้:

const 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', '+', '/' };

unsigned char decoding_table[256];

for (int i = 0; i < 256; i++)
    decoding_table[i] = '\0';

for (int i = 0; i < 64; i++)
    decoding_table[(unsigned char)encoding_table[i]] = i;

for (int i = 0; i < 256; i++)
    cout << "0x" << (int(decoding_table[i]) < 16 ? "0" : "") << hex << int(decoding_table[i]) << (i != 255 ? "," : "") << ((i+1) % 16 == 0 ? '\n' : '\0');

cin.ignore();

และรหัสแก้ไขที่ฉันใช้คือ:

        static const 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 const unsigned char decoding_table[256] = {
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f,
            0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
            0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
            0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

        char* base64_encode(const unsigned char *data, size_t input_length, size_t &output_length) {

            const int mod_table[] = { 0, 2, 1 };

            output_length = 4 * ((input_length + 2) / 3);

            char *encoded_data = (char*)malloc(output_length);

            if (encoded_data == nullptr)
                return nullptr;

            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 (input_length % 4 != 0)
                return nullptr;

            output_length = input_length / 4 * 3;

            if (data[input_length - 1] == '=') (output_length)--;
            if (data[input_length - 2] == '=') (output_length)--;

            unsigned char* decoded_data = (unsigned char*)malloc(output_length);

            if (decoded_data == nullptr)
                return nullptr;

            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;

        };

1

นี่คือตัวถอดรหัสที่เขียนขึ้นโดยเฉพาะเพื่อหลีกเลี่ยงความจำเป็นในการใช้บัฟเฟอร์โดยการเขียนลงในฟังก์ชันพัตชาร์โดยตรง สิ่งนี้อ้างอิงจากการใช้งานของ wikibook https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C

วิธีนี้ใช้ไม่ง่ายเหมือนกับตัวเลือกอื่น ๆ ข้างต้น อย่างไรก็ตามสามารถใช้ได้ในระบบฝังตัวซึ่งคุณต้องการถ่ายโอนไฟล์ขนาดใหญ่โดยไม่ต้องจัดสรรบัฟเฟอร์ขนาดใหญ่อื่นเพื่อเก็บสตริง datauri base64 ที่เป็นผลลัพธ์ (น่าเสียดายที่ datauri ไม่ยอมให้คุณระบุชื่อไฟล์)

void datauriBase64EncodeBufferless(int (*putchar_fcptr)(int), const char* type_strptr, const void* data_buf, const size_t dataLength)
{
  const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  const uint8_t *data = (const uint8_t *)data_buf;
  size_t x = 0;
  uint32_t n = 0;
  int padCount = dataLength % 3;
  uint8_t n0, n1, n2, n3;

  size_t outcount = 0;
  size_t line = 0;

  putchar_fcptr((int)'d');
  putchar_fcptr((int)'a');
  putchar_fcptr((int)'t');
  putchar_fcptr((int)'a');
  putchar_fcptr((int)':');
  outcount += 5;

  while (*type_strptr != '\0')
  {
    putchar_fcptr((int)*type_strptr);
    type_strptr++;
    outcount++;
  }

  putchar_fcptr((int)';');
  putchar_fcptr((int)'b');
  putchar_fcptr((int)'a');
  putchar_fcptr((int)'s');
  putchar_fcptr((int)'e');
  putchar_fcptr((int)'6');
  putchar_fcptr((int)'4');
  putchar_fcptr((int)',');
  outcount += 8;

  /* increment over the length of the string, three characters at a time */
  for (x = 0; x < dataLength; x += 3)
  {
    /* these three 8-bit (ASCII) characters become one 24-bit number */
    n = ((uint32_t)data[x]) << 16; //parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0

    if((x+1) < dataLength)
       n += ((uint32_t)data[x+1]) << 8;//parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0

    if((x+2) < dataLength)
       n += data[x+2];

    /* this 24-bit number gets separated into four 6-bit numbers */
    n0 = (uint8_t)(n >> 18) & 63;
    n1 = (uint8_t)(n >> 12) & 63;
    n2 = (uint8_t)(n >> 6) & 63;
    n3 = (uint8_t)n & 63;

    /*
     * if we have one byte available, then its encoding is spread
     * out over two characters
     */

    putchar_fcptr((int)base64chars[n0]);
    putchar_fcptr((int)base64chars[n1]);
    outcount += 2;

    /*
     * if we have only two bytes available, then their encoding is
     * spread out over three chars
     */
    if((x+1) < dataLength)
    {
      putchar_fcptr((int)base64chars[n2]);
      outcount += 1;
    }

    /*
     * if we have all three bytes available, then their encoding is spread
     * out over four characters
     */
    if((x+2) < dataLength)
    {
      putchar_fcptr((int)base64chars[n3]);
      outcount += 1;
    }

    /* Breaking up the line so it's easier to copy and paste */
    int curr_line = (outcount/80);
    if( curr_line != line )
    {
      line = curr_line;
      putchar_fcptr((int)'\r');
      putchar_fcptr((int)'\n');
    }
  }

  /*
  * create and add padding that is required if we did not have a multiple of 3
  * number of characters available
  */
  if (padCount > 0)
  {
    for (; padCount < 3; padCount++)
    {
      putchar_fcptr((int)'=');
    }
  }

  putchar_fcptr((int)'\r');
  putchar_fcptr((int)'\n');
}

นี่คือการทดสอบ

#include <stdio.h>
#include <stdint.h>
#include <string.h>

int main(void)
{
  char str[] = "test";
  datauriBase64EncodeBufferless(putchar, "text/plain;charset=utf-8", str, strlen(str));
  return 0;
}

ผลลัพธ์ที่คาดหวัง: data:text/plain;charset=utf-8;base64,dGVzdA==


1

EVP_EncodeBlockและEVP_DecodeBlockฟังก์ชั่นทำให้มันง่ายมาก:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>

char *base64(const unsigned char *input, int length) {
  const int pl = 4*((length+2)/3);
  char *output = calloc(pl+1, 1); //+1 for the terminating null that EVP_EncodeBlock adds on
  const int ol = EVP_EncodeBlock(output, input, length);
  if (ol != pl) { fprintf(stderr, "Whoops, encode predicted %d but we got %d\n", pl, ol); }
  return output;
}

unsigned char *decode64(const char *input, int length) {
  const int pl = 3*length/4;
  unsigned char *output = calloc(pl+1, 1);
  const int ol = EVP_DecodeBlock(output, input, length);
  if (pl != ol) { fprintf(stderr, "Whoops, decode predicted %d but we got %d\n", pl, ol); }
  return output;
}

0

โซลูชันนี้ใช้คำตอบของ Schulwitz (การเข้ารหัส / ถอดรหัสโดยใช้ OpenSSL) แต่สำหรับ C ++ (คำถามเดิมเกี่ยวกับ C แต่มีคำตอบ C ++ อื่นอยู่แล้วที่นี่) และใช้การตรวจสอบข้อผิดพลาด (จึงปลอดภัยกว่าที่จะใช้) :

#include <openssl/bio.h>

std::string base64_encode(const std::string &input)
{
    BIO *p_bio_b64 = nullptr;
    BIO *p_bio_mem = nullptr;

    try
    {
        // make chain: p_bio_b64 <--> p_bio_mem
        p_bio_b64 = BIO_new(BIO_f_base64());
        if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
        BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less

        p_bio_mem = BIO_new(BIO_s_mem());
        if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
        BIO_push(p_bio_b64, p_bio_mem);

        // write input to chain
        // write sequence: input -->> p_bio_b64 -->> p_bio_mem
        if (BIO_write(p_bio_b64, input.c_str(), input.size()) <= 0)
            { throw std::runtime_error("BIO_write failed"); }

        if (BIO_flush(p_bio_b64) <= 0)
            { throw std::runtime_error("BIO_flush failed"); }

        // get result
        char *p_encoded_data = nullptr;
        auto  encoded_len    = BIO_get_mem_data(p_bio_mem, &p_encoded_data);
        if (!p_encoded_data) { throw std::runtime_error("BIO_get_mem_data failed"); }

        std::string result(p_encoded_data, encoded_len);

        // clean
        BIO_free_all(p_bio_b64);

        return result;
    }
    catch (...)
    {
        if (p_bio_b64) { BIO_free_all(p_bio_b64); }
        throw;
    }
}

std::string base64_decode(const std::string &input)
{
    BIO *p_bio_mem = nullptr;
    BIO *p_bio_b64 = nullptr;

    try
    {
        // make chain: p_bio_b64 <--> p_bio_mem
        p_bio_b64 = BIO_new(BIO_f_base64());
        if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); }
        BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines

        p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length());
        if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); }
        BIO_push(p_bio_b64, p_bio_mem);

        // read result from chain
        // read sequence (reverse to write): buf <<-- p_bio_b64 <<-- p_bio_mem
        std::vector<char> buf((input.size()*3/4)+1);
        std::string result;
        for (;;)
        {
            auto nread = BIO_read(p_bio_b64, buf.data(), buf.size());
            if (nread  < 0) { throw std::runtime_error("BIO_read failed"); }
            if (nread == 0) { break; } // eof

            result.append(buf.data(), nread);
        }

        // clean
        BIO_free_all(p_bio_b64);

        return result;
    }
    catch (...)
    {
        if (p_bio_b64) { BIO_free_all(p_bio_b64); }
        throw;
    }
}

โปรดทราบว่า base64_decode ส่งคืนสตริงว่างหากอินพุตไม่ถูกต้องตามลำดับ base64 (openssl ทำงานในลักษณะดังกล่าว)


อืม ... การใช้ไลบรารี openssl ในการถอดรหัส / เข้ารหัส base64 จะใช้โค้ดมากกว่าการใช้งานโดยตรง (คำตอบที่ดีที่สุดสำหรับคำถามนี้) ...
anton_rh

-2

นี่คือตัวเข้ารหัสเวอร์ชันที่ได้รับการปรับให้เหมาะสมสำหรับคำตอบที่ยอมรับซึ่งรองรับการแบ่งบรรทัดสำหรับ MIME และโปรโตคอลอื่น ๆ (สามารถใช้การเพิ่มประสิทธิภาพของซิมลาร์กับตัวถอดรหัสได้):

 char *base64_encode(const unsigned char *data,
                    size_t input_length,
                    size_t *output_length,
                    bool addLineBreaks)

    *output_length = 4 * ((input_length + 2) / 3);
    if (addLineBreaks) *output_length += *output_length / 38; //  CRLF after each 76 chars

    char *encoded_data = malloc(*output_length);
    if (encoded_data == NULL) return NULL;

    UInt32 octet_a;
    UInt32 octet_b;
    UInt32 octet_c;
    UInt32 triple;
    int lineCount = 0;
    int sizeMod = size - (size % 3); // check if there is a partial triplet
    // adding all octet triplets, before partial last triplet
    for (; offset < sizeMod; ) 
    {
        octet_a = data[offset++];
        octet_b = data[offset++];
        octet_c = data[offset++];

        triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

        encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];
        if (addLineBreaks)
        {
            if (++lineCount == 19)
            {
                encoded_data[mBufferPos++] = 13;
                encoded_data[mBufferPos++] = 10;
                lineCount = 0;
            }
        }
    }

    // last bytes
    if (sizeMod < size)
    {
        octet_a = data[offset++]; // first octect always added
        octet_b = offset < size ? data[offset++] : (UInt32)0; // conditional 2nd octet
        octet_c = (UInt32)0; // last character is definitely padded

        triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

        encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];

        // add padding '='
        sizeMod = size % 3; 
        // last character is definitely padded
        encoded_data[mBufferPos - 1] = (byte)'=';
        if (sizeMod == 1) encoded_data[mBufferPos - 2] = (byte)'=';
    }
 }

snipped ของคุณไม่สามารถคอมไพล์ได้: ไม่มีตัวแปรขนาดและฉันหวังว่ามันจะไม่ใช่ตัวแปรส่วนกลางในโค้ดของคุณ
Max Lapshin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.