Golang DES 加解密

【導讀】本文介紹了 DES 加密原理和作用,和 golang 中 DES 加密解密機制的相應實現。

概念理解

DES 是以 64 比特的明文爲一個單位來進行加密,並生成 64 比特的密文。由於它每次只能處理特定長度的一塊數據,所以 DES 屬於分組密碼算法。cypto/des包提供了有關 des 加密的功能。

模式

由於分組密碼算法只能加密固定長度的分組,所以當加密的明文超過分組密碼的長度時,就需要對分組密碼算法進行迭代,而迭代的方法就稱爲分組密碼的模式。模式主要有 ECB(電子密碼本)、CBC(密碼分組鏈接模式)、CTR(計數器模式)、OFB(輸出反饋模式)、CFB(密碼反饋模式) 五種。下面簡單介紹下前兩種:

  1. ECB(electronic code book) 是最簡單的方式,它將明文分組加密後的結果直接成爲密文分組。
    優缺點:模式操作簡單;明文中的重複內容將在密文中表現出來,特別對於圖像數據和明文變化較少的數據;適於短報文的加密傳遞。

  2. CBC(cipher block chaining) 的原理是加密算法的輸入是當前的明文分組和前一密文分組的異或,第一個明文分組和一個初始向量進行異或,這樣同一個明文分組重複出現時會產生不同的密文分組。
    特點:同一個明文分組重複出現時產生不同的密文分組;加密函數的輸入是當前的明文分組和前一個密文分組的異或;每個明文分組的加密函數的輸入與明文分組之間不再有固定的關係;適合加密長消息。

填充方式

在按 8 個字節對 DES 進行加密或解密時,如果最後一段字節不足 8 位,就需要對數據進行補位。即使加密或解密的數據剛好是 8 的倍數時,也會再補 8 位。舉個栗子,如果末尾剛好出現 1,這時你就無法判斷這個 1 是原來數據,還是經過補位得到的 1。因此,可以再補 8 位進行標識。填充方式主要有以下幾種:pkcs7padding、pkcs5padding、zeropadding、iso10126、ansix923。

  1. pkcs7padding 和 pkcs5padding 的填充方式相同,填充字節的值都等於填充字節的個數。例如需要填充 4 個字節,則填充的值爲 "4 4 4 4"。

  2. zeropadding 填充字節的值都爲 0。

密碼

DES 的密鑰長度是 64 比特,但由於每隔 7 個比特會設置一個用於錯誤檢測的比特,因此其實質密鑰長度爲 56 比特。

偏移量

上面模式中,例如 CBC,再加密第一個明文分組時,由於不存在 “前一個密文分組”,因此需要事先準備一個長度爲一個分組的比特序列來代替 “前一個密文分組”,這個比特序列成爲初始化向量,也稱偏移量,通常縮寫爲 IV。一般來說,每次加密時都會隨機產生一個不同的比特序列來作爲初始化向量。偏移量的長度必須和塊的大小相同。

輸出

加密後的字節在顯示時可以進行 hex 和 base64 編碼,hex 是十六進制編碼,base64 是一種基於 64 個可打印字符來標識二進制數據的方法。

下面以上面提到的幾種模式和填充方式爲例,進行演示如何在代碼中使用。

加密模式採用 ECB、填充方式採用 pkcs5padding、密碼使用 "12345678", 輸出時經 hex 編碼。自己可以通過一些在線測試工具進行測試,看結果是否一致。

package main

import (
 "crypto/des"
 "qiniupkg.com/x/errors.v7"
 "bytes"
 "fmt"
 "encoding/hex"
)

func main() {
 data:=[]byte("hello world")
 key:=[]byte("12345678")
 result,err:=DesECBEncrypt(data,key)
 if err != nil {
  fmt.Println(err)
 }
 a:=hex.EncodeToString(result)
 fmt.Println(a)
}

func DesECBEncrypt(data, key []byte) ([]byte, error) {
    //NewCipher創建一個新的加密塊
 block, err := des.NewCipher(key)
 if err != nil {
  return nil, err
 }

 bs := block.BlockSize()
 data = Pkcs5Padding(data, bs)
 if len(data)%bs != 0 {
  return nil, errors.New("need a multiple of the blocksize")
 }

 out := make([]byte, len(data))
 dst := out
 for len(data) > 0 {
        //Encrypt加密第一個塊,將其結果保存到dst
  block.Encrypt(dst, data[:bs])
  data = data[bs:]
  dst = dst[bs:]
 }
 return out, nil
}

func Pkcs5Padding(ciphertext []byte, blockSize int) []byte {
 padding := blockSize - len(ciphertext)%blockSize
 padtext := bytes.Repeat([]byte{byte(padding)}, padding)
 return append(ciphertext, padtext...)
}

下面加密模式採用 CBC、填充方式採用 pkcs5padding、密碼使用 "12345678"、偏移量 "43218765",輸出時以 hex 方式輸出。自己可以通過一些在線測試工具進行測試,看結果是否一致。

package main

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

func main() {
 data := []byte("hello world")
 key := []byte("12345678")
 iv := []byte("43218765")

 result, err := DesCBCEncrypt(data, key, iv)
 if err != nil {
  fmt.Println(err)
 }
 b := hex.EncodeToString(result)
 fmt.Println(b)
}

func DesCBCEncrypt(data, key, iv []byte) ([]byte, error) {
 block, err := des.NewCipher(key)
 if err != nil {
  return nil, err
 }

 data = pkcs5Padding(data, block.BlockSize())
 cryptText := make([]byte, len(data))

 blockMode := cipher.NewCBCEncrypter(block, iv)
 blockMode.CryptBlocks(cryptText, data)
 return cryptText, nil
}

func pkcs5Padding(cipherText []byte, blockSize int) []byte {
 padding := blockSize - len(cipherText)%blockSize
 padText := bytes.Repeat([]byte{byte(padding)}, padding)
 return append(cipherText, padText...)
}

第三方包

github.com/marspere/goencrypt包實現了多種加密算法,包括對稱加密和非對稱加密等。

package main

import (
 "fmt"
 "github.com/marspere/goencrypt"
)

func main() {
 // key爲12345678
 // iv爲空
 // 採用ECB分組模式
 // 採用pkcs5padding填充模式
 // 輸出結果使用base64進行加密
 cipher := goencrypt.NewDESCipher([]byte("12345678")[]byte(""), goencrypt.ECBMode, goencrypt.Pkcs5, goencrypt.PrintBase64)
 cipherText, err := cipher.DESEncrypt([]byte("hello world"))
 if err != nil {
  fmt.Println(err)
  return
 }
 fmt.Println(cipherText)
}

轉自:benben_2015

blog.csdn.net/benben_2015/article/details/81254023

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3JVPWF_EEr8r6Q41ACJopQ