@MD5碼以及相關衍生
在寫易賣網專案的時候,要求我把用戶密碼加密,在網上查閱資料,再次進行筆記記錄,代碼皆非本人敲寫,只是統合自己所需要的記錄翻閱學習,滿懷感激
**
1.JAVA中包java.security是java提供的加密API——MessageDigest
**
- 作用:提供MD5,SHA-1,SHA-256等的加密演算法,可接受任意長度的輸入,并產生固定長度的輸出,輸出一般可稱為摘要或散列
- 我們一般可以使用MessageDigest進行一些常規的加密操作,具體的使用如下:
//實體化一個MessageDigest物件,通過提供的靜態的getInstance方法,方法中引數指的是加密的演算法,大小寫無所謂,
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
//輸入待加密的字串
messageDigest.update("待加密的字串");
//加密之后生成的密文的位元組陣列
byte[] value = messageDigest.digest();
//一般不會直接使用生成的位元組陣列,而是轉化成16進行字串,長度一般可以設定
//下來將提供位元組陣列轉化為16進制字串的方法``
/**
* 字串陣列決議成16進制字串
* md : 待轉化的位元組陣列
* needLen: 需要轉化的16進制字串的長度,一般都是偶數
* 說明:此演算法可以設定生成的16進制字串的長度,是拿原位元組陣列的前needLen/2長度的位元組陣列轉化而來的
* 如果不需要特定長度,直接全部轉,可以設定needLen的長度為md.length*2,獲取去掉needLen,設定buf的長度為j*2,for回圈的
* 終止條件為i<j*2 即可
* */
private static String tranform16Str(byte[] md, int needLen){
char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f'};
try {
int j = md.length;
char buf[] = new char[needLen];
int k = 0;
for (int i = 0; i < needLen/2; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
log.error("加密后的密文轉化為16進制字串程序中出現例外,",e);
}
return null;
}
簡單示例:
/*MySQL*/
//為了避免用戶密碼直接裸露存盤在資料庫中,應對用戶密碼進行加密,有些資料庫就有加密函式,比如oracle,有個示例如下,進行對密碼欄位加密:
CREATE OR REPLACE function md5(input_string VARCHAR2) return varchar2
IS
raw_input RAW(128) := UTL_RAW.CAST_TO_RAW(input_string);
decrypted_raw RAW(2048);
error_in_input_buffer_length EXCEPTION;
BEGIN
--dbms_output.put_line(sysdate || '> 加密前的資料:' || input_string);
sys.dbms_obfuscation_toolkit.MD5(input => raw_input,
checksum => decrypted_raw);
--dbms_output.put_line(sysdate || '> 加密后的資料:' || rawtohex(decrypted_raw));
return lower(rawtohex(decrypted_raw));
END;
而這個方法也存在缺陷性,只適合某一特定的資料庫,一旦資料庫換了(不是所有資料庫都有加密解密存盤函式),這個加密方法就不能用了,
這里要討論的是用java方法對資料進行MD5碼的加密,相對也是比較簡單,因為java有現成的類java.security,MessageDigest幫我們生成MD5碼,
思路是把密碼字符竄進行MD5碼的轉換存盤在資料庫中,用戶登入的時候把登入輸入的密碼字符竄進行MD5碼的轉換,再與資料庫中存盤的密碼MD5碼進行比較;如用戶修改密碼就拿新的密碼的MD5碼進行替換,
生成MD5碼的函式如下:
/**
* 把字符竄轉化成MD5碼,主要針對密碼
* @param str 待轉碼的字符竄
* @return MD5碼字符竄
* @author Tony Lin Added on 2008-9-27
*/
public String getMD5String(String str){
try{
byte psw[] = str.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(psw);
return this.toHex(md.digest());
} catch (IllegalStateException e) {
return null;
} catch (NoSuchAlgorithmException e) {
return null;
}
}
/**
* 把byte型陣列類容拼錯成字符竄
* @param buffer The byte array to be converted
* @return String
* @author Tony Lin Added on 2008-9-27
*/
public String toHex(byte buffer[]) {
StringBuffer sb = new StringBuffer();
String s = null;
for (int i = 0; i < buffer.length; i++) {
s = Integer.toHexString((int) buffer[i] & 0xff);
if (s.length() < 2) {
sb.append('0');
}
sb.append(s);
}
return sb.toString();
}
上述原文
上述原文
衍生
- 續接(1)第一點
給一個檔案進行加密怎么辦,檔案那么長又該如何加密?
//上面有這么一句
messageDigest.update("待加密的字串");
/*從上面的注釋上看,這個是添加待加密的明文的,
那么如果需要給一個檔案進行加密怎么辦,檔案那么長又該如何加密?*/
//1.首先先把檔案讀取到一個位元組陣列里面
File file = new File(filePath);
InputStream in = new FileInputStream(file);
byte[] allData = readInputStream(in);//獲取到檔案的內容
/*接下來需要給這些內容進行加密,就需要使用到上面的MessageDigest加密的Api了,
有兩種方式:*/
//方式1:一段一段往里面塞
int len = allData.length;
int i = 0;
while(true){
try{
int arrLen = (len - i * 4096) > 4096 ? 4096 : (len - i * 4096);
byte[] content = new byte[arrLen];
System.arraycopy(getData, i * 4096, content, 0, arrLen);
messageDigest.update(content);
i++;
}catch (Exception e){
log.info("位元組陣列拷貝出現例外,表示完成 i ={}", i);
break;
}
}
byte[] transform = messageDigest.digest();
//說明,MessageDigest呼叫digest()方法之后 輸入的摘要將被重置,意思就是之后需要再加密的話 可以直接使用之前已有的物件
String miwen = tranform16Str(transform, transform.length);
//方式2:一次性全部往里面塞
messageDigest.update(allData);
byte[] second = messageDigest.digest();
//之后再進行16進制的轉換操作,
#####################################################################
/*上述兩種方法的結果拿到的是一樣的,
那么就說明多次的update操作 只是單純的摘要內容的追加操作,*/
/**
* 獲取輸入流中的內容到位元組陣列里面
**/
public static byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
return bos.toByteArray();
}
buffer[i] & 0xff
代碼中加密的時候 用到bytes[i] & 0xFF
MessageDigest md5 = MessageDigest.getInstance(“MD5”);
bytes = md5.digest(basestring.toString().getBytes("UTF-8"));
String hex = Integer.toHexString(bytes[i] & 0xFF);
首先你得清楚幾個個概念
1.byte的取值范圍
byte java 中一個位元組 8位 即 -2^7—2^7-1 范圍是 -128——127 (*對這個有疑問嗎?在2 中解答)
2.計算機中負數的存盤的值是它的補碼
補碼計算方法 負數取值是它的絕對值,的二進制數,取反,加1,那么對于 -128——-1的存盤 就知道了吧
舉個例子 -12 絕對值------> 12 就是 0000 1100 取反------> 1111 0011 加 1 1111 0100 (曉得不)
那么-128——127 就可以理解了
-128 是 絕對值 128 二進制--------> 1000 0000 取反 0111 1111(127) 加1 ,1000 0000 (128)
-1 的絕對值 是 1 0000 0001 取反 1111 1110 加1 1111 1111 (255)
計算機 中 -128——-1 對應存盤的是 128-255
再看 2^7-1 也就是127 剛好 是0111 1111 為啥減1 因為 128 已經被占了 所以
-128——127 在資料庫中真實存的是 128——255 外加 0——127 也就是 byte真正在計算機中存盤范圍是
0——255 只不過我們說的是它的取值范圍 是 -128——127
3.byte 轉 int 16進制 (int -2^31——2^31-1)
bytes[i] & 0xFF
byte和int運算,計算機會自動將 byte轉化為32位,然后再運算,也就是8位轉32位,那么計算機會自動將高位補1
所以-12 在計算機種就是 1111 0011--------> 1111 1111 1111 1111 1111 1111 1111 0100
然后 & 0xFF
0x 代表16進制 每一位取值范圍是 0 —— 15
但是 10——15之間是兩位數字了,會和10進制分部開 所以 10——15就對應了 A——F
那么0xFF 就是 0000 0000 0000 0000 0000 0000 1111 1111
&運算 得到 0000 0000 0000 0000 0000 0000 1111 0100
有沒有發現什么?低八位 1111 0011 沒有改變 ,那么為什么 & 0xFF而不是別的呢?
原因就是 byte 轉int 前24位都被計算機強制 變成了1, 1111 1111 1111 1111 1111 1111 1111 0100 轉化成int已經不是
-12在計算機中存盤的值了,& 0xFF 0000 0000 0000 0000 0000 0000 1111 1111 恰好前24位都是 0,進行&運算,只會保留
低八位,也就是byte中原來在計算機中存盤的值 1111 0100,
上述原文
integer.tohexstring
Integer.toHexString這個方法是把位元組(轉換成了int)以16進制的方式顯示,
別人的相關筆記
位運算子
位運算子
- C語言中六種位運算子:
- “&” 按位與
- “|” 按位或
- “^” 按位異或
- “~”取反
- “<<”左移
- “>>”右移
因為專案內容,我主要只記錄了&:按位與
按位與運算子"&"是雙目運算子, 其功能是參與運算的兩數各對應的二進位相與,只有對應的兩個二進位均為1時,結果位才為1 ,否則為0,參與運算的數以補碼方式出現,
例如:9&5可寫算式如下: 00001001 (9的二進制補碼)&00000101 (5的二進制補碼) 00000001 (1的二進制補碼)可見9&5=1, 按位與運算通常用來對某些位清0或保留某些位,例如把a 的高八位清 0 , 保留低八位, 可作 a&255 運算 ( 255 的二進制數為11111111),
十進制轉化為二進制
進制轉換
十進制整數轉換為二進制整數十進制整數轉換為二進制整數采用"除2取余,逆序排列"法,具體做法是:用2整除十進制整數,可以得到一個商和余數;再用2去除商,又會得到一個商和余數,如此進行,直到商為小于1時為止,然后把先得到的余數作為二進制數的低位有效位,后得到的余數作為二進制數的高位有效位,依次排列起來,
十進制整數轉二進制
如:
255=(11111111)B
255/2=127=余1
127/2=63余1
63/2=31=余1
31/2=15=余1
15/2=7余1
7/2=3=余1
3/2=1=余1
1/2=0===余1
我發現,上面“9”的二進制00001001 (9的二進制補碼)和我根據演算法算出來的二進制1001不一樣,那么千米那那么多“0”是什么
補碼(二進制補碼)
計算機中的有符號數有三種表示方法,即原碼、反碼和補碼,三種表示方法均有符號位和數值位兩部分,符號位都是用0表示“正”,用1表示“負”,而數值位,三種表示方法各不相同 ,在計算機系統中,數值一律用補碼來表示和存盤,原因在于,使用補碼,可以將符號位和數值域統一處理;同時,加法和減法也可以統一處理 ,
所以前面的“1001”就是數值位,因而,前面的一串“0”就是符號位
在介紹補碼概念之前,先介紹一下“模”的概念:“模”是指一個計量系統的計數范圍,如過去計量糧食用的斗、時鐘等,計算機也可以看成一個計量機器,因為計算機的字長是定長的,即存盤和處理的位數是有限的,因此它也有一個計量范圍,即都存在一個“模”,如:時鐘的計量范圍是0~11,模=12,表示n位的計算機計量范圍是 ,模= .“模”實質上是計量器產生“溢位”的量,它的值在計量器上表示不出來,計量器上只能表示出模的余數,任何有模的計量器,均可化減法為加法運算 ,
就是取反后加1,
假設當前時針指向8點,而準確時間是6點,調整時間可有以下兩種撥法:一種是倒撥2小時,即8-2=6;另一種是順撥10小時,8+10=12+6=6,即8-2=8+10=8+12-2(mod 12).在12為模的系統里,加10和減2效果是一樣的,因此凡是減2運算,都可以用加10來代替,若用一般公式可表示為:a-b=a-b+mod=a+mod-b,對“模”而言,2和10互為補數,實際上,以12為模的系統中,11和1,8和4,9和3,7和5,6和6都有這個特性,共同的特點是兩者相加等于模,對于計算機,其概念和方法完全一樣,n位計算機,設n=8,所能表示的最大數是11111111,若再加1成100000000(9位),但因只有8位,最高位1自然丟失,又回到了 00000000,所以8位二進制系統的模為 ,在這樣的系統中減法問題也可以化成加法問題,只需把減數用相應的補數表示就可以了,把補數用到計算機對數的處理上,就是補碼 ,
例:+9的補碼是00001001,(備注:這個+9的補碼是用8位2進制來表示的,補碼表示方式很多,還有16位二進制補碼表示形式,以及32位二進制補碼表示形式,64位進制補碼表示形式等,每一種補碼表示形式都只能表示有限的數字,)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/128035.html
標籤:其他
