MD
簡介
MD
的全稱是Message
Digest Algorithm
在
年代初由MIT的計算機科學實驗室和RSA Data Security Inc發明
經MD
MD
和MD
發展而來
Message
Digest泛指字節串(Message)的Hash變換
就是把一個任意長度的字節串變換成一定長的大整數
請注意我使用了
字節串
而不是
字符串
這個詞
是因為這種變換只與字節的值有關
與字符集或編碼方式無關
MD
將任意長度的
字節串
變換成一個
bit的大整數
並且它是一個不可逆的字符串變換算法
換句話說就是
即使你看到源程序和算法描述
也無法將一個MD
的值變換回原始的字符串
從數學原理上說
是因為原始的字符串有無窮多個
這有點象不存在反函數的數學函數
MD
的典型應用是對一段Message(字節串)產生fingerprint(指紋)
以防止被
篡改
舉個例子
你將一段話寫在一個叫readme
txt文件中
並對這個readme
txt產生一個MD
的值並記錄在案
然後你可以傳播這個文件給別人
別人如果修改了文件中的任何內容
你對這個文件重新計算MD
時就會發現
如果再有一個第三方的認證機構
用MD
還可以防止文件作者的
抵賴
這就是所謂的數字簽名應用
MD
還廣泛用於加密和解密技術上
在很多操作系統中
用戶的密碼是以MD
值(或類似的其它算法)的方式保存的
用戶Login的時候
系統是把用戶輸入的密碼計算成MD
值
然後再去和系統中保存的MD
值進行比較
而系統並不
知道
用戶的密碼是什麼
一些黑客破獲這種密碼的方法是一種被稱為
跑字典
的方法
有兩種方法得到字典
一種是日常搜集的用做密碼的字符串表
另一種是用排列組合方法生成的
先用MD
程序計算出這些字典項的MD
值
然後再用目標的MD
值在這個字典中檢索
即使假設密碼的最大長度為
同時密碼只能是字母和數字
共
+
+
=
個字符
排列組合出的字典的項數則是P(
)+P(
)…
+P(
)
那也已經是一個很天文的數字了
存儲這個字典就需要TB級的磁盤組
而且這種方法還有一個前提
就是能獲得目標賬戶的密碼MD
值的情況下才可以
在很多電子商務和社區應用中
管理用戶的Account是一種最常用的基本功能
盡管很多Application Server提供了這些基本組件
但很多應用開發者為了管理的更大的靈活性還是喜歡采用關系數據庫來管理用戶
懶惰的做法是用戶的密碼往往使用明文或簡單的變換後直接保存在數據庫中
因此這些用戶的密碼對軟件開發者或系統管理員來說可以說毫無保密可言
本文的目的是介紹MD
的Java Bean的實現
同時給出用MD
來處理用戶的Account密碼的例子
這種方法使得管理員和程序設計者都無法看到用戶的密碼
盡管他們可以初始化它們
但重要的一點是對於用戶密碼設置習慣的保護
有興趣的讀者可以從這裡取得MD
也就是RFC
的文本
實現策略
MD
的算法在RFC
中實際上已經提供了C的實現
我們其實馬上就能想到
至少有兩種用Java實現它的方法
第一種是
用Java語言重新寫整個算法
或者再說簡單點就是把C程序改寫成Java程序
第二種是
用JNI(Java Native Interface)來實現
核心算法仍然用這個C程序
用Java類給它包個殼
但我個人認為
JNI應該是Java為了解決某類問題時的沒有辦法的辦法(比如與操作系統或I/O設備密切相關的應用)
同時為了提供和其它語言的互操作性的一個手段
使用JNI帶來的最大問題是引入了平台的依賴性
打破了SUN所鼓吹的
一次編寫到處運行
的Java好處
因此
我決定采取第一種方法
一來和大家一起嘗試一下
一次編寫到處運行
的好處
二來檢驗一下Java
現在對於比較密集的計算的效率問題
實現過程
限於這篇文章的篇幅
同時也為了更多的讀者能夠真正專注於問題本身
我不想就某一種Java集成開發環境來介紹這個Java Bean的制作過程
介紹一個方法時我發現步驟和命令很清晰
我相信有任何一種Java集成環境三天以上經驗的讀者都會知道如何把這些代碼在集成環境中編譯和運行
用集成環境講述問題往往需要配很多屏幕截圖
這也是我一直對集成環境很頭疼的原因
我使用了一個普通的文本編輯器
同時使用了Sun公司標准的JDK
for Windows NT
其實把C轉換成Java對於一個有一定C語言基礎的程序員並不困難
這兩個語言的基本語法幾乎完全一致.我大概花了一個小時的時間完成了代碼的轉換工作
我主要作了下面幾件事
把必須使用的一些#define的宏定義變成Class中的final static
這樣保證在一個進程空間中的多個Instance共享這些數據
刪去了一些無用的#if define
因為我只關心MD
這個推薦的C實現同時實現了MD
MD
和 MD
而且有些#if define還和C不同編譯器有關
將一些計算宏轉換成final static 成員函數
所有的變量命名與原來C實現中保持一致
在大小寫上作一些符合Java習慣的變化
計算過程中的C函數變成了private方法(成員函數)
關鍵變量的位長調整
定義了類和方法
需要注意的是
很多早期的C編譯器的int類型是
bit的
MD
使用了unsigned long int
並認為它是
bit的無符號整數
而在Java中int是
bit的
long是
bit的
在MD
的C實現中
使用了大量的位操作
這裡需要指出的一點是
盡管Java提供了位操作
由於Java沒有unsigned類型
對於右移位操作多提供了一個無符號右移
>>>
等價於C中的 >> 對於unsigned 數的處理
因為Java不提供無符號數的運算
兩個大int數相加就會溢出得到一個負數或異常
因此我將一些關鍵變量在Java中改成了long類型(
bit)
我個人認為這比自己去重新定義一組無符號數的類同時重載那些運算符要方便
同時效率高很多並且代碼也易讀
OO(Object Oriented)的濫用反而會導致效率低下
限於篇幅
這裡不再給出原始的C代碼
有興趣對照的讀者朋友可以去看RFC
MD
java源代碼
測試
在RFC
中
給出了Test suite用來檢驗你的實現是否正確
MD
(
) = d
d
cd
f
b
e
ecf
e
MD
(
a
) =
cc
b
c
f
b
a
c
e
MD
(
abc
) =
cd
fb
d
f
d
e
f
MD
(
message digest
) = f
b
d
cb
d
a
f
aaf
d
MD
(
abcdefghijklmnopqrstuvwxyz
) = c
fcd
d
e
dfb
cca
e
b
……
這些輸出結果的含義是指
空字符串
的MD
值是d
d
cd
f
b
e
ecf
e
字符串
a
的MD
值是
cc
b
c
f
b
a
c
e
……
編譯並運行我們的程序
javac –d
MD
java
java beartool
MD
為了將來不與別人的同名程序沖突
我在我的程序的第一行使用了package beartool;
因此編譯命令javac –d
MD
java 命令在我們的工作目錄下自動建立了一個beartool目錄
目錄下放著編譯成功的 MD
class
我們將得到和Test suite同樣的結果
當然還可以繼續測試你感興趣的其它MD
變換
例如
java beartool
MD
將給出
的MD
值
可能是我的計算機知識是從Apple II和Z
單板機開始的
我對大寫十六進制代碼有偏好
如果您想使用小寫的Digest String只需要把byteHEX函數中的A
B
C
D
E
F改成a
b
c
d
e
f就可以了
MD
據稱是一種比較耗時的計算
我們的Java版MD
一閃就算出來了
沒遇到什麼障礙
而且用肉眼感覺不出來Java版的MD
比C版的慢
為了測試它的兼容性
我把這個MD
class文件拷貝到我的另一台Linux+IBM JDK
的機器上
執行後得到同樣結果
確實是
一次編寫到處運行了
Java Bean簡述
現在
我們已經完成並簡單測試了這個Java Class
我們文章的標題是做一個Java Bean
其實普通的Java Bean很簡單
並不是什麼全新的或偉大的概念
就是一個Java的Class
盡管 Sun規定了一些需要實現的方法
但並不是強制的
而EJB(Enterprise Java Bean)無非規定了一些必須實現(非常類似於響應事件)的方法
這些方法是供EJB Container使用(調用)的
在一個Java Application或Applet裡使用這個bean非常簡單
最簡單的方法是你要使用這個類的源碼工作目錄下建一個beartool目錄
把這個class文件拷貝進去
然後在你的程序中import beartool
MD
就可以了
最後打包成
jar或
war是保持這個相對的目錄關系就行了
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26240.html