1. 程式人生 > >Android MD5校驗碼的生成與演算法實現

Android MD5校驗碼的生成與演算法實現

在Java中,java.security.MessageDigest (rt.jar中)已經定義了 MD5 的計算,所以我們只需要簡單地呼叫即可得到 MD5 的128 位整數。然後將此 128 位計 16 個位元組轉換成 16 進製表示即可。 

    下面是一個可生成字串或檔案MD5校驗碼的例子,測試過,可當做工具類直接使用,其中最主要的是getMD5String(String s)和getFileMD5String(File file)兩個方法,分別用於生成字串的md5校驗值和生成檔案的md5校驗值,getFileMD5String_old(File file)方法可刪除,不建議使用:

Java程式碼  收藏程式碼
  1. package  com.why.md5;  
  2. import  java.io.File;  
  3. import  java.io.FileInputStream;  
  4. import  java.io.IOException;  
  5. import  java.io.InputStream;  
  6. import  java.nio.MappedByteBuffer;  
  7. import  java.nio.channels.FileChannel;  
  8. import  java.security.MessageDigest;  
  9. import  java.security.NoSuchAlgorithmException;  
  10. public class  MD5Util {  
  11.     /**  
  12.      * 預設的密碼字串組合,用來將位元組轉換成 16 進製表示的字元,apache校驗下載的檔案的正確性用的就是預設的這個組合  
  13.      */
  14.     protected static char  hexDigits[] = {  '0' '1' '2' '3' '4' '5' '6' ,  
  15.             '7' '8'
    '9' 'a' 'b' 'c' 'd' 'e' 'f'  };  
  16.     protected static  MessageDigest messagedigest =  null ;  
  17.     static  {  
  18.         try  {  
  19.             messagedigest = MessageDigest.getInstance("MD5" );  
  20.         } catch  (NoSuchAlgorithmException nsaex) {  
  21.             System.err.println(MD5Util.class .getName()  
  22.                     + "初始化失敗,MessageDigest不支援MD5Util。" );  
  23.             nsaex.printStackTrace();  
  24.         }  
  25.     }  
  26.     /**  
  27.      * 生成字串的md5校驗值  
  28.      *   
  29.      * @param s  
  30.      * @return  
  31.      */
  32.     public static  String getMD5String(String s) {  
  33.         return  getMD5String(s.getBytes());  
  34.     }  
  35.     /**  
  36.      * 判斷字串的md5校驗碼是否與一個已知的md5碼相匹配  
  37.      *   
  38.      * @param password 要校驗的字串  
  39.      * @param md5PwdStr 已知的md5校驗碼  
  40.      * @return  
  41.      */
  42.     public static boolean  checkPassword(String password, String md5PwdStr) {  
  43.         String s = getMD5String(password);  
  44.         return  s.equals(md5PwdStr);  
  45.     }  
  46.     /**  
  47.      * 生成檔案的md5校驗值  
  48.      *   
  49.      * @param file  
  50.      * @return  
  51.      * @throws IOException  
  52.      */
  53.     public static  String getFileMD5String(File file)  throws  IOException {         
  54.         InputStream fis;  
  55.         fis = new  FileInputStream(file);  
  56.         byte [] buffer =  new byte [ 1024 ];  
  57.         int  numRead =  0 ;  
  58.         while  ((numRead = fis.read(buffer)) >  0 ) {  
  59.             messagedigest.update(buffer, 0 , numRead);  
  60.         }  
  61.         fis.close();  
  62.         return  bufferToHex(messagedigest.digest());  
  63.     }  
  64.     /**  
  65.      * JDK1.4中不支援以MappedByteBuffer型別為引數update方法,並且網上有討論要慎用MappedByteBuffer,  
  66.      * 原因是當使用 FileChannel.map 方法時,MappedByteBuffer 已經在系統內佔用了一個控制代碼,  
  67.      * 而使用 FileChannel.close 方法是無法釋放這個控制代碼的,且FileChannel有沒有提供類似 unmap 的方法,  
  68.      * 因此會出現無法刪除檔案的情況。  
  69.      *   
  70.      * 不推薦使用  
  71.      *   
  72.      * @param file  
  73.      * @return  
  74.      * @throws IOException  
  75.      */
  76.     public static  String getFileMD5String_old(File file)  throws  IOException {  
  77.         FileInputStream in = new  FileInputStream(file);  
  78.         FileChannel ch = in.getChannel();  
  79.         MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0 ,  
  80.                 file.length());  
  81.         messagedigest.update(byteBuffer);  
  82.         return  bufferToHex(messagedigest.digest());  
  83.     }  
  84.     public static  String getMD5String( byte [] bytes) {  
  85.         messagedigest.update(bytes);  
  86.         return  bufferToHex(messagedigest.digest());  
  87.     }  
  88.     private static  String bufferToHex( byte  bytes[]) {  
  89.         return  bufferToHex(bytes,  0 , bytes.length);  
  90.     }  
  91.     private static  String bufferToHex( byte  bytes[],  int  m,  int  n) {  
  92.         StringBuffer stringbuffer = new  StringBuffer( 2  * n);  
  93.         int  k = m + n;  
  94.         for  ( int  l = m; l < k; l++) {  
  95.             appendHexPair(bytes[l], stringbuffer);  
  96.         }  
  97.         return  stringbuffer.toString();  
  98.     }  
  99.     private static void  appendHexPair( byte  bt, StringBuffer stringbuffer) {  
  100.         char  c0 = hexDigits[(bt &  0xf0 ) >>  4 ]; // 取位元組中高 4 位的數字轉換, >>> 為邏輯右移,將符號位一起右移,此處未發現兩種符號有何不同 
  101.         char  c1 = hexDigits[bt &  0xf ]; // 取位元組中低 4 位的數字轉換 
  102.         stringbuffer.append(c0);  
  103.         stringbuffer.append(c1);  
  104.     }  
  105.     public static void  main(String[] args)  throws  IOException {  
  106.         long  begin = System.currentTimeMillis();  
  107.         File file = new  File( "C:/12345.txt" );  
  108.         String md5 = getFileMD5String(file);  
  109. //      String md5 = getMD5String("a");
  110.         long  end = System.currentTimeMillis();  
  111.         System.out.println("md5:"  + md5 +  " time:"  + ((end - begin) /  1000 ) +  "s" );  
  112.     }  
  113. }  
package com.why.md5;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Util {
	/**
	 * 預設的密碼字串組合,用來將位元組轉換成 16 進製表示的字元,apache校驗下載的檔案的正確性用的就是預設的這個組合
	 */
	protected static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6',
			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

	protected static MessageDigest messagedigest = null;
	static {
		try {
			messagedigest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException nsaex) {
			System.err.println(MD5Util.class.getName()
					+ "初始化失敗,MessageDigest不支援MD5Util。");
			nsaex.printStackTrace();
		}
	}
	
	/**
	 * 生成字串的md5校驗值
	 * 
	 * @param s
	 * @return
	 */
	public static String getMD5String(String s) {
		return getMD5String(s.getBytes());
	}
	
	/**
	 * 判斷字串的md5校驗碼是否與一個已知的md5碼相匹配
	 * 
	 * @param password 要校驗的字串
	 * @param md5PwdStr 已知的md5校驗碼
	 * @return
	 */
	public static boolean checkPassword(String password, String md5PwdStr) {
		String s = getMD5String(password);
		return s.equals(md5PwdStr);
	}
	
	/**
	 * 生成檔案的md5校驗值
	 * 
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static String getFileMD5String(File file) throws IOException {		
		InputStream fis;
	    fis = new FileInputStream(file);
	    byte[] buffer = new byte[1024];
	    int numRead = 0;
	    while ((numRead = fis.read(buffer)) > 0) {
	    	messagedigest.update(buffer, 0, numRead);
	    }
	    fis.close();
		return bufferToHex(messagedigest.digest());
	}

	/**
	 * JDK1.4中不支援以MappedByteBuffer型別為引數update方法,並且網上有討論要慎用MappedByteBuffer,
	 * 原因是當使用 FileChannel.map 方法時,MappedByteBuffer 已經在系統內佔用了一個控制代碼,
	 * 而使用 FileChannel.close 方法是無法釋放這個控制代碼的,且FileChannel有沒有提供類似 unmap 的方法,
	 * 因此會出現無法刪除檔案的情況。
	 * 
	 * 不推薦使用
	 * 
	 * @param file
	 * @return
	 * @throws IOException
	 */
	public static String getFileMD5String_old(File file) throws IOException {
		FileInputStream in = new FileInputStream(file);
		FileChannel ch = in.getChannel();
		MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,
				file.length());
		messagedigest.update(byteBuffer);
		return bufferToHex(messagedigest.digest());
	}

	public static String getMD5String(byte[] bytes) {
		messagedigest.update(bytes);
		return bufferToHex(messagedigest.digest());
	}

	private static String bufferToHex(byte bytes[]) {
		return bufferToHex(bytes, 0, bytes.length);
	}

	private static String bufferToHex(byte bytes[], int m, int n) {
		StringBuffer stringbuffer = new StringBuffer(2 * n);
		int k = m + n;
		for (int l = m; l < k; l++) {
			appendHexPair(bytes[l], stringbuffer);
		}
		return stringbuffer.toString();
	}

	private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
		char c0 = hexDigits[(bt & 0xf0) >> 4];// 取位元組中高 4 位的數字轉換, >>> 為邏輯右移,將符號位一起右移,此處未發現兩種符號有何不同 
		char c1 = hexDigits[bt & 0xf];// 取位元組中低 4 位的數字轉換 
		stringbuffer.append(c0);
		stringbuffer.append(c1);
	}
	
	public static void main(String[] args) throws IOException {
		long begin = System.currentTimeMillis();

		File file = new File("C:/12345.txt");
		String md5 = getFileMD5String(file);

//		String md5 = getMD5String("a");
		
		long end = System.currentTimeMillis();
		System.out.println("md5:" + md5 + " time:" + ((end - begin) / 1000)	+ "s");
	}
}

   MD5的全稱是Message-digest Algorithm 5(資訊-摘要演算法),用於確保資訊傳輸完整一致。90年代初由MIT的電腦科學實驗室和RSA Data Security Inc的Ronald L. Rivest開發出來,經MD2、MD3和MD4發展而來。

    任何一個字串或檔案,無論是可執行程式、影象檔案、臨時檔案或者其他任何型別的檔案,也不管它體積多大,都有且只有一個獨一無二的MD5資訊碼,並且如果這個檔案被修改過,它的MD5碼也將隨之改變。

    Message-Digest泛指位元組串(Message)的Hash變換,就是把一個任意長度的位元組串變換成一定長的大整數。注意這裡說的是“位元組串”而不是“字串”,因為這種變換隻與位元組的值有關,與字符集或編碼方式無關。

    MD5用的是雜湊函式,在計算機網路中應用較多的不可逆加密演算法有RSA公司發明的MD5演算法和由美國國家技術標準研究所建議的安全雜湊演算法SHA。

    MD5將任意長度的“位元組串”變換成一個128bit的大整數,並且它是一個不可逆的字串變換演算法,換句話說就是,即使你看到源程式和演算法描述,也無法 將一個MD5的值變換回原始的字串,從數學原理上說,是因為原始的字串有無窮多個,這有點象不存在反函式的數學函式。所以,要遇到了md5密碼的問 題,比較好的辦法是:你可以用這個系統中的md5()函式重新設一個密碼,如admin,把生成的一串密碼的Hash值覆蓋原來的Hash值就行了。

    MD5的典型應用是對一段Message(位元組串)產生fingerprint(指紋),以防止被“篡改”。舉個例子,你將一段話寫在一個叫 readme.txt檔案中,並對這個readme.txt產生一個MD5的值並記錄在案,然後你可以傳播這個檔案給別人,別人如果修改了檔案中的任何內 容,你對這個檔案重新計算MD5時就會發現(兩個MD5值不相同)。如果再有一個第三方的認證機構,用MD5還可以防止檔案作者的“抵賴”,這就是所謂的 數字簽名應用。


  MD5還廣泛用於作業系統的登陸認證上,如Unix、各類BSD系統登入密碼、數字簽名等諸多方。如在UNIX系統中使用者的密碼是以 MD5(或其它類似的演算法)經Hash運算後儲存在檔案系統中。當用戶登入的時候,系統把使用者輸入的密碼進行MD5 Hash運算,然後再去和儲存在檔案系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟,系統在並不知道使用者密碼的明碼的情況下就可 以確定使用者登入系統的合法性。這可以避免使用者的密碼被具有系統管理員許可權的使用者知道。

    現在被黑客使用最多的一種破譯密碼的方法就是一種被稱為"跑字典"的方法。有兩種方法得到字典,一種是日常蒐集的用做密碼的字串表,另一種是用排列組合 方法生成的,先用MD5程式計算出這些字典項的MD5值,然後再用目標的MD5值在這個字典中檢索。我們假設密碼的最大長度為8位位元組(8 Bytes),同時密碼只能是字母和數字,共26+26+10=62個字元,排列組合出的字典的項數則是 P(62,1)+P(62,2)….+P(62,8),那也已經是一個很天文的數字了,儲存這個字典就需要TB級的磁碟陣列,而且這種方法還有一個前提, 就是能獲得目標賬戶的密碼MD5值的情況下才可以。這種加密技術被廣泛的應用於UNIX系統中,這也是為什麼UNIX系統比一般作業系統更為堅固一個重要 原因。

MD5演算法
      md5演算法定義在RFC 1321中,由Ron Rivest(RSA公司)在1992年提出。然而很多學者已經找出了構造md5衝突的方法。這些人中包括中國山東大學的王教授和Hans Dobbertin。所以,單純使用md5的資訊認證模式變得不可靠了。但並不是說md5不能夠使用。

    MD5以512位分組來處理輸入的資訊,且每一分組又被劃分為16個32位子分組,經過了一系列的處理後,演算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位雜湊值。

MD5演算法的計算步驟:

1.通過新增一個1和若干個0的方式,把輸入資料長度(按照位元組算)變成64m+56
2.新增8個位元組到輸入資料中去,這樣輸入資料長度變成了64的倍數
3.把資料劃分成塊,每塊64個位元組
4.初始情況下,輸出為:                                                                   
  m_state[0] = 0x67452301L;
  m_state[1] = 0xefcdab89L;
  m_state[2] = 0x98badcfeL;
  m_state[3] = 0x10325476L;
5.分別對每塊進行計算。輸出最後結果。

    MD5的演算法在RFC1321中實際上已經提供了C的實現,需要注意的是,很多早期的C編譯器的int型別是16 bit的,MD5使用了unsigned long int,並認為它是32bit的無符號整數。而在Java中int是32 bit的,long是64 bit的。在MD5的C實現中,使用了大量的位操作。這裡需要指出的一點是,儘管Java提供了位操作,由於Java沒有unsigned型別,對於右移 位操作多提供了一個無符號右移:>>>,等價於C中的 >> 對於unsigned 數的處理。

下面是一個MD5演算法的Java實現:

Java程式碼  收藏程式碼
  1. package  com.why.md5;  
  2. /*******************************************************************************  
  3.  * MD5_SRC 類實現了RSA Data Security, Inc.在提交給IETF的RFC1321中的MD5_SRC message-digest  
  4.  * 演算法。  
  5.  ******************************************************************************/
  6. public class  MD5_SRC {  
  7.     /*  
  8.      * 下面這些S11-S44實際上是一個4*4的矩陣,在原始的C實現中是用#define 實現的, 這裡把它們實現成為static  
  9.      * final是表示了只讀,且能在同一個程序空間內的多個 Instance間共享  
  10.      */
  11.     static final int  S11 =  7 ;  
  12.     static final int  S12 =  12 ;  
  13.     static final int  S13 =  17 ;  
  14.     static final int  S14 =  22 ;  
  15.     static final int  S21 =  5 ;  
  16.     static final int  S22 =  9 ;  
  17.     static final int  S23 =  14 ;  
  18.     static final int  S24 =  20 ;  
  19.     static final int  S31 =  4 ;  
  20.     static final int  S32 =  11 ;  
  21.     static final int  S33 =  16 ;  
  22.     static final int  S34 =  23 ;  
  23.     static final int  S41 =  6 ;  
  24.     static final int  S42 =  10 ;  
  25.     static final int  S43 =  15 ;  
  26.     static final int  S44 =  21 ;  
  27.     static final byte [] PADDING = { - 128 0 0 0 0 0 0 0 0 0 0 0 0 ,  
  28.             0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ,  
  29.             0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ,  
  30.             0 0 0 0 0 0 0  };  
  31.     /*  
  32.      * 下面的三個成員是keyBean計算過程中用到的3個核心資料,在原始的C實現中 被定義到keyBean_CTX結構中  
  33.      */
  34.     private long [] state =  new long [ 4 ];  // state (ABCD)
  35.     private long [] count =  new long [ 2 ];  // number of bits, modulo 2^64 (lsb first)
  36.     private byte [] buffer =  new byte [ 64 ];  // input buffer
  37.     /*  
  38.      * digestHexStr是keyBean的唯一一個公共成員,是最新一次計算結果的 16進位制ASCII表示.  
  39.      */
  40.     public  String digestHexStr;  
  41.     /*  
  42.      * digest,是最新一次計算結果的2進位制內部表示,表示128bit的keyBean值.  
  43.      */
  44.     private byte [] digest =  new byte [ 16 ];  
  45.     /*  
  46.      * getkeyBeanofStr是類keyBean最主要的公共方法,入口引數是你想要進行keyBean變換的字串  
  47.      * 返回的是變換完的結果,這個結果是從公共成員digestHexStr取得的.  
  48.      */
  49.     public  String getkeyBeanofStr(String inbuf) {  
  50.         keyBeanInit();  
  51.         keyBeanUpdate(inbuf.getBytes(), inbuf.length());  
  52.         keyBeanFinal();  
  53.         digestHexStr = "" ;  
  54.         for  ( int  i =  0 ; i <  16 ; i++) {  
  55.             digestHexStr += byteHEX(digest[i]);  
  56.         }  
  57.         return  digestHexStr;  
  58.     }  
  59.     // 這是keyBean這個類的標準建構函式,JavaBean要求有一個public的並且沒有引數的建構函式
  60.     public  MD5_SRC() {  
  61.         keyBeanInit();  
  62.         return ;  
  63.     }  
  64.     /* keyBeanInit是一個初始化函式,初始化核心變數,裝入標準的幻數 */
  65.     private void  keyBeanInit() {  
  66.         count[0 ] = 0L;  
  67.         count[1 ] = 0L;  
  68.         // /* Load magic initialization constants.
  69.         state[0 ] = 0x67452301L;  
  70.         state[1 ] = 0xefcdab89L;  
  71.         state[2 ] = 0x98badcfeL;  
  72.         state[3 ] = 0x10325476L;  
  73.         return ;  
  74.     }  
  75.     /*  
  76.      * F, G, H ,I 是4 個基本的keyBean函式,在原始的keyBean的C實現中,由於它們是  
  77.      * 簡單的位運算,可能出於效率的考慮把它們實現成了巨集,在java中,我們把它們 實現成了private 方法,名字保持了原來C中的。  
  78.      */  
  79.     private long  F( long  x,  long  y,  long  z) {  
  80.         return  (x & y) | ((~x) & z);  
  81.     }  
  82.     private long  G( long  x,  long  y,  long  z) {  
  83.         return  (x & z) | (y & (~z));  
  84.     }  
  85.     private long  H( long  x,  long  y,  long  z) {  
  86.         return  x ^ y ^ z;  
  87.     }  
  88.     private long  I( long  x,  long  y,  long  z) {  
  89.         return  y ^ (x | (~z));  
  90.     }  
  91.     /*  
  92.      * FF,GG,HH和II將呼叫F,G,H,I進行近一步變換 FF, GG, HH, and II transformations for  
  93.      * rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent  
  94.      * recomputation.  
  95.      */
  96.     private long  FF( long  a,  long  b,  long  c,  long  d,  long  x,  long  s,  long  ac) {  
  97.         a += F(b, c, d) + x + ac;  
  98.         a = ((int ) a << s) | (( int ) a >>> ( 32  - s));  
  99.         a += b;  
  100.         return  a;  
  101.     }  
  102.     private long  GG( long  a,  long  b,  long  c,  long  d,  long  x,  long  s,  long  ac) {  
  103.         a += G(b, c, d) + x + ac;  
  104.         a = ((int ) a << s) | (( int ) a >>> ( 32  - s));  
  105.         a += b;  
  106.         return  a;  
  107.     }  
  108.     private long  HH( long  a,  long  b,  long  c,  long  d,  long  x,  long  s,  long  ac) {  
  109.         a += H(b, c, d) + x + ac;  
  110.         a = ((int ) a << s) | (( int ) a >>> ( 32  - s));  
  111.         a += b;  
  112.         return  a;  
  113.     }  
  114.     private long  II( long  a,  long  b,  long  c,  long  d,  long  x,  long  s,  long  ac) {  
  115.         a += I(b, c, d) + x + ac;  
  116.         a = ((int ) a << s) | (( int ) a >>> ( 32  - s));  
  117.         a += b;  
  118.         return  a;  
  119.     }  
  120.     /*  
  121.      * keyBeanUpdate是keyBean的主計算過程,inbuf是要變換的位元組串,inputlen是長度,這個  
  122.      * 函式由getkeyBeanofStr呼叫,呼叫之前需要呼叫keyBeaninit,因此把它設計成private的  
  123.      */
  124.     private void  keyBeanUpdate( byte [] inbuf,  int  inputLen) {  
  125.         int  i, index, partLen;  
  126.         byte [] block =  new byte [ 64 ];  
  127.         index = (int ) (count[ 0 ] >>>  3 ) &  0x3F ;  
  128.         // /* Update number of bits */
  129.         if  ((count[ 0 ] += (inputLen <<  3 )) < (inputLen <<  3 ))  
  130.             count[1 ]++;  
  131.         count[1 ] += (inputLen >>>  29 );  
  132.         partLen = 64  - index;  
  133.         // Transform as many times as possible.
  134.         if  (inputLen >= partLen) {  
  135.             keyBeanMemcpy(buffer, inbuf, index, 0 , partLen);  
  136.             keyBeanTransform(buffer);  
  137.             for  (i = partLen; i +  63  < inputLen; i +=  64 ) {  
  138.                 keyBeanMemcpy(block, inbuf, 0 , i,  64 );  
  139.                 keyBeanTransform(block);  
  140.             }  
  141.             index = 0 ;  
  142.         } else
  143.             i = 0 ;  
  144.         // /* Buffer remaining input */
  145.         keyBeanMemcpy(buffer, inbuf, index, i, inputLen - i);  
  146.     }  
  147.     /*  
  148.      * keyBeanFinal整理和填寫輸出結果  
  149.      */
  150.     private void  keyBeanFinal() {  
  151.         byte [] bits =  new byte [ 8 ];  
  152.         int  index, padLen;  
  153.         // /* Save number of bits */
  154.         Encode(bits, count, 8 );  
  155.         // /* Pad out to 56 mod 64.
  156.         index = (int ) (count[ 0 ] >>>  3 ) &  0x3f ;  
  157.         padLen = (index < 56 ) ? ( 56  - index) : ( 120  - index);  
  158.         keyBeanUpdate(PADDING, padLen);  
  159.         // /* Append length (before padding) */
  160.         keyBeanUpdate(bits, 8 );  
  161.         // /* Store state in digest */
  162.         Encode(digest, state, 16 );  
  163.     }  
  164.     /*  
  165.      * keyBeanMemcpy是一個內部使用的byte陣列的塊拷貝函式,從input的inpos開始把len長度的  
  166.      * 位元組拷貝到output的outpos位置開始  
  167.      */
  168.     private void  keyBeanMemcpy( byte [] output,  byte [] input,  int  outpos,  
  169.             int  inpos,  int  len) {  
  170.         int  i;  
  171.         for  (i =  0 ; i < len; i++)  
  172.             output[outpos + i] = input[inpos + i];  
  173.     }  
  174.     /*  
  175.      * keyBeanTransform是keyBean核心變換程式,由keyBeanUpdate呼叫,block是分塊的原始位元組  
  176.      */
  177.     private void  keyBeanTransform( byte  block[]) {  
  178.         long  a = state[ 0 ], b = state[ 1 ], c = state[ 2 ], d = state[ 3 ];  
  179.         long [] x =  new long [ 16 ];  
  180.         Decode(x, block, 64 );  
  181.         /* Round 1 */
  182.         a = FF(a, b, c, d, x[0 ], S11, 0xd76aa478L);  /* 1 */
  183.         d = FF(d, a, b, c, x[1 ], S12, 0xe8c7b756L);  /* 2 */
  184.         c = FF(c, d, a, b, x[2 ], S13, 0x242070dbL);  /* 3 */
  185.         b = FF(b, c, d, a, x[3 ], S14, 0xc1bdceeeL);  /* 4 */
  186.         a = FF(a, b, c, d, x[4 ], S11, 0xf57c0fafL);  /* 5 */
  187.         d = FF(d, a, b, c, x[5 ], S12, 0x4787c62aL);  /* 6 */
  188.         c = FF(c, d, a, b, x[6 ], S13, 0xa8304613L);  /* 7 */
  189.         b = FF(b, c, d, a, x[7 ], S14, 0xfd469501L);  /* 8 */
  190.         a = FF(a, b, c, d, x[8 ], S11, 0x698098d8L);  /* 9 */
  191.         d = FF(d, a, b, c, x[9 ], S12, 0x8b44f7afL);  /* 10 */
  192.         c = FF(c, d, a, b, x[10 ], S13, 0xffff5bb1L);  /* 11 */
  193.         b = FF(b, c, d, a, x[11 ], S14, 0x895cd7beL);  /* 12 */
  194.         a = FF(a, b, c, d, x[12 ], S11, 0x6b901122L);  /* 13 */
  195.         d = FF(d, a, b, c, x[13 ], S12, 0xfd987193L);  /* 14 */
  196.         c = FF(c, d, a, b, x[14 ], S13, 0xa679438eL);  /* 15 */
  197.         b = FF(b, c, d, a, x[15 ], S14, 0x49b40821L);  /* 16 */
  198.         /* Round 2 */
  199.         a = GG(a, b, c, d, x[1 ], S21, 0xf61e2562L);  /* 17 */
  200.         d = GG(d, a, b, c, x[6 ], S22, 0xc040b340L);  /* 18 */
  201.         c = GG(c, d, a, b, x[11 ], S23, 0x265e5a51L);  /* 19 */
  202.         b = GG(b, c, d, a, x[0 ], S24, 0xe9b6c7aaL);  /* 20 */
  203.         a = GG(a, b, c, d, x[5 ], S21, 0xd62f105dL);  /* 21 */
  204.         d = GG(d, a, b, c, x[10 ], S22, 0x2441453L);  /* 22 */
  205.         c = GG(c, d, a, b, x[15 ], S23, 0xd8a1e681L);  /* 23 */
  206.         b = GG(b, c, d, a, x[4 ], S24, 0xe7d3fbc8L);  /* 24 */
  207.         a = GG(a, b, c, d, x[9 ], S21, 0x21e1cde6L);  /* 25 */
  208.         d = GG(d, a, b, c, x[14 ], S22, 0xc33707d6L);  /* 26 */
  209.         c = GG(c, d, a, b, x[3 ], S23, 0xf4d50d87L);  /* 27 */
  210.         b = GG(b, c, d, a, x[8 ], S24, 0x455a14edL);  /* 28 */
  211.         a = GG(a, b, c, d, x[13 ], S21, 0xa9e3e905L);  /* 29 */
  212.         d = GG(d, a, b, c, x[2 ], S22, 0xfcefa3f8L);  /* 30 */
  213.         c = GG(c, d, a, b, x[7 ], S23, 0x676f02d9L);  /* 31 */
  214.         b = GG(b, c, d, a, x[12 ], S24, 0x8d2a4c8aL);  /* 32 */
  215.         /* Round 3 */
  216.         a = HH(a, b, c, d, x[5 ], S31, 0xfffa3942L);  /* 33 */
  217.         d = HH(d, a, b, c, x[8 ], S32, 0x8771f681L);  /* 34 */
  218.         c = HH(c, d, a, b, x[11 ], S33, 0x6d9d6122L);  /* 35 */
  219.         b = HH(b, c, d, a, x[14 ], S34, 0xfde5380cL);  /* 36 */
  220.         a = HH(a, b, c, d, x[1 ], S31, 0xa4beea44L);  /* 37 */
  221.         d = HH(d, a, b, c, x[4 ], S32, 0x4bdecfa9L);  /* 38 */
  222.         c = HH(c, d, a, b, x[7 ], S33, 0xf6bb4b60L);  /* 39 */
  223.         b = HH(b, c, d, a, x[10 ], S34, 0xbebfbc70L);  /* 40 */
  224.         a = HH(a, b, c, d, x[13 ], S31, 0x289b7ec6L);  /* 41 */
  225.         d = HH(d, a, b, c, x[0 ], S32, 0xeaa127faL);  /* 42 */
  226.         c = HH(c, d, a, b, x[3 ], S33, 0xd4ef3085L);  /* 43 */
  227.         b = HH(b, c, d, a, x[6 ], S34, 0x4881d05L);  /* 44 */
  228.         a = HH(a, b, c, d, x[9 ], S31, 0xd9d4d039L);  /* 45 */
  229.         d = HH(d, a, b, c, x[12 ], S32, 0xe6db99e5L);  /* 46 */
  230.         c = HH(c, d, a, b, x[15 ], S33, 0x1fa27cf8L);  /* 47 */
  231.         b = HH(b, c, d, a, x[2 ], S34, 0xc4ac5665L);  /* 48 */
  232.         /* Round 4 */
  233.         a = II(a, b, c, d, x[0 ], S41, 0xf4292244L);  /* 49 */
  234.         d = II(d, a, b, c, x[7 ], S42, 0x432aff97L);  /* 50 */
  235.         c = II(c, d, a, b, x[14 ], S43, 0xab9423a7L);  /* 51 */
  236.         b = II(b, c, d, a, x[5 ], S44, 0xfc93a039L);  /* 52 */
  237.         a = II(a, b, c, d, x[12 ], S41, 0x655b59c3L);  /* 53 */
  238.         d = II(d, a, b, c, x[3 ], S42, 0x8f0ccc92L);  /* 54 */
  239.         c = II(c, d, a, b, x[10 ], S43, 0xffeff47dL);  /* 55 */
  240.         b = II(b, c, d, a, x[1 ], S44, 0x85845dd1L);  /* 56 */
  241.         a = II(a, b, c, d, x[8 ], S41, 0x6fa87e4fL);  /* 57 */
  242.         d = II(d, a, b, c, x[15 ], S42, 0xfe2ce6e0L);  /* 58 */
  243.         c = II(c, d, a, b, x[6 ], S43, 0xa3014314L);  /* 59 */
  244.         b = II(b, c, d, a, x[13 ], S44, 0x4e0811a1L);  /* 60 */
  245.         a = II(a, b, c, d, x[4 ], S41, 0xf7537e82L);  /* 61 */
  246.         d = II(d, a, b, c, x[11 ], S42, 0xbd3af235L);  /* 62 */
  247.         c = II(c, d, a, b, x[2 ], S43, 0x2ad7d2bbL);  /* 63 */
  248.         b = II(b, c, d, a, x[9 ], S44, 0xeb8