對(duì)稱(chēng)與非對(duì)稱(chēng)加密算法的區(qū)別。
對(duì)稱(chēng)加密算法
加密(encryption)與解密(decryption)用的是同樣的密鑰(secret key),這種加密方式加密速度非常快生年,適合經(jīng)常發(fā)送數(shù)據(jù)的場(chǎng)合。缺點(diǎn)是密鑰的傳輸比較麻煩鸟缕。
非對(duì)稱(chēng)加密算法
非對(duì)稱(chēng)加密為數(shù)據(jù)的加密與解密提供了一個(gè)非常安全的方法晶框,它使用了一對(duì)密鑰,公鑰(public key)和私鑰(private key)懂从。私鑰只能由一方安全保管授段,不能外泄,而公鑰則可以發(fā)給任何請(qǐng)求它的人番甩。非對(duì)稱(chēng)加密使用這對(duì)密鑰中的一個(gè)進(jìn)行加密侵贵,而解密則需要另一個(gè)密鑰。比如缘薛,你向銀行請(qǐng)求公鑰窍育,銀行將公鑰發(fā)給你卡睦,你使用公鑰對(duì)消息加密,那么只有私鑰的持有人--銀行才能對(duì)你的消息解密漱抓。與對(duì)稱(chēng)加密不同的是表锻,銀行不需要將私鑰通過(guò)網(wǎng)絡(luò)發(fā)送出去,因此安全性大大提高乞娄。
常見(jiàn)的非對(duì)稱(chēng)加密算法為RSA瞬逊、ECC和EIGamal。
一仪或、Hex編碼與解碼
Hex 全稱(chēng)是Intel HEX确镊。Hex文件是由一行行符合Intel HEX文件格式的文本所構(gòu)成的ASCII文本文件。在Intel HEX文件中范删,每一行包含一個(gè)HEX記錄蕾域。這些記錄由對(duì)應(yīng)機(jī)器語(yǔ)言碼和/或常量數(shù)據(jù)的十六進(jìn)制編碼數(shù)字組成。
如下:
99到旦、105旨巷、224,7
編碼之后的數(shù)據(jù)為:“6369e007”厢绝,是一個(gè)字符串契沫。
pom依賴? ? ?
<dependency>
? <groupId>commons-codec</groupId>
? <artifactId>commons-codec</artifactId>
? <version>1.10</version>
</dependency>
編碼與解碼測(cè)試如下:
/**
? * Hex編碼
*/
public static String encodeHex(byte[] input) {
? ? return Hex.encodeHexString(input);
}
/**
? * Hex解碼
? */
public static byte[] decodeHex(String input) {
? ? try {
? ? ? ? return Hex.decodeHex(input.toCharArray());
? ? } catch (DataBindingException e) {
? ? ? ? throw new RuntimeException();
? ? } catch (DecoderException e) {
? ? ? ? throw new RuntimeException();
? ? }
}
二带猴、Base64編碼與解碼
/**
? * Base64編碼
? */
public static String encodeBase64(byte[] input) {
? ? return Base64.encodeBase64String(input);
}
/**
? * Base64編碼, URL安全(將Base64中的URL非法字符'+'和'/'轉(zhuǎn)為'-'和'_', 見(jiàn)RFC3548)
? */
public static String encodeUrlSafeBase64(byte[] input) {
? ? return Base64.encodeBase64URLSafeString(input);
}
/**
? * Base64解碼.
? */
public static byte[] decodeBase64(String input) {
? ? return Base64.decodeBase64(input);
}
測(cè)試如下:
public class Base64Test {
????public static void main(String[] args){
????????String str = "Hello World";
????????try{
????????????System.out.println("RESULT: " + encodeStr(str));
????????} catch(UnsupportedEncodingException e){
????????????e.printStackTrace();
????????}
????}
}
輸出結(jié)果為:
1.? RESULT: SGVsbG8gV29ybGQ=?
上面輸出的字符串是“Hello world”字符串的8位二進(jìn)制值被連接在一起昔汉,然后以6位分組。隨后每個(gè)組都被轉(zhuǎn)換成一個(gè)單獨(dú)的數(shù)字并映射到Base64的索引拴清。
binary? dec Base64
010010? 18? S
000110? 6? G
010101? 21? V
101100? 44? s
011011? 27? b
000110? 6? G
111100? 60? 8
100000? 32? g
010101? 29? d
110110? 54? 2
111101? 61? 9
110010? 50? y
011011? 27? b
000110? 6? G
010000? 16? Q
注意:字符串最后加上了“=”靶病,其意思表示字符串編碼的結(jié)束。
三口予、URL編碼與解碼
當(dāng)URL地址里包含非西歐字符的字符串時(shí)娄周,瀏覽器都會(huì)將這些非西歐字符串轉(zhuǎn)換成application/x-www-form-urlencoded MIME 字符串。在開(kāi)發(fā)過(guò)程中沪停,我們可能涉及將普通字符串和這種特殊字符串的相關(guān)轉(zhuǎn)換煤辨,這就需要使用 URLDecoder 和 URLEncoder類(lèi)進(jìn)行實(shí)現(xiàn),其中:
·? ? ? URLDecoder類(lèi)包含一個(gè)decode(String s,String enc)靜態(tài)方法木张,它可以將application/x-www-form-urlencoded MIME字符串轉(zhuǎn)成普通字符串众辨;
·? ? ? URLEncoder類(lèi)包含一個(gè)encode(String s,String enc)靜態(tài)方法,它可以將普通字符串轉(zhuǎn)換成application/x-www-form-urlencoded MIME字符串舷礼。
普通字符串轉(zhuǎn)與 application/x-www-form-urlencoded MIME 字符串之間的轉(zhuǎn)化:
public class URLDecoderTest {
? public static void main(String[] args) throws Exception {
? ? // 將application/x-www-form-urlencoded字符串轉(zhuǎn)換成普通字符串
? ? // 其中的字符串直接從上圖所示窗口復(fù)制過(guò)來(lái),chrome 默認(rèn)用 UTF-8 字符集進(jìn)行編碼鹃彻,所以也應(yīng)該用對(duì)應(yīng)的字符集解碼
? ? System.out.println("采用UTF-8字符集進(jìn)行解碼:");
? ? String keyWord = URLDecoder.decode("%E5%A4%A9%E6%B4%A5%E5%A4%A7%E5%AD%A6+Rico", "UTF-8");
? ? System.out.println(keyWord);
? ? System.out.println("\n 采用GBK字符集進(jìn)行解碼:");
? ? System.out.println(URLDecoder.decode("%E5%A4%A9%E6%B4%A5%E5%A4%A7%E5%AD%A6+Rico", "GBK"));
? ? // 將普通字符串轉(zhuǎn)換成application/x-www-form-urlencoded字符串
? ? System.out.println("\n 采用utf-8字符集:");
? ? String urlStr = URLEncoder.encode("天津大學(xué)", "utf-8");
? ? System.out.println(urlStr);
? ? System.out.println("\n 采用GBK字符集:");
? ? String urlStr2 = URLEncoder.encode("天津大學(xué)", "GBK");
? ? System.out.println(urlStr2);
? }
}
/*
Output:
采用UTF-8字符集進(jìn)行解碼:
天津大學(xué) Rico
采用GBK字符集進(jìn)行解碼:
澶╂觸澶у Rico
采用utf-8字符集:
%E5%A4%A9%E6%B4%A5%E5%A4%A7%E5%AD%A6
采用GBK字符集:
? ? %CC%EC%BD%F2%B4%F3%D1%A7
*/
四、AES加密或解密
高級(jí)加密標(biāo)準(zhǔn)(AES,Advanced Encryption Standard)為最常見(jiàn)的對(duì)稱(chēng)加密算法妻献。對(duì)稱(chēng)加密算法也就是加密和解密用相同的密鑰
密鑰K:用來(lái)加密明文的密碼蛛株,在對(duì)稱(chēng)加密算法中团赁,加密與解密的密鑰是相同的。密鑰為接收方與發(fā)送方協(xié)商產(chǎn)生谨履,但不可以直接在網(wǎng)絡(luò)上傳輸欢摄,否則會(huì)導(dǎo)致密鑰泄漏,通常是通過(guò)非對(duì)稱(chēng)加密算法加密密鑰笋粟,然后再通過(guò)網(wǎng)絡(luò)傳輸給對(duì)方剧浸,或者直接面對(duì)面商量密鑰。密鑰是絕對(duì)不可以泄漏的矗钟,否則會(huì)被攻擊者還原密文唆香,竊取機(jī)密數(shù)據(jù)。
1吨艇、密鑰
就是把明文轉(zhuǎn)換為密文躬它,密文轉(zhuǎn)換為明文的一把鑰匙。接下來(lái)我們會(huì)用ASE加密技術(shù)生成一把密鑰东涡。
/**
* 生成AES密鑰,可選長(zhǎng)度為128,192,256位.
*/
public static byte[] generateAesKey(int keysize) {
? try {
? ? KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
? ? keyGenerator.init(keysize);
? ? SecretKey secretKey = keyGenerator.generateKey();
? ? return secretKey.getEncoded();
? } catch (GeneralSecurityException e) {
? ? throw unchecked(e);
? }
}
2冯吓、加密,將明文和密鑰一起疮跑,作為參數(shù)傳入组贺。
/**
* ASE 加密
* @param str 明文
* @param key 秘鑰
* @return
*/
public static String enStr(String str, byte[] key) {
Cipher cipher = null;
SecretKey generateKey = null;
? try {
? ? generateKey = new SecretKeySpec(key, "AES");
? ? cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
? ? cipher.init(Cipher.ENCRYPT_MODE, generateKey);
? ? byte[] resultBytes = cipher.doFinal(str.getBytes());
? ? return Hex.encodeHexString(resultBytes);
? } catch (Exception e) {
? ? logger.error("AES加密出錯(cuò)", e);
? }
? return null;
}
3、解密祖娘,解密就是加密的互逆過(guò)程失尖,所以代碼很類(lèi)似。
/**
* 解密
* @param key 秘鑰
* @param str 密文
* @return
*/
public static String deStr(String str, byte[] key) {
? Cipher cipher = null;
? SecretKey generateKey = null;
? try {
? generateKey = new SecretKeySpec(key, "AES");
? cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
? cipher.init(Cipher.DECRYPT_MODE, generateKey);
? byte[] result = Hex.decodeHex(str.toCharArray());
? return new String(cipher.doFinal(result));
? } catch(Exception e) {
? ? ? logger.error("ASE解密出錯(cuò)", e);
? }
? return null;
}
通用公共抽冉ニ铡:
/**
* 使用AES加密或解密無(wú)編碼的原始字節(jié)數(shù)組, 返回?zé)o編碼的字節(jié)數(shù)組結(jié)果.
*
* @param input 原始字節(jié)數(shù)組
* @param key 符合AES要求的密鑰
* @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
*/
private static byte[] aes(byte[] input, byte[] key, int mode) {
? ? try {
? ? ? ? SecretKey secretKey = new SecretKeySpec(key, AES);
? ? ? ? Cipher cipher = Cipher.getInstance(AES);
? ? ? ? cipher.init(mode, secretKey);
? ? ? ? return cipher.doFinal(input);
? ? } catch (GeneralSecurityException e) {
? ? ? ? throw unchecked(e);
? ? }
}
五掀潮、DES加密與解密(3DES)
數(shù)據(jù)加密標(biāo)準(zhǔn)(DES Data Encryption Standard)
DES算法的入口參數(shù)有三個(gè):Key、Data琼富、Mode仪吧。
其中Key為8個(gè)字節(jié)共64位,是DES算法的工作密鑰鞠眉;
Data也為8個(gè)字節(jié)64位薯鼠,是要被加密或被解密的數(shù)據(jù);
Mode為DES的工作方式械蹋,有兩種:加密或解密出皇。
DES算法是這樣工作的:
如Mode為加密,則用Key 去把數(shù)據(jù)Data進(jìn)行加密朝蜘,生成Data的密碼形式(64位)作為DES的輸出結(jié)果恶迈;
如Mode為解密,則用Key去把密碼形式的數(shù)據(jù)Data解密,還原為Data的明碼形式(64位)作為DES的輸出結(jié)果暇仲。
3DES加密:
3DES是DES加密算法的一種模式步做,它使用3條64位的密鑰對(duì)數(shù)據(jù)進(jìn)行三次加密。數(shù)據(jù)加密標(biāo)準(zhǔn)(DES)是美國(guó)的一種由來(lái)已久的加密標(biāo)準(zhǔn)奈附,它使用對(duì)稱(chēng)密鑰加密法全度。
3DES(即Triple DES)是DES向AES過(guò)渡的加密算法(1999年,NIST將3-DES指定為過(guò)渡的加密標(biāo)準(zhǔn))斥滤,是DES的一個(gè)更安全的變形将鸵。它以DES為基本模塊,通過(guò)組合分組方法設(shè)計(jì)出分組加密算法佑颇。
設(shè)Ek()和Dk()代表DES算法的加密和解密過(guò)程顶掉,K代表DES算法使用的密鑰,P代表明文挑胸,C代表密表痒筒,這樣,
3DES加密過(guò)程為:C=Ek3(Dk2(Ek1(P)))
3DES解密過(guò)程為:P=Dk1((EK2(Dk3(C)))
K1茬贵、K2簿透、K3決定了算法的安全性,若三個(gè)密鑰互不相同解藻,本質(zhì)上就相當(dāng)于用一個(gè)長(zhǎng)為168位的密鑰進(jìn)行加密老充。多年來(lái),它在對(duì)付強(qiáng)力攻擊時(shí)是比較安全的螟左。若數(shù)據(jù)對(duì)安全性要求不那么高啡浊,K1可以等于K3。在這種情況下路狮,密鑰的有效長(zhǎng)度為112位虫啥。
五蔚约、RSA加密與解密
非對(duì)稱(chēng)加密奄妨,公鑰加密,私鑰解密苹祟,反之亦然砸抛。由于需要大數(shù)的乘冪求模等算法,運(yùn)行速度慢树枫,不易于硬件實(shí)現(xiàn)直焙。
通常私鑰長(zhǎng)度有512bit,1024bit砂轻,2048bit奔誓,4096bit,長(zhǎng)度越長(zhǎng),越安全厨喂,但是生成密鑰越慢和措,加解密也越耗時(shí)。
既然是加密蜕煌,那肯定是不希望別人知道我的消息派阱,所以只有我才能解密,所以可得出公鑰負(fù)責(zé)加密斜纪,私鑰負(fù)責(zé)解密贫母;
同理,既然是簽名盒刚,那肯定是不希望有人冒充我發(fā)消息腺劣,只有我才能發(fā)布這個(gè)簽名,所以可得出私鑰負(fù)責(zé)簽名因块,公鑰負(fù)責(zé)驗(yàn)證誓酒。
六、MD5加密(不可逆)
MD5即Message-Digest Algorithm 5(信息-摘要算法5), 非對(duì)稱(chēng)的加密算法
public static String MD5(String key) {
char hexDigits[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
? try {
? ? byte[] btInput = key.getBytes();
? ? // 獲得MD5摘要算法的 MessageDigest 對(duì)象
? ? MessageDigest mdInst = MessageDigest.getInstance("MD5");
? ? // 使用指定的字節(jié)更新摘要
? ? mdInst.update(btInput);
? ? // 獲得密文
? ? byte[] md = mdInst.digest();
? ? // 把密文轉(zhuǎn)換成十六進(jìn)制的字符串形式
? ? int j = md.length;
? ? charstr[] = newchar[j * 2];
? ? int k = 0;
? ? for (int i = 0; i < j; i++) {
? ? ? byte byte0 = md[i];
? ? ? str[k++] = hexDigits[byte0 >>> 4 & 0xf];
? ? ? str[k++] = hexDigits[byte0 & 0xf];
? ? }
? ? return new String(str);
? } catch (Exception e) {
? ? returnnull;
? }
}
總結(jié)
哈希函數(shù),比如MD5,SHA贮聂,這些都不是加密算法靠柑。要注意他們的區(qū)別和用途,很多網(wǎng)友都把md5說(shuō)成是加密算法吓懈,這是嚴(yán)重不正確的啊歼冰。
哈希函數(shù):MD5,SHA 是沒(méi)有密鑰的耻警,相當(dāng)與指紋的概念隔嫡,因此也是不可逆的;
md5是128位的甘穿,SHA有不同的算法腮恩,有128,256等位温兼。秸滴。。如SHA-256,SHA-384
然后就是 Base64,這更加不屬于加密算法的范圍了募判,它只是將byte[]數(shù)組進(jìn)行了轉(zhuǎn)換荡含,為什么要轉(zhuǎn)換呢?就是因?yàn)楹芏嗉用芎蟮拿芪暮笳咭恍┨厥獾?/b>byte[]數(shù)組需要顯示出來(lái)届垫,或者需要進(jìn)行傳遞(電子郵件)释液,但是直接轉(zhuǎn)換就會(huì)導(dǎo)致很多不可顯示的字符,會(huì)丟失一些信息装处,因此就轉(zhuǎn)換位Base64編碼误债,這些都是可顯示的字符。所以轉(zhuǎn)換后,長(zhǎng)度會(huì)增加寝蹈。它是可逆的糟袁。
再就是 3DES,DES 這才是加密算法,因此也是可逆的躺盛,加解密需要密鑰,也就是你說(shuō)的key
最后是 RSA ,這是公鑰密碼项戴,也就是加密和解密密鑰不同,也是可逆的槽惫。