個人博客:haichenyi.com。感謝關注
補充知識點:
字節(jié):也就是 byte 是一種統(tǒng)計單位,表示數(shù)量的多少
字符:是指計算機中使用的文字和符號,比如:1、2、3、A、S、D、$、%等等符號
字節(jié)與字符的對應關系:它們完全不是一個概念,所以,沒有什么有沒有區(qū)別這個說法。不同的編碼,兩者的對應關系是不相同的,我這里就說常用的兩種編碼:
- ASCII碼中,一個英文字母(不區(qū)分大小寫)占一個字節(jié),一個中文漢字占兩個字節(jié)
- UTF-8中,一個英文字母占一個字節(jié),一個中文漢字占三個字節(jié)
??我們加密最終常常操作的是bit,而我們加密首先得到的是byte數(shù)組的,byte的取值范圍-128~127,中間包括0,剛好256個。也就是2^8=256。并且,1 byte = 8 bit(1kb = 1024 byte = 8^1024 bit等等)
??我們獲得了byte之后,要把byte數(shù)組轉(zhuǎn)成String字符串,String其實就是char數(shù)組,我們java有一個new String(char[] chars),應該都用過。我們轉(zhuǎn)成字符串的前提是轉(zhuǎn)成char數(shù)組,由于,1 char = 2 byte,所以,我們byte轉(zhuǎn)成char長度擴大了1倍。
四種分類
- MD5加密
- Base64加密
- 對稱加密
- 非對稱加密
MD5加密——不可逆
概念
??MD5加密是我們常見的加密算法,是不可逆的,也就是說加密完成之后,無法解密轉(zhuǎn)成原來的內(nèi)容。MD5加密算法其實是一種散列函數(shù),使用的是hash算法。MD5的原文是無線多個,但是MD5的值是有限的。所以一個MD5的值可能對應多個原文。SHA算法跟MD5是差不多的,只是MD5是128位,SHA是160位,多32位
??為什么MD5的值是有限多個呢?主流的MD5使用的是將任意長度的字節(jié)串映射為一個128bit的大整數(shù)。也就是一共有2^128種可能,所以說這個數(shù)字是有限的,而,我們的原文則是無限多個。發(fā)現(xiàn)兩段原文對應同一個MD5的值概率非常小,也就忽略不記了。
使用
String name1 = "haichenyi";
String name2 = "海晨憶";
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] digest1 = md5.digest(name1.getBytes("UTF-8"));
Log.v("WZ","length1:"+digest1.length);
byte[] digest2 = md5.digest(name2.getBytes("UTF-8"));
Log.v("WZ","length1:"+digest2.length);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
??我們通過MD5,得到的是一個byte數(shù)組(這個byte數(shù)組的長度跟我們的1byte=8bit沒有直接關系),我們需要做的就是對這個byte數(shù)組進行操作,我們習慣上就是把這個btye轉(zhuǎn)成16進制數(shù)存進數(shù)據(jù)庫,當然,你也可以轉(zhuǎn)成其他的類型存到數(shù)據(jù)庫。這里給出幾個byte數(shù)組轉(zhuǎn)16進制字符串的方法,親測可用:
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 方法一:
* byte[] to hex string
*
* @param bytes
* @return
*/
public String bytesToHexFun1(byte[] bytes) {
// 一個byte為8位,可用兩個十六進制位標識
char[] buf = new char[bytes.length * 2];
int a = 0;
int index = 0;
for (byte b : bytes) { // 使用除與取余進行轉(zhuǎn)換
if (b < 0) {
a = 256 + b;
} else {
a = b;
}
buf[index++] = HEX_CHAR[a / 16];
buf[index++] = HEX_CHAR[a % 16];
}
return new String(buf);
}
/**
* 方法二:
* byte[] to hex string
*
* @param bytes
* @return
*/
public String bytesToHexFun2(byte[] bytes) {
char[] buf = new char[bytes.length * 2];
int index = 0;
for(byte b : bytes) { // 利用位運算進行轉(zhuǎn)換,可以看作方法一的變種
buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
buf[index++] = HEX_CHAR[b & 0xf];
}
return new String(buf);
}
/**
* 方法三:
* byte[] to hex string
*
* @param bytes
* @return
*/
public String bytesToHexFun3(byte[] bytes) {
StringBuilder buf = new StringBuilder(bytes.length * 2);
for(byte b : bytes) { // 使用String的format方法進行轉(zhuǎn)換
buf.append(String.format("%02x", new Integer(b & 0xff)));
}
return buf.toString();
}
/**
* 將16進制字符串轉(zhuǎn)換為byte[]
*
* @param str
* @return
*/
public byte[] toBytes(String str) {
if(str == null || str.trim().equals("")) {
return new byte[0];
}
byte[] bytes = new byte[str.length() / 2];
for(int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
Base64加密——可逆
流程
- 要知道的是有一個64個數(shù)的表,也稱Base64編碼表。可以字節(jié)定義,不過都是用的一樣的。
- Base64是按照字符長度,以3個字符為一組
- 接著增對每組的每個字符,取ASCII編碼
- 然后將獲得的編碼轉(zhuǎn)換成8bit的二進制,就會得到3*8=24bit的字節(jié)
- 然后將這24bit的字節(jié)以6個bit為一組,分成4組
- 接著在每組前面填兩個高位0,湊成每組8bit
- 最后將這每組8bit的二進制轉(zhuǎn)成十進制,對應下面的Base64編碼表
Base64 編碼表
| value | char | value | char | value | char | value | char |
|---|---|---|---|---|---|---|---|
| 0 | A | 16 | Q | 32 | g | 48 | w |
| 1 | B | 17 | R | 33 | h | 49 | x |
| 2 | C | 18 | S | 34 | i | 50 | y |
| 3 | D | 19 | T | 35 | j | 51 | z |
| 4 | E | 20 | U | 36 | k | 52 | 0 |
| 5 | F | 21 | V | 37 | l | 53 | 1 |
| 6 | G | 22 | U | 38 | m | 54 | 2 |
| 7 | H | 23 | X | 39 | n | 55 | 3 |
| 8 | I | 24 | Y | 40 | o | 56 | 4 |
| 9 | J | 25 | Z | 41 | p | 57 | 5 |
| 10 | K | 26 | a | 42 | q | 58 | 6 |
| 11 | L | 27 | b | 43 | r | 59 | 7 |
| 12 | M | 28 | c | 44 | s | 60 | 8 |
| 13 | N | 29 | d | 45 | t | 61 | 9 |
| 14 | O | 30 | e | 46 | u | 62 | + |
| 15 | P | 31 | f | 47 | v | 63 | / |
使用
String str = "hai";
byte[] encode = Base64.encode(str.getBytes(), Base64.NO_WRAP);
try {
String a = new String(encode,"UTF-8");
String a1 = new String(encode,"US-ASCII");
Log.v("wz",a);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String after = Base64.encodeToString(str.getBytes(), Base64.NO_WRAP);
Log.v("wz","after-->"+after);
解析
??這里,我要使用Base64加密"hai"這個字符串,根據(jù)上面的流程:
| 待加密字符串 | h | a | i |
|---|---|---|---|
| ASCII編碼 | 104 | 97 | 105 |
| 二進制 | 01101000 | 01100001 | 01101001 |
下面,上面的表不好表示,我再換一個表,下一步,該6位分一組了
現(xiàn)在的字符串:01101000 01100001 01101001
| 六位分一組 | 011010 | 000110 | 000101 | 101001 |
|---|---|---|---|---|
| 每組前面補0 | 00011010 | 00000110 | 00000101 | 00101001 |
| 轉(zhuǎn)成10進制 | 26 | 6 | 5 | 41 |
| Base64編碼 | a | G | F | p |
結(jié)果圖:
PS:
- Base64.encodeToString()方法直接轉(zhuǎn)成加密后的字符串
- Base64.encode()方法返回的byte數(shù)組是16進制的,不用手動在去轉(zhuǎn)一遍16進制