最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java編碼算法與哈希算法深入分析使用方法

 更新時(shí)間:2022年11月05日 13:41:11   作者:OlaiolaiO  
首先,我們一起來(lái)學(xué)習(xí)一下編碼算法,舉例說(shuō)明,ASCII碼就是我們常見(jiàn)的一種編碼,字母a的編碼是十六進(jìn)制的0x61,字母b是0x62,以此類(lèi)推。哈希算法,可被稱為摘要算法。因此,哈希算法的加密是單向的,不可用密文解密得到明文

一、編碼算法

1.什么是編碼

ASCII 碼就是一種編碼,字母 A 的編碼是十六進(jìn)制的 0x41 ,字母 B 是 0x42 ,以此類(lèi)推。

因?yàn)?ASCII 編碼最多只能有 127 個(gè)字符,要想對(duì)更多的文字進(jìn)行編碼,就需要用占用 2個(gè)字節(jié)的 Unicode 。而中文的"中"字使用 Unicode 編碼就是 0x4e2d ,使用 UTF8 則需要 3 個(gè)字節(jié)編碼;因此,最簡(jiǎn)單的編碼是直接給每個(gè)字符指定一個(gè)若干字節(jié)表示的整數(shù),復(fù)雜一點(diǎn)的 編碼就需要根據(jù)一個(gè)已有的編碼推算出來(lái)。比如 UTF-8 編碼,它是一種不定長(zhǎng)編碼, 但可以從給定字符的 Unicode 編碼推算出來(lái)。

2.URL編碼

URL 編碼是瀏覽器發(fā)送數(shù)據(jù)給服務(wù)器時(shí)使用的編碼,它通常附加在 URL 的參數(shù)部 分,例如: https://www.baidu.com/s?wd=%E4%B8%AD%E6%96%87

之所以需要 URL 編碼,是因?yàn)槌鲇诩嫒菪钥紤],很多服務(wù)器只識(shí)別 ASCII 字符。 但如果 URL 中包含中文、日文這些非 ASCII 字符怎么辦?不要緊, URL 編碼有一套 規(guī)則:

  • 如果字符是 A ~ Z , a ~ z , 0 ~ 9 以及 - 、 _ 、 . 、 * ,則保持不變;
  • 如果是其他字符,先轉(zhuǎn)換為 UTF-8 編碼,然后對(duì)每個(gè)字節(jié)以 %XX 表示。

例如:字符"中"的 UTF-8 編碼是 0xe4b8ad ,因此,它的 URL 編碼 是 %E4%B8%AD 。 URL 編碼總是大寫(xiě)。

Java 標(biāo)準(zhǔn)庫(kù)提供了一個(gè) URLEncoder 類(lèi)來(lái)對(duì)任意字符串進(jìn)行 URL 編碼:

import java.net.URLEncoder;
public class Main {
    public static void main(String[] args) {
        String encoded = URLEncoder.encode("中文!", "utf-8");
		System.out.println(encoded);
    }
}

上述代碼的運(yùn)行結(jié)果是 %E4%B8%AD%E6%96%87%21 ,"中"的 URL 編碼 是 %E4%B8%AD ,"文"的URL編碼是 %E6%96%87 , ! 雖然是 ASCII 字符,也要對(duì)其編 碼為 %21 。

如果服務(wù)器收到 URL 編碼的字符串,就可以對(duì)其進(jìn)行解碼,還原成原始字符串。 Java 標(biāo)準(zhǔn)庫(kù)的 URLDecoder 就可以解碼:

public class Main {
    public static void main(String[] args) {
        String decoded = URLDecoder.decode("%E4%B8%AD%E6%96%87%21", "utf-8");
		System.out.println(decoded);
    }
}

特別注意: URL 編碼是編碼算法,不是加密算法。 URL 編碼的目的是把任意文本數(shù)據(jù)編碼為 % 前綴 表示的文本,編碼后的文本僅包含 A ~ Z , a ~ z , 0 ~ 9 , - , _ , . , * 和 % ,便于瀏覽 器和服務(wù)器處理。

3.Base64編碼

URL 編碼是對(duì)字符進(jìn)行編碼,表示成 %xx 的形式,而 Base64 編碼是對(duì)二進(jìn)制數(shù) 據(jù)進(jìn)行編碼,表示成文本格式。

Base64 編碼可以把任意長(zhǎng)度的二進(jìn)制數(shù)據(jù)變?yōu)榧兾谋荆⑶壹兾谋緝?nèi)容中且只包 含指定字符內(nèi)容: A ~ Z 、 a ~ z 、 0 ~ 9 、 + 、 / 、 = 。它的原理是把 3 字 節(jié)的二進(jìn)制數(shù)據(jù)按 6bit 一組,用 4 個(gè)int整數(shù)表示,然后查表,把 int 整數(shù)用索引對(duì) 應(yīng)到字符,得到編碼后的字符串。

6 位整數(shù)的范圍總是 0 ~ 63 ,所以,能用 64 個(gè)字符表示:字符 A ~ Z 對(duì)應(yīng)索 引 0 ~ 25 ,字符 a ~ z 對(duì)應(yīng)索引 26 ~ 51 ,字符 0 ~ 9 對(duì)應(yīng)索引 52 ~ 61 ,最 后兩個(gè)索引 62 、 63 分別用字符 + 和 / 表示。

舉個(gè)例子: 3 個(gè) byte 數(shù)據(jù)分別是 e4 、 b8 、 ad ,按 6bit 分組得到 39 、 0b 、 22 和 2d :

┌───────────────┬───────────────┬───────────────┐
│      e4       │      b8       │      ad       │
└───────────────┴───────────────┴───────────────┘
┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│1│1│1│0│0│1│0│0│1│0│1│1│1│0│0│0│1│0│1│0│1│1│0│1│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
┌───────────┬───────────┬───────────┬───────────┐
│    39     │    0b     │    22     │    2d     │
└───────────┴───────────┴───────────┴───────────┘

在 Java 中,二進(jìn)制數(shù)據(jù)就是 byte[] 數(shù)組。 Java 標(biāo)準(zhǔn)庫(kù)提供了 Base64 來(lái)對(duì) byte[] 數(shù)組進(jìn)行編解碼:

public class Main {
    public static void main(String[] args) {
        byte[] input = new byte[] { (byte) 0xe4, (byte) 0xb8, (byte) 0xad };
        String b64encoded = Base64.getEncoder().encodeToString(input);
        System.out.println(b64encoded);
    }
}

編碼后得到字符串結(jié)果: 5Lit4 。要對(duì)這個(gè)字符使用 Base64 解碼,仍然用 Base64 這個(gè)類(lèi):

public class Main {
    public static void main(String[] args) {
        byte[] output = Base64.getDecoder().decode("5Lit");
        System.out.println(Arrays.toString(output)); // [-28, -72, -83]
    }
}

因?yàn)闃?biāo)準(zhǔn)的 Base64 編碼會(huì)出現(xiàn) + 、 / 和 = ,所以不適合把 Base64 編碼后的字符串放到 URL 中。 一種針對(duì) URL 的 Base64 編碼可以在 URL 中使用的 Base64 編碼,它僅僅是把 + 變成 - , / 變成 _ :

public class Main {
    public static void main(String[] args) {
        // 原始字節(jié)內(nèi)容
		byte[] input = new byte[] { 0x01, 0x02, 0x7f, 0x00 };
		// 分別使用兩種方式進(jìn)行編碼
		String b64Encode = Base64.getEncoder().encodeToString(input);
        String b64UrlEncoded = Base64.getUrlEncoder().encodeToString(input);
        // 結(jié)果完全一致
        System.out.println(b64Encode); 
        System.out.println(b64UrlEncoded);
        // 分別使用兩種方式進(jìn)行重新解碼
        byte[] output1 = Base64.getDecoder().decode(b64Encode);
        System.out.println(Arrays.toString(output1));
        byte[] output2 = Base64.getUrlDecoder().decode(b64UrlEncoded);
        System.out.println(Arrays.toString(output2));
    }
}
  • Base64 編碼的目的是把二進(jìn)制數(shù)據(jù)變成文本格式,這樣在很多文本中就可以處理二進(jìn) 制數(shù)據(jù)。例如,電子郵件協(xié)議就是文本協(xié)議,如果要在電子郵件中添加一個(gè)二進(jìn)制文 件,就可以用 Base64 編碼,然后以文本的形式傳送。
  • Base64 編碼的缺點(diǎn)是傳輸效率會(huì)降低,因?yàn)樗言紨?shù)據(jù)的長(zhǎng)度增加了1/3。和 URL 編碼一樣, Base64 編碼是一種編碼算法,不是加密算法。
  • 如果把 Base64 的 64 個(gè)字符編碼表?yè)Q成 32 個(gè)、 48 個(gè)或者 58 個(gè),就可以使 用 Base32 編碼, Base48 編碼和 Base58 編碼。字符越少,編碼的效率就會(huì)越低。

二、哈希算法

1.概述

哈希算法( Hash )又稱摘要算法( Digest ),它的作用是:對(duì)任意一組輸入數(shù) 據(jù)進(jìn)行計(jì)算,得到一個(gè)固定長(zhǎng)度的輸出摘要。哈希算法的目的:為了驗(yàn)證原始數(shù)據(jù)是否被篡改。

哈希算法最重要的特點(diǎn)就是:

  • 相同的輸入一定得到相同的輸出;
  • 不同的輸入大概率得到不同的輸出。

Java字符串的 hashCode() 就是一個(gè)哈希算法,它的輸入是任意字符串,輸出是固定 的 4 字節(jié) int 整數(shù):

"hello".hashCode(); // 0x5e918d2
"hello, java".hashCode(); // 0x7a9d88e8
"hello, bob".hashCode(); // 0xa0dbae2f

兩個(gè)相同的字符串永遠(yuǎn)會(huì)計(jì)算出相同的 hashCode ,否則基于 hashCode 定位的 HashMap 就無(wú)法正常工 作。這也是為什么當(dāng)我們自定義一個(gè) class 時(shí),覆寫(xiě) equals() 方法時(shí)我們必須正確覆寫(xiě) hashCode() 方法。

2.哈希碰撞

哈希碰撞是指,兩個(gè)不同的輸入得到了相同的輸出:

"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0
"通話".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03

碰撞能不能避免?答案是不能。碰撞是一定會(huì)出現(xiàn)的,因?yàn)檩敵龅淖止?jié)長(zhǎng)度是固定的, String 的 hashCode() 輸出是 4 字節(jié)整數(shù),最多只有 4294967296 種輸出,但輸入的數(shù)據(jù)長(zhǎng)度是不固定的,有無(wú)數(shù)種輸入。所以,哈希算 法是把一個(gè)無(wú)限的輸入集合映射到一個(gè)有限的輸出集合,必然會(huì)產(chǎn)生碰撞。

碰撞不可怕,我們擔(dān)心的不是碰撞,而是碰撞的概率,因?yàn)榕鲎哺怕实母叩完P(guān)系到哈希算法的安全性。一個(gè)安全的哈希算法必須滿足:

  • 碰撞概率低;
  • 不能猜測(cè)輸出:輸入的任意一個(gè) bit 的變化會(huì)造成輸出完全不同,這樣就很難從輸出反推輸入(只能依靠 暴力窮舉)。

假設(shè)一種哈希算法有如下規(guī)律:

hashA("java001") = "123456"
hashA("java002") = "123457"
hashA("java003") = "123458"

那么很容易從輸出 123459 反推輸入,這種哈希算法就不安全。安全的哈希算法從輸出是看不出任何規(guī)律的:

hashB("java001") = "123456"
hashB("java002") = "580271"
hashB("java003") = ???

3.常用哈希算法

常用的哈希算法有:根據(jù)碰撞概率,哈希算法的輸出長(zhǎng)度越長(zhǎng),就越難產(chǎn)生碰撞,也就越安全。

①.MD5

import java.security.MessageDigest;
public class main {
	public static void main(String[] args)  {
		// 創(chuàng)建一個(gè)MessageDigest實(shí)例:
        MessageDigest md = MessageDigest.getInstance("MD5");
        // 反復(fù)調(diào)用update輸入數(shù)據(jù):
        md.update("Hello".getBytes("UTF-8"));
        md.update("World".getBytes("UTF-8"));
        // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6
        byte[] results = md.digest(); 
        StringBuilder sb = new StringBuilder();
        for(byte bite : results) {
        	sb.append(String.format("%02x", bite));
        }
        System.out.println(sb.toString());
	}
}

運(yùn)行上述代碼,可以得到輸入HelloWorld 的 MD5 是 68e109f0f40ca72a15e05cc22786f8e6

使用 MessageDigest 時(shí),我們首先根據(jù)哈希算法獲取一個(gè) MessageDigest 實(shí)例,然后, 反復(fù)調(diào)用 update(byte[]) 輸入數(shù)據(jù)。當(dāng)輸入結(jié)束后,調(diào)用 digest() 方法獲得 byte [] 數(shù)組表示的摘要,最后,把它轉(zhuǎn)換為十六進(jìn)制的字符串。

②.SHA-1

import java.security.MessageDigest;
public class main {
	public static void main(String[] args)  {
		// 創(chuàng)建一個(gè)MessageDigest實(shí)例:
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        // 反復(fù)調(diào)用update輸入數(shù)據(jù):
        md.update("Hello".getBytes("UTF-8"));
        md.update("World".getBytes("UTF-8"));
        // 20 bytes: db8ac1c259eb89d4a131b253bacfca5f319d54f2
        byte[] results = md.digest(); 
        StringBuilder sb = new StringBuilder();
        for(byte bite : results) {
        	sb.append(String.format("%02x", bite));
        }
        System.out.println(sb.toString());
	}
}

類(lèi)似的,計(jì)算 SHA-256 ,我們需要傳入名稱" SHA-256 ",計(jì)算 SHA-512 ,我們需要傳入名稱" SHA-512 "。

③.RipeMD-160

BouncyCastle是一個(gè)提供了很多哈希算法和加密算法的第三方開(kāi)源庫(kù)。它提供了 Java 標(biāo)準(zhǔn)庫(kù)沒(méi) 有的一些算法,例如, RipeMD160 哈希算法。 RIPEMD160 是一種基于 Merkle-Damgård 結(jié)構(gòu)的加密哈希函數(shù),它是比特幣標(biāo)準(zhǔn)之一。 RIPEMD-160 是 RIPEMD 算法的增強(qiáng)版本, RIPEMD-160 算法可以產(chǎn)生出 160 位的的哈希摘要。

用法:

  • 首先,我們必須把 BouncyCastle 提供的 bcprov-jdk15on-1.70.jar 添加至 classpath 。
  • 其次,Java標(biāo)準(zhǔn)庫(kù)的 java.security 包提供了一種標(biāo)準(zhǔn)機(jī)制,允許第三方提供商無(wú)縫接入。我們要使用 Bouncy Castle 提供的 RipeMD160 算法,需要先把 BouncyCastle 注冊(cè)一下:
public class Main {
    public static void main(String[] args) throws Exception {
        // 注冊(cè)BouncyCastle提供的通知類(lèi)對(duì)象BouncyCastleProvider
        Security.addProvider(new BouncyCastleProvider());
        // 獲取RipeMD160算法的"消息摘要對(duì)象"(加密對(duì)象)
        MessageDigest md = MessageDigest.getInstance("RipeMD160");
        // 更新原始數(shù)據(jù)
        md.update("HelloWorld".getBytes());
        // 獲取消息摘要(加密)
        byte[] result = md.digest();
        // 消息摘要的字節(jié)長(zhǎng)度和內(nèi)容
        System.out.println(result.length); // 160位=20字節(jié)
        System.out.println(Arrays.toString(result));
        // 16進(jìn)制內(nèi)容字符串
        String hex = new BigInteger(1,result).toString(16);
        System.out.println(hex.length()); // 20字節(jié)=40個(gè)字符
        System.out.println(hex);
    }
}

4.哈希算法的用途

校驗(yàn)下載文件

因?yàn)橄嗤妮斎胗肋h(yuǎn)會(huì)得到相同的輸出,因此,如果輸入被修改了,得到的輸出就會(huì)不同。

如何判斷下載到本地的軟件是原始的、未經(jīng)篡改的文件?我們只需要自己計(jì)算一下本地文件的哈希值,再 與官網(wǎng)公開(kāi)的哈希值對(duì)比,如果相同,說(shuō)明文件下載正確,否則,說(shuō)明文件已被篡改。

存儲(chǔ)用戶密碼

如果直接將用戶的原始口令存放到數(shù)據(jù)庫(kù)中,會(huì)產(chǎn)生極大的安全風(fēng)險(xiǎn): 數(shù)據(jù)庫(kù)管理員能夠看到用戶明文口令; 數(shù)據(jù)庫(kù)數(shù)據(jù)一旦泄漏,黑客即可獲取用戶明文口令。

三、小結(jié)

  • URL 編碼和 Base64 編碼都是編碼算法,它們不是加密算法;
  • URL 編碼的目的是把任意文本數(shù)據(jù)編碼為 % 前綴表示的文本,便于瀏覽器和服務(wù)器處理;
  • Base64 編碼的目的是把任意二進(jìn)制數(shù)據(jù)編碼為文本,但編碼后數(shù)據(jù)量會(huì)增加 1/3 。
  • 哈希算法可用于驗(yàn)證數(shù)據(jù)完整性,具有防篡改檢測(cè)的功能;
  • 常用的哈希算法有 MD5 、SHA-1 等;
  • 用哈希存儲(chǔ)口令時(shí)要考慮彩虹表攻擊。

到此這篇關(guān)于Java編碼算法與哈希算法深入分析使用方法的文章就介紹到這了,更多相關(guān)Java編碼算法與哈希算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶

    Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶

    這篇文章主要給大家介紹了關(guān)于Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Spring Boot日志的打印與持久化詳細(xì)解析

    Spring Boot日志的打印與持久化詳細(xì)解析

    Spring Boot默認(rèn)使用SLF4J+Logback 記錄日志,并提供了默認(rèn)配置,即使我們不進(jìn)行任何額外配,也可以使用SLF4J+Logback進(jìn)行日志輸出
    2022-07-07
  • IntelliJ IDEA 2019.1.1 for MAC 下載和注冊(cè)碼激活教程圖解

    IntelliJ IDEA 2019.1.1 for MAC 下載和注

    這篇文章主要介紹了IntelliJ IDEA 2019.1.1 for MAC 下載和注冊(cè)碼激活,教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 詳解一個(gè)簡(jiǎn)單的Servlet容器的設(shè)計(jì)與實(shí)現(xiàn)

    詳解一個(gè)簡(jiǎn)單的Servlet容器的設(shè)計(jì)與實(shí)現(xiàn)

    Servlet算是Java Web開(kāi)發(fā)請(qǐng)求鏈路調(diào)用棧中底層的一個(gè)技術(shù),而了解一個(gè)Servlet容器的實(shí)現(xiàn)有助于更好的理解JavaWeb開(kāi)發(fā),所以下面就來(lái)看看如何設(shè)計(jì)與實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Servlet容器吧
    2023-07-07
  • SpringBoot自動(dòng)裝配Condition的實(shí)現(xiàn)

    SpringBoot自動(dòng)裝配Condition的實(shí)現(xiàn)

    Spring4.0新增@Conditional注解,用于條件化Bean的注冊(cè),通過(guò)實(shí)現(xiàn)Condition接口并重寫(xiě)matches方法,可以控制Bean的創(chuàng)建與否,感興趣的可以了解一下
    2024-10-10
  • java中int初始化可以為0,但不能為NULL問(wèn)題

    java中int初始化可以為0,但不能為NULL問(wèn)題

    這篇文章主要介紹了java中int初始化可以為0,但不能為NULL問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法

    SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法

    Redisson是一個(gè)用于Java的Redis客戶端,它提供了在分布式環(huán)境下操作Redis數(shù)據(jù)庫(kù)的簡(jiǎn)單、高效的方式,本文主要介紹了SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • 在Java與Kotlin之間如何進(jìn)行互操作詳解

    在Java與Kotlin之間如何進(jìn)行互操作詳解

    這篇文章主要給大家介紹了關(guān)于在Java和Kotlin之間如何進(jìn)行互操作的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • SpringBoot @PathVariable使用時(shí)遇到的問(wèn)題及解決

    SpringBoot @PathVariable使用時(shí)遇到的問(wèn)題及解決

    這篇文章主要介紹了SpringBoot @PathVariable使用時(shí)遇到的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 利用Java如何獲取Mybatis動(dòng)態(tài)生成的sql接口實(shí)現(xiàn)

    利用Java如何獲取Mybatis動(dòng)態(tài)生成的sql接口實(shí)現(xiàn)

    MyBatis 的強(qiáng)大特性之一便是它的動(dòng)態(tài)SQL,下面這篇文章主要給大家介紹了關(guān)于利用Java如何獲取Mybatis動(dòng)態(tài)生成的sql接口實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01

最新評(píng)論

安福县| 五常市| 涡阳县| 黄大仙区| 色达县| 比如县| 凤台县| 江津市| 湖北省| 独山县| 资溪县| 彩票| 巴林左旗| 灵丘县| 天全县| 黄冈市| 凤阳县| 延吉市| 黄浦区| 桓仁| 浦东新区| 昆明市| 同德县| 罗城| 逊克县| 台南市| 横山县| 宝丰县| 永州市| 山阳县| 赤壁市| 昆山市| 罗源县| 磴口县| 抚顺县| 绍兴县| 嘉义县| 宜兴市| 来宾市| 香河县| 湖南省|