您好,登錄后才能下訂單哦!
DES加密算法,為對稱加密算法中的一種。70年代初由IBM研發,后1977年被美國國家標準局采納為數據加密標準,即DES全稱的由來:Data Encryption Standard。對稱加密算法,是相對于非對稱加密算法而言的。兩者區別在于,對稱加密在加密和解密時使用同一密鑰,而非對稱加密在加密和解密時使用不同的密鑰,即公鑰和私鑰。常見的DES、3DES、AES均為對稱加密算法,而RSA、橢圓曲線加密算法,均為非對稱加密算法。
?
DES是以64比特的明文為一個單位來進行加密的,超過64比特的數據,要求按固定的64比特的大小分組,分組有很多模式,后續單獨總結,暫時先介紹DES加密算法。DES使用的密鑰長度為64比特,但由于每隔7個比特設置一個奇偶校驗位,因此其密鑰長度實際為56比特。奇偶校驗為最簡單的錯誤檢測碼,即根據一組二進制代碼中1的個數是奇數或偶數來檢測錯誤。
?
DES的基本結構,由IBM公司的Horst Feistel設計,因此稱Feistel網絡。在Feistel網絡中,加密的每個步驟稱為輪,經過初始置換后的64位明文,進行了16輪Feistel輪的加密過程,最后經過終結置換后形成最終的64位密文。如下為Feistel網絡的示意圖:
?
64比特明文被分為左、右兩部分處理,右側數據和子密鑰經過輪函數f生成用于加密左側數據的比特序列,與左側數據異或運算,運算結果輸出為加密后的左側,右側數據則直接輸出為右側。
其中子密鑰為本輪加密使用的密鑰,每次Feistel均使用不同的子密鑰。子密鑰的計算,以及輪函數的細節,稍后下文介紹。由于一次Feistel輪并不會加密右側,因此需要將上一輪輸出后的左右兩側對調后,重復Feistel輪的過程,DES算法共計進行16次Feistel輪,最后一輪輸出后左右兩側無需對調。
?
DES加密和解密的過程一致,均使用Feistel網絡實現,區別僅在于解密時,密文作為輸入,并逆序使用子密鑰。
go標準庫中DES算法實現如下:
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標準庫中使用的初始置換表和終結置換表如下:
//初始置換表
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。但注意每輪循環左移的位數,有如下規定:
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子密鑰計算的代碼如下:
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置換表:
//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輪函數內部,均經過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輪函數代碼如下:
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盒置換表:
//擴展置換表
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盒置換表:
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
DES是一個經典的對稱加密算法,但也缺陷明顯,即56位的密鑰安全性不足,已被證實可以在短時間內破解。為解決此問題,出現了3DES,也稱Triple DES,3DES為DES向AES過渡的加密算法,它使用3條56位的密鑰對數據進行三次加密。為了兼容普通的DES,3DES并沒有直接使用加密->加密->加密的方式,而是采用了加密->解密->加密的方式。當三重密鑰均相同時,前兩步相互抵消,相當于僅實現了一次加密,因此可實現對普通DES加密算法的兼容。
?
3DES解密過程,與加密過程相反,即逆序使用密鑰。
如下為三重DES示意圖:
如下為3DES兼容DES示意圖:
go標準中3DES加密算法的實現如下:
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速度更快、安全性更高,后續單獨總結。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。