6 Go 密碼學(三)對稱加密 DES、TDES、AES

一、對稱加密概述

我們在開發中常會遇到這種需求:通信的兩端需要傳輸安全級別較高的數據,這需要我們傳輸的加密數據既要難以破解,又要可逆的解密過程。哈希算法雖難以破解,但并非適用于通信中的加解密傳輸。這就需要基于秘鑰管理的加密技術了。對稱加密是最簡單快速的加密方式,所謂對稱加密,即加解密雙方都掌握相同的秘鑰,通過同一秘鑰完成加解密操作。常用的對稱加密算法有:

  • DES:Data Encryption Standard,即數據加密標準;
  • TDES(TripleDES) :三重DES;
  • AES:Rijndael加密法,Advanced Encryption Standard高級加密標準。

Go標準庫中的加密相關包都有相應的支持,下面我們來逐一演示:

二、DES對稱加密

DES算法原理:明文按64位進行分組,密鑰長64位,密鑰事實上是56位參與DES運算(第8、16、24、32、40、48、56、64位是校驗位, 使得每個密鑰都有奇數個1)分組后的明文組和56位的密鑰按位替代或交換的方法形成密文組的加密方法。

所以要實現DES加密需要準備如下要求:

  • ==準備一個8字節(64Bit)的秘鑰==;
  • 原文需要按8字節(64Bit)長度進行分組,共有五種分組模式,此處我們不具體展開,我們用常用的CBC(密碼分組鏈接模式),這種模式需要一個與分組長度相同的初始化向量,有興趣的可搜索了解其他模式;
  • 對末尾的未夠8字節的數據進行填充,把原文轉為64Bit的整數倍;
  • ==實現加密時的未位分組填充算法和解密時的刪除密文末尾分組算法。==

以上,秘鑰和初始化向量值需要自己管理,末尾分組的填充算法和刪除算法需要自己實現。下面我們實現一個填充算法和刪除算法:

//填充明文最后一個分組工具方法
//src - 原始數據
//blockSize - 每個分組的數據長度
func paddingBytes(src []byte, blockSize int) []byte {
    //1.求出最后一個分組要填充多個字節
    padding := blockSize - len(src)%blockSize
    //2.創建新的切片,切片的字節數為填充的字節數,并初始化化,每個字節的值為填充的字節數
    padBytes := bytes.Repeat([]byte{byte(padding)}, padding)
    //3.將創建出的新切片和原始數據進行連接
    newBytes := append(src, padBytes...)

    //4.返回新的字符串
    return newBytes
}

//刪除密文末尾分組填充的工具方法
func unPaddingBytes(src []byte) []byte {
    //1.求出要處理的切片的長度
    l := len(src)
    //2.取出最后一個字符,得到其整型值
    n := int(src[l-1])

    //3.將切片末尾的number個字節刪除
    return src[:l-n]
}

以上實現思路為:計算出最后一個分組需要填充的字節數(1-8),并把這個數作為需要填充的占位符,再把填充后的分組與源數據拼接就得到待加密的字符串。

Go通過crypto/des包支持DES加密算法:


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

const (
    MYKEY = "abcdefgh" //八字節密匙
    IV    = "aaaabbbb" //CBC模式的初始化向量
)



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

    return dst
}

//使用des進行解密
func DecryptDES(src, key []byte) []byte {
    //1. 創建并返回一個使用DES算法的cipher.Block接口
    block, err := des.NewCipher(key)
    if err != nil {
        panic(err)
    }
    //2. 創建一個密碼分組為鏈接模式的,底層使用DES解密的BlockMode接口
    cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
    //3. 數據塊解密
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)
    //4. 去掉最后一組填充數據
    newBytes := unPaddingBytes(dst)
    return newBytes
}

使用以上加解密方法:

//測試DES加解密算法
func TestDES() {
    fmt.Println("測試DES對稱加密:")
    srcString := "GO 密碼學 —— DES對稱加密實現"
    //加密
    cryptBytes := myDES.EncryptDES([]byte(srcString), []byte(myDES.MYKEY))
    fmt.Println("加密效果:")
    fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte長度:", len([]byte(srcString)))
    fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
    fmt.Println()

    //解密
    deCryptBytes := myDES.DecryptDES(cryptBytes, []byte(myDES.MYKEY))
    fmt.Println("解密效果:")
    fmt.Println("src :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
    fmt.Println("dst :", hex.EncodeToString(deCryptBytes), "[]byte長度:", len(deCryptBytes))

    fmt.Println()
    fmt.Println("原字串為:" + srcString)
    fmt.Println("解密字符為:" + string(deCryptBytes))
}

//OUTPUT:
測試DES對稱加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e2809420444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41
dst : e50636c0545eb031b3b36d363c050deb0640806475b04da4e5b54641eb635b1394d70f86c12f4119319904475235d441 []byte長度: 48

解密效果:
src : e50636c0545eb031b3b36d363c050deb0640806475b04da4e5b54641eb635b1394d70f86c12f4119319904475235d441 []byte長度: 48
dst : 474f20e5af86e7a081e5ada620e28094e2809420444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41

原字串為:GO 密碼學 —— DES對稱加密實現
解密字符為:GO 密碼學 —— DES對稱加密實現

以上就是Go 中DES的加解密實現,可見其加密的安全性依賴于秘鑰的管理,而且其只有64Bit的秘鑰長度在算力越來越高的現在已經不夠安全了,這也是對稱加密的弱點,在實際的網絡項目中,秘鑰的分發需要依賴其他方式的配合。

三、TDES(TripleDES)對稱加密

對于DES秘鑰較弱的問題做了一些改進,這就是三重DES加密算法,其原理與DES類似,唯一不同的是秘鑰從一組八字節變成三組二十四字節(192Bit)。其加密強度也和秘鑰設置相關,此處演示我們也用CBC分組模式,三重加密執行順序分別為,加密->解密->加密。

為了兼容DES,只有當秘鑰一組和秘鑰二組相同時,3DES其加密安全性和DES相同,其余秘鑰的分組設置都是三重DES加密。

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

const (
    MYKEY = "abcdefgh12345678ABCDEFGH" //三組八字節密匙,即24字節
    IV    = "aaaabbbb"                 //CBC模式的初始化向量,8字節
)

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

    return dst
}

//使用3des進行解密
func DecryptTDES(src, key []byte) []byte {
    //1. 創建并返回一個使用DES算法的cipher.Block接口
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        panic(err)
    }
    //2. 創建一個密碼分組為鏈接模式的,底層使用DES解密的BlockMode接口
    cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
    //3. 數據塊解密
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)
    //4. 去掉最后一組填充數據,和DES的刪除算法一樣
    newBytes := unPaddingBytes(dst)
    return newBytes
}

使用以上加解密方法:

//測試三重DES對稱加密算法
func TestTDES() {
    fmt.Println("測試TDES對稱加密:")
    srcString := "GO 密碼學 —— TDES對稱加密實現"
    //加密
    cryptBytes := myTDES.EncryptTDES([]byte(srcString), []byte(myTDES.MYKEY))
    fmt.Println("加密效果:")
    fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte長度:", len([]byte(srcString)))
    fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
    fmt.Println()

    //解密
    deCryptBytes := myTDES.DecryptTDES(cryptBytes, []byte(my3DES.MYKEY))
    fmt.Println("解密效果:")
    fmt.Println("src bytes:", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
    fmt.Println("dst bytes:", hex.EncodeToString(deCryptBytes), "[]byte長度:", len(deCryptBytes))

    fmt.Println()
    fmt.Println("原字串為:" + srcString)
    fmt.Println("解密字符為:" + string(deCryptBytes))
}

//OUTPUT:
測試TDES對稱加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e280942033444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 42
dst : 41da5be3a5b9f05438c2896173d141f43c7b12168e80ef299b35a46b76799ab0d3c573f1fc544a6ec3a135f4d5b912ba []byte長度: 48

解密效果:
src bytes: 41da5be3a5b9f05438c2896173d141f43c7b12168e80ef299b35a46b76799ab0d3c573f1fc544a6ec3a135f4d5b912ba []byte長度: 48
dst bytes: 474f20e5af86e7a081e5ada620e28094e280942033444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 42

原字串為:GO 密碼學 —— TDES對稱加密實現
解密字符為:GO 密碼學 —— TDES對稱加密實現

四、AES對稱加密

隨著時代的發展,DES和3DES于現今的算力和性能要求來說已經逐漸落伍,為了適應更高的安全性和性能需要,業界采用的一種區塊加密標準。這個標準用來替代原先的DES,已經被多方分析且廣為全世界所使用。這就是AES對稱加密算法,在現今的項目開發中,建議使用這種加密方式。

AES的基本要求是,采用對稱分組密碼體制,密鑰的長度最少支持為128、192、256,分組長度128位。

所以要實現AES加密需要準備如下要求:

  • ==準備一個十六字節(128Bit)的秘鑰==;
  • 原文需要按十六字節(128Bit)長度進行分組,采用常用的CBC(密碼分組鏈接模式),準備十六字節(128Bit)長度的初始化向量值;
  • ==實現加密時的未位分組填充算法和解密時的刪除密文末尾分組算法。這里的填充和刪除算法和DES相同,不再贅述==

Go通過crypto/aes包支持DES加密算法:


import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
)

const (
    MYKEY = "abcdefgh12345678" //十六字節密匙
    IV    = "aaaabbbb12345678" //CBC模式的初始化向量:與key等長:十六字節
)



//使用aes進行對稱加密
func EncryptAES(src, key []byte) []byte {
    //1. 創建并返回一個使用DES算法的cipher.Block接口
    block, err := aes.NewCipher([]byte(key))
    if err != nil {
        panic(err)
    }
    //2. 對最后一個明文分組進行數據填充
    src = paddingBytes(src, block.BlockSize())
    //3. 創建一個密碼分組為鏈接模式的,底層使用DES加密的BlockMode接口
    cbcDecrypter := cipher.NewCBCEncrypter(block, []byte(IV))
    //4. 加密連續的數據并返回
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)

    return dst
}

//使用aes進行解密
func DecryptAES(src, key []byte) []byte {
    //1. 創建并返回一個使用DES算法的cipher.Block接口
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    //2. 創建一個密碼分組為鏈接模式的,底層使用DES解密的BlockMode接口
    cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
    //3. 數據塊解密
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)
    //4. 去掉最后一組填充數據
    newBytes := unPaddingBytes(dst)
    return newBytes
}

以上,其用法和DES差不多,只不過秘鑰管理稍微不同而已。

//測試AES對稱加密算法
func TestAES() {
    fmt.Println("測試AES對稱加密:")
    srcString := "GO 密碼學 —— AES對稱加密實現"
    //加密
    cryptBytes := myAES.EncryptAES([]byte(srcString), []byte(myAES.MYKEY))
    fmt.Println("加密效果:")
    fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte長度:", len([]byte(srcString)))
    fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
    fmt.Println()

    //解密
    deCryptBytes := myAES.DecryptAES(cryptBytes, []byte(myAES.MYKEY))
    fmt.Println("解密效果:")
    fmt.Println("src bytes:", hex.EncodeToString(cryptBytes), "[]byte長度:", len(cryptBytes))
    fmt.Println("dst bytes:", hex.EncodeToString(deCryptBytes), "[]byte長度:", len(deCryptBytes))

    fmt.Println()
    fmt.Println("原字串為:" + srcString)
    fmt.Println("解密字符為:" + string(deCryptBytes))
}

//OUTPUT:
測試AES對稱加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e2809420414553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41
dst : 07a22e3059c8e9da2a3c1db2801b478a18f3ca7b33ace2f19931d2100a792681c61ac7ff3a61e0973be91638b3e9b1e1 []byte長度: 48

解密效果:
src bytes: 07a22e3059c8e9da2a3c1db2801b478a18f3ca7b33ace2f19931d2100a792681c61ac7ff3a61e0973be91638b3e9b1e1 []byte長度: 48
dst bytes: 474f20e5af86e7a081e5ada620e28094e2809420414553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte長度: 41

原字串為:GO 密碼學 —— AES對稱加密實現
解密字符為:GO 密碼學 —— AES對稱加密實現
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容