Go語言 Go加密之DES

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

接著RSA加密解密,我們繼續來看看DES的加密解密

一、DES簡介

DES(Data Encryption Standard)是對稱式加密演算法,也就是加密和解密用相同的密鑰。其入口參數有三個:key、data、mode。key為加密解密使用的密鑰,data為加密解密的資料,mode為其工作模式。當模式為加密模式時,明文按照64位進行分組,形成明文組,key用於對資料加密,當模式為解密模式時,key用於對資料解密。實際運用中,密鑰只用到了64位中的56位,這樣才具有高的安全性。DES 的常見變體是三重 DES,使用 168 位的金鑰組資料進行三次加密的一種機制;它通常(但非始終)提供極其強大的安全性。如果三個 56 位的子項目都相同,則三重 DES 向後相容 DES。

DES加密,涉及到加密模式和填充方式,所以,和其他語言加解密時,應該約定好加密模式和填充方式。(模式定義了Cipher如何應用密碼編譯演算法。改變模式可以容許一個塊加密程式變為流加密程式。)

關於區塊編碼器:分組密碼每次加密一個資料分組,這個分組的位元可以是隨意的,一般選擇64或者128位。另一方面,流加密程式每次可以加密或解密一個位元組的資料,這就使它比流加密的應用程式更為有用。

在用DES加密解密時,經常會涉及到一個概念:塊(block,也叫分組),模式(比如cbc),初始向量(iv),填充方式(padding,包括none,用’\0′填充,pkcs5padding或pkcs7padding)。多語言加密解密互動時,需要確定好這些。比如這麼定:

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

二、Go DES加密解密
1、crypto/des包

Go中crypto/des包實現了 Data Encryption Standard (DES) and the Triple Data Encryption Algorithm (TDEA)。查看該包文檔,發現相當簡單:
定義了DES塊大小(8bytes),定義了一個KeySizeError。另外定義了兩個我們需要特別關注的函數,即
1        func NewCipher(key []byte) (cipher.Block, error)
2        func NewTripleDESCipher(key []byte) (cipher.Block, error)

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

那麼,cipher這個包是幹嘛用的呢?它實現了標準的塊加密模式。我們看一下cipher.Block
1        type Block interface {
2            // BlockSize returns the cipher's block size.
3            BlockSize() int
4         
5            // Encrypt encrypts the first block in src into dst.
6            // Dst and src may point at the same memory.
7            Encrypt(dst, src []byte)
8         
9            // Decrypt decrypts the first block in src into dst.
10            // Dst and src may point at the same memory.
11            Decrypt(dst, src []byte)
12        }

這是一個介面

對稱式加密,按塊方式,我們經常見到CBC、ECB之類的,這些是加密模式。可以參考:DES加密模式詳解 http://linux.bokee.com/6956594.html
Go中定義了一個介面BlockMode代表各種模式
1        type BlockMode interface {
2            // BlockSize returns the mode's block size.
3            BlockSize() int
4         
5            // CryptBlocks encrypts or decrypts a number of blocks. The length of
6            // src must be a multiple of the block size. Dst and src may point to
7            // the same memory.
8            CryptBlocks(dst, src []byte)
9        }

該包還提供了擷取BlockMode執行個體的兩個方法
1        func NewCBCDecrypter(b Block, iv []byte) BlockMode
2        func NewCBCEncrypter(b Block, iv []byte) BlockMode

即一個CBC加密,一個CBC解密

對於按流方式加密的,定義了一個介面:
1        type Stream interface {
2            // XORKeyStream XORs each byte in the given slice with a byte from the
3            // cipher's key stream. Dst and src may point to the same memory.
4            XORKeyStream(dst, src []byte)
5        }

同樣也提供了擷取實現該介面的執行個體

這裡,我們只討論CBC模式
3、加密解密

1)DES
DES加密代碼如下:
1        func DesEncrypt(origData, key []byte) ([]byte, error) {
2             block, err := des.NewCipher(key)
3             if err != nil {
4                  return nil, err
5             }
6             origData = PKCS5Padding(origData, block.BlockSize())
7             // origData = ZeroPadding(origData, block.BlockSize())
8             blockMode := cipher.NewCBCEncrypter(block, key)
9             crypted := make([]byte, len(origData))
10              // 根據CryptBlocks方法的說明,如下方式初始化crypted也可以
11             // crypted := origData
12             blockMode.CryptBlocks(crypted, origData)
13             return crypted, nil
14        }

以上代碼使用DES加密(des.NewCipher),加密模式為CBC(cipher.NewCBCEncrypter(block, key)),填充方式PKCS5Padding,該函數的代碼如下:
1        func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
2             padding := blockSize - len(ciphertext)%blockSize
3             padtext := bytes.Repeat([]byte{byte(padding)}, padding)
4             return append(ciphertext, padtext...)
5        }

可見,資料長度剛好是block size的整數倍時,也進行了填充,如果不進行填充,unpadding會搞不定。
另外,為了方便,初始向量直接使用key充當了。

DES解密代碼如下:
1        func DesDecrypt(crypted, key []byte) ([]byte, error) {
2             block, err := des.NewCipher(key)
3             if err != nil {
4                  return nil, err
5             }
6             blockMode := cipher.NewCBCDecrypter(block, key)
7             origData := make([]byte, len(crypted))
8             // origData := crypted
9             blockMode.CryptBlocks(origData, crypted)
10             origData = PKCS5UnPadding(origData)
11             // origData = ZeroUnPadding(origData)
12             return origData, nil
13        }

可見,解密無非是調用cipher.NewCBCDecrypter,最後unpadding,其他跟加密幾乎一樣。相應的PKCS5UnPadding:
1        func ZeroUnPadding(origData []byte) []byte {
2             length := len(origData)
3             // 去掉最後一個位元組 unpadding 次
4             unpadding := int(origData[length-1])
5             return origData[:(length - unpadding)]
6        }

2)、3DES

加密代碼:
1        // 3DES加密
2        func TripleDesEncrypt(origData, key []byte) ([]byte, error) {
3             block, err := des.NewTripleDESCipher(key)
4             if err != nil {
5                  return nil, err
6             }
7             origData = PKCS5Padding(origData, block.BlockSize())
8             // origData = ZeroPadding(origData, block.BlockSize())
9             blockMode := cipher.NewCBCEncrypter(block, key[:8])
10             crypted := make([]byte, len(origData))
11             blockMode.CryptBlocks(crypted, origData)
12             return crypted, nil
13        }

對比DES,發現只是換了NewTripleDESCipher。不過,需要注意的是,密鑰長度必須24byte,否則直接返回錯誤。關於這一點,PHP中卻不是這樣的,只要是8byte以上就行;而Java中,要求必須是24byte以上,內部會取前24byte(相當於就是24byte)。

另外,初始化向量長度是8byte(目前各個語言都是如此,不是8byte會有問題)。然而,如果你用的Go是1.0.3(或以下),iv可以不等於8byte。其實,在cipher.NewCBCEncrypter方法中有注釋:
The length of iv must be the same as the Block’s block size.
可是代碼中的實現卻沒有做判斷。不過,go tips中修正了這個問題,如果iv不等於block size(des為8),則直接panic。所以,對於加解密,一定要測試,保證iv等於block size,否則可能會panic:
1        func NewCBCDecrypter(b Block, iv []byte) BlockMode {
2             if len(iv) != b.BlockSize() {
3                  panic("cipher.NewCBCDecrypter: IV length must equal block size")
4             }
5             return (*cbcDecrypter)(newCBC(b, iv))
6        }

此處之所有用panic而不是返回error,個人猜測,是由於目前發布的版本,該方法沒有返回error,修改方法簽名會導致相容性問題,因此用panic了。

解密代碼:
1        // 3DES解密
2        func TripleDesDecrypt(crypted, key []byte) ([]byte, error) {
3             block, err := des.NewTripleDESCipher(key)
4             if err != nil {
5                  return nil, err
6             }
7             blockMode := cipher.NewCBCDecrypter(block, key[:8])
8             origData := make([]byte, len(crypted))
9             // origData := crypted
10             blockMode.CryptBlocks(origData, crypted)
11             origData = PKCS5UnPadding(origData)
12             // origData = ZeroUnPadding(origData)
13             return origData, nil
14        }
三、和其他語言互動:加解密

這次,我寫了PHP、Java的版本,具體代碼放在github上。這裡說明一下,Java中,預設模式是ECB,且沒有用”\0″填充的情況,只有NoPadding和PKCS5Padding;而PHP中(mcrypt擴充),預設填充方式是”\0″,而且,當資料長度剛好是block size的整數倍時,預設不會填充”\0″,這樣,如果資料剛好是block size的整數倍且結尾字元是”\0″,會有問題。

綜上,跨語言加密解密,應該使用PKCS5Padding填充。


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.