天天看點

DES、3DES 加密算法的調用

DES加密常用的概念

  • 加密模式
    • ECB模式 全稱Electronic Codebook模式,譯為電子密碼本模式
    • CBC模式 全稱Cipher Block Chaining模式,譯為密文分組連結模式
    • CFB模式 全稱Cipher FeedBack模式,譯為密文回報模式
    • OFB模式 全稱Output Feedback模式,譯為輸出回報模式。
    • CTR模式 全稱Counter模式,譯為計數器模式。
  • 初始向量

    當加密第一個明文分組時,由于不存在 “前一個密文分組”,是以需要事先準備一個長度為一個分組的比特序列來代替 “前一個密文分組”,這個比特序列稱為初始化向量(InitializationVector),通常縮寫為 IV。

  • 填充方式

    當明文長度不為分組長度的整數倍時,需要在最後一個分組中填充一些資料使其湊滿一個分組長度。

    • NoPadding

      API或算法本身不對資料進行處理,加密資料由加密雙方約定填補算法。例如若對字元串資料進行加解密,可以補充\0或者空格,然後trim

    • PKCS5Padding

      加密前:資料位元組長度對8取餘,餘數為m,若m>0,則補足8-m個位元組,位元組數值為8-m,即差幾個位元組就補幾個位元組,位元組數值即為補充的位元組數,若為0則補充8個位元組的8

      解密後:取最後一個位元組,值為m,則從資料尾部删除m個位元組,剩餘資料即為加密前的原文。

      加密字元串為為AAA,則補位為AAA55555;加密字元串為BBBBBB,則補位為BBBBBB22;加密字元串為CCCCCCCC,則補位為CCCCCCCC88888888。

    • PKCS7Padding

      PKCS7Padding 的填充方式和PKCS5Padding 填充方式一樣。隻是加密塊的位元組數不同。PKCS5Padding明确定義了加密塊是8位元組,PKCS7Padding加密快可以是1-255之間。

實作 DES 加密和解密

采用3DES、CBC模式、pkcs5padding,初始向量用key充當;另外,對于zero padding,還得約定好,對于資料長度剛好是block size的整數倍時,是否需要額外填充。

crypto/des包

Go中crypto/des包實作了 Data Encryption Standard (DES) and the Triple Data Encryption Algorithm (TDEA)。檢視該封包檔,發現相當簡單:定義了DES塊大小(8bytes),定義了一個KeySizeError。另外定義了兩個我們需要特别關注的函數,即

func NewCipher(key []byte) (cipher.Block, error)

    func NewTripleDESCipher(key []byte) (cipher.Block, error)
           

他們都是用來獲得一個cipher.Block。從名字可以很容易知道,DES使用NewCipher,3DES使用NewTripleDESCipher。參數都是密鑰(key)

加密

使用DES加密

(des.NewCipher)

,加密模式為CBC

(cipher.NewCBCEncrypter(block, key))

,填充方式

PKCS5Padding

func DesEncrypt(origData, key []byte) ([]byte, error) {

    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }
    origData = PKCS5Padding(origData, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, key)
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}
           

解密

func DesDecrypt(crypted, key []byte) ([]byte, error) {
    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }
    blockMode := cipher.NewCBCDecrypter(block, key)
    origData := make([]byte, len(crypted))
    blockMode.CryptBlocks(origData, crypted)
    origData = PKCS5UnPadding(origData)
    return origData, nil
}
           

des加密的詳細代碼

package main

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

func main() {
    key := []byte("12345678")
    result, err := DesEncrypt([]byte("hello world"), key)
    if err != nil {
        panic(err)
    }
    fmt.Println(base64.StdEncoding.EncodeToString(result))


    origData, err := DesDecrypt(result, key)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(origData))
}

func DesEncrypt(origData, key []byte) ([]byte, error) {
    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }
    origData = PKCS5Padding(origData, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, key)
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}

func DesDecrypt(crypted, key []byte) ([]byte, error) {
    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }
    blockMode := cipher.NewCBCDecrypter(block, key)
    origData := make([]byte, len(crypted))
    blockMode.CryptBlocks(origData, crypted)
    origData = PKCS5UnPadding(origData)
    return origData, nil
}

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

func PKCS5UnPadding(origData []byte) []byte {
    length := len(origData)
    // 去掉最後一個位元組 unpadding 次
    unpadding := int(origData[length])
    return origData[:(length - unpadding)]
}
           

3DES 加密及解密

加密

對比DES,發現隻是換了NewTripleDESCipher。不過,需要注意的是,密鑰長度必須24byte,否則直接傳回錯誤。

func TripleDesEncrypt(origData, key []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }
    origData = PKCS5Padding(origData, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, key[])
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}
           

主函數

func main() {
    // 3DES加解密
    key := []byte("123456789012345678901234")
    result, err := TripleDesEncrypt([]byte("hello world"), key)
    if err != nil {
        panic(err)
    }
    fmt.Println(base64.StdEncoding.EncodeToString(result))
    origData, err := TripleDesDecrypt(result, key)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(origData))
}
           

如果我們把主函數中

key

改為25位的

1234567890123456789012345

運作

go run 3des.go

,提示出現如下錯誤:

go rundes.go
panic: crypto/des: invalid key size

goroutine [running]:
           

詳細代碼

package main

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

func main() {
    // 3DES加解密
    key := []byte("123456789012345678901234")
    result, err := TripleDesEncrypt([]byte("hello world"), key)
    if err != nil {
        panic(err)
    }
    fmt.Println(base64.StdEncoding.EncodeToString(result))
    origData, err := TripleDesDecrypt(result, key)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(origData))
}


// 3DES加密
func TripleDesEncrypt(origData, key []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }
    origData = PKCS5Padding(origData, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, key[])
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}

// 3DES解密
func TripleDesDecrypt(crypted, key []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }
    blockMode := cipher.NewCBCDecrypter(block, key[])
    origData := make([]byte, len(crypted))
    blockMode.CryptBlocks(origData, crypted)
    origData = PKCS5UnPadding(origData)
    return origData, nil
}

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

func PKCS5UnPadding(origData []byte) []byte {
    length := len(origData)
    // 去掉最後一個位元組 unpadding 次
    unpadding := int(origData[length])
    return origData[:(length - unpadding)]
}
           

繼續閱讀