整理下Base64相关的东西(OpenSSL/GO...)

Base64在不同语言对接时,其实是有些小坑的,之前有碰过。

首先Base64的是啥? 阮一峰有一篇写得很赞的文章说得很明白。

然后是怎么编码?

我用OpenSSL写了个Base64解编码实现:

/*
 用openssl来做Base64加解密.
 Author: xcl
 Date:2015-9-17
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>

#include "openssl/ssl.h"  
#include <openssl/bio.h>
#include <openssl/evp.h>

#if defined(WIN32) || defined(_WIN64)
	#pragma comment(lib, "libeay32.lib")
	#pragma comment(lib, "ssleay32.lib")
#endif

/*
NO_PADDING : 略去加密字符串最后的”=”
NO_WRAP : 略去所有的换行符

Android中的Base64.DEFAULT会在每超过76个字符后,自动加换行符,
并且在字符串最后也会加一个换行符.
所以与其对接时(比如IOS)要注意这点.可建议其选Base64.NO_WRAP类型.

而OpenSSL的命令行进行Base64时,则每超过64个字符就自动加换行符,并且也在最末尾自动加换行.
解编码命令行如下:
echo "Hello"|openssl enc -base64 
echo "SGVsbG8K"|openssl enc -base64 -d

*/
enum Base64{ NO_PADDING = 0, NO_WRAP};

std::string  Decode(std::string data,const int mode) { 
	printf("[Decode] 解密前:%s\r\n", data.c_str());
	size_t length = data.length();
	if (length == 0)return "";

	BIO *b64 = BIO_new(BIO_f_base64());
	if (mode == Base64::NO_WRAP) {
		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
	}
	 
	BIO *mem = nullptr;
	mem = BIO_new_mem_buf(const_cast<char *>(data.c_str()), -1);
	mem = BIO_push(b64, mem);

	//char inbuf[512];
	//int inlen;
	//while ((inlen = BIO_read(b64, inbuf, 512)) > 0) {
	//	printf("解密后:%s \n\r\n", inbuf);
	//}		
	char * buffer = (char *)malloc(length);
	memset(buffer, 0, length);
	BIO_read(mem, buffer, length);	
	std::string ret(buffer);
	free(buffer);

	BIO_free_all(mem);
	printf("[Decode] 解密后:%s\r\n", ret.c_str());
	return ret;
}


std::string Encode(std::string data,const int mode) {	
	printf("[Encode] 加密前:%s\r\n", data.c_str());
	if (data.length() == 0)return "";

	//写入时,采用base64编码 
	BIO *b64 = BIO_new(BIO_f_base64());

	// 如果打开 BIO_FLAGS_BASE64_NO_NL ,则传入时要带"\n" 即"Hello\n",
	// 否则,还原后会丢失两个字符.
	if (Base64::NO_WRAP == mode) {
		data.append("\n");
		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
	}

	// Create a memory BIO
	BIO *mem = BIO_new(BIO_s_mem());
	b64 = BIO_push(b64, mem);

	BIO_write(b64, data.c_str(),data.length());
	BIO_flush(b64);
	//BIO_puts(b64, data.c_str());

	//得到base64后的字符串
	BUF_MEM *pBuf = nullptr;
	BIO_get_mem_ptr(mem, &pBuf);
	std::string ret(pBuf->data);	
	free(pBuf);

	BIO_set_close(b64, BIO_NOCLOSE);
	BIO_free_all(b64);

	printf("[Encode] 加密后:%s\r\n", ret.c_str());
	return ret;
}

void main() {
	std::string s = "Hello";

	printf("//\r\n");
	printf("NO_PADDING:\r\n");
	Base64 mode = Base64::NO_PADDING;
	s = Encode(s,mode);
    Decode(s,mode);

	printf("//\r\n");
	printf("NO_WRAP:\r\n");
	
	s = "Hello";
	mode = Base64::NO_WRAP;
	s = Encode(s, mode);
	Decode(s, mode);

	system("pause");	
}

/*
//
NO_PADDING:
[Encode] 加密前:Hello
[Encode] 加密后:SGVsbG8=

[Decode] 解密前:SGVsbG8=

[Decode] 解密后:Hello
//
NO_WRAP:
[Encode] 加密前:Hello

[Encode] 加密后:SGVsbG8K
[Decode] 解密前:SGVsbG8K
[Decode] 解密后:Hello

请按任意键继续. . .
*/

对于Golang而言,官网就提供了一个很好的例子,有标准的Base64和URL的编码我在这贴下这个例子:

/* 
 Go的Base64加解密. 
 Author: xcl 
 Date:2015-9-17 
*/  
package main

import b64 "encoding/base64"
import "fmt"

func main() {
	data := "abc123!?$*&()'-=@~"

	fmt.Println(data)

	sEnc := b64.StdEncoding.EncodeToString([]byte(data))
	fmt.Println("STD编码:", sEnc)

	sDec, _ := b64.StdEncoding.DecodeString(sEnc)
	fmt.Println("STD解码:", string(sDec))

	uEnc := b64.URLEncoding.EncodeToString([]byte(data))
	fmt.Println("URL编码:", uEnc)
	uDec, _ := b64.URLEncoding.DecodeString(uEnc)
	fmt.Println("URL解码:", string(uDec))
}

/*
运行结果:
    a+b/c123!?$*&()'-=@~
    STD编码: YStiL2MxMjMhPyQqJigpJy09QH4=
    STD解码: a+b/c123!?$*&()'-=@~
    URL编码: YStiL2MxMjMhPyQqJigpJy09QH4=
    URL解码: a+b/c123!?$*&()'-=@~
*/


  那StdEncoding与URLEncoding有啥区别?
        可以先去看看Go标准库中Base64的源码:      
const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
          URLEncoding被称为 URL安全的Base64编码。适用于以URL方式传递Base64编码结果的场景。
          它的特点是,将字符串中的加号+换成中划线-,并且将斜杠/换成下划线_,同时尾部保持填充等号=。
         
为什么在URL时要替换标准BASE64这两个字符?
    引用 维基百科的说明:
            因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。
            其实就是为了不影响入库和SQL查询,所以才这样的.

         如果对Go是怎么实现的感兴趣,这有源码.

 嗯,大致就这些.


BLOG: http://blog.csdn.net/xcl168



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值