1. 程式人生 > >雜湊演算法及其拓展

雜湊演算法及其拓展

本篇是iOS逆向開發的遞進篇-關於雜湊演算法、數字簽名及對稱加密等,下面我們著重講解此內容,希望對大家有所幫助!!!

 

 一、雜湊

1.1 基本內容

雜湊表也稱為散列表(Hash table),是根據關鍵碼值(key,value),直接進行訪問的資料結構。通過把關鍵碼對映到表中的一個位置來進行訪問記錄,用來加快查詢速度。對映函式也稱之為雜湊函式,存放記錄陣列稱為散列表。

假設沒有記憶體限制,直接可以將鍵作為陣列的索引,那麼所有的查詢僅僅需要一次即可完成。但是這種理想的情況也不會一直出現,因為牽扯到記憶體問題。從另一個角度來說,如果沒有時間來限制,我們也可以使用無序陣列並進行順序查詢,這樣也會使用較少的記憶體。

使用雜湊查詢演算法分為兩個步驟:

  1. 使用Hash函式將被要查詢的鍵轉化為陣列中的一個索引。理想情況下,不同的鍵都可以轉為不同的索引值。但這僅僅是理想情況下,在實際的開發運算中,我們還是要處理兩個或者多個鍵值雜湊到同個索引值的情況。
  2. 要處理碰撞衝突的過程。

目前本人部落格關於講述雜湊思想查詢元素的部落格有:https://www.cnblogs.com/guohai-stronger/p/11506990.html,還會持續更新此類演算法思想有關的題目。

 

1.2 雜湊函式的兩種解決碰撞的方式

1.2.1 拉鍊法(separate chaining)

 拉鍊法簡單說就是連結串列+陣列。將鍵來通過Hash函式對映為大小為M的陣列下標索引,陣列的每個元素指向連結串列,連結串列的每個節點儲存著雜湊出來的索引值為節點下標的鍵對值。

舉一個例子:

給定一組資料為{45,27,55,24,10,53,32,14,23,01,42,20},假設散列表長度為13,用拉鍊法解決構造的雜湊表。拉鍊法表示如下:

上面就是拉鍊法的圖示,下面我們講解拉鍊法的程式碼實現:

public class SeparateChainingHashST<Key, Value> {
    //SequetialSearchST
    private int N;//鍵值對總數
    private int M;//散列表的大小
    private SequentialSearchST<Key, Value>[] st;//存放連結串列物件的陣列
    public SeparateChainingHashST() {//預設的建構函式會使用997條連結串列
        this(997);
    }
    public SeparateChainingHashST(int M) {
        //建立M條連結串列
        this.M = M;
        //創造一個(SequentialSearchST<Key, Value>[])型別的,長度為M的陣列
        st = (SequentialSearchST<Key, Value>[]) new SequentialSearchST[M];
        for(int i = 0; i < M; i++) {
            //為每一個數組元素申請一個空間
            st[i] = new SequentialSearchST();
        }
    }
    private int hash(Key key) {
        return (key.hashCode() & 0x7fffffff) % M;
    }
    public Value get(Key key) {
        return (Value)st[hash(key)].get(key);
    }
    public void put(Key key, Value val) {
        st[hash(key)].put(key, val);
    }
    public void delete(Key key) {
        st[hash(key)].delete(key);
    }
    public Iterable<Key> keys(){
        Queue<Key> queue = new Queue<Key>();
        for(int i = 0; i < M; i++) {
            System.out.println("第" + i +"個元素的連結串列");
            for(Key key : st[i].keys()) {
                queue.enqueue(key);
                System.out.print(key + " " + get(key) + " ,");
            }
            System.out.println();
        }
        return queue;
    }
    public static void main(String[] args) {
        SeparateChainingHashST<String, Integer> st = new SeparateChainingHashST<String, Integer>(5);
        for (int i = 0; i < 13; i++) {
            String key = StdIn.readString();
            st.put(key, i);
        }
        for (String s : st.keys())
            StdOut.println(s + " " + st.get(s));
        st.delete("M");
        StdOut.println("*************************************");
        for (String s : st.keys()) {
             StdOut.println(s + " " + st.get(s));
        }
    }
}

上面就是拉鍊表的基本內容,如果想進一步瞭解,可以檢視資料結構相關書籍。

 

1.2.2 開放定址法

開放定址法包括線性探測法和平方探測法。

開放定址法是由關鍵碼得到的雜湊地址一旦發生了衝突,假如已經存在了元素,就會去尋找下一個空的雜湊地址,只需要雜湊表足夠的大,空的雜湊地址總能找到,並將元素存入進去。

 

1.3 雜湊的特點

  • 演算法是公開的
  • 對相同的資料運算,得到的結果是一樣的
  • 對不同的資料運算,如用MD5得到的結果預設為128位,32個字元(16進位制)
  • 這玩意沒辦法進行逆運算
  • 資訊摘要,資訊的指紋,都是用來資料識別的

 

1.4 雜湊用途加密方式

1.4.1 使用者密碼的加密

1.4.1.1 直接使用MD5加密
//密碼
    NSString * pwd = @"123456";
    
    //MD5 直接加密 e10adc3949ba59abbe56e057f20f883e
    //不足:不夠安全了。可以反查詢!
  pwd = pwd.md5String;

我們也可以通過終端,通過輸入md5 -s "內容",如下得到md5,32個字元

1.4.1.2 加鹽
//足夠複雜!
static NSString * salt = @"(*(*(DS*YFHIUYF(*&DSFHUS(*AD&";
pwd = [pwd stringByAppendingString:salt].md5String;

運用加鹽方式弊端: 鹽都是是固定的,把它寫死在程式裡面,一旦洩露就會不安全了!

1.4.1.3 HMAC
/** HMAC
     *  使用一個金鑰加密,並且做兩次雜湊!
     *  在實際開發中,金鑰(KEY)來自於伺服器(動態的)!
     *  一個賬號,對應一個KEY,而且還可以跟新!
     */
    pwd = [pwd hmacMD5StringWithKey:@"hank"];

在我們日常開發中,如果一個是有非常好的後臺開發素質,會在登入註冊介面返回來一個時間戳,對於這個時間戳可以很好地運用到HMAC中

通過上面:

假如將時間戳運用到裡面中,和HMAC雜湊值拼接此時的時間戳(直到分,不到秒)發給伺服器,然後伺服器根據客戶端發來的字元,進行解析;如果此時這個過程到了下一分鐘(201812032050 58s發,伺服器收到已經201812032051 20s ),伺服器會做一個分鐘-1進行驗證

 

1.4.2 搜尋引擎

我們在搜尋幾個詞語時,假如在資料庫檢索“國孩”,“真的”,“很帥”,對於我們搜尋其中的任何一個詞,都可以通過雜湊檢索出來,雜湊內部是怎麼做到的呢?

下面是三個詞在md5下的32位字元值:

 雜湊通過將“國孩”,“真的”,“很帥”的雜湊值進行想加,得到了也是一個32位字串

 

1.4.3 版權

對於很多原始檔上傳至某個平臺上時,該平臺會給原始檔設定唯一一個雜湊值,如果有盜版上傳至該平臺,會被拒絕

 

二、數字簽名

數字簽名是對原始資料的HASH值,用非對稱RSA加密

明文資料和HASH值如果通過直接傳遞就會有篡改的風險,因此我們要對資料加密。但是明文資料是比較大的,不太適合運用RSA非對稱加密,那麼資料的HASH值是比較小,這個資料如果用來校驗,這樣就完全可以使用RSA進行加密。當我們在資料傳遞的時候,可以通過將明文資料+RSA加密的校驗資料一起傳送給對方,RSA加密的校驗資料,稱之為簽名。

 

下面我們來講述一下數字簽名驗證的過程:當對方拿到資料之後,如何驗證呢?

  • 首先傳遞資料時會將原始的資料和數字簽名共同傳送
  • 對方拿到資料之後,先進行校驗,拿到了原始資料,經過同樣的HASH演算法得到資料的HASH值
  • 緊接著通過非對稱加密,將數字簽名中的校驗HASH解密出來
  • 對比兩個HASH值是否是一致的,這樣就可以很好地判斷資料是否被人篡改啦

上面是過程,下面有一份圖解:

 

三、對稱加密

對稱加密就是明文通過金鑰得到密文,然後密文通過金鑰解密得到明文。

常見演算法:

  • DES:資料加密的標準(用的比較少)
  • 3DES:(資料三次DES加密,強度增強了)
  • AES:(高階密碼標準)--鑰匙串訪問用到了

應用模式如下圖解:

總結,上面就是關於雜湊的基本內容和拓展,希望對大家對關於理解雜湊有更深的感觸!!!下一篇我們將繼續講述iOS逆向開發的另一篇----應用簽名和重簽名。