openssl-base64编码(二)

Posted by 周思进 on May 15, 2022

前文<openssl-base64编码> 对base64原理和命令使用做了说明,本文对 openssl 的接口调用实现 base64 编解码操作。

openssl 提供两类 EVP 封装接口可进行 base64 编码,一种像摘要计算调用 EVP 接口一样,需要 init、update 等操作,此接口对于最终输出会进行格式化操作,每64个字符就会进行换行,具体示例代码如下:

// 省略错误判断
int base64_encode(unsigned char *p_in, size_t inlen, unsigned char *p_out, size_t *outlen)
{
    EVP_ENCODE_CTX *ctx = NULL;
    int total_len = 0;
    int tmp_len = 0;

    ctx = EVP_ENCODE_CTX_new();
    EVP_EncodeInit(ctx);
    EVP_EncodeUpdate(ctx, p_out, &tmp_len, p_in, inlen);
    total_len += tmp_len;

    EVP_EncodeFinal(ctx, p_out+total_len, &tmp_len);
    total_len += tmp_len;

    *outlen = total_len;
    EVP_ENCODE_CTX_free(ctx);
    return 0;
}

int base64_decode(unsigned char *p_in, size_t inlen, unsigned char *p_out, size_t *outlen)
{
    EVP_ENCODE_CTX *ctx = NULL;
    int total_len = 0;
    int tmp_len = 0;

    ctx = EVP_ENCODE_CTX_new();
    EVP_DecodeInit(ctx);
    EVP_DecodeUpdate(ctx, p_out, &tmp_len, p_in, inlen);
    total_len += tmp_len;

    EVP_DecodeFinal(ctx, p_out+total_len, &tmp_len);   // 无实际数据要处理,tmp_len为0
    total_len += tmp_len;

    *outlen = total_len;
    EVP_ENCODE_CTX_free(ctx);
    return 0;
}

测试代码及执行结果:

int main(int argc, char const *argv[]) {
    unsigned char str[] = {"1234567812345678123456781234567812345678123456781234567812345678"};
    unsigned char in[1024] = {0};
    unsigned char out[1024] = {0};
    int outlen = sizeof(out);
    int inlen = sizeof(in);

    base64_encode(str, strlen(str), out, &outlen);
    printf("out:\n%s\n", out);
    base64_decode(out, outlen, in, &inlen);
    printf("in:\n%s\n", in);

    return 0;
}

// 运行结果
base64 $ ./a.out
out:
MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4
MTIzNDU2NzgxMjM0NTY3OA==

in:
1234567812345678123456781234567812345678123456781234567812345678

实际常用场景并不需要对输出结果进行格式化输出,所以编程会直接调用 EVP_EncodeBlock 完整编码操作,但解码操作结果长度,需要自己根据输入数据有多少字节进行了填充,进行相减,具体如下:

// 省略错误判断
int base64_encode(unsigned char *p_in, size_t inlen, unsigned char *p_out, size_t *outlen)
{
    *outlen = EVP_EncodeBlock(p_out, p_in, inlen);
    return 0;
}

int base64_decode(unsigned char *p_in, size_t inlen, unsigned char *p_out, size_t *outlen)
{
    unsigned char *p = NULL;
    int i = 0;
    int pad = 0;
    int len = 0;

    p = p_in + inlen - 1;
    for (i = 0; i < 4; i++)
    {
        if (*p == '=')
            pad++;
        p--;
    }

    printf("padlen:%d\n", pad);
    len = EVP_DecodeBlock(p_out, p_in, inlen);
    printf("len:%d\n", len);
    *outlen = len - pad;
    return 0;
}

测试代码及执行结果:

int main(int argc, char const *argv[]) {
    unsigned char str[] = {"1234567812345678123456781234567812345678123456781234567812345678"};
    unsigned char in[1024] = {0};
    unsigned char out[1024] = {0};
    int outlen = sizeof(out);
    int inlen = sizeof(in);

    base64_encode(str, strlen(str), out, &outlen);
    printf("out:\n%s\n", out);
    base64_decode(out, outlen, in, &inlen);
    printf("inlen:%d; in:\n%s\n", inlen, in);

    return 0;
}

// 运行结果
base64 $ ./a.out
out:
MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3ODEyMzQ1Njc4MTIzNDU2NzgxMjM0NTY3OA==
padlen:2
len:66
inlen:64; in:
1234567812345678123456781234567812345678123456781234567812345678