您的位置:首页精文荟萃软件资讯 → 奇妙的Base64编码

奇妙的Base64编码

时间:2004/10/7 18:19:00来源:本站整理作者:蓝点我要评论(0)

 各位看官应该都是资深的网虫了,小弟斗胆在此问问大家,平时上网时,除了泡MM、到论坛灌水、扔版砖……之外,进行的最多的是什么活动?对了,你一定会说:是收发电子邮件!(谁敢说自己没收/发过电子邮件的?拉出去枪毙了!!) 
收/发E-mail的时候有一个安全性的问题--假想一下,你花了一整天时间给系花写的情书,在发送的过程中被隔壁宿舍张三那小子截获了(难道他是黑客??),更糟的是他是你的情敌啊……天,后果不堪设想!!因此,我们必须有一种比较可靠的加密方法,能够对电子邮件的明文进行转换,至少要得出一个无法被别人一眼就看出内容来的东西,而且编码/解码的速度还要足够快。(这时你可以再假想一下啦,张三那家伙截获了你的肉麻情书,可是他一看:“咦?怎么乱七八糟的?垃圾邮件!!”--这样一来你不就逃过大难了?!) 

Base64就是在这种背景下产生的加密方法。它的特点是:1、速度非常快。2、能够将字符串A转换成字符串B,而且如果你光看字符串B,是绝对猜不出字符串A的内容来的。不信吗?让我们来看看下面这串东西: 

xOO6w6Osu7bTrbniwdnAz8LetcTnzbfXzOy12KOh 

呵呵,是什么啊?猜出来了吗?其实它就是下面这段文字经过Base64编码产生的东东: 

你好,欢迎光临老罗的缤纷天地! 

介绍说完啦,让我们开始探讨实质性的东西。 

Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045~RFC2049,上面有MIME的详细规范。 

Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。 

这样说会不会太抽象了?不怕,我们来看一个例子: 

转换前 aaaaaabb ccccdddd eeffffff 
转换后 00aaaaaa 00bbcccc 00ddddee 00ffffff 

应该很清楚了吧?上面的三个字节是原文,下面的四个字节是转换后的Base64编码,其前两位均为0。 

转换后,我们用一个码表来得到我们想要的字符串(也就是最终的Base64编码),这个表是这样的:(摘自RFC2045) 


Table 1: The Base64 Alphabet 

value Encoding value Encoding value Encoding value Encoding 
0 A 17 R 34 i 51 z 
1 B 18 S 35 j 52 0 
2 C 19 T 36 k 53 1 
3 D 20 U 37 l 54 2 
4 E 21 V 38 m 55 3 
5 F 22 W 39 n 56 4 
6 G 23 X 40 o 57 5 
7 H 24 Y 41 p 58 6 
8 I 25 Z 42 q 59 7 
9 J 26 a 43 r 60 8 
10 K 27 b 44 s 61 9 
11 L 28 c 45 t 62 + 
12 M 29 d 46 u 63 / 
13 N 30 e 47 v 
14 O 31 f 48 w (pad) = 
15 P 32 g 49 x 
16 Q 33 h 50 y 


让我们再来看一个实际的例子,加深印象! 

转换前 10101101 10111010 01110110 
转换后 00101011 00011011 00101001 00110110 
十进制 43 27 42 54 
对应码表中的值 r b q 2 


所以上面的24位编码,编码后的Base64值为 rbq2 
解码同理,把 rbq2 的二进制位连接上再重组得到三个8位值,得出原码。 
(解码只是编码的逆过程,在此我就不多说了,另外有关MIME的RFC还是有很多的,如果需要详细情况请自行查找。) 

用更接近于编程的思维来说,编码的过程是这样的: 

第一个字符通过右移2位获得第一个目标字符的Base64表位置,根据这个数值取到表上相应的字符,就是第一个目标字符。 
然后将第一个字符左移6位加上第二个字符右移4位,即获得第二个目标字符。 
再将第二个字符左移4位加上第三个字符右移6位,获得第三个目标字符。 
最后取第三个字符的右6位即获得第四个目标字符. 

So easy! That’s all!!! 

可是等等……聪明的你可能会问到,原文的字节数量应该是3的倍数啊,如果这个条件不能满足的话,那该怎么办呢? 

我们的解决办法是这样的:原文的字节不够的地方可以用全0来补足,转换时Base64编码用=号来代替。这就是为什么有些Base64编码会以一个或两个等号结束的原因,但等号最多只有两个。因为: 

余数 = 原文字节数 MOD 3 

所以余数任何情况下都只可能是0,1,2这三个数中的一个。如果余数是0的话,就表示原文字节数正好是3的倍数(最理想的情况啦)。如果是1的话,为了让Base64编码是4的倍数,就要补2个等号;同理,如果是2的话,就要补1个等号。 

讲到这里,大伙儿应该全明白了吧?如果还有不清楚的话就返回去再仔细看看,其实不难理解的。 

下面我给出一个演示Base64编码/解码的程序,希望能对您有用。同时也希望您帮我完善它,利用它做出更多的用途,到时别忘了通知我一声啊!(我现在太忙了) 


DLL的源代码:Base64Dll.asm 

;*********************************************** 
;程序名称:演示Base64编码/解码原理 
;作者:罗聪 
;日期:2002-9-14 
;出处:http://laoluoc.yeah.net(老罗的缤纷天地) 
;注意事项:如欲转载,请保持本程序的完整,并注明: 
;转载自“老罗的缤纷天地”(http://laoluoc.yeah.net) 
;*********************************************** 

.386 
.model flat, stdcall 
option casemap:none 

include \masm32\include\windows.inc 
include \masm32\include\kernel32.inc 
include \masm32\include\user32.inc 
includelib \masm32\lib\kernel32.lib 
includelib \masm32\lib\user32.lib 

DllEntry proto :HINSTANCE, :DWORD, :DWORD 
Base64Encode proto :DWORD, :DWORD 
Base64Decode proto :DWORD, :DWORD 

.data 
;Base64 -> ASCII mapping table 
base64_alphabet db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 

;ASCII -> Base64 mapping table 
base64table db 43 dup (255) 
db 62,255,255,255,63,52,53,54,55,56,57,58,59,60,61,255 
db 255,255,0,255,255,255,0,1,2,3,4,5,6,7,8,9,10,11,12,13 
db 14,15,16,17,18,19,20,21,22,23,24,25,255,255,255,255 
db 255,255,26,27,28,29,30,31,32,33,34,35,36,37,38 
db 39,40,41,42,43,44,45,46,47,48,49,50,51 
db 132 dup (255) 

.code 
DllEntry proc hInst: HINSTANCE, reason: DWORD, reserved1: DWORD 
mov eax, TRUE 
ret 
DllEntry endp 


;********************************************************** 
;函数功能:进行Base64编码 
;参数: 
; source = 传入的字符串 
; destination = 返回的编码 
;********************************************************** 
Base64Encode proc uses ebx edi esi source:DWORD, destination:DWORD 
LOCAL sourcelen:DWORD 

invoke lstrlen, source 
mov sourcelen, eax 

mov esi, source 
mov edi, destination 
@@base64loop: 
xor eax, eax 
.if sourcelen == 1 
lodsb ;source ptr + 1 
mov ecx, 2 ;bytes to output = 2 
mov edx, 03D3Dh ;padding = 2 byte 
dec sourcelen ;length - 1 
.elseif sourcelen == 2 
lodsw ;source ptr + 2 
mov ecx, 3 ;bytes to output = 3 
mov edx, 03Dh ;padding = 1 byte 
sub sourcelen, 2 ;length - 2 
.else 
lodsd 
mov ecx, 4 ;bytes to output = 4 
xor edx, edx ;padding = 0 byte 
dec esi ;source ptr + 3 (+4-1) 
sub sourcelen, 3 ;length - 3 
.endif 

xchg al,ah ;flip eax completely 
rol eax, 16 ;can this be done faster 
xchg al,ah 

@@: 
push eax 
and eax, 0FC000000h ;get the last 6 high bits 
rol eax, 6 ;rotate them into al 
mov al, byte ptr [offset base64_alphabet + eax] ;get encode character 
stosb ;write to destination 
pop eax 
shl eax, 6 ;shift left 6 bits 
dec ecx 
jnz @B ;loop 

cmp sourcelen, 0 
jnz @@base64loop ;main loop 

mov eax, edx ;add padding and null terminate 
stosd 

ret 
Base64Encode endp 


;********************************************************** 
;函数功能:进行Base64解码 
;参数: 
; source = 传入的编码 
; destination = 返回的字符串 
;********************************************************** 
Base64Decode proc uses ebx edi esi source:DWORD, destination:DWORD 
LOCAL sourcelen:DWORD 

invoke lstrlen, source 
mov sourcelen, eax 

mov esi, source ;esi <- source 
mov edi, destination ;edi <- destination 
mov ecx, sourcelen 
shr ecx, 2 
cld 

;-------------[decoding part]--------------- 

@@outer_loop: 
push ecx 
mov ecx, 4 
xor ebx, ebx 
lodsd 
@@inner_loop: 
push eax 
and eax, 0ffh 
mov al, byte ptr [offset base64table + eax] 
cmp al, 255 
je @@invalid_char 
shl ebx, 6 
or bl, al 
pop eax 
shr eax, 8 
dec ecx 
jnz @@inner_loop 
mov eax, ebx 
shl eax, 8 
xchg ah, al 
ror eax, 16 
xchg ah, al 
stosd 
dec edi 
pop ecx 
dec ecx 
jnz @@outer_loop 
xor eax, eax 
jmp @@decode_done 

;------------------------------------------- 

@@invalid_char: 
mov eax, -1 
@@decode_done: 
ret 
Base64Decode ENDP 


end DllEntry 
;******************** over ******************** 
;by LC 


-------------------------------------------------------------------------------- 


测试程序:base64.asm 

;*********************************************** 
;程序名称:演示Base64编码/解码原理 
;作者:罗聪 
;日期:2002-9-14 
;出处:http://laoluoc.yeah.net(老罗的缤纷天地) 
;注意事项:如欲转载,请保持本程序的完整,并注明: 
;转载自“老罗的缤纷天地”(http://laoluoc.yeah.net) 
;*********************************************** 

.386 
.model flat, stdcall 
option casemap:none 

include \masm32\include\windows.inc 
include \masm32\include\kernel32.inc 
include \masm32\include\user32.inc 
include Base64Dll.inc 
includelib \masm32\lib\kernel32.lib 
includelib \masm32\lib\user32.lib 
includelib Base64Dll.lib 

WndProc proto :DWORD, :DWORD, :DWORD, :DWORD 

.const 
IDC_BUTTON_ENCODE equ 3000 
IDC_BUTTON_DECODE equ 3001 
IDC_EDIT_INPUT equ 3002 
MAXSIZE equ 260 

.data 
szDlgName db "lc_dialog", 0 
szCaption db "BASE64 demo by LC", 0 
szBuffer db 255 dup(0) 
szText db 340 dup(0) 
szMsg db 450 dup(0) 
szTemplate_Encode db "字符串 ""%s"" 的Base64编码是:", 13, 10, 13, 10, "%s", 0 
szTemplate_Decode db "编码 ""%s"" 经过Base64还原后的字符串是:", 13, 10, 13, 10, "%s", 0 

.code 
main: 
invoke GetModuleHandle, NULL 
invoke DialogBoxParam, eax, offset szDlgName, 0, WndProc, 0 
invoke ExitProcess, eax 

WndProc proc uses edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
LOCAL hEdit: HWND 

.if uMsg == WM_CLOSE 
invoke EndDialog, hWnd, 0 

.elseif uMsg == WM_COMMAND 
mov eax, wParam 
mov edx, eax 
shr edx, 16 
movzx eax, ax 
.if edx == BN_CLICKED 
.if eax == IDCANCEL 
invoke EndDialog, hWnd, NULL 

.elseif eax == IDC_BUTTON_ENCODE || eax == IDOK 
;取得用户输入的字符串: 
invoke GetDlgItemText, hWnd, IDC_EDIT_INPUT, addr szBuffer, 255 

;进行 ASCII->Base64 转换: 
invoke Base64Encode, addr szBuffer, addr szText 

;格式化输出: 
invoke wsprintf, addr szMsg, addr szTemplate_Encode, addr szBuffer, addr szText 

;显示结果: 
invoke MessageBox, hWnd, addr szMsg, addr szCaption, MB_OK 

.elseif eax == IDC_BUTTON_DECODE 
;取得用户输入的字符串: 
invoke GetDlgItemText, hWnd, IDC_EDIT_INPUT, addr szBuffer, 255 

;进行 Base64->ASCII 转换: 
invoke Base64Decode, addr szBuffer, addr szText 

;格式化输出: 
invoke wsprintf, addr szMsg, addr szTemplate_Decode, addr szBuffer, addr szText 

;显示结果: 
invoke MessageBox, hWnd, addr szMsg, addr szCaption, MB_OK 
.endif 

;全选edit里面的内容: 
invoke GetDlgItem, hWnd, IDC_EDIT_INPUT 
invoke SendMessage, eax, EM_SETSEL, 0, -1 

.endif 
.else 
mov eax, FALSE 
ret 
.endif 
mov eax, TRUE 
ret 
WndProc endp 

end main 
;******************** over ******************** 
;by LC 


-------------------------------------------------------------------------------- 


测试程序的资源文件:base64.rc 

#include "resource.h" 

#define IDC_BUTTON_ENCODE 3000 
#define IDC_BUTTON_DECODE 3001 
#define IDC_EDIT_INPUT 3002 
#define IDC_STATIC -1 

LC_DIALOG DIALOGEX 10, 10, 195, 60 
style DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | 
WS_SYSMENU 
CAPTION "Base64 demo by LC" 
FONT 9, "宋体", 0, 0, 0x0 
BEGIN 
LTEXT "请输入字符串:", IDC_STATIC, 11, 7, 130, 10 
EDITTEXT IDC_EDIT_INPUT, 11, 20, 173, 12, ES_AUTOHSCROLL 
DEFPUSHBUTTON "编码(&E)", IDC_BUTTON_ENCODE, 38, 39, 52, 15 
PUSHBUTTON "解码(&D)", IDC_BUTTON_DECODE, 104, 39, 52, 15 
END 


如果你发现了有bug,一定要告诉我啊,并请来信讨论!lcother@163.net 

最后给大家留下一个小小的习题,你知道下面这串Base64编码的原文是什么吗? :) 
0LvQu8T6xM3XxdDU19O/tM3qztK1xEJhc2U2NL3Ms8yjoSCjuqOp

VB: Base64 加/解密 (只适用于英文)

使用: 
call encode(.......) 加密 
call decode(.......) 解密 

' -------- Cut Begins here----- 

Private Const base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 

Public Function Encode(DecryptedText As String) As String 
Dim c1, c2, c3 As Integer 
Dim w1 As Integer 
Dim w2 As Integer 
Dim w3 As Integer 
Dim w4 As Integer 
Dim n As Integer 
Dim retry As String 
For n = 1 To Len(DecryptedText) Step 3 
c1 = Asc(Mid$(DecryptedText, n, 1)) 
c2 = Asc(Mid$(DecryptedText, n + 1, 1) + Chr$(0)) 
c3 = Asc(Mid$(DecryptedText, n + 2, 1) + Chr$(0)) 
w1 = Int(c1 / 4) 
w2 = (c1 And 3) * 16 + Int(c2 / 16) 
If Len(DecryptedText) >= n + 1 Then w3 = (c2 And 15) * 4 + Int(c3 / 64) Else w3 = -1 
If Len(DecryptedText) >= n + 2 Then w4 = c3 And 63 Else w4 = -1 

retry = retry + mimeencode(w1) + mimeencode(w2) + mimeencode(w3) + mimeencode(w4) 
Next 
Encode = retry 
End Function 

Public Function Decode(a As String) As String 
Dim w1 As Integer 
Dim w2 As Integer 
Dim w3 As Integer 
Dim w4 As Integer 
Dim n As Integer 
Dim retry As String 

For n = 1 To Len(a) Step 4 
w1 = mimedecode(Mid$(a, n, 1)) 
w2 = mimedecode(Mid$(a, n + 1, 1)) 
w3 = mimedecode(Mid$(a, n + 2, 1)) 
w4 = mimedecode(Mid$(a, n + 3, 1)) 
If w2 >= 0 Then retry = retry + Chr$(((w1 * 4 + Int(w2 / 16)) And 255)) 
If w3 >= 0 Then retry = retry + Chr$(((w2 * 16 + Int(w3 / 4)) And 255)) 
If w4 >= 0 Then retry = retry + Chr$(((w3 * 64 + w4) And 255)) 
Next 
Decode = retry 
End Function 

Private Function mimeencode(w As Integer) As String 
If w >= 0 Then mimeencode = Mid$(base64, w + 1, 1) Else mimeencode = "" 
End Function 

Private Function mimedecode(a As String) As Integer 
If Len(a) = 0 Then mimedecode = -1: Exit Function 
mimedecode = InStr(base64, a) - 1 
End Function 
' -------- Cut Ends -----

相关阅读 Windows错误代码大全 Windows错误代码查询激活windows有什么用Mac QQ和Windows QQ聊天记录怎么合并 Mac QQ和Windows QQ聊天记录Windows 10自动更新怎么关闭 如何关闭Windows 10自动更新windows 10 rs4快速预览版17017下载错误问题Win10秋季创意者更新16291更新了什么 win10 16291更新内容windows10秋季创意者更新时间 windows10秋季创意者更新内容kb3150513补丁更新了什么 Windows 10补丁kb3150513是什么

文章评论
发表评论

热门文章 360快剪辑怎么使用 36金山词霸如何屏幕取词百度收购PPS已敲定!3

最新文章 微信3.6.0测试版更新了微信支付漏洞会造成哪 360快剪辑怎么使用 360快剪辑软件使用方法介酷骑单车是什么 酷骑单车有什么用Apple pay与支付宝有什么区别 Apple pay与贝贝特卖是正品吗 贝贝特卖网可靠吗

人气排行 xp系统停止服务怎么办?xp系统升级win7系统方电脑闹钟怎么设置 win7电脑闹钟怎么设置office2013安装教程图解:手把手教你安装与qq影音闪退怎么办 QQ影音闪退解决方法VeryCD镜像网站逐个数,电驴资料库全集同步推是什么?同步推使用方法介绍QQ2012什么时候出 最新版下载EDiary——一款好用的电子日记本