師弟不講武德,這編碼算法也講得忒溜了……

2020-12-12 酷扯兒

本文轉載自【微信公眾號:五角錢的程式設計師,ID:xianglin965】經微信公眾號授權轉載,如需轉載與原文作者聯繫

一起學習、成長、溫情的熱愛生活

1.編碼算法

什麼是編碼?

ASCII碼

就是一種編碼,

字母A

的編碼是

十六進位的0x41

,字母B是

0x42

,以此類推:

因為ASCII編碼最多只能有127個字符,要想對更多的文字進行編碼,就需要用

Unicode

。而中文的

使用Unicode編碼就是

0x4e2d

,使用

UTF-8

則需要

3個字節

編碼:

因此,最簡單的編碼是直接給

每個字符

指定一個

若干字節表示的整數

,複雜一點的編碼就需要

根據一個已有的編碼推算出來

比如: UTF-8編碼,它是一種不定長編碼,但可以從給定字符的Unicode編碼推算出來1.1 URL編碼

1.什麼是URL編碼算法

URL編碼是

瀏覽器

發送數據給伺服器時使用的編碼,它通常拼接在·URL的參數部分

例如:https://www.baidu.com/s?wd=%E4%B8%AD%E6%96%87之所以需要URL編碼,是因為出於

兼容性考慮

很多伺服器

只識別ASCII字符

。但如果URL中

包含中文、日文

這些非ASCII字符怎麼辦?不要緊,URL編碼有一套規則:

如果字符是A~Z,a~z,0~9以及-、_、.、*,則保持不變;如果是其他字符先轉換為UTF-8編碼然後對每個字節以%XX表示。例如:字符"中"的UTF-8編碼是0xe4b8ad,因此,它的URL編碼是%E4%B8%AD。URL編碼總是大寫。2.Java中使用URL編碼算法

Java標準庫提供了一個

URLEncoder

類來對任意字符串進行URL編碼

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

image

上述代碼的運行結果是

%E4%B8%AD%E6%96%87%21

,中的URL編碼是

%E4%B8%AD

,文的URL編碼是

%E6%96%87

!

雖然是

ASCII

字符,也要對其編碼為

%21

和標準的URL編碼稍有不同,URLEncoder把空格字符編碼成「+」,而現在的URL編碼標準要求空格被編碼為「%20」,不過,伺服器都可以處理這兩種情況。!!!要特別注意URL編碼是`編碼算法`,`不是加密算法`。URL編碼的目的是`把任意文本數據編碼為%前綴表示的文本`,編碼後的文本僅包含`A~Z,a~z,0~9,-,_,.,和%,便於瀏覽器和伺服器處理。</li> </ul> 如果伺服器收到URL編碼的字符串,就可以對其進行解碼,還原成原始字符串。Java標準庫的URLDecoder`就可以解碼:

import java.io.UnsupportedEncodingException;import java.net.URLDecoder;public class TestURLDecoder {public static void main(String[] args) throws UnsupportedEncodingException { String decoded = URLDecoder.decode("%E4%B8%AD%E6%96%87%21", "UTF-8"); System.out.println(decoded); }}

1.2 Base64編碼

1.什麼是Base64編碼算法

URL編碼是對字符進行編碼,表示成%xx的形式而Base64編碼是對二進位數據進行編碼,表示成文本格式Base64編碼可以把任意長度的二進位數據變為純文本,且只包含A~Z、a~z、0~9、+、/、=這些字符。它的原理是3位元組的二進位數據按6bit(1個字節=2bit)一組4個int整數表示然後查表int整數索引對應到字符得到編碼後的字符串

舉個例子:

3個byte

數據分別是

e4、b8、ad

,按

6bit分組

得到

39、0b、22和2d

因為

6位整數

的範圍總是

0~63

,所以,能用

64個字符

表示:字符

A~Z

對應索引

0~25

,字符

a~z

對應索引

26~51

,字符

0~9

對應索引

52~61

,最後兩個索引

62、63

分別用字符

+和/

表示。

2.Java中使用Base64編碼算法

在Java中,二進位數據就是

byte[]數組

。Java標準庫提供了

Base64

來對

byte[]數組

進行

編解碼

import java.util.Arrays;import java.util.Base64;public class TestBase64 {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); System.out.println("-----------------------"); //編碼後得到5Lit4個字符。要對Base64解碼,仍然用Base64這個類進行解碼 byte[] output = Base64.getDecoder().decode("5Lit"); System.out.println(Arrays.toString(output)); // [-28, -72, -83] }}

如果輸入的byte[]數組長度不是3的整數倍腫麼辦?

這種情況下,需要對輸入的末尾補一個或兩個0x00編碼後,在結尾加一個=表示補充了1個0x00,加兩個=表示補充了2個0x00解碼的時候,去掉末尾補充的一個或兩個0x00即可實際上,因為

編碼後的長度加上=總是4的倍數

,所以

即使不加=也可以計算出原始輸入的byte[]

Base64編碼的時候可以用withoutPadding()去掉=,解碼出來的結果是一樣的:

import java.util.Arrays;import java.util.Base64;public class TestBase642 {public static void main(String[] args) { byte[] input = new byte[]{(byte) 0xe4, (byte) 0xb8, (byte) 0xad, 0x21}; String b64encoded = Base64.getEncoder().encodeToString(input); System.out.println("encodeToString=>"+b64encoded); //用withoutPadding()去掉= String b64encoded2 = Base64.getEncoder().withoutPadding().encodeToString(input); System.out.println("withoutPadding=>"+b64encoded2); //將去掉=的Base64編碼重新解碼 byte[] output = Base64.getDecoder().decode(b64encoded2); System.out.println(Arrays.toString(output)); }}

因為標準的Base64編碼會出現

+、/和=

,所以不適合把Base64編碼後的字符串放到URL中

一種針對URL的Base64編碼可以在URL中使用的Base64編碼,它僅僅是把+變成-,/變成_

import java.util.Arrays;import java.util.Base64;public class TestBase64URL {public static void main(String[] args) { //編碼 byte[] input = new byte[]{0x01, 0x02, 0x7f, 0x00}; String b64encoded = Base64.getUrlEncoder().encodeToString(input); System.out.println(b64encoded); //解碼 byte[] output = Base64.getUrlDecoder().decode(b64encoded); System.out.println(Arrays.toString(output)); }}

Base64編碼的目的是把二進位數據變成文本格式這樣在很多文本中就可以處理二進位數據例如,電子郵件協議就是文本協議,如果要在電子郵件中添加一個二進位文件,就可以用Base64編碼,然後以文本的形式傳送。Base64編碼的缺點是:傳輸效率會降低,因為它把原始數據的長度增加了1/3和URL編碼一樣,Base64編碼是一種編碼算法,不是加密算法如果把Base64的64個字符編碼表換成32個、48個或者58個,就可以使用Base32編碼,Base48編碼和Base58編碼字符越少,編碼的效率就會越低。小結

URL編碼和Base64編碼都是編碼算法,它們不是加密算法URL編碼的目的是把任意文本數據編碼為"%"前綴表示的文本,便於瀏覽器和伺服器處理;Base64編碼的目的是把任意二進位數據編碼為文本,但編碼後數據量會增加1/3,傳輸效率會降低

2. 什麼是哈希算法?

哈希算法(Hash)又稱

摘要算法(Digest)

,它的作用是:對任意一組輸入數據進行計算,得到一個固定長度的輸出摘要。

哈希算法最重要的特點就是:

相同的輸入一定得到相同的輸出;不同的輸入大概率得到不同的輸出。哈希算法的目的就是為了

驗證原始數據是否被篡改

Java字符串的

hashCode()

方法就是一個哈希算法,它的

輸入

是任意字符串

輸出

是固定的

4位元組int整數:

System.out.println("hello".hashCode()); // 99162322System.out.println("hello, java".hashCode()); // 2057144552System.out.println("hello, bob".hashCode()); // -1596215761

兩個相同的字符串永遠會計算出相同的hashCode

否則基於

hashCode

定位的HashMap就無法正常工作。這也是為什麼當我們自定義一個class時,

覆寫equals()方法時我們必須正確覆寫hashCode()方法。

2.1 哈希碰撞

哈希碰撞是指:

兩個不同的輸入得到了相同的輸出:

System.out.println("AaAaAa".hashCode());; // 1952508096System.out.println("BBAaBB".hashCode());// 1952508096

碰撞能不能避免?答案是不能

碰撞是一定會出現的

,因為

輸出的字節長度是固定的

String的hashCode()輸出是4位元組整數,最多只有4294967296種輸出,但輸入的數據長度是不固定的,有無數種輸入所以,哈希算法是把一個無限的輸入集合映射到一個有限的輸出集合,必然會產生碰撞。碰撞不可怕,我們擔心的不是碰撞,而是

碰撞的概率

,因為

碰撞概率的高低關係

到哈希算法的

安全性

。一個安全的哈希算法必須滿足:

碰撞概率低;不能猜測輸出。**不能猜測輸出是指 : **輸入的任意一個bit的變化會造成輸出完全不同,這樣就很難從輸出反推輸入(只能依靠暴力窮舉)。假設一種哈希算法有如下規律:

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

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

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

2.2 常用的哈希算法

根據碰撞概率

哈希算法的輸出長度越長

越難產生碰撞

也就

越安全

MD5 加密後的位數有兩種:16 位與 32 位。16 位實際上是從 32 位字符串中取中間的第 9 位到第 24 位的部分,用 Java 語言來說,即:String md5_16 = md5_32.substring(8, 24);

MD5 加密後的字符串又分為

大寫與小寫兩種

,也就是

其中的字母是大寫還是小寫

所以對字符串「yjclsx」進行 MD5 加密後的結果類型有這些:

Java 中 MD5 加密的結果默認是32位小寫。

2.3 Java中使用哈希算法

Java標準庫提供了

常用的哈希算法,並且有一套統一的接口

。我們以

MD5算法為例

,看看如何對輸入計算哈希:

import java.math.BigInteger;import java.security.MessageDigest;public class TestMD5 {public static void main(String[] args) throws Exception { // 創建一個MessageDigest實例: MessageDigest md = MessageDigest.getInstance("MD5"); // 反覆調用update輸入數據: md.update("Hello".getBytes("UTF-8")); md.update("World".getBytes("UTF-8")); byte[] result = md.digest(); // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6 //轉換為十六進位的字符串 System.out.println(new BigInteger(1, result).toString(16)); }}

image

使用MessageDigest時,我們首先根據哈希算法獲取一個

MessageDigest實例

,然後,反覆

調用update(byte[])輸入數據

。當輸入結束後,調用

digest()方法獲得byte[]數組表示的摘要

,最後,把它轉換為十六進位的字符串。

2.4 哈希算法的用途

因為相同的輸入永遠會得到相同的輸出,因此,如果輸入被修改了,得到的輸出就會不同。

我們在網站上下載軟體的時候,經常看到下載頁顯示的哈希:

如何判斷下載到本地的軟體是原始的、未經篡改的文件?

我們只需要自己計算一下本地文件的哈希值,再與官網公開的哈希值對比,如果相同,說明文件下載正確,否則,說明文件已被篡改哈希算法的另一個重要用途是存儲用戶密碼。如果直接將用戶的

原始密碼存放到資料庫中

,會產生極大的安全風險:

資料庫管理員能夠看到用戶明文密碼;資料庫數據一旦洩漏,黑客即可獲取用戶明文密碼。不存儲用戶的原始口令,那麼如何對用戶進行認證?

方法是存儲用戶密碼的哈希,例如:MD5在用戶輸入原始密碼後,系統計算用戶輸入的原始密碼的MD5並與資料庫存儲的MD5對比,如果一致,說明密碼正確,否則,密碼錯誤。因此資料庫存儲用戶名和密碼的表內容應該像下面這樣:

這樣一來,

資料庫管理員看不到用戶的原始密碼

。即使資料庫洩漏,黑客也無法拿到用戶的原始密碼。想要拿到用戶的原始密碼,必須用

暴力窮舉

的方法,

一個密碼一個密碼地試,直到某個密碼計算的MD5恰好等於指定值

使用哈希密碼時,還要注意防止彩虹表攻擊。

什麼是彩虹表呢?上面講到了,如果只拿到MD5,從MD5反推明文密碼,只能使用暴力窮舉的方法。然而黑客並不笨,暴力窮舉會消耗大量的算力和時間。但是,如果有一個預先計算好的常用密碼和它們的MD5的對照表這個表就是彩虹表。如果用戶使用了

常用密碼

黑客從MD5一下就能反查到原始密碼

bob的MD5:

f30aa7a662c728b7407c54ae6bfd27d1

原始密碼:

hello123

alice的MD5:

25d55ad283aa400af464c76d713c07ad

原始密碼:

12345678

tim的MD5:

bed128365216c019988915ed3add75fb

原始秘密:

passw0rd

這就是為什麼不要使用常用密碼,以及不要使用生日作為密碼的原因。

加鹽

即使用戶使用了常用密碼,我們也可以採取措施來

抵禦彩虹表攻擊

方法是對

每個密碼額外添加隨機數

,這個方法稱之為

加鹽(salt)

digest = md5(salt+inputPassword)

經過加鹽處理的資料庫表,內容如下:

image

加鹽的目的在於

使黑客的彩虹表失效,即使用戶使用常用密碼,也無法從MD5反推原始密碼。

2.5 .SHA-1

SHA-1也是一種哈希算法它的輸出是

160 bits,即20位元組

SHA-1是由美國國家安全局開發的,SHA算法實際上是一個系列,包括SHA-0(已廢棄)、SHA-1、SHA-256、SHA-512等。在Java中使用SHA-1,和MD5完全一樣,只需要把算法名稱改為

"SHA-1"

import java.math.BigInteger;import java.security.MessageDigest;public class TestSHA1 {public static void main(String[] args) throws Exception { // 創建一個MessageDigest實例: MessageDigest md = MessageDigest.getInstance("SHA-1"); // 反覆調用update輸入數據: md.update("Hello".getBytes("UTF-8")); md.update("World".getBytes("UTF-8")); byte[] result = md.digest(); // 20 bytes: 6f44e49f848dd8ed27f73f59ab5bd4631b3f6b0d //轉為16進位字符串 System.out.println(new BigInteger(1, result).toString(16)); }}

image

小結

哈希算法可用於驗證數據完整性,具有防篡改檢測的功能;常用的哈希算法有MD5、SHA-1等;用哈希存儲口令時要考慮彩虹表攻擊。

相關焦點

  • 年輕人不講武德是什麼梗出處在哪 馬保國不講武德梗為什麼這麼火
    年輕人不講武德是什麼梗出處在哪 馬保國不講武德梗為什麼這麼火 最近年輕人不講武德的這個梗瘋狂的在B站和抖音上刷屏
  • 常德人究竟講不講武德?
    太極大師馬保國的一句「年輕人不講武德,偷襲我這個69歲的老人家」最近火了,很多人都在模仿這個句式。 年輕人不講武德是什麼梗和意思 年輕人不講武德:這句話是太極大師馬保國說的,現在被用來形容年輕人很猛。
  • 年輕人不講武德是什麼梗和意思 年輕人不講武德梗出處
    年輕人不講武德是什麼梗和意思 年輕人不講武德梗出處時間:2020-11-22 23:07   來源:今日頭條   責任編輯:毛青青 川北在線核心提示:原標題:年輕人不講武德是什麼梗和意思 年輕人不講武德梗出處 太極大師馬保國的一句年輕人不講武德,偷襲我這個69歲的老人家最近火了,很多人都在模仿這個句式,用法還挺廣泛
  • 「不講武德」?湖北一景區有著罕見奇觀,為何被指「不講武德」?
    然而,恩施境內有一個景點,自然生態保護完好,風景如畫,近年卻屢遭吐槽,網友直呼「不講武德」,這是為何呢?屏山位於恩施州鶴峯縣容美鎮境內,峽谷縱橫、谷深水幽,還有許多溶洞、暗河溝渠,懸崖絕壁更是隨處可見。相對封閉的自然環境使得這裡長期與世隔絕,人煙稀少,自然生態保護完整。
  • 《劍來》:蠻荒天下,不講武德
    妖族出了一個白澤,得禮聖這位小夫子兩聲「先生」,將所有大妖真名告訴了他,並協助他鑄造九鼎立於世間九座大山之巔。自此,萬妖退居山林,隱世不出,人族登山修行,才有了一方天地蔚為大觀的美好風物。再後來,妖族協助人族登天,勝後,面對擁有三教祖師的人族,無奈只能被趕到貧瘠的蠻荒天下,得一片棲息天地。
  • 「耗子尾汁」、「不講武德」用英文怎麼說?
    最近,「形意太極拳」大師馬保國火了,跟著他火的還有「耗子尾汁」(好自為之)和「不講武德」等梗,下面我們就來看下相關的英文怎麼說吧~ 「武德」怎講?
  • 老裡弗斯不講武德起來,那是教科書級別的
    老裡弗斯不講武德起來,那是教科書級別的
  • 年輕人不講武德是什麼意思? 這梗常用來形容年輕人很猛
    年輕人不講武德是什麼意思? 這梗常用來形容年輕人很猛時間:2020-10-30 17:19   來源:遊俠網   責任編輯:沫朵 川北在線核心提示:原標題:年輕人不講武德是什麼意思? 這梗常用來形容年輕人很猛 太極大師馬保國的一句年輕人不講武德,偷襲我這個69歲的老人家最近火了,那麼這個梗到底是什麼意思?
  • 年輕人不講武德是什麼梗 怎麼突然火了
    年輕人不講武德是什麼梗 怎麼突然火了時間:2020-11-22 23:08   來源:今日頭條   責任編輯:毛青青 川北在線核心提示:原標題:年輕人不講武德是什麼梗 怎麼突然火了 隨著網際網路的快速發展,越來越多的新鮮事情通過網絡傳播,最近是不是你也聽到很多口頭禪變了,是不是聽見了周圍很多人在講年輕人不講武德,
  • 你的價值在於能跟「不講武德」的人一起合作
    愛回收的小哥上門做完基本測試後,然後拿著手機反覆端詳,我想我一直都帶套的,你拿個放大鏡也看不出問題啊,終於,他告訴我手機充電口附近因長期插拔有磨損掉漆,我湊過去看了半天,看到了,我只能對小哥的火眼金睛佩服得五體投地。小哥說公司規定這種手機算是「外表有劃痕、掉漆」,要扣掉 100 元,我理解這是商家的營銷手段,為了省時也不跟小哥計較,900元成交了。
  • 《五哈》陳赫坑張雨劍不講武德,鄧超成為中老年婦女偶像!
    這一期《五哈》開始了他們的囧遊郵輪之旅。在鄧超、陳赫、鹿晗一行人登船選擇住所之時,鄧紫棋偽裝成服務員完美騙過所有人,在上一期留下的嘉賓面前端茶量體溫都沒有人讓人發現。然後眾人開始了挑選房間,遊戲達人的陳赫一眼看中了網吧房,讓鹿晗、張顏齊、王晨藝這些遊戲達人羨慕中又不敢反駁。
  • 馬保國經典語錄:年輕人不講武德,是真的不講武德?
    馬保國可能也沒想到自己是以「語錄」方式火的;人們可能也沒料到馬老師會趁著這股風表示要參演電影了。今年貢獻了這麼多部「傳世佳作」的馬老師,作為主演這回真的要演電影了,演的還是個神秘的武術老師傅。面對這上億的流量,馬老師倒也是看得開,直接表示自己已經退出武林。一句火爆全網的,「年輕人不講武德」,幽默風趣,讓我們生活又添加了不少樂趣。他表示只接正能量題材,並且只演正面角色,因為其形象是「推廣傳統功夫」。
  • 鬼畜大師馬保國是哪裡人 馬保國的年輕人不講武德是什麼意思
    馬保國的年輕人不講武德是什麼意思他之前籍籍無名,但是不久前僅憑一場比賽就火遍全網。#馬保國回應惡搞#他的格鬥視頻被上傳到100多個視頻網站上,不僅佔據了B站鬼畜視頻的主流,還將渾元形意太極武術發揚光大,他就是「國際功夫巨星」馬保國。說起馬保國,或許沒多少人知道他的武術有多厲害,但很多人都知道由他創造的兩個網絡熱詞。
  • 馬保國退隱一天就復出拍電影,網友:電影叫年輕人不講武德
    一鞭兩鞭三四鞭,五鞭一出倒一片,混元太極馬大師,從出江湖要變天,不講武德年輕人,一拳打眼大師疼。大師只是年紀大,神功一出誰都怕。最近很多人一定聽過這樣一句話,年輕人不講武德,這話就出自渾元形意太極拳掌門人馬保國大師之口。說到馬大師,最近可了不得了。我說現在的年輕人不但不講武德,還沒有口德,馬大師用他畢生所學五鞭神功為傳統武術證明,卻被網友狂噴,直接把馬大師噴成網絡最火的人,各種鬼畜的視頻出現在各大網絡平臺。
  • 《魔獸世界》PVP七大不講武德技能,誰才是第一名?
    「不講武德」不等同於IMBA技能,後者多了幾分讓敵人手足無措的「無解」成分,而「不講武德」則著重於讓對方「無奈」或「難受」,面對此類技能真的是不能大意:輸了事小,被秀一臉就難受了,面子往哪放?就算你相繼躲開了對方的左重兜、右邊腿與左刺拳那又有何用?
  • 社區團購,到底講不講武德?
    如果將這句話放在當前異軍突起的社區團購身上,我想再適合不過了。有人說網際網路巨頭「不講武德」,奪走菜販生意,將社區團購推上了風口浪尖。過去行業觀察者們看社區團購,有的站在網際網路巨頭的角度,有的站在生鮮供應鏈的角度,有的站在電商流量的角度等等,不一而足。
  • 「耗子尾汁」、「不講武德」,用英文怎麼說?
    馬大師經典語錄:①兩個健身房的年輕人不講武德,偷襲我這個69歲的老同志。②我勸年輕人好自為之(耗子尾汁),好好反思。③二百多斤的英國大力士搬不動我一根手指頭。字面意思是「這是你自己的葬禮」,實際延伸的意思是:「你如果要去就去吧,但一切後果自負!」
  • 抖音年輕人不講武德是什麼梗
    抖音年輕人不講武德是什麼梗?最近大家經常在網上說什麼「年輕人不講武德」的梗,這是什麼梗呢,最近的熱梗好多啊,小編寫不完了噻,那麼大家知道抖音年輕人不講武德是什麼梗嗎!下面就快和小編一起看看吧!
  • 不講武德趙匡胤,是怎麼放倒李煜的?
    前言:「臥榻之側,豈容他人酣睡」這是宋太祖趙匡胤滅唐宣言,可是你知道,不講武德的趙匡胤是怎麼收拾李煜的嗎?一、泰山壓頂。開寶七年(974年)九月,宋太祖趙匡胤以南唐國主李煜「倔強不朝」為藉口,正式宣布討伐南唐。很快,趙匡胤水路並進,為李煜準備了五路大軍的豪華套餐。
  • 年輕人不講武德是什麼梗和意思 很多人都在模仿這個句式
    年輕人不講武德是什麼梗和意思 很多人都在模仿這個句式時間:2020-10-12 21:32   來源:九遊網   責任編輯:凌君 川北在線核心提示:原標題:年輕人不講武德是什麼梗和意思 很多人都在模仿這個句式 太極大師馬保國的一句年輕人不講武德,偷襲我這個69歲的老人家最近火了,很多人都在模仿這個句式,用法還挺廣泛