密碼學之對稱加密

本文將介紹比特序列運算中的異或運算,同時簡單介紹DES、3DES、AES等對稱加密算法,最后給出對應的Golang加密代碼。

源代碼

比特序列密碼

首先我們要明白二個概念,一個是計算機編碼,我們都知道計算機操作的對象并不是文字圖像,而是有0和1排列組成的比特序列;一個是異或運算(XOR), 二個不同的比特位異或運算結果是1,不同的比特位異或運算結果是0。

假設我們將01001100這個比特序列成為A,將10101010這個比特序列成為B,那么A與B的異或運算結果就如下所示:

image

由于二個相同的數進行異或運算的結果是0,因此如果將A與B異或的結果再與B進行異或預算,則結果就會變回A。


image

可能你已經發現了,上面的計算和加密、解密的步驟非常相似。

  • 將明文A與秘鑰B進行加密運算,得到密文A⊕B
  • 將密文A⊕B用秘鑰B進行解密,得到明文A

實際上,主要選擇一個合適的B,僅僅使用異或運算就可以實現一個高強度的密碼。

DES

DES(Data EncryptionStandard) 是1977年美國聯邦信息處理標準(FIPS)中所采用的一種對稱密碼(FIPS46.3)。DES一直以來被美國以及其他國家的政府和銀行等廣泛使用。然而,隨著計算機的進步,現在DES已經能夠被暴力破解,強度大不如前了。

RSA公司舉辦過破解DES密鑰的比賽(DESChallenge),我們可以看一看RSA公司官方公布的比賽結果:

  • 1997年的DES Challenge1中用了96天破譯密鑰
  • 1998年的DES ChallengeIl-I中用了41天破譯密鑰
  • 1998年的DES ChallengeII-2中用了56小時破譯密鑰
  • 1999年的DES ChallengeIll中只用了22小時15分鐘破譯密鑰

由于DES的密文可以在短時間內被破譯,因此除了用它來解密以前的密文以外,現在我們不應該再使用DES了。

加密和解密

DES是一種將64比特的明文加密成64比特的密文的對稱密碼算法, 它的密鑰長度是56比特。盡管<font color="red">從規格上來說,DES的密鑰長度是64比特,但由于每隔7比特會設置一個用于錯誤檢查的比特,因此實質上其密鑰長度是56比特</font>。

<font color="red">DES是以64比特的明文(比特序列)為一個單位來進行加密的</font>,這個64比特的單位稱為分組。一般來說,以分組為單位進行處理的密碼算法稱為分組密碼(blockcipher),DES就是分組密碼的一種。

DES每次只能加密64比特的數據,如果要加密的明文比較長,就需要對DES加密進行迭代(反復),而迭代的具體方式就稱為模式(mode)(后續文章詳細講解分組密碼的模式)。

大B -> bit

小b -> byte

秘鑰長度(56bit + 8bit)/8 = 8byte

  • DES的加密與解密 - 圖例
image

Go中對DES的操作

加解密實現思路
  • 加密 - CBC分組模式

    1. 創建并返回一個使用DES算法的cipher.Block接口
      • 秘鑰長度為64bit, 即 64/8 = 8字節(byte)
    2. 對最后一個明文分組進行數據填充
      • DES是以64比特的明文(比特序列)為一個單位來進行加密的
      • 最后一組不夠64bit, 則需要進行數據填充( 參考第三章)
    3. 創建一個密碼分組為鏈接模式的, 底層使用DES加密的BlockMode接口
    4. 加密連續的數據塊
  • 解密

    1. 創建并返回一個使用DES算法的cipher.Block接口
    2. 創建一個密碼分組為鏈接模式的, 底層使用DES解密的BlockMode接口
    3. 數據塊解密
    4. 去掉最后一組的填充數據
加解密的代碼實現

在Go中使用DES需要導入的包:

import (
    "crypto/des"
    "crypto/cipher"
    "fmt"
    "bytes"
)

DES加密代碼:

// src -> 要加密的明文
// key -> 秘鑰, 大小為: 8byte
func DesEncrypt_CBC(src, key []byte) []byte{
    // 1. 創建并返回一個使用DES算法的cipher.Block接口
    block, err := des.NewCipher(key)
    // 2. 判斷是否創建成功
    if err != nil{
        panic(err)
    }
    // 3. 對最后一個明文分組進行數據填充
    src = PKCS5Padding(src, block.BlockSize())
    // 4. 創建一個密碼分組為鏈接模式的, 底層使用DES加密的BlockMode接口
    //    參數iv的長度, 必須等于b的塊尺寸
    tmp := []byte("helloAAA")
    blackMode := cipher.NewCBCEncrypter(block, tmp)
    // 5. 加密連續的數據塊
    dst := make([]byte, len(src))
    blackMode.CryptBlocks(dst, src)

    fmt.Println("加密之后的數據: ", dst)

    // 6. 將加密數據返回
    return dst
}

DES解密代碼

// src -> 要解密的密文
// key -> 秘鑰, 和加密秘鑰相同, 大小為: 8byte
func DesDecrypt_CBC(src, key []byte) []byte {
    // 1. 創建并返回一個使用DES算法的cipher.Block接口
    block, err := des.NewCipher(key)
    // 2. 判斷是否創建成功
    if err != nil{
        panic(err)
    }
    // 3. 創建一個密碼分組為鏈接模式的, 底層使用DES解密的BlockMode接口
    tmp := []byte("helloAAA")
    blockMode := cipher.NewCBCDecrypter(block, tmp)
    // 4. 解密數據
    dst := src
    blockMode.CryptBlocks(src, dst)
    // 5. 去掉最后一組填充的數據
    dst = PKCS5UnPadding(dst)

    // 6. 返回結果
    return dst
}

最后一個分組添加填充數據和移除添加數據代碼

// 使用pks5的方式填充
func PKCS5Padding(ciphertext []byte, blockSize int) []byte{
    // 1. 計算最后一個分組缺多少個字節
    padding := blockSize - (len(ciphertext)%blockSize)
    // 2. 創建一個大小為padding的切片, 每個字節的值為padding
    padText := bytes.Repeat([]byte{byte(padding)}, padding)
    // 3. 將padText添加到原始數據的后邊, 將最后一個分組缺少的字節數補齊
    newText := append(ciphertext, padText...)
    return newText
}

// 刪除pks5填充的尾部數據
func PKCS5UnPadding(origData []byte) []byte{
    // 1. 計算數據的總長度
    length := len(origData)
    // 2. 根據填充的字節值得到填充的次數
    number := int(origData[length-1])
    // 3. 將尾部填充的number個字節去掉
    return origData[:(length-number)]
}

測試函數

func DESText() {
    // 加密
    key := []byte("11111111")
    result := DesEncrypt_CBC([]byte("床前明月光, 疑是地上霜. 舉頭望明月, 低頭思故鄉."), key)
    fmt.Println(base64.StdEncoding.EncodeToString(result))
    // 解密
    result = DesDecrypt_CBC(result, key)
    fmt.Println("解密之后的數據: ", string(result))
}

重要的函數說明

  1. 生成一個底層使用DES加/解密的Block接口對象

    函數對應的包: import "crypto/des"
    func NewCipher(key []byte) (cipher.Block, error)
     - 參數 key: des對稱加密使用的密碼, 密碼長度為64bit, 即8byte
     - 返回值 cipher.Block: 創建出的使用DES加/解密的Block接口對象
    
  2. 創建一個密碼分組為CBC模式, 底層使用b加密的BlockMode接口對象

    函數對應的包: import "crypto/cipher"
    func NewCBCEncrypter(b Block, iv []byte) BlockMode
        - 參數 b: 使用des.NewCipher函數創建出的Block接口對象
        - 參數 iv: 事先準備好的一個長度為一個分組長度的比特序列, 每個分組為64bit, 即8byte
        - 返回值: 得到的BlockMode接口對象
    
  3. 使用cipher包的BlockMode接口對象對數據進行加/解密

    接口對應的包: import "crypto/cipher"
    type BlockMode interface {
        // 返回加密字節塊的大小
        BlockSize() int
        // 加密或解密連續的數據塊,src的尺寸必須是塊大小的整數倍,src和dst可指向同一內存地址
        CryptBlocks(dst, src []byte)
    }
    接口中的 CryptBlocks(dst, src []byte) 方法:
        - 參數 dst: 傳出參數, 存儲加密或解密運算之后的結果 
        - 參數 src: 傳入參數, 需要進行加密或解密的數據切片(字符串)
    
  4. 創建一個密碼分組為CBC模式, 底層使用b解密的BlockMode接口對象

    函數對應的包: import "crypto/cipher"
    func NewCBCDecrypter(b Block, iv []byte) BlockMode
        - 參數 b: 使用des.NewCipher函數創建出的Block接口對象
        - 參數 iv: 事先準備好的一個長度為一個分組長度的比特序列, 每個分組為64bit, 即8byte, 
                   該序列的值需要和NewCBCEncrypter函數的第二個參數iv值相同
        - 返回值: 得到的BlockMode接口對象
    
  5. 自定義函數介紹

    對稱加密加密需要對數據進行分組, 保證每個分組的數據長度相等, 如果最后一個分組長度不夠, 需要進行填充
    func PKCS5Padding(ciphertext []byte, blockSize int) []byte
        - 參數 ciphertext: 需要加密的原始數據
        - 參數 blockSize: 每個分組的長度, 跟使用的加密算法有關系
         * des:64bit, 8byte
         * 3des:64bit, 8byte
         * aes: 128bit, 16byte
    

三重DES

現在DES已經可以在現實的時間內被暴力破解,因此我們需要一種用來替代DES的分組密碼,三重DES就是出于這個目的被開發出來的。

三重DES(triple-DES)是為了增加DES的強度,將DES重復3次所得到的一種密碼算法,通??s寫為3DES。

三重DES的加密

三重DES的加解密機制如下圖所示:

加->解->加 -> 目的是為了兼容DES(如果三個秘鑰都一樣)

3des秘鑰長度24字節 = 1234567a 1234567b 1234567a

明文: 10

秘鑰1: 2

秘鑰2: 3

秘鑰3: 4

加密算法: 明文+秘鑰

解密算法: 密文-秘鑰

10+2-3+4

image

)


image

明文經過三次DES處理才能變成最后的密文,由于DES密鑰的長度實質上是56比特,因此<font color="red">三重DES的密鑰長度就是56×3=168比特, 加上用于錯誤檢測的標志位8x3, 共192bit</font>。

從上圖我們可以發現,三重DES并不是進行三次DES加密(加密-->加密-->加密),而是<font color="red">加密-->解密-->加密</font>的過程。在加密算法中加人解密操作讓人感覺很不可思議,實際上這個方法是IBM公司設計出來的,目的是為了讓三重DES能夠兼容普通的DES。

<font color="red">當三重DES中所有的密鑰都相同時,三重DES也就等同于普通的DES了</font>。這是因為在前兩步加密-->解密之后,得到的就是最初的明文。因此,以前用DES加密的密文,就可以通過這種方式用三重DES來進行解密。也就是說,三重DES對DES具備向下兼容性。

如果密鑰1和密鑰3使用相同的密鑰,而密鑰2使用不同的密鑰(也就是只使用兩個DES密鑰),這種三重DES就稱為DES-EDE2。EDE表示的是加密(Encryption) -->解密(Decryption)-->加密(Encryption)這個流程。

密鑰1、密鑰2、密鑰3全部使用不同的比特序列的三重DES稱為DES-EDE3。

盡管三重DES目前還被銀行等機構使用,但其處理速度不高,而且在安全性方面也逐漸顯現出了一些問題。

Go中對3DES的操作

加解密實現思路
  • 加密 - CBC分組模式

    1. 創建并返回一個使用3DES算法的cipher.Block接口
      • 秘鑰長度為64bit3=192bit, 即 192/8 = 24字節(byte)*
    2. 對最后一個明文分組進行數據填充
      • 3DES是以64比特的明文(比特序列)為一個單位來進行加密的
      • 最后一組不夠64bit, 則需要進行數據填充( 參考第三章)
    3. 創建一個密碼分組為鏈接模式的, 底層使用3DES加密的BlockMode接口
    4. 加密連續的數據塊
  • 解密

    1. 創建并返回一個使用3DES算法的cipher.Block接口
    2. 創建一個密碼分組為鏈接模式的, 底層使用3DES解密的BlockMode接口
    3. 數據塊解密
    4. 去掉最后一組的填充數據
加解密的代碼實現

3DES加密代碼

// 3DES加密
func TripleDESEncrypt(src, key []byte) []byte {
    // 1. 創建并返回一個使用3DES算法的cipher.Block接口
    block, err := des.NewTripleDESCipher(key)
    if err != nil{
        panic(err)
    }
    // 2. 對最后一組明文進行填充
    src = PKCS5Padding(src, block.BlockSize())
    // 3. 創建一個密碼分組為鏈接模式, 底層使用3DES加密的BlockMode模型
    blockMode := cipher.NewCBCEncrypter(block, key[:8])
    // 4. 加密數據
    dst := src
    blockMode.CryptBlocks(dst, src)
    return dst
}

3DES解密代碼

// 3DES解密
func TripleDESDecrypt(src, key []byte) []byte {
    // 1. 創建3DES算法的Block接口對象
    block, err := des.NewTripleDESCipher(key)
    if err != nil{
        panic(err)
    }
    // 2. 創建密碼分組為鏈接模式, 底層使用3DES解密的BlockMode模型
    blockMode := cipher.NewCBCDecrypter(block, key[:8])
    // 3. 解密
    dst := src
    blockMode.CryptBlocks(dst, src)
    // 4. 去掉尾部填充的數據
    dst = PKCS5UnPadding(dst)
    return dst
}

重要的函數說明

  1. 生成一個底層使用3DES加/解密的Block接口對象

    函數對應的包: import "crypto/des"
    func NewTripleDESCipher(key []byte) (cipher.Block, error)
     - 參數 key: 3des對稱加密使用的密碼, 密碼長度為(64*3)bit, 即(8*3)byte
     - 返回值 cipher.Block: 創建出的使用DES加/解密的Block接口對象
    
  2. 創建一個密碼分組為CBC模式, 底層使用b加密的BlockMode接口對象

    函數對應的包: import "crypto/cipher"
    func NewCBCEncrypter(b Block, iv []byte) BlockMode
        - 參數 b: 使用des.NewTripleDESCipher 函數創建出的Block接口對象
        - 參數 iv: 事先準備好的一個長度為一個分組長度的比特序列, 每個分組為64bit, 即8byte
        - 返回值: 得到的BlockMode接口對象
    
  3. 使用cipher包的BlockMode接口對象對數據進行加/解密

    接口對應的包: import "crypto/cipher"
    type BlockMode interface {
        // 返回加密字節塊的大小
        BlockSize() int
        // 加密或解密連續的數據塊,src的尺寸必須是塊大小的整數倍,src和dst可指向同一內存地址
        CryptBlocks(dst, src []byte)
    }
    接口中的 CryptBlocks(dst, src []byte) 方法:
        - 參數 dst: 傳出參數, 存儲加密或解密運算之后的結果 
        - 參數 src: 傳入參數, 需要進行加密或解密的數據切片(字符串)
    
  4. 創建一個密碼分組為CBC模式, 底層使用b解密的BlockMode接口對象

    函數對應的包: import "crypto/cipher"
    func NewCBCDecrypter(b Block, iv []byte) BlockMode
        - 參數 b: 使用des.NewTripleDESCipher 函數創建出的Block接口對象
        - 參數 iv: 事先準備好的一個長度為一個分組長度的比特序列, 每個分組為64bit, 即8byte, 
                   該序列的值需要和NewCBCEncrypter函數的第二個參數iv值相同
        - 返回值: 得到的BlockMode接口對象
    

AES

AES(Advanced Encryption Standard)是取代其前任標準(DES)而成為新標準的一種對稱密碼算法。全世界的企業和密碼學家提交了多個對稱密碼算法作為AES的候選,最終在2000年從這些候選算法中選出了一種名為Rijndael的對稱密碼算法,并將其確定為了AES。

Rijndael的分組長度為128比特,密鑰長度可以以32比特為單位在128比特到256比特的范圍內進行選擇(不過在AES的規格中,密鑰長度只有128、192和256比特三種)。

  • 128bit = 16字節
  • 192bit = 24字節
  • 256bit = 32字節

在go提供的接口中秘鑰長度只能是16字節

AES的加密和解密

和DES—樣,AES算法也是由多輪所構成的,下圖展示了每一輪的大致計算步驟。DES使用Feistel網絡作為其基本結構,而AES沒有使用Feistel網絡,而是使用了SPN Rijndael的輸人分組為128比特,也就是16字節。首先,需要逐個字節地對16字節的輸入數據進行SubBytes處理。所謂SubBytes,就是以每個字節的值(0~255中的任意值)為索引,從一張擁有256個值的替換表(S-Box)中查找出對應值的處理,也是說,將一個1字節的值替換成另一個1字節的值。

SubBytes之后需要進行ShiftRows處理,即將SubBytes的輸出以字節為單位進行打亂處理。從下圖的線我們可以看出,這種打亂處理是有規律的。

ShiftRows之后需要進行MixCo1umns處理,即對一個4字節的值進行比特運算,將其變為另外一個4字節值。

最后,需要將MixColumns的輸出與輪密鑰進行XOR,即進行AddRoundKey處理。到這里,AES的一輪就結東了。實際上,在AES中需要重復進行10 ~ 14輪計算。

通過上面的結構我們可以發現輸入的所有比特在一輪中都會被加密。和每一輪都只加密一半輸人的比特的Feistel網絡相比,這種方式的優勢在于加密所需要的輪數更少。此外,這種方式還有一個優勢,即SubBytes,ShiftRows和MixColumns可以分別按字節、行和列為單位進行并行計算。

image
  • SubBytes -- 字節代換
  • ShiftRows -- 行移位代換
  • MixColumns -- 列混淆
  • AddRoundKey -- 輪密鑰加

下圖展示了AES中一輪的解密過程。從圖中我們可以看出,SubBytes、ShiftRows、MixColumns分別存在反向運算InvSubBytes、InvShiftRows、InvMixColumns,這是因為AES不像Feistel網絡一樣能夠用同一種結構實現加密和解密。

image
  • InvSubBytes -- 逆字節替代
  • InvShiftRows -- 逆行移位
  • InvMixColumns -- 逆列混淆

Go中對AES的使用

加解密實現思路
  • 加密 - CBC分組模式

    1. 創建并返回一個使用AES算法的cipher.Block接口
      • 秘鑰長度為128bit, 即 128/8 = 16字節(byte)
    2. 對最后一個明文分組進行數據填充
      • AES是以128比特的明文(比特序列)為一個單位來進行加密的
      • 最后一組不夠128bit, 則需要進行數據填充( 參考第三章)
    3. 創建一個密碼分組為鏈接模式的, 底層使用AES加密的BlockMode接口
    4. 加密連續的數據塊
  • 解密

    1. 創建并返回一個使用AES算法的cipher.Block接口
    2. 創建一個密碼分組為鏈接模式的, 底層使用AES解密的BlockMode接口
    3. 數據塊解密
    4. 去掉最后一組的填充數據
加解密的代碼實現

AES加密代碼

// AES加密
func AESEncrypt(src, key []byte) []byte{
    // 1. 創建一個使用AES加密的塊對象
    block, err := aes.NewCipher(key)
    if err != nil{
        panic(err)
    }
    // 2. 最后一個分組進行數據填充
    src = PKCS5Padding(src, block.BlockSize())
    // 3. 創建一個分組為鏈接模式, 底層使用AES加密的塊模型對象
    blockMode := cipher.NewCBCEncrypter(block, key[:block.BlockSize()])
    // 4. 加密
    dst := src
    blockMode.CryptBlocks(dst, src)
    return dst
}

AES解密

// AES解密
func AESDecrypt(src, key []byte) []byte{
    // 1. 創建一個使用AES解密的塊對象
    block, err := aes.NewCipher(key)
    if err != nil{
        panic(err)
    }
    // 2. 創建分組為鏈接模式, 底層使用AES的解密模型對象
    blockMode := cipher.NewCBCDecrypter(block, key[:block.BlockSize()])
    // 3. 解密
    dst := src
    blockMode.CryptBlocks(dst, src)
    // 4. 去掉尾部填充的字
    dst = PKCS5UnPadding(dst)
    return dst
}

重要的函數說明

  1. 生成一個底層使用AES加/解密的Block接口對象

    函數對應的包: import "crypto/aes"
    func NewCipher(key []byte) (cipher.Block, error)
     - 參數 key: aes對稱加密使用的密碼, 密碼長度為128bit, 即16byte
     - 返回值 cipher.Block: 創建出的使用AES加/解密的Block接口對象
    
  2. 創建一個密碼分組為CBC模式, 底層使用b加密的BlockMode接口對象

    函數對應的包: import "crypto/cipher"
    func NewCBCEncrypter(b Block, iv []byte) BlockMode
        - 參數 b: 使用aes.NewCipher函數創建出的Block接口對象
        - 參數 iv: 事先準備好的一個長度為一個分組長度的比特序列, 每個分組為64bit, 即8byte
        - 返回值: 得到的BlockMode接口對象
    
  3. 使用cipher包的BlockMode接口對象對數據進行加/解密

    接口對應的包: import "crypto/cipher"
    type BlockMode interface {
        // 返回加密字節塊的大小
        BlockSize() int
        // 加密或解密連續的數據塊,src的尺寸必須是塊大小的整數倍,src和dst可指向同一內存地址
        CryptBlocks(dst, src []byte)
    }
    接口中的 CryptBlocks(dst, src []byte) 方法:
        - 參數 dst: 傳出參數, 存儲加密或解密運算之后的結果 
        - 參數 src: 傳入參數, 需要進行加密或解密的數據切片(字符串)
    
  4. 創建一個密碼分組為CBC模式, 底層使用b解密的BlockMode接口對象

    函數對應的包: import "crypto/cipher"
    func NewCBCDecrypter(b Block, iv []byte) BlockMode
        - 參數 b: 使用des.NewCipher函數創建出的Block接口對象
        - 參數 iv: 事先準備好的一個長度為一個分組長度的比特序列, 每個分組為128bit, 即16byte, 
                   該序列的值需要和NewCBCEncrypter函數的第二個參數iv值相同
        - 返回值: 得到的BlockMode接口對象
    

應選擇哪種對稱加密

前面介紹了DES、三重DES和AES等對稱密碼,那么我們到底應該使用哪一種對稱密碼算法呢?

  1. 今后最好不要將DES用于新的用途,因為隨著計算機技術的進步,現在用暴力破解法已經能夠在現實的時間內完成對DES的破譯。但是,在某些情況下也需要保持與舊版本軟件的兼容性。
  2. 出于兼容性的因素三重DES在今后還會使用一段時間,但會逐漸被AES所取代。
  3. 今后大家應該使用的算法是AES(Rijndael),因為它安全、快速,而且能夠在各種平臺上工作。此外,由于全世界的密碼學家都在對AES進行不斷的驗證,因此即便萬一發現它有什么缺陷,也會立刻告知全世界并修復這些缺陷。

一般來說,我們不應該使用任何自制的密碼算法,而是應該使用AES。因為AES在其選定過程中,經過了全世界密碼學家所進行的高品質的驗證工作,而對于自制的密碼算法則很難進行這樣的驗證。

歡迎與我交流


image

本篇文章由一文多發平臺ArtiPub自動發布

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372