这次使用openssl,发现openssl不仅可以用来做加密和解密,实际上也可以用来做文件的处理,比如base64转码、解码,文件md5的计算等。实现这些,即可以从命令行去做,也可以通过代码去实现。
1. 命令行操作
1. base64转码、解码
转码:
openssl base64 -in file.bin -out file.b64 对file.bin文件进行转码,转码后的文件为file.b64
上述命令转码生成的文件带有换行符,如果想要生成的base64文件不带换行符,那么使用
openssl base64 -A -in file.bin -out file.b64。
解码:
openssl base64 -d -in file.b64 -out file.bin 对file.b64进行解码,解码后的文件为file.bin
如果转码时有“-A”,那么解码时也要有-A。
注:对于大文件,加A可能会有问题。我拿2.3G的绿皮书电影做测试,发现加A后,转码再解码,文件变更大了,电影也无法播放。不加A就没有问题。另外,man openssl-enc中,搜索Bug,也有如下描述:
The -A option when used with large files doesn't work properly
2. md5码的生成
openssl dgst -md5 a.out 计算a.out文件的md5码
如果生成sha256码,将上面的md5改为sha256即可。
2. 代码实现文件操作
1. base64转码、解码
有换行符的转码方式,使用的接口为:
EVP_ENCODE_CTX_new,EVP_EncodeUpdate,EVP_EncodeFinal,和EVP_ENCODE_CTX_free。
static int t_tob64(char *dst, const unsigned char *src, int size)
{
EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
int outl = 0, outl2 = 0;
if (ctx == NULL)
return 0;
EVP_EncodeInit(ctx);
if (!EVP_EncodeUpdate(ctx, (unsigned char *)dst + outl, &outl2, src,
size)) {
EVP_ENCODE_CTX_free(ctx);
return 0;
}
cout<<"first len is "<
这个函数将长度为size个字节的,地址为src的源数据,转码后保存在dst中。dst的地址长度应该是多大呢?
我参考了一些资料,经过验证,用下面这个方法,计算出来的与实际需要使用的更加接近。inputLen是输入字符串的长度。outLen是预计输出字符串的长度。76是指根据RF2045,base64转码后,每超过76个字符,会添加回车换行。前面的%3操作,是补位操作。
if(inputLen%3==0)
outLen = inputLen/3 *4;
else
outLen = (inputLen/3 +1)*4;
outLen = outLen + (outLen-1)/76*2;
按照这种方法来设计缓存,比实际需要的长度略大。而且我自己最后生成base64时发现,并不是76个字符有一个换行符,而是64个字符有一个换行符。如果把上面的76换成64,然后将回车符用一个字符计算,也就是outLen = outLen + (outLen-1)/64,这样预算的缓存长度最后与实际需要的缓存长度是匹配的。
不带换行符的转码方式,使用的接口:
EVP_EncodeBlock。
int t_tob64_block(char *dst, const unsigned char *src, int size)
{
int outLen=0;
outLen = EVP_EncodeBlock((unsigned char *)dst ,src,size);
return outLen;
}
这种的长度计算只要考虑补位的长度即可。
2. md5等码值的计算
使用的接口包括EVP_MD_CTX_new,EVP_DigestInit_ex,EVP_DigestUpdate,EVP_DigestFinal_ex和EVP_MD_CTX_free。
int main(int argc, char *argv[])
{
EVP_MD_CTX *mdctx;
const EVP_MD *md;
string content = "";
readFile("file.bin",content);
unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len, i;
if (argv[1] == NULL) {
printf("Usage: mdtest digestname\n");
exit(1);
}
md = EVP_get_digestbyname(argv[1]);
if (md == NULL) {
printf("Unknown message digest %s\n", argv[1]);
exit(1);
}
mdctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(mdctx, md, NULL);
EVP_DigestUpdate(mdctx, (char*)content.c_str(), content.length());
//EVP_DigestUpdate(mdctx, mess2, strlen(mess2));
EVP_DigestFinal_ex(mdctx, md_value, &md_len);
EVP_MD_CTX_free(mdctx);
printf("Digest is: ");
for (i = 0; i < md_len; i++)
printf("%02x", md_value[i]);
printf("\n");
exit(0);
}
这里输入的参数是码值算法的名字,通过EVP_get_digestbyname函数,根据名字,找到对应的算法。名字怎么命名呢?openssl dgst -list就可以找出所有的支持的算法。如下所示:
因此使用md5,只要输入md5即可。