1. 程式人生 > >最全加密演算法之對稱加密和非對稱加密

最全加密演算法之對稱加密和非對稱加密

常見加密演算法 :

DES(Data Encryption Standard):資料加密標準,速度較快,適用於加密大量資料的場合; 

3DES(Triple DES):是基於DES,對一塊資料用三個不同的金鑰進行三次加密,強度更高;

RC2和 RC4:用變長金鑰對大量資料進行加密,比 DES 快;

IDEA(International Data Encryption Algorithm)國際資料加密演算法:使用 128 位金鑰提供非常強的安全性;

RSA:由 RSA 公司發明,是一個支援變長金鑰的公共金鑰演算法,需要加密的檔案塊的長度也是可變的;

DSA(Digital Signature Algorithm):數字簽名演算法,是一種標準的 DSS(數字簽名標準);

AES(Advanced Encryption Standard):高階加密標準,是下一代的加密演算法標準,速度快,安全級別高,目前 AES 標準的一個實現是 Rijndael 演算法;

BLOWFISH,它使用變長的金鑰,長度可達448位,執行速度很快; 

其它演算法,如ElGamal、Deffie-Hellman、新型橢圓曲線演算法ECC等。 比如說,MD5,你在一些比較正式而嚴格的網站下的東西一般都會有MD5值給出,如安全焦點的軟體工具,每個都有MD5。嚴格來說MD5並不能算是一種加密演算法,只能說是一種摘要演算法(資料摘要演算法是密碼學演算法中非常重要的一個分支,它通過對所有資料提取指紋資訊以實現資料簽名、資料完整性校驗等功能,由於其不可逆性,有時候會被用做敏感資訊的加密。資料摘要演算法也被稱為雜湊(

Hash)演算法、雜湊演算法。)

MD5分類:

1、CRC8、CRC16、CRC32

CRC(Cyclic Redundancy Check,迴圈冗餘校驗)演算法出現時間較長,應用也十分廣泛,尤其是通訊領域,現在應用最多的就是 CRC32 演算法,它產生一個4位元組(32位)的校驗值,一般是以8位十六進位制數,如FA 12 CD 45等。CRC演算法的優點在於簡便、速度快,嚴格的來說,CRC更應該被稱為資料校驗演算法,但其功能與資料摘要演算法類似,因此也作為測試的可選演算法。

在 WinRAR、WinZIP 等軟體中,也是以 CRC32 作為檔案校驗演算法的。一般常見的簡單檔案校驗(Simple File Verify – SFV)也是以 CRC32演算法為基礎,它通過生成一個字尾名為 .SFV 的文字檔案,這樣可以任何時候可以將檔案內容 

CRC32運算的結果與 .SFV 檔案中的值對比來確定此檔案的完整性。

與 SFV 相關工具軟體有很多,如MagicSFV、MooSFV等。

2、MD2 、MD4、MD5

這是應用非常廣泛的一個演算法家族,尤其是 MD5(Message-Digest Algorithm 5,訊息摘要演算法版本5),它由MD2、MD3、MD4發展而來,由Ron Rivest(RSA公司)在1992年提出,被廣泛應用於資料完整性校驗、資料(訊息)摘要、資料加密等。MD2、MD4、MD5 都產生16位元組(128位)的校驗值,一般用32位十六進位制數表示。MD2的演算法較慢但相對安全,MD4速度很快,但安全性下降,MD5比MD4更安全、速度更快。

在網際網路上進行大檔案傳輸時,都要得用MD5演算法產生一個與檔案匹配的、儲存MD5值的文字檔案(字尾名為 .md5或.md5sum),這樣接收者在接收到檔案後,就可以利用與 SFV 類似的方法來檢查檔案完整性,絕大多數大型軟體公司或開源組織都是以這種方式來校驗資料完整性,而且部分作業系統也使用此演算法來對使用者密碼進行加密,另外,它也是目前計算機犯罪中資料取證的最常用演算法。

與MD5 相關的工具有很多,如 WinMD5等。

3、SHA1、SHA256、SHA384、SHA512

SHA(Secure Hash Algorithm)是由美國專門制定密碼演算法的標準機構-- 美國國家標準技術研究院(NIST)制定的,SHA系列演算法的摘要長度分別為:SHA為20位元組(160位)、SHA256為32位元組(256位)、 SHA384為48位元組(384位)、SHA512為64位元組(512位),由於它產生的資料摘要的長度更長,因此更難以發生碰撞,因此也更為安全,它是未來資料摘要演算法的發展方向。由於SHA系列演算法的資料摘要長度較長,因此其運算速度與MD5相比,也相對較慢。

SHA1的應用較為廣泛,主要應用於CA和數字證書中,另外在網際網路中流行的BT軟體中,也是使用SHA1來進行檔案校驗的。

4、RIPEMD、PANAMA、TIGER、ADLER32 等

RIPEMD是Hans Dobbertin等3人在對MD4,MD5缺陷分析基礎上,於1996年提出來的,有4個標準128、160、256和320,其對應輸出長度分別為16位元組、20位元組、32位元組和40位元組。

TIGER由Ross在1995年提出。Tiger號稱是最快的Hash演算法,專門為64位機器做了優化。

常用的加密演算法有對稱加密和非對稱加密兩大型別:

對稱加密

簡介:

對稱加密(也叫私鑰加密)指加密和解密使用相同金鑰的加密演算法。有時又叫傳統密碼演算法,就是加密金鑰能夠從解密金鑰中推算出來,同時解密金鑰也可以從加密金鑰中推算出來。而在大多數的對稱演算法中,加密金鑰和解密金鑰是相同的,所以也稱這種加密演算法為祕密金鑰演算法或單金鑰演算法。它要求傳送方和接收方在安全通訊之前,商定一個金鑰。對稱演算法的安全性依賴於金鑰,洩漏金鑰就意味著任何人都可以對他們傳送或接收的訊息解密,所以金鑰的保密性對通訊性至關重要。

特點:

對稱加密演算法的特點是演算法公開、計算量小、加密速度快、加密效率高。

不足之處是,交易雙方都使用同樣鑰匙,安全性得不到保證。此外,每對使用者每次使用對稱加密演算法時,都需要使用其他人不知道的惟一鑰匙,這會使得發收信雙方所擁有的鑰匙數量呈幾何級數增長,金鑰管理成為使用者的負擔。對稱加密演算法在分散式網路系統上使用較為困難,主要是因為金鑰管理困難,使用成本較高。而與公開金鑰加密演算法比起來,對稱加密演算法能夠提供加密和認證卻缺乏了簽名功能,使得使用範圍有所縮小。在計算機專網系統中廣泛使用的對稱加密演算法有DES和IDEA等。美國國家標準局倡導的AES即將作為新標準取代DES。

具體演算法:DES演算法3DES演算法,TDEA演算法,Blowfish演算法,RC5演算法,IDEA演算法。

原理應用:對稱加密演算法的優點在於加解密的高速度和使用長金鑰時的難破解性。假設兩個使用者需要使用對稱加密方法加密然後交換資料,則使用者最少需要2個金鑰並交換使用,如果企業內使用者有n個,則整個企業共需要n×(n-1) 個金鑰,金鑰的生成和分發將成為企業資訊部門的惡夢。對稱加密演算法的安全性取決於加密金鑰的儲存情況,但要求企業中每一個持有金鑰的人都保守祕密是不可能的,他們通常會有意無意的把金鑰洩漏出去--如果一個使用者使用的金鑰被入侵者所獲得,入侵者便可以讀取該使用者金鑰加密的所有文件,如果整個企業共用一個加密金鑰,那整個企業文件的保密性便無從談起。

對稱加密演算法中最經典的演算法莫過於DES加密演算法。DES加密採用的是分組加密的方法,使用56位金鑰加密64位明文,最後產生64位密文。DES演算法的基本流程如圖6-2所示。

圖6-2  DES加密演算法基本流程

現在對圖6-2的整個流程做簡要的分析。DES對64位的明文分組M進行操作,M經過一個初始置換IP置換成m0,將m0明文分成左半部分和右半部分m0=(L0,R0),各32位長。然後進行16輪完全相同的運算,這些運算稱為函式f,在運算過程中,資料與密匙結合。經過16輪運算之後,可以看到第16輪運算,將右側第15輪運算的結果(R15)作為左側運算的最終結果(L16),而右側最後的結果(R16)為左側第15輪運算結果(L15)和函式f運算結果的異或運算所得。此後,再將左、右部分合在一起經過一個逆置換,輸出密文。

實際加密過程要分成兩個同時進行的過程,即加密過程和金鑰生成過程,如圖6-3所示。結合圖6-2和圖6-3簡單講解金鑰生成過程。

圖6-3  加密與金鑰生成

如圖6-3所示,在16輪迴圈的每一輪中,密匙位移位,然後再從密匙的64位中選出48位。通過一個擴充套件置換將資料的右半部分擴充套件成48位,並通過一個異或操作替代成新的32位資料,在將其置換一次。這四步運算構成了圖6-2中的函式f。然後,通過另一個異或運算,函式f的輸出與左半部分結合,其結果成為新的右半部分,原來的右半部分成為新的左半部分。該操作重複16次。

DES演算法的解密過程和加密過程幾乎完全相同,只是使用金鑰的順序相反。

關於DES演算法的更加詳細的細節不在本書的講解範圍之內,請讀者參考相關資料。

NIST(National Institute of Standards and Technology,美國國家標準技術研究院)在1999年釋出了新的DES加密標準,3DES取代DES成為新的加密標準。3DES採用168位的金鑰,三重加密,但速度較慢。之後,又出現了AES(Advanced Encryption Standard,先進加密標準)等高階對稱機密演算法。

TripleDES加密演算法

由於DES演算法安全性方面的原因,為了提高DES演算法的抗攻擊性,因此提出了Triple-DES演算法。

Triple-DES演算法的基本原理是:用兩個金鑰對資料進行3次加密/解密運算。即首先使用第一個金鑰對資料進行加密,然後用第二個金鑰對其進行解密,最後用第一個金鑰再加密。這兩個金鑰可以是同一個,也可以不同,它們也可以來源於一個128位金鑰,只是在加密/解密時將其分割成兩個64位的金鑰,分別輪換使用這兩個64位金鑰去完成加密/解密運算。Triple-DES演算法保留了DES演算法運算速度快的特點,通過增加運算次數和金鑰長度(兩個64位金鑰相當於128位金鑰)來增加破解者的破解時間,但是從密碼學本身來說,其安全強度並沒有增加。

RC系列演算法

現在我們用到的RC系列演算法包括RC2、RC4、RC5、RC6演算法,其中RC4是序列密碼演算法,其他三種是分組密碼演算法。

(1)  RC2演算法

該演算法設計的目的是用來取代DES演算法,它採用金鑰長度可變的對明文采取64位分組的分組加密演算法,屬於Festel網路結構。

(2)  RC4演算法

該演算法是一個金鑰長度可變的面向位元組流的加密演算法,以隨機置換為基礎。該演算法執行速度快,每輸出1位元組的結果僅需要8~16位元組的機器指令。RC4演算法比較容易描述,它首先用8~2048位可變長度的金鑰初始化一個256位元組的狀態向量S。S的成員標記為S[0],S[1],…,S[255],整個置換過程都包含0~255的8位元數。對於加密和解密,設位元組資料為K,由S中256個元素按一定方式選出一個元素生成,每生成一個K值,元素中的資料就要被重新置換一次。RC4初始化的虛擬碼如程式碼清單6-1所示。

程式碼清單6-1  RC4初始化的虛擬碼

for i=0 to 255

{

  S[i]=i;

  T[i]=K[i mod KeyLen ];

}

j=0;

for i=0 to 255

{

  j=(j+T[i]+S[i]) mod 256;

swap(S[i],S[j];

}

如程式碼清單6-1所示,初始化開始時,S的元素按從0到255依次賦值,同時建立一個臨時向量T。如果金鑰K的長度為256位元組,則將K賦值給T。否則,若金鑰長度為KeyLen位元組,則將K的值賦給T的前KeyLen個元素,並迴圈重複用K餘下的元素賦給T剩下的元素。從“j=0”開始,用T產生S的初始置換。從S[0]~S[255],對每個S[i]根據T[i]確定的方案,將S[i]置換成S中的另一位元組。

在完成S的初始化之後,輸入金鑰將被拋棄,接下來將使用金鑰流,生成金鑰流的虛擬碼如程式碼清單6-2所示。

程式碼清單6-2  生成金鑰流

i=0;

j=0;

while(true)

{

   i=(i+1) mod 256;

   j=(j+S[i]) mod 256;

   swap(S[i],j[i];

   T=(S[i]+S[j]) mod 256;

   K=S[T];

}

如程式碼清單6-2所示,金鑰流的生成是從S[0]到S[255],對每個S[i]根據當前S的值將S[i]與S中的另一位元組替換,當S[255]完成置換後操作繼續重複。在加密過程中將K的值與下一明文位元組異或,解密過程中將K的值與下一密文位元組異或即可。

(3)  RC5演算法

該演算法是一種分組長度、金鑰長度、加密迭代輪數都可變的分組加密演算法。該演算法主要包含三部分內容:金鑰擴充套件、加密演算法和解密演算法。該演算法包含三個引數:w(字的長度,單位:位)、r(迭代次數)、b(金鑰長度,單位:位元組)。由於RC5演算法需要(2r+2)個w位金鑰,所以需要金鑰擴充套件。

通過金鑰擴充套件,把金鑰K擴充套件成金鑰陣S,它由K所決定的t=2(r+1)個隨機二進位制字構成。金鑰擴充套件演算法利用了兩個幻常數:

Pw=Odd((e-2)2w)[1]

Qw=Odd((Φ-1)2w)[2]

函式Odd(x)的結果為與x最近的奇整數。金鑰擴充套件的第一步是將金鑰K轉換成字格式,利用K的各位元組構造字陣L。金鑰擴充套件的第二步是利用模232線性同餘演算法初始化S陣。金鑰擴充套件的第三步是L陣和S陣混合。

加密過程也很簡單。假設選用的資料分組長度為2w位(w允許的值有16、32和64),迭代次數為r輪(r為0~255)。首先將明文劃分成兩個w位的字A和B,運算過程如程式碼清單6-3所示。

程式碼清單6-3  RC5演算法加密過程

A=A+S0;

B=B+S1;

for i=1 to r

{

  A=((A+B)<<<B))+S2i;

  B= ((B+A) <<<A)) +S2i+1;

}

程式碼清單6-3的結果輸出到暫存器A和B中。

解密過程是加密過程的逆運算,基本過程如程式碼清單6-4所示。

程式碼清單6-4  RC5演算法解密過程

for i=r down to 1

{

  B=((B- S2i+1)>>>A)+A;

  A=((A- S2i)>>>B)+B;

}

B=B-S1;

A=A-S0;

說明 加密和解密過程中的加減都是模2w的,表示逐位異或。<<<表示迴圈左移,>>>表示迴圈右移。

(4)  RC6演算法

RC6秉承了RC5設計簡單、廣泛使用資料相關的迴圈移位思想,同時增強了抵抗攻擊的能力,改進了RC5中迴圈移位的位數依賴於暫存器中所有位的不足。

RC6的特點是輸入的明文由原先2個區塊擴充套件為4個區塊,另外,在運算方面則是使用了整數乘法,而整數乘法的使用則在每一個運算回合中增加了擴散(diffusion)的行為,並且使得即使很少的回合數也有很高的安全性。同時,RC6中所用的操作可以在大部分處理器上高效率地實現,提高了加密速度。RC6是一種安全、架構完整而且簡單的區塊加密法。它提供了較好的測試結果和引數方面相當大的彈性。RC6可以抵抗所有已知的攻擊,能夠提供AES所要求的安全性,可以說是近幾年來相當優秀的一種加密法。

Rijndael演算法

Rijndael是一個反覆運算的加密演算法,它允許可變動的資料區塊及金鑰的長度。資料區塊與金鑰長度的變動是各自獨立的。

在Rijndael演算法中定義了兩個名詞:

1)        State:在運算過程中所產生的中間值,是一個4×Nb的矩陣,Nb可由資料長度除以32位求得,也就是把資料分割成Nb個區塊。

2)        Cipher Key:用來做加密運算的金鑰,形式是一個4×Nk的矩陣,Nk可由金鑰長度除以32位求得,也就是把金鑰分割成Nk個32位的子金鑰。

在Rijndael演算法中,運算的回合數(Nr)是由Nb及Nk決定的,回合數的變動定義如表6-1所示。

                表6-1  Rijndael演算法回合變動定義

Nr

Nb=4

Nb=6

Nb=8

Nk=4

10

12

14

Nk=6

12

12

14

Nk=8

14

14

14

在Rijndael中使用了許多位元組層級的運算,而這些運算是以GF(28)為基礎架構。也有一些採用了4-byte的字組運算。各種運算的定義如下:

(1) GF(28)的定義

假設一個位元組b由b7b6b5b4b3b2b1b0組成,可以把這些bi想象成一個7次多項式的係數,而這些係數不是0就是1:

b7 x7+ b6 x6+ b5 x5+ b4 x4+ b3 x3+ b2 x2+ b1 x + b0

例如,(57)16的二進位制表示法為(0101,0111)2,表示成多項式,則為:

x6+ x4+ x2+ x + 1

(2)加法

兩個多項式的加法,則是定義為相同指數項的係數和再模2,簡單地說,就是作EXOR運算(1+1=0)。例如:

(57)16+(83)16=(01010111)2+(10000011)2 = (11010100)2 = (D4)16

或是

(x6+x4+x2+x+1)+(x7+x+1)=x7+x6+x4+x2

(3)乘法

在乘法運算中,多項式相乘之後的結果很容易造成溢位的問題,解決溢位的方式是把相乘的結果,再模餘一個不可分解的多項式m(x)。在Rijndael中,定義一個這樣的多項式為m(x)=x8+x4+x3+x+1或是(11B)16例如:

(57)16‧(83)16

 =(x6+ x4+ x2+ x + 1)‧(x7+ x + 1)

 =x13+ x11+ x9+ x8+ x7+x7+ x5+ x3+ x2+x+x6+ x4+ x2+ x + 1

=(x13+x11+x9+x8+x6+x5+x4+x3+1+x13+x11+x9+x8+x6+x5+x4+x3+1)mod(x8+x4+x3+x+1)

= x7+ x6+ 1

=(C1)16

(4)乘以X

若把b(x)乘以x,得到

b7 x8+ b6 x7+ b5 x6+ b4 x5+ b3 x4+ b2 x3+ b1 x2 + b0x

若b7=0,不會發生溢位問題,答案即是正確的;若b7=1,發生溢位問題,必須減去m(x)。可以把這種運算表示為xtime(x),其運算方式為left shift(若溢位則和(1B)16做EXOR運算),例如:

 ‘57’· ‘13’ = ‘FE’

‘57’ · ‘02’ = xtime(57) = ‘AE’

‘57’ · ‘04’ = xtime(AE) = ‘47’

‘57’ · ‘08’ = xtime(47) = ‘8E’

‘57’ · ‘10’ = xtime(8E) = ‘07’

‘57’ · ‘13’ = ‘57’ · (‘01’⊕‘02’⊕‘10’) = ‘57’⊕‘AE’⊕‘07’ = ‘FE’

Rijndael演算法分為四個步驟:

步驟 1  位元組轉換。

位元組轉換是一個以位元組為單位的非線性取代運算,取代表(S-box)是經過2個運算過程而建立,並且是可逆的。首先找出每個位元組在GF(28)中的乘法反元素;接著經過1個仿射(Affine)轉換運算,轉換運算的定義如圖6-4所示。

圖6-4  轉換運算定義

取代表生成之後就可以進行位元組取代運算。取代運算的示意圖如圖6-5所示。

圖6-5  取代運算示意圖

進行如圖6-5的取代運算之後,結果如圖6-6所示。

圖6-6 取代運算後的S-Box

說明 位元組取代轉換的反運算:計算仿射對應之後的相反運算可得到S-1-box,以此S-1-box做位元組取代(Subbytes)即可。

步驟 2  移行轉換。

在這個轉換中,State的每一行以不同的偏移量做環狀位移:第0 行不動,第1 行位移1 C個位元組,第2 行位移2 C個位元組,第3 行位移3 C個位元組。位移的偏移量C1,C2,C3跟區塊的數目(Nb)有關,關係見圖6-7。

圖6-7  C1、 C2、 C3與區塊數目(Nb)的關係

移行轉換(Shift rows)運算對於State 的影響如圖6-8所示。

圖6-8  移行轉換對State的影響

說明 移行轉換的反運算:對第2、第3 及第4 行做Nb-C1、、Nb-C2、Nb-C3個位元組的環狀位移即可。

步驟 3  混列轉換。

在這個轉換中,把State當做一個GF(28)中的多項式。並且對一個固定的多項式C(X)作乘法,如果發生溢位,則再模X4 +1. 表示如下:

C(X) ='03' X3 +'01' X2 +'01' X +'02'

C(X)與X4 +1互質,令

B(X) = C(X)⊕ A(X)

以矩陣乘法表示如圖6-9所示。

圖6-9  混列轉換的矩陣乘法表示

State經過混列(Mix columns)運算之後的變化如圖6-9所示。

圖6-9  混列運算對State的影響

說明 混列轉換的反運算是乘以一個特殊的多項式D(X):

('03' X3 +'01' X2 +'01' X +'02' )⊕ D(X)

='01' D(X)

='0B' X3 +'0D' X2 +'09' X +'0E'。

步驟 4  輪金鑰加。

這個運算主要是把每一個回合金鑰(Roundkey)透過簡單的Bitwise exor加入到每一個State中,如圖6-10所示。

圖6-10  Bitwise exor加入後的State

說明 此時Add round key的逆是它自身。

------------------注:本文部分內容改變自《.NET 安全揭祕》

[1]其中,e為自然對數底,e= 2.718281828459……

[2]其中,Φ為黃金分割率。

                                                    非對稱加密

對稱加密演算法是一種金鑰的保密方法。

非對稱加密演算法需要兩個金鑰:公開金鑰(publickey)和私有金鑰(privatekey)。公開金鑰與私有金鑰是一對,如果用公開金鑰對資料進行加密,只有用對應的私有金鑰才能解密;如果用私有金鑰對資料進行加密,那麼只有用對應的公開金鑰才能解密。因為加密和解密使用的是兩個不同的金鑰,所以這種演算法叫作非對稱加密演算法。 非對稱加密演算法實現機密資訊交換的基本過程是:甲方生成一對金鑰並將其中的一把作為公用金鑰向其它方公開;得到該公用金鑰的乙方使用該金鑰對機密資訊進行加密後再發送給甲方;甲方再用自己儲存的另一把專用金鑰對加密後的資訊進行解密。

另一方面,甲方可以使用乙方的公鑰對機密資訊進行簽名後再發送給乙方;乙方再用自己的私匙對資料進行驗籤。

甲方只能用其專用金鑰解密由其公用金鑰加密後的任何資訊。 非對稱加密演算法的保密性比較好,它消除了終端使用者交換金鑰的需要。

非對稱密碼體制的特點:演算法強度複雜、安全性依賴於演算法與金鑰但是由於其演算法複雜,而使得加密解密速度沒有對稱加密解密的速度快。對稱密碼體制中只有一種金鑰,並且是非公開的,如果要解密就得讓對方知道金鑰。所以保證其安全性就是保證金鑰的安全,而非對稱金鑰體制有兩種金鑰,其中一個是公開的,這樣就可以不需要像對稱密碼那樣傳輸對方的金鑰了。這樣安全性就大了很多。

起源:W.Diffie和M.Hellman 1976年在IEEE Trans.on Information刊物上發表了“ New Direction in Cryptography”文章,提出了“非對稱密碼體制即公開金鑰密碼體制”的概念,開創了密碼學研究的新方向。

工作原理:

公鑰和私鑰的產生

假設Alice想要通過一個不可靠的媒體接收Bob的一條私人訊息。她可以用以下的方式來產生一個公鑰和一個私鑰:

隨意選擇兩個大的質數p和q,p不等於q,計算N=pq。

根據尤拉函式,不大於N且與N互質的整數個數為(p-1)(q-1)

選擇一個整數e與(p-1)(q-1)互質,並且e小於(p-1)(q-1)

用以下這個公式計算d:d×e≡1(mod(p-1)(q-1))

將p和q的記錄銷燬。

(N,e)是公鑰,(N,d)是私鑰。(N,d)是祕密的。Alice將她的公鑰(N,e)傳給Bob,而將她的私鑰(N,d)藏起來。

加密訊息

假設Bob想給Alice送一個訊息m,他知道Alice產生的N和e。他使用起先與Alice約好的格式將m轉換為一個小於N的整數n,比如他可以將每一個字轉換為這個字的Unicode碼,然後將這些數字連在一起組成一個數字。假如他的資訊非常長的話,他可以將這個資訊分為幾段,然後將每一段轉換為n。用下面這個公式他可以將n加密為c:

加密公式加密公式

計算c並不複雜。Bob算出c後就可以將它傳遞給Alice。

解密訊息

Alice得到Bob的訊息c後就可以利用她的金鑰d來解碼。她可以用以下這個公式來將c轉換為n:

解密公式解密公式

得到n後,她可以將原來的資訊m重新復原。

解碼的原理是

解碼原理1解碼原理1

以及ed≡1(modp-1)和ed≡1(modq-1)。由費馬小定理可證明(因為p和q是質數)

解碼原理3解碼原理3

解碼原理2解碼原理2

這說明(因為p和q是不同的質數,所以p和q互質)

解碼原理5解碼原理5

主要應用:

非對稱加密(公鑰加密):指加密和解密使用不同金鑰的加密演算法,也稱為公私鑰加密。假設兩個使用者要加密交換資料,雙方交換公鑰,使用時一方用對方的公鑰加密,另一方即可用自己的私鑰解密。如果企業中有n個使用者,企業需要生成n對金鑰,並分發n個公鑰。由於公鑰是可以公開的,使用者只要保管好自己的私鑰即可,因此加密金鑰的分發將變得 十分簡單。同時,由於每個使用者的私鑰是唯一的,其他使用者除了可以可以通過資訊傳送者的公鑰來驗證資訊的來源是否真實,還可以確保傳送者無法否認曾傳送過該資訊。非對稱加密的缺點是加解密速度要遠遠慢於對稱加密,在某些極端情況下,甚至能比非對稱加密慢上1000倍。

DSACryptoServiceProvider

RSACryptoServiceProvider

//加密

UnicodeEncoding encoding = new UnicodeEncoding();

byte[] PasswordBytes = encoding.GetBytes(password);//將密碼轉換為位元組陣列RSACryptoServiceProvider crypt=new RSACryptoServiceProvider();//RSA加密演算法,非對稱PasswordBytes=crypt.Encrypt(password ,false);//加密位元組陣列,這是加密後的密碼值,放入資料庫中的表字段中。

string key=crypt.ToXmlString(true);//輸出金鑰為XML格式的字串,且包含私鑰,這個字串要作為資料庫表中的一個欄位同用戶的密碼放在一起。

//解密

RSACryptoServiceProvider crypt=new RSACryptoServiceProvider();//已隨機生成了一個金鑰對

crypt.Clear();//毀掉當前金鑰對

crypt.FromXmlString(key)//輸入金鑰對,key是從資料庫表字段中讀取的那個XML格式的字串,即金鑰欄位PasswordBytes=crypt.Decrypt(password ,false);//解密位元組陣列,返回原始密碼給使用者

上面方法的一個特點是每個使用者對應一個金鑰(包含公鑰和私鑰),它們都是隨機生成的,所以各不相同。不過缺點也是很明顯的,就是金鑰儲存在資料庫中,如果資料庫被攻破金鑰就洩漏了。

還有另外一個方法就是依照上面方法隨機生成一個金鑰對(包含公鑰和私鑰),通過ToXmlString(true)方法匯出,然後把這個XML字串格式的金鑰放到你的Web程式的Web.config檔案的AppSetting節點裡面,然後通過FromXmlString(key)方法讀入金鑰,這樣就意味著所有的使用者密碼都用同一個金鑰對加密和解密。

主要功能:

非對稱加密體系不要求通訊雙方事先傳遞金鑰或有任何約定就能完成保密通訊,並且金鑰管理方便,可實現防止假冒和抵賴,因此,更適合網路通訊中的保密通訊要求。

主要演算法:

RSA、Elgamal、揹包演算法、Rabin、HD,ECC(橢圓曲線加密演算法)。

使用最廣泛的是RSA演算法,Elgamal是另一種常用的非對稱加密演算法。

Elgamal由Taher Elgamal於1985年發明,其基礎是DiffieˉHellman金鑰交換演算法,後者使通訊雙方能通過公開通訊來推匯出只有他們知道的祕密金鑰值[DiffieˉHellman]。DiffieˉHellman是Whitfield Diffie和Martin Hellman於1976年發明的,被視為第一種 非對稱加密演算法,DiffieˉHellman 與RSA的不同之處在於,DiffieˉHellman不是加密演算法,它只是生成可用作對稱金鑰的祕密數值。在DiffieˉHellman金鑰交換過程中,傳送方和接收方分別生成一個祕密的隨機數,並根據隨機數推匯出公開值,然後,雙方再交換公開值。DiffieˉHellman演算法的基礎是具備生成共享金鑰的能力。只要交換了公開值,雙方就能使用自己的私有數和對方的公開值來生成對稱金鑰,稱為共享金鑰,對雙方來說,該對稱金鑰是相同的,可以用於使用對稱加密演算法加密資料。

與RSA相比,DiffieˉHellman的優勢之一是每次交換金鑰時都使用一組新值,而使用RSA演算法時,如果攻擊者獲得了私鑰,那麼他不僅能解密之前截獲的訊息,還能解密之後的所有訊息。然而,RSA可以通過認證(如使用X.509數字證書)來防止中間人攻擊,但Diff ieˉHellman在應對中間人攻擊時非常脆弱。

與對稱加密演算法的區別

首先,用於訊息解密的金鑰值與用於訊息加密的金鑰值不同;

其次,非對稱加密演算法比對稱加密演算法慢數千倍,但在保護通訊安全方面,非對稱加密演算法卻具有對稱密碼難以企及的優勢。

為說明這種優勢,使用對稱加密演算法的例子來強調:

Alice使用金鑰K加密訊息並將其傳送給Bob,Bob收到加密的訊息後,使用金鑰K對其解密以恢復原始訊息。這裡存在一個問題,即Alice如何將用於加密訊息的金鑰值傳送給 Bob?答案是,Alice傳送金鑰值給Bob時必須通過獨立的安全通訊通道(即沒人能監聽到該通道中的通訊)。

這種使用獨立安全通道來交換對稱加密演算法金鑰的需求會帶來更多問題:

首先,有獨立的安全通道,但是安全通道的頻寬有限,不能直接用它傳送原始訊息。

其次,Alice和Bob不能確定他們的金鑰值可以保持多久而不洩露(即不被其他人知道)以及何時交換新的金鑰值

當然,這些問題不只Alice會遇到,Bob和其他每個人都會遇到,他們都需要交換金鑰並處理這些金鑰管理問題(事實上,X9.17是一項DES金鑰管理ANSI標準[ANSIX9.17])。如果Alice要給數百人傳送訊息,那麼事情將更麻煩,她必須使用不同的金鑰值來加密每條訊息。例如,要給200個人傳送通知,Alice需要加密訊息200次,對每個接收方加密一次訊息。顯然,在這種情況下,使用對稱加密演算法來進行安全通訊的開銷相當大。

非對稱加密演算法的主要優勢就是使用兩個而不是一個金鑰值:一個金鑰值用來加密訊息,另一個金鑰值用來解密訊息。這兩個金鑰值在同一個過程中生成,稱為金鑰對。用來加密訊息的金鑰稱為公鑰,用來解密訊息的金鑰稱為私鑰。用公鑰加密的訊息只能用與之對應的私鑰來解密,私鑰除了持有者外無人知道,而公鑰卻可通過非安全管道來發送或在目錄中釋出。

Alice需要通過電子郵件給Bob傳送一個機密文件。首先,Bob使用電子郵件將自己的公鑰傳送給Alice。然後Alice用Bob的公鑰對文件加密並通過電子郵件將加密訊息傳送給Bob。由於任何用Bob 的公鑰加密的訊息只能用Bob的私鑰解密,因此即使窺探者知道Bob的公鑰,訊息也仍是安全的。Bob在收到加密訊息後,用自己的私鑰進行解密從而恢復原始文件。

                                              Base64加密演算法

Base64加密後進行GZIP壓縮處理,獲得明文,安全性得到了保證。

簡介

  標準的Base64並不適合直接放在URL裡傳輸,因為URL編碼器會把標準Base64中的“/”和“+”字元變為形如“%XX”的形式,而這些“%”號在存入資料庫時還需要再進行轉換,因為ANSI SQL中已將“%”號用作萬用字元。

  為解決此問題,可採用一種用於URL的改進Base64編碼,它不在末尾填充'='號,並將標 準Base64中的“+”和“/”分別改成了“*”和“-”,這樣就免去了在URL編解碼和資料庫儲存時所要作的轉換,避免了編碼資訊長度在此過程中的增 加,並統一了資料庫、表單等處物件識別符號的格式。

  另有一種用於正則表示式的改進Base64變種,它將“+”和“/”改成了“!”和“-”,因為“+”,“*”以及前面在IRCu中用到的“[”和“]”在正則表示式中都可能具有特殊含義。

  此外還有一些變種,它們將“+/”改為“_-”或“._”(用作程式語言中的識別符號名稱)或“.-”(用於XML中的Nmtoken)甚至“_:”(用於XML中的Name)。

  Base64要求把每三個8Bit的位元組轉換為四個6Bit的位元組(3*8 = 4*6 = 24),然後把6Bit再添兩位高位0,組成四個8Bit的位元組,也就是說,轉換後的字串理論上將要比原來的長1/3。

規則

  關於這個編碼的規則:

  ①.把3個字元變成4個字元..

  ②每76個字元加一個換行符..

  ③.最後的結束符也要處理..

  這樣說會不會太抽象了?不怕,我們來看一個例子:

  轉換前 aaaaaabb ccccdddd eeffffff

  轉換後 00aaaaaa 00bbcccc 00ddddee 00ffffff

  應該很清楚了吧?上面的三個位元組是原文,下面的四個位元組是轉換後的Base64編碼,其前兩位均為0。

  轉換後,我們用一個碼錶來得到我們想要的字串(也就是最終的Base64編碼),這個表是這樣的:(摘自RFC2045)

轉換表  

索引

對應字元

索引

對應字元

索引

對應字元

索引

對應字元

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

15

P

32

g

49

x

16

Q

33

h

50

y

pad

=

舉例

  讓我們再來看一個實際的例子,加深印象!

  轉換前 10101101 10111010 01110110

  轉換後 00101011 00011011 00101001 00110110

  十進位制 43 27 41 54

  對應碼錶中的值 r b p 2

  所以上面的24位編碼,編碼後的Base64值為rbp2

  解碼同理,把 rbq2 的二進位制位連線上再重組得到三個8位值,得出原碼。

  (解碼只是編碼的逆過程,在此我就不多說了,另外有關MIME的RFC還是有很多的,如果需要詳細情況請自行查詢。)

  用更接近於程式設計的思維來說,編碼的過程是這樣的:

  第一個字元通過右移2位獲得第一個目標字元的Base64表位置,根據這個數值取到表上相應的字元,就是第一個目標字元。

  然後將第一個字元左移4位加上第二個字元右移4位,即獲得第二個目標字元。

  再將第二個字元左移2位加上第三個字元右移6位,獲得第三個目標字元。

  最後取第三個字元的右6位即獲得第四個目標字元。

  在以上的每一個步驟之後,再把結果與 0x3F 進行 AND 位操作,就可以得到編碼後的字元了。

  可是等等……聰明的你可能會問到,原文的位元組數量應該是3的倍數啊,如果這個條件不能滿足的話,那該怎麼辦呢?

  我們的解決辦法是這樣的:原文的位元組不夠的地方可以用全0來補足,轉換時Base64編碼用=號來代替。這就是為什麼有些Base64編碼會以一個或兩個等號結束的原因,但等號最多隻有兩個。因為:

  餘數 = 原文位元組數 MOD 3

  所以餘數任何情況下都只可能是0,1,2這三個數中的一個。如果餘數是0的話,就表示原文位元組數正好是3的倍數(最理想的情況啦)。如果是1的話,為了讓Base64編碼是3的倍數,就要補2個等號;同理,如果是2的話,就要補1個等號。

各種下載軟體地址

       先以“迅雷下載”為例: 很多下載類網站都提供“迅雷下載”的連結,其地址通常是加密的迅雷專用下載地址。

  如thunder://QUFodHRwOi8vd3d3LmJhaWR1LmNvbS9pbWcvc3NsbTFfbG9nby5naWZaWg==

  其實迅雷的“專用地址”也是用Base64加密的,其加密過程如下:

  一、在地址的前後分別新增AA和ZZ

  如www.baidu.com/img/sslm1_logo.gif變成

  AAwww.baidu.com/img/sslm1_logo.gifZZ

  二、對新的字串進行Base64編碼

  如AAwww.baidu.com/img/sslm1_logo.gifZZ用Base64編碼得到

  QUF3d3cuYmFpZHUuY29tL2ltZy9zc2xtMV9sb2dvLmdpZlpa

  三、在上面得到的字串前加上“thunder://”就成了

  thunder://QUF3d3cuYmFpZHUuY29tL2ltZy9zc2xtMV9sb2dvLmdpZlpa

  另:

  Flashget的與迅雷類似,只不過在第一步時加的“料”不同罷了,Flashget在地址前後加的“料”是[FLASHGET]

  而QQ旋風的乾脆不加料,直接就對地址進行Base64編碼了

各語言實現方式

      php 

     [下列程式碼僅在GBK中實現,UTF8程式碼請把 if($button=="迅雷地址->普通地址") echosubstr(base64_decode(str_ireplace("thunder://","",$txt1)),2,-2);這句改為if($button=="迅雷地址->普通地址") echosubstr(mb_convert_encoding(base64_decode(str_ireplace("thunder://","",$txt1))),2,-2);並把charset=gb2312改為charset=utf-8]

  1. <?php   
  2.   $txt1=trim($_POST['text1']);   
  3.   $txt2=trim($_POST['text2']);   
  4.   $txt3=trim($_POST['text3']);   
  5.   $button=$_POST['button'];   
  6.   ?>   
  7.   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >   
  8.   <html>   
  9.   <head>   
  10.   <title>迅雷和FlashGet,QQ旋風地址 地址轉換工具</title>   
  11.   <meta http-equiv="Content-Type" content="text/html; charset=gb2312">   
  12.   <meta content="迅雷,FlashGet,地址轉換," name="keywords">   
  13.   </head>   
  14.   <body>   
  15.   <form name="form1" method="post" action="">   
  16.   <hr size="1">   
  17.   <h3>迅雷轉換</h3>   
  18.   <P>轉換地址:   
  19.   <input name="text1" value="<?php echo $txt1;?>" type="text" style="width:516px;" /></P>   
  20.   <P>轉換後的:   
  21.   <input type="text" value="<?php   
  22.   if($button=="普通地址->迅雷地址") echo "thunder://".base64_encode("AA".$txt1."ZZ");   
  23.   if($button=="迅雷地址->普通地址") echo substr(base64_decode(str_ireplace("thunder://","",$txt1)),2,-2);   
  24.   ?>" style="width:516px;" /></P>   
  25.   <P>   
  26.   <input type="submit" name="button" value="普通地址->迅雷地址" />   
  27.   <input type="submit" name="button" value="迅雷地址->普通地址" /></P>   
  28.   <h3>FlashGet轉換</h3>   
  29.   <P>FlashGet地址:   
  30.   <input name="text2" value="<?php echo $txt2;?>" type="text" style="width:516px;" /></P>   
  31.   <P>轉換後 地址:   
  32.   <input type="text" value="<?php   
  33.   if($button=="普通地址->FlashGet地址") echo "flashget://".base64_encode($txt2);   
  34.   if($button=="FlashGet地址->普通地址") echo str_ireplace("[FLASHGET]","",base64_decode(str_ireplace("flashget://","",$txt2)));   
  35.   ?>" style="width:516px;" /></P>   
  36.   <P>   
  37.   <input type="submit" value="普通地址->FlashGet地址" name="button" />   
  38.   <input type="submit" value="FlashGet地址->普通地址" name="button" /></P>   
  39.   <h3>QQ旋風轉換</h3>   
  40.   <P>QQ旋風地址:   
  41.   <input name="text3" value="<?php echo $txt3;?>" type="text" style="width:516px;" /></P>   
  42.   <P>轉換後 地址:   
  43.   <input type="text" value="<?php   
  44.   if($button=="普通地址->QQ旋風") echo "qqdl://".base64_encode($txt3);   
  45.   if($button=="QQ旋風->普通地址") echo base64_decode(str_ireplace("qqdl://","",$txt3));   
  46.   ?>" style="width:516px;" /></P>   
  47.   <P>   
  48.   <input type="submit" value="普通地址->QQ旋風" name="button" />   
  49.   <input type="submit" value="QQ旋風->普通地址" name="button" /></P>   
  50.   </form>   
  51.   </body>   
  52.   </html>  

VB

  1. Option Explicit   
  2.   ' Base64 Encoding/Decoding Algorithm   
  3.   ' By: David Midkiff ([email protected])   
  4.   '   
  5.   ' This algorithms encodes and decodes data into Base64   
  6.   ' format. This format is extremely more efficient than   
  7.   ' Hexadecimal encoding.   
  8.   Private m_bytIndex(0 To 63) As Byte   
  9.   Private m_bytReverseIndex(0 To 255) As Byte   
  10.   Private Const k_bytEqualSign As Byte = 61   
  11.   Private Const k_bytMask1 As Byte = 3   
  12.   Private Const k_bytMask2 As Byte = 15   
  13.   Private Const k_bytMask3 As Byte = 63   
  14.   Private Const k_bytMask4 As Byte = 192   
  15.   Private Const k_bytMask5 As Byte = 240   
  16.   Private Const k_bytMask6 As Byte = 252   
  17.   Private Const k_bytShift2 As Byte = 4   
  18.   Private Const k_bytShift4 As Byte = 16   
  19.   Private Const k_bytShift6 As Byte = 64   
  20.   Private Const k_lMaxBytesPerLine As Long = 152   
  21.   Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)   
  22.   Public Function Decode64(sInput As String) As String   
  23.   If sInput = "" Then Exit Function   
  24.   Decode64 = StrConv(DecodeArray64(sInput), vbUnicode)   
  25.   End Function   
  26.   Private Function DecodeArray64(sInput As String) As Byte()   
  27.   Dim bytInput() As Byte   
  28.   Dim bytWorkspace() As Byte   
  29.   Dim bytResult() As Byte   
  30.   Dim lInputCounter As Long   
  31.   Dim lWorkspaceCounter As Long   
  32.   bytInput = Replace(Replace(sInput, vbCrLf, ""), "=", "")   
  33.   ReDim bytWorkspace(LBound(bytInput) To (UBound(bytInput) * 2)) As Byte   
  34.   lWorkspaceCounter = LBound(bytWorkspace)   
  35.   For lInputCounter = LBound(bytInput) To UBound(bytInput)   
  36.   bytInput(lInputCounter) = m_bytReverseIndex(bytInput(lInputCounter))   
  37.   Next lInputCounter   
  38.   For lInputCounter = LBound(bytInput) To (UBound(bytInput) - ((UBound(bytInput) Mod 8) + 8)) Step 8   
  39.   bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)   
  40.   bytWorkspace(lWorkspaceCounter + 1) = ((bytInput(lInputCounter + 2) And k_bytMask2) * k_bytShift4) + (bytInput(lInputCounter + 4) \ k_bytShift2)   
  41.   bytWorkspace(lWorkspaceCounter + 2) = ((bytInput(lInputCounter + 4) And k_bytMask1) * k_bytShift6) + bytInput(lInputCounter + 6)   
  42.   lWorkspaceCounter = lWorkspaceCounter + 3   
  43.   Next lInputCounter   
  44.   Select Case (UBound(bytInput) Mod 8):   
  45.   Case 3:   
  46.   bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)   
  47.   Case 5:   
  48.   bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)   
  49.   bytWorkspace(lWorkspaceCounter + 1) = ((bytInput(lInputCounter + 2) And k_bytMask2) * k_bytShift4) + (bytInput(lInputCounter + 4) \ k_bytShift2)   
  50.   lWorkspaceCounter = lWorkspaceCounter + 1   
  51.   Case 7:   
  52.   bytWorkspace(lWorkspaceCounter) = (bytInput(lInputCounter) * k_bytShift2) + (bytInput(lInputCounter + 2) \ k_bytShift4)   
  53.   bytWorkspace(lWorkspaceCounter + 1) = ((bytInput(lInputCounter + 2) And k_bytMask2) * k_bytShift4) + (bytInput(lInputCounter + 4) \ k_bytShift2)   
  54.   bytWorkspace(lWorkspaceCounter + 2) = ((bytInput(lInputCounter + 4) And k_bytMask1) * k_bytShift6) + bytInput(lInputCounter + 6)   
  55.   lWorkspaceCounter = lWorkspaceCounter + 2   
  56.   End Select   
  57.   ReDim bytResult(LBound(bytWorkspace) To lWorkspaceCounter) As Byte   
  58.   If LBound(bytWorkspace) = 0 Then lWorkspaceCounter = lWorkspaceCounter + 1   
  59.   CopyMemory VarPtr(bytResult(LBound(bytResult))), VarPtr(bytWorkspace(LBound(bytWorkspace))), lWorkspaceCounter   
  60.   DecodeArray64 = bytResult   
  61.   End Function   
  62.   Public Function Encode64(ByRef sInput As String) As String   
  63.   If sInput = "" Then Exit Function   
  64.   Dim bytTemp() As Byte   
  65.   bytTemp = StrConv(sInput, vbFromUnicode)   
  66.   Encode64 = EncodeArray64(bytTemp)   
  67.   End Function   
  68.   Private Function EncodeArray64(ByRef bytInput() As Byte) As String   
  69.   On Error GoTo ErrorHandler   
  70.   Dim bytWorkspace() As Byte, bytResult() As Byte   
  71.   Dim bytCrLf(0 To 3) As Byte, lCounter As Long   
  72.   Dim lWorkspaceCounter As Long, lLineCounter As Long   
  73.   Dim lCompleteLines As Long, lBytesRemaining As Long   
  74.   Dim lpWorkSpace As Long, lpResult As Long   
  75.   Dim lpCrLf As Long   
  76.   If UBound(bytInput) < 1024 Then   
  77.   ReDim bytWorkspace(LBound(bytInput) To (LBound(bytInput) + 4096)) As Byte   
  78.   Else   
  79.   ReDim bytWorkspace(LBound(bytInput) To (UBound(bytInput) * 4)) As Byte   
  80.   End If   
  81.   lWorkspaceCounter = LBound(bytWorkspace)   
  82.   For lCounter = LBound(bytInput) To (UBound(bytInput) - ((UBound(bytInput) Mod 3) + 3)) Step 3   
  83.   bytWorkspace(lWorkspaceCounter) = m_bytIndex((bytInput(lCounter) \ k_bytShift2))   
  84.   bytWorkspace(lWorkspaceCounter + 2) = m_bytIndex(((bytInput(lCounter) And k_bytMask1) * k_bytShift4) + ((bytInput(lCounter + 1)) \ k_bytShift4))   
  85.   bytWorkspace(lWorkspaceCounter + 4) = m_bytIndex(((bytInput(lCounter + 1) And k_bytMask2) * k_bytShift2) + (bytInput(lCounter + 2) \ k_bytShift6))   
  86.   bytWorkspace(lWorkspaceCounter + 6) = m_bytIndex(bytInput(lCounter + 2) And k_bytMask3)   
  87.   lWorkspaceCounter = lWorkspaceCounter + 8   
  88.   Next lCounter   
  89.   Select Case (UBound(bytInput) Mod 3):   
  90.   Case 0:   
  91.   bytWorkspace(lWorkspaceCounter) = m_bytIndex((bytInput(lCounter) \ k_bytShift2))   
  92.   bytWorkspace(lWorkspaceCounter + 2) = m_bytIndex((bytInput(lCounter) And k_bytMask1) * k_bytShift4)   
  93.   bytWorkspace(lWorkspaceCounter + 4) = k_bytEqualSign   
  94.   bytWorkspace(lWorkspaceCounter + 6) = k_bytEqualSign   
  95.   Case 1:   
  96.   bytWorkspace(lWorkspaceCounter) = m_bytIndex((bytInput(lCounter) \ k_bytShift2))   
  97.   bytWorkspace(lWorkspaceCounter + 2) = m_bytIndex(((bytInput(lCounter) And k_bytMask1) * k_bytShift4) + ((bytInput(lCounter + 1)) \ k_bytShift4))   
  98.   bytWorkspace(lWorkspaceCounter + 4) = m_bytIndex((bytInput(lCounter + 1) And k_bytMask2) * k_bytShift2)   
  99.   bytWorkspace(lWorkspaceCounter + 6) = k_bytEqualSign   
  100.   Case 2:   
  101.   bytWorkspace(lWorkspaceCounter) = m_bytIndex((bytInput(lCounter) \ k_bytShift2))   
  102.   bytWorkspace(lWorkspaceCounter + 2) = m_bytIndex(((bytInput(lCounter) And k_bytMask1) * k_bytShift4) + ((bytInput(lCounter + 1)) \ k_bytShift4))   
  103.   bytWorkspace(lWorkspaceCounter + 4) = m_bytIndex(((bytInput(lCounter + 1) And k_bytMask2) * k_bytShift2) + ((bytInput(lCounter + 2)) \ k_bytShift6))   
  104.   bytWorkspace(lWorkspaceCounter + 6) = m_bytIndex(bytInput(lCounter + 2) And k_bytMask3)   
  105.   End Select   
  106.   lWorkspaceCounter = lWorkspaceCounter + 8   
  107.   If lWorkspaceCounter <= k_lMaxBytesPerLine Then   
  108.   EncodeArray64 = Left$(bytWorkspace, InStr(1, bytWorkspace, Chr$(0)) - 1)   
  109.   Else   
  110.   bytCrLf(0) = 13   
  111.   bytCrLf(1) = 0   
  112.   bytCrLf(2) = 10   
  113.   bytCrLf(3) = 0   
  114.   ReDim bytResult(LBound(bytWorkspace) To UBound(bytWorkspace))   
  115.   lpWorkSpace = VarPtr(bytWorkspace(LBound(bytWorkspace)))   
  116.   lpResult = VarPtr(bytResult(LBound(bytResult)))   
  117.   lpCrLf = VarPtr(bytCrLf(LBound(bytCrLf)))   
  118.   lCompleteLines = Fix(lWorkspaceCounter / k_lMaxBytesPerLine)   
  119.   For lLineCounter =