BASE64编码的由来:
因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASCII码的控制字符就 不能通过邮件传送。这样用途就受到了很大的限制,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。最好的方法就是在不改变传统协议的情 况下,做一种扩展方案来支持二进制文件的传送。把不可打印的字符也能用可打印字符来表示,问题就解决了。BASE64编码应运而生,BASE64就是一种基于64个可打印字符来表示二进制数据的表示方法。
本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。
BASE64相关的头文件在evp.h中、源文件在crypto/evp目录中,主要为encode.c。
主要结构:
struct evp_Encode_Ctx_st {
int num;
int length;
unsigned char enc_data[80];
int line_num;
unsigned int flags;
};
typedef struct evp_Encode_Ctx_st EVP_ENCODE_CTX;
这个结构定义了BASE64编解码内部的缓冲信息。主要字段含义:
num—— 当前缓冲的未编码的块大小。
length —— 定义内部进行编解码计算的块大小,如编码时设定为48,解码时设定为64。
enc_data —— 内部编解码缓冲区。
line_num —— 行号,未使用。
flags—— 相关标志,如EVP_ENCODE_CTX_NO_NEWLINES表示编码时执行分行处理。
在1.1.1中,大多数的数据结构已经不再向使用者开放,从封装的角度来看,这是更合理的。如果你在头文件中找不到结构定义,不妨去源码中搜一搜。
主要函数:
EVP_ENCODE_CTX *EVP_ENCODE_CTX_new(void);
创建编解码上下文对象,编码和解码的上下文对象是一样的。
void EVP_ENCODE_CTX_free(EVP_ENCODE_CTX *ctx);
释放编解码上下文对象。
void EVP_EncodeInit(EVP_ENCODE_CTX *ctx);
初使化编码上下文。
int EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl);
对一段内存执行编码,输出编码数据。
成功返回1,失败返回0。
void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl);
接收余下的编码数据。
int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int n);
代替上面的三个函数,直接一次性编码。
void EVP_DecodeInit(EVP_ENCODE_CTX *ctx);
初使化解码上下文。
int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl);
对一段内存执行解码,输出解码数据。返回值定义:
-1 出错
0 部分数据在上下文中,需要继续调用EVP_DecodeUpdate或EVP_DecodeFinal。
1 完全处理完成。
int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned
char *out, int *outl);
接收余下的解码数据。
int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n);
代替上面的三个函数,直接一次性解码。
使用举例:
下面这个例子同时使用流式和整体方法演示了BASE64编码和解码。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/evp.h>
namespace dakuang {}
int main(int argc, char* argv[])
{
{
printf("use stream EVP method >> \n");
EVP_ENCODE_CTX* pCtx = EVP_ENCODE_CTX_new();
const char* pTextSrc = "1234567890abcdefg";
int nTextSrcLen = strlen(pTextSrc);
unsigned char* pCipher = (unsigned char*)malloc(nTextSrcLen * 2);
memset(pCipher, 0, nTextSrcLen * 2);
int nCipherLen = 0;
EVP_EncodeInit(pCtx);
int nOutLen = 0;
int ret = EVP_EncodeUpdate(pCtx, pCipher + nCipherLen, &nOutLen, (const unsigned char*)pTextSrc, nTextSrcLen);
printf("encode ret:%d \n", ret);
printf("encode cipher len:%d body:[%s] \n", nOutLen, pCipher + nCipherLen);
nCipherLen += nOutLen;
nOutLen = 0;
EVP_EncodeFinal(pCtx, pCipher + nCipherLen, &nOutLen);
printf("encode cipher len:%d body:[%s] \n", nOutLen, pCipher + nCipherLen);
nCipherLen += nOutLen;
EVP_ENCODE_CTX_free(pCtx);
pCtx = EVP_ENCODE_CTX_new();
unsigned char* pText = (unsigned char*)malloc(nCipherLen);
memset(pText, 0, nCipherLen);
int nTextLen = 0;
EVP_DecodeInit(pCtx);
nOutLen = 0;
ret = EVP_DecodeUpdate(pCtx, pText + nTextLen, &nOutLen, (const unsigned char*)pCipher, nCipherLen);
printf("decode ret:%d \n", ret);
printf("decode text len:%d body:[%s] \n", nOutLen, pText + nTextLen);
nTextLen += nOutLen;
nOutLen = 0;
//EVP_DecodeFinal(pCtx, pCipher, &nCipherLen);
EVP_DecodeFinal(pCtx, pText + nTextLen, &nOutLen);
printf("decode text len:%d body:[%s] \n", nOutLen, pText + nTextLen);
nTextLen += nOutLen;
EVP_ENCODE_CTX_free(pCtx);
free(pCipher);
free(pText);
}
{
printf("use block EVP method >> \n");
const char* pTextSrc = "1234567890abcdefg";
int nTextSrcLen = strlen(pTextSrc);
unsigned char* pCipher = (unsigned char*)malloc(nTextSrcLen * 2);
int nCipherLen = EVP_EncodeBlock(pCipher, (const unsigned char*)pTextSrc, nTextSrcLen);
printf("encode ret:%d \n", nCipherLen);
printf("cipher:[%s] \n", pCipher);
unsigned char* pText = (unsigned char*)malloc(nCipherLen);
int nTextLen = EVP_DecodeBlock(pText, (const unsigned char*)pCipher, nCipherLen);
printf("decode ret:%d \n", nTextLen);
printf("text:[%s] \n", pText);
free(pCipher);
free(pText);
}
return 0;
}
输出:
use stream EVP method >>
encode ret:1
encode cipher len:0 body:[]
encode cipher len:25 body:[MTIzNDU2Nzg5MGFiY2RlZmc=
]
decode ret:0
decode text len:17 body:[1234567890abcdefg]
decode text len:0 body:[]
use block EVP method >>
encode ret:24
cipher:[MTIzNDU2Nzg5MGFiY2RlZmc=]
decode ret:18
text:[1234567890abcdefg]