區塊鏈背後的資訊保安(2) DES、3DES加密演算法原理及其GO語言實現
# DES、3DES加密演算法原理及其GO語言實現
DES加密演算法,為對稱加密演算法中的一種。70年代初由IBM研發,後1977年被美國國家標準局採納為資料加密標準,即DES全稱的由來:Data Encryption Standard。
對稱加密演算法,是相對於非對稱加密演算法而言的。兩者區別在於,對稱加密在加密和解密時使用同一金鑰,而非對稱加密在加密和解密時使用不同的金鑰,即公鑰和私鑰。
常見的DES、3DES、AES均為對稱加密演算法,而RSA、橢圓曲線加密演算法,均為非對稱加密演算法。
DES是以64位元的明文為一個單位來進行加密的,超過64位元的資料,要求按固定的64位元的大小分組,分組有很多模式,後續單獨總結,暫時先介紹DES加密演算法。
DES使用的金鑰長度為64位元,但由於每隔7個位元設定一個奇偶校驗位,因此其金鑰長度實際為56位元。奇偶校驗為最簡單的錯誤檢測碼,即根據一組二進位制程式碼中1的個數是奇數或偶數來檢測錯誤。
## Feistel網路
DES的基本結構,由IBM公司的Horst Feistel設計,因此稱Feistel網路。
在Feistel網路中,加密的每個步驟稱為輪,經過初始置換後的64位明文,進行了16輪Feistel輪的加密過程,最後經過終結置換後形成最終的64位密文。
如下為Feistel網路的示意圖:
64位元明文被分為左、右兩部分處理,右側資料和子金鑰經過輪函式f生成用於加密左側資料的位元序列,與左側資料異或運算,運算結果輸出為加密後的左側,右側資料則直接輸出為右側。
其中子金鑰為本輪加密使用的金鑰,每次Feistel均使用不同的子金鑰。子金鑰的計算,以及輪函式的細節,稍後下文介紹。
由於一次Feistel輪並不會加密右側,因此需要將上一輪輸出後的左右兩側對調後,重複Feistel輪的過程,DES演算法共計進行16次Feistel輪,最後一輪輸出後左右兩側無需對調。
DES加密和解密的過程一致,均使用Feistel網路實現,區別僅在於解密時,密文作為輸入,並逆序使用子金鑰。
go標準庫中DES演算法實現如下:
```go
func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
b := binary.BigEndian.Uint64(src)
//初始置換
b = permuteInitialBlock(b)
left, right := uint32(b>>32), uint32(b)
var subkey uint64
//共計16次feistel輪
for i := 0; i < 16; i++ {
//加密和解密使用子金鑰順序相反
if decrypt {
subkey = subkeys[15-i]
} else {
subkey = subkeys[i]
}
//feistel輪函式
left, right = right, left^feistel(right, subkey)
}
//最後一輪無需對調
preOutput := (uint64(right) << 32) | uint64(left)
//終結置換
binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput))
}
//程式碼位置src/crypto/des/block.go
```
## 初始置換和終結置換
進入Feistel輪之前,64位明文需做一次初始置換。Feistel輪結束後,需做一次反向操作,即終結置換。
初始置換和終結置換目的是為加強硬體的破解難度而加的。
附go標準庫中使用的初始置換表和終結置換表如下:
```go
//初始置換表
var initialPermutation = [64]byte{
6, 14, 22, 30, 38, 46, 54, 62,
4, 12, 20, 28, 36, 44, 52, 60,
2, 10, 18, 26, 34, 42, 50, 58,
0, 8, 16, 24, 32, 40, 48, 56,
7, 15, 23, 31, 39, 47, 55, 63,
5, 13, 21, 29, 37, 45, 53, 61,
3, 11, 19, 27, 35, 43, 51, 59,
1, 9, 17, 25, 33, 41, 49, 57,
}
//終結置換表
var finalPermutation = [64]byte{
24, 56, 16, 48, 8, 40, 0, 32,
25, 57, 17, 49, 9, 41, 1, 33,
26, 58, 18, 50, 10, 42, 2, 34,
27, 59, 19, 51, 11, 43, 3, 35,
28, 60, 20, 52, 12, 44, 4, 36,
29, 61, 21, 53, 13, 45, 5, 37,
30, 62, 22, 54, 14, 46, 6, 38,
31, 63, 23, 55, 15, 47, 7, 39,
}
//程式碼位置src/crypto/des/const.go
```
## 子金鑰的計算
DES初始金鑰為64位,其中8位用於奇偶校驗,實際金鑰為56位,64位初始金鑰經過PC-1金鑰置換後,生成56位串。
經PC-1置換後56位的串,分為左右兩部分,各28位,分別左移1位,形成C0和D0,C0和D0合併成56位,經PC-2置換後生成48位子金鑰K0。
C0和D0分別左移1位,形成C1和D1,C1和D1合併成56位,經PC-2置換後生成子金鑰K1。
以此類推,直至生成子金鑰K15。但注意每輪迴圈左移的位數,有如下規定:
```go
var ksRotations = [16]uint8{1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}
//程式碼位置src/crypto/des/const.go
```
如下為子金鑰計算示意圖:
go標準庫中DES子金鑰計算的程式碼如下:
```go
func (c *desCipher) generateSubkeys(keyBytes []byte) {
key := binary.BigEndian.Uint64(keyBytes)
//PC-1金鑰置換,生成56位串
permutedKey := permuteBlock(key, permutedChoice1[:])
//56位串分左右兩部分,各28位,ksRotate為依次迴圈左移1位
leftRotations := ksRotate(uint32(permutedKey >> 28))
rightRotations := ksRotate(uint32(permutedKey<<4) >> 4)
//生成子金鑰
for i := 0; i < 16; i++ {
//合併左右兩部分,之後PC-2置換
pc2Input := uint64(leftRotations[i])<<28 | uint64(rightRotations[i])
c.subkeys[i] = permuteBlock(pc2Input, permutedChoice2[:])
}
}
//程式碼位置src/crypto/des/block.go
```
附go標準庫中使用的PC-1置換表和PC-2置換表:
```go
//PC-1置換表
var permutedChoice1 = [56]byte{
7, 15, 23, 31, 39, 47, 55, 63,
6, 14, 22, 30, 38, 46, 54, 62,
5, 13, 21, 29, 37, 45, 53, 61,
4, 12, 20, 28, 1, 9, 17, 25,
33, 41, 49, 57, 2, 10, 18, 26,
34, 42, 50, 58, 3, 11, 19, 27,
35, 43, 51, 59, 36, 44, 52, 60,
}
//PC-2置換表
var permutedChoice2 = [48]byte{
42, 39, 45, 32, 55, 51, 53, 28,
41, 50, 35, 46, 33, 37, 44, 52,
30, 48, 40, 49, 29, 36, 43, 54,
15, 4, 25, 19, 9, 1, 26, 16,
5, 11, 23, 8, 12, 7, 17, 0,
22, 3, 10, 14, 6, 20, 27, 24,
}
//程式碼位置src/crypto/des/const.go
```
## Feistel輪函式
每次Feistel輪函式內部,均經過4種運算,即:
* 1、擴充套件置換:右側32位做擴充套件置換,擴充套件置換將32位輸入擴充套件成為48位輸出,使得擴充套件後輸出資料長度與48位子金鑰等長。
* 2、異或運算:右側32位擴充套件置換為48位後,與48位子金鑰做異或運算。
* 3、S盒置換:將異或運算後的48位結果,分成8個6位的塊,每塊通過S盒置換產生4位的輸出,8個塊S盒置換後組成32位的輸出。
S盒置換的過程為:6位中取第1位和第6位組成行號,剩餘第2、3、4、5位組成列號,從S盒置換表中取出相應行、列的十進位制數,並轉化為4位二進位制數,即為S盒輸出。
* 4、P盒置換:S盒置換後的32位輸出資料,進行P盒置換,仍然輸出為32位資料。
如下為Feistel輪函式示意圖:
go標準庫中DES Feistel輪函式程式碼如下:
```go
func feistel(right uint32, key uint64) (result uint32) {
//右側32位擴充套件置換為48位,並與48位子金鑰做異或運算
sBoxLocations := key ^ expandBlock(right)
var sBoxResult uint32
for i := uint8(0); i < 8; i++ {
//sBoxLocations>>42、sBoxLocations <<= 6,按每6位分塊
sBoxLocation := uint8(sBoxLocations>>42) & 0x3f
sBoxLocations <<= 6
//6位中取第1位和第6位組成行號
row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4)
//剩餘第2、3、4、5位組成列號
column := (sBoxLocation >> 1) & 0xf
//feistelBox包括了S盒置換和P盒置換的實現
sBoxResult ^= feistelBox[i][16*row+column]
}
return sBoxResult
}
var feistelBox [8][64]uint32
//P盒置換
func permuteBlock(src uint64, permutation []uint8) (block uint64) {
for position, n := range permutation {
bit := (src >> n) & 1
block |= bit << uint((len(permutation)-1)-position)
}
return
}
//初始化feistelBox
func init() {
for s := range sBoxes {
for i := 0; i < 4; i++ {
for j := 0; j < 16; j++ {
f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s)))
f = permuteBlock(f, permutationFunction[:])
feistelBox[s][16*i+j] = uint32(f)
}
}
}
}
//程式碼位置src/crypto/des/block.go
```
附go標準庫中使用的擴充套件置換表和P盒置換表:
```go
//擴充套件置換表
var expansionFunction = [48]byte{
0, 31, 30, 29, 28, 27, 28, 27,
26, 25, 24, 23, 24, 23, 22, 21,
20, 19, 20, 19, 18, 17, 16, 15,
16, 15, 14, 13, 12, 11, 12, 11,
10, 9, 8, 7, 8, 7, 6, 5,
4, 3, 4, 3, 2, 1, 0, 31,
}
//P盒置換表
var permutationFunction = [32]byte{
16, 25, 12, 11, 3, 20, 4, 15,
31, 17, 9, 6, 27, 14, 1, 22,
30, 24, 8, 18, 0, 5, 29, 23,
13, 19, 2, 26, 10, 21, 28, 7,
}
//程式碼位置src/crypto/des/const.go
```
附go標準庫中使用的S盒置換表:
```go
var sBoxes = [8][4][16]uint8{
// S-box 1
{
{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
{0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
{4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13},
},
// S-box 2
{
{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
{3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
{0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9},
},
// S-box 3
{
{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
{1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12},
},
// S-box 4
{
{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
{3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14},
},
// S-box 5
{
{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
{4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3},
},
// S-box 6
{
{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
{10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
{9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
{4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13},
},
// S-box 7
{
{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
{13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
{1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
{6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12},
},
// S-box 8
{
{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
{1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
{7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
{2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11},
},
}
//程式碼位置src/crypto/des/const.go
```
## 3DES
DES是一個經典的對稱加密演算法,但也缺陷明顯,即56位的金鑰安全性不足,已被證實可以在短時間內破解。
為解決此問題,出現了3DES,也稱Triple DES,3DES為DES向AES過渡的加密演算法,它使用3條56位的金鑰對資料進行三次加密。
為了相容普通的DES,3DES並沒有直接使用加密->加密->加密的方式,而是採用了加密->解密->加密的方式。
當三重金鑰均相同時,前兩步相互抵消,相當於僅實現了一次加密,因此可實現對普通DES加密演算法的相容。
3DES解密過程,與加密過程相反,即逆序使用金鑰。
如下為三重DES示意圖:
如下為3DES相容DES示意圖:
go標準中3DES加密演算法的實現如下:
```go
type tripleDESCipher struct {
cipher1, cipher2, cipher3 desCipher
}
func NewTripleDESCipher(key []byte) (cipher.Block, error) {
if len(key) != 24 {
return nil, KeySizeError(len(key))
}
c := new(tripleDESCipher)
c.cipher1.generateSubkeys(key[:8])
c.cipher2.generateSubkeys(key[8:16])
c.cipher3.generateSubkeys(key[16:])
return c, nil
}
//3DES加密
func (c *tripleDESCipher) Encrypt(dst, src []byte) {
c.cipher1.Encrypt(dst, src)
c.cipher2.Decrypt(dst, dst)
c.cipher3.Encrypt(dst, dst)
}
//3DES解密
func (c *tripleDESCipher) Decrypt(dst, src []byte) {
c.cipher3.Decrypt(dst, src)
c.cipher2.Encrypt(dst, dst)
c.cipher1.Decrypt(dst, dst)
}
//程式碼位置src/crypto/des/cipher.go
```
## 後記
相比DES,3DES因金鑰長度變長,安全性有所提高,但其處理速度不高。
因此又出現了AES加密演算法,AES較於3DES速度更快、安全性更高,後續單獨總結。
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- 區塊鏈背後的資訊保安(1)AES加密演算法原理及其GO語言實現區塊鏈加密演算法Go
- 兄弟連區塊鏈教程區塊鏈背後的資訊保安2DES、3DES加密演算法原理二區塊鏈3D加密演算法
- 區塊鏈背後的資訊保安(5) 對稱加密演算法的分組模式及其Go語言實現區塊鏈加密演算法模式Go
- 區塊鏈背後的資訊保安(4)RSA加解密及簽名演算法的技術原理及其Go語言實現區塊鏈解密演算法Go
- 區塊鏈背後的資訊保安(3)橢圓曲線加解密及簽名演算法的技術原理及其Go語言實現區塊鏈解密演算法Go
- GO語言實現區塊鏈Part6 Transactions 2Go區塊鏈
- GO語言實現區塊鏈Part2 Proof-of-WorkGo區塊鏈
- C語言實現DES加密C語言加密
- DES/3DES/AES 三種對稱加密演算法在 Java 中的實現3D加密演算法Java
- GO語言實現區塊鏈Part7 NetworkGo區塊鏈
- GO語言實現區塊鏈Part4 Transactions 1Go區塊鏈
- GO語言實現區塊鏈Part3 Persistence and CLIGo區塊鏈
- GO語言實現區塊鏈Part1 Basic PrototypeGo區塊鏈
- 3DES演算法的起源與演進:保障資訊保安的重要里程碑3D演算法
- GO語言實現區塊鏈Part5 AddressesGo區塊鏈
- Golang實現ECB模式3DES演算法Golang模式3D演算法
- 如何在NEO區塊鏈上實現資訊加密區塊鏈加密
- 使用 Go 語言打造區塊鏈(二)Go區塊鏈
- 區塊鏈,中心去,何曾著眼看君王?用Go語言實現區塊鏈技術,透過Golang秒懂區塊鏈區塊鏈Golang
- 區塊鏈-NFT 的實現原理區塊鏈
- 實戰區塊鏈技術培訓之Go語言區塊鏈Go
- 區塊鏈共識演算法(2)PoW挖礦演算法原理及其在比特幣、以太坊中的實現區塊鏈演算法比特幣
- go 語言與區塊鏈基礎講解Go區塊鏈
- 區塊鏈開發之Go語言—IO操作區塊鏈Go
- 兄弟連區塊鏈教程區塊鏈資訊保安3橢圓曲線加解密及簽名演算法的技術原理二區塊鏈解密演算法
- JAVA和C# 3DES加密解密JavaC#3D加密解密
- Go 語言區塊鏈測試實踐指南(一):GO單元測試Go區塊鏈
- 用於加密貨幣和區塊鏈的語言Simplicity加密區塊鏈
- java 實現 DES加密 解密演算法Java加密解密演算法
- (二)區塊鏈的共識演算法:PoS 及其 例子 程式碼 實現區塊鏈演算法
- 區塊鏈的原理與golang實現例子區塊鏈Golang
- Python2 解密易語言DES加密的密文Python解密加密
- 使用Go語言從零編寫PoS區塊鏈(譯)Go區塊鏈
- Go語言實現HTTPS加密協議GoHTTP加密協議
- 比原鏈CTO James | Go語言成為區塊鏈主流開發語言的四點理由Go區塊鏈
- GIFTO背後區塊鏈技術的分類區塊鏈
- 區塊鏈系列-----加密演算法彙總區塊鏈加密演算法
- 比特幣和區塊鏈(2):比特幣中區塊鏈的實現比特幣區塊鏈