当前位置: 首页 > news >正文

【Golang】手搓DES加密

代码非常长 有六百多行

参考一位博主的理论实现
通俗易懂,十分钟读懂DES

还有很多不足的地方
感觉只是个思路

S盒(理论既定)

package src// 定义S - 盒的置换表
var SBoxes = [8][4][16]int{{{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},},{{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},},{{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},},{{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},},{{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},},{{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},},{{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},},{{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},},
}

主代码

可以适应各种输入
但是时间有限没能调通
位运算真的折磨人(悲

package mainimport ("bufio""crypto/rand""encoding/binary""fmt""main/src""os""strings"
)// 如果不足六十四位密钥 进行随机填充
func PadKeyWithRandom(key []byte) []byte {// 输出原始密钥长度fmt.Println("原始密钥长度:", len(key))// 计算需要填充的字节数padLen := 8 - len(key)// 创建一个新的字节切片,长度为64位 8字节newKey := make([]byte, 8)// 复制原始密钥到新密钥的开头copy(newKey, key)// 生成随机字节填充剩余字节randomBytes := make([]byte, padLen)_, err := rand.Read(randomBytes)if err != nil {panic(err)}// 将随机字节添加到密钥for i := len(key); i < 8; i++ {newKey[i] = randomBytes[i-len(key)]}// 输出填充后的密钥长度fmt.Println("填充后的密钥长度:", len(newKey))return newKey
}// 如果明文不足64位bit 就填充
func PadPlaintext(plaintext []byte) []byte {for len(plaintext) < 8 {plaintext = append(plaintext, 0x00)}return plaintext
}// 初始置换 最开始
func InitialPermutation(text []byte) []byte {if len(text) != 8 {panic("Invalid len of 64 bits for Init")}var piece = [][]int{{58, 50, 42, 34, 26, 18, 10, 2},{60, 52, 44, 36, 28, 20, 12, 4},{62, 54, 46, 38, 30, 22, 14, 6},{64, 56, 48, 40, 32, 24, 16, 8},{57, 49, 41, 33, 25, 17, 9, 1},{59, 51, 43, 35, 27, 19, 11, 3},{61, 53, 45, 37, 29, 21, 13, 5},{63, 55, 47, 39, 31, 23, 15, 7},}result := make([]byte, 8)for i := 0; i < len(piece); i++ {for j := 0; j < 8; j++ {index := piece[i][j] - 1// 确定该比特位所在的字节在整个密钥字节切片中的某个字节索引byteIndex := index / 8// 在当前字节内,要处理的比特位相对于字节最高位(第 7 位)的偏移量   就是先确定某个字节再瞬移到要取的比特位bitOffset := 7 - (index % 8)// 分前后四字节的两段// key[bitIndex/8]就是简单的选定要找的字节// (1 << (7 - (bitIndex % 8)))作用是挪动1去某个位置,通过与操作来提取出原始字节中我们想要的那个比特位// 然后开始为放入c0服务// 将前面提取出的比特位再向右移动 7 - (bitIndex % 8) 位,这一步的目的是将提取出的比特位调整到最低位// 最后将调整到最低位的比特位再向左移动 bitOffset 位,这样就可以将该比特位设置到 c0 中相应字节的正确位置上result[byteIndex] |= (text[index/8] & (1 << (7 - (index % 8)))) >> (7 - (index % 8)) << bitOffset}}fmt.Printf("明文/密文 初始置换结果:%v\n", result)return result
}// 逆置换 最后
func ReversePermutation(text []byte) []byte {if len(text) != 8 {panic("Invalid len of 64 bits for Reverse")}var piece = [][]int{{40, 8, 48, 16, 56, 24, 64, 32},{39, 7, 47, 15, 55, 23, 63, 31},{38, 6, 46, 14, 54, 22, 62, 30},{37, 5, 45, 13, 53, 21, 61, 29},{36, 4, 44, 12, 52, 20, 60, 28},{35, 3, 43, 11, 51, 19, 59, 27},{34, 2, 42, 10, 50, 18, 58, 26},{33, 1, 41, 9, 49, 17, 57, 25},}result := make([]byte, 8)for i := 0; i < len(piece); i++ {for j := 0; j < 8; j++ {index := piece[i][j] - 1// 确定该比特位所在的字节在整个密钥字节切片中的某个字节索引byteIndex := index / 8// 在当前字节内,要处理的比特位相对于字节最高位(第 7 位)的偏移量   就是先确定某个字节再瞬移到要取的比特位bitOffset := 7 - (index % 8)result[byteIndex] |= (text[index/8] & (1 << (7 - (index % 8)))) >> (7 - (index % 8)) << bitOffset}}return result
}// 拆开得L0和R0
func SplitLR(text []byte) ([]byte, []byte) {if len(text) != 8 {panic("Invalid SplitLR of 64 bits")}left := text[0:4]right := text[4:8]return left, right
}// 拼起来L16和R16
func MergeLR(left, right []byte) []byte {if len(left) != 4 || len(right) != 4 {panic("Invalid left or right for MergeLR 64 bits size")}text := make([]byte, 8)copy(text[0:4], left)copy(text[4:8], right)return text
}/*
// 每个加密轮应该包括扩展、生成每轮子密钥、进行异或、S盒、P盒
func Encrypt(L, R []byte) ([]byte, []byte) {Expand32To48(R)
}
*/// 1. 扩展32->48
func Expand32To48(input []byte) []byte {if len(input) != 4 {panic("Invalid len of 32bits(4 bytes) for Expand32To48")}// 置换表permutationTable := [][]int{{32, 1, 2, 3, 4, 5},{4, 5, 6, 7, 8, 9},{8, 9, 10, 11, 12, 13},{12, 13, 14, 15, 16, 17},{16, 17, 18, 19, 20, 21},{20, 21, 22, 23, 24, 25},{24, 25, 26, 27, 28, 29},{28, 29, 30, 31, 32, 1},}result := make([]byte, 6)var bitcount = 0  //用于统计result的比特位置var bittarget = 0 //统计result放入的具体偏移量for row := 0; row < len(permutationTable); row++ {for col := 0; col < len(permutationTable[row]); col++ {index := permutationTable[row][col] - 1bittarget = bitcount % 8result[bitcount/8] = input[index/8] & (1 << (7 - (index % 8))) >> (7 - (index % 8)) << bittargetbitcount++}}return result
}// 2. 生成子密钥
// 密钥调度算法可以将64位的主密钥分成16个子密钥,每个子密钥48位,用于每轮加密中与输入数据进行异或运算。
func GenerateSubkey(key []byte) [][]byte {// 置换选择1C0, D0 := PC1Permutation(key)var subkeys [][]bytefor i := 1; i <= 16; i++ {c := CircularLeftShift(C0, i)d := CircularLeftShift(D0, i)subkey := PC2Permutation(c, d)subkeys = append(subkeys, subkey)// 详细打印当前生成的子密钥内容fmt.Printf("生成第 %d 个 子密钥:\n", i)fmt.Printf("子密钥内容(十六进制):")for _, b := range subkey {fmt.Printf("%02x ", b)}fmt.Println()// 更新左移结果 一共十六轮C0 = cD0 = d}fmt.Println()return subkeys}// 2.1 实现PC - 1置换操作
func PC1Permutation(key []byte) ([]byte, []byte) {if len(key) != 8 {panic("Invalid key length for PC1Permutation. Expected 8 bytes (64 bits).")}pc1Table := [][]int{{57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18},{10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36},{63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22},{14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4},}c0 := make([]byte, 4)d0 := make([]byte, 4)for i := 0; i < len(pc1Table); i++ {for j := 0; j < len(pc1Table[i]); j++ {// 要处理原始密钥中的第 pc表值-1 个比特位bitIndex := pc1Table[i][j] - 1// 确定该比特位所在的字节在整个密钥字节切片中的某个字节索引byteIndex := bitIndex / 8// 在当前字节内,要处理的比特位相对于字节最高位(第 7 位)的偏移量   就是先确定某个字节再瞬移到要取的比特位bitOffset := 7 - (bitIndex % 8)// 分前后四字节的两段if byteIndex < 4 {// key[bitIndex/8]就是简单的选定要找的字节// (1 << (7 - (bitIndex % 8)))作用是挪动1去某个位置,通过与操作来提取出原始字节中我们想要的那个比特位// 然后开始为放入c0服务// 将前面提取出的比特位再向右移动 7 - (bitIndex % 8) 位,这一步的目的是将提取出的比特位调整到最低位// 最后将调整到最低位的比特位再向左移动 bitOffset 位,这样就可以将该比特位设置到 c0 中相应字节的正确位置上c0[byteIndex] |= (key[bitIndex/8] & (1 << (7 - (bitIndex % 8)))) >> (7 - (bitIndex % 8)) << bitOffset// 输出提取后比特位在c0中的位置//fmt.Printf("提取后比特位在c0中的位置: %d\n", byteIndex)// 输出c0的当前状态//fmt.Printf("c0当前状态: %v\n", c0)} else {// 减去 4,因为 d0 是从原始密钥经过处理后得到的后半部分  其他类同d0[byteIndex-4] |= (key[bitIndex/8] & (1 << (7 - (bitIndex % 8)))) >> (7 - (bitIndex % 8)) << bitOffset}}}fmt.Println("PC-1置换结果如下:")fmt.Println("c0 length:", len(c0))fmt.Println("c0 content:", c0)fmt.Println("d0 length:", len(d0))fmt.Println("d0 content:", d0)return c0, d0}// 2.2 循环左移操作
func CircularLeftShift(data []byte, times int) []byte {num := binary.BigEndian.Uint32(data)// 将uint32转换为二进制字符串binaryStr := fmt.Sprintf("%032b", num)fmt.Printf("num的二进制表示: %s\n", binaryStr)result := make([]byte, 4)if times > 16 || times < 1 {panic("ShiftLeft Error")}var shiftTable = [16]int{1, 1, 2, 2, 2, 2, 2, 2,1, 2, 2, 2, 2, 2, 2, 1,}// 由于在数值中,高位在左,低位在右,所以采用右移,在大端模式下是左移var out uint32 = numfor i := 0; i < shiftTable[times-1]; i++ {// 获取最低位h := num & 1//fmt.Printf("h:%d\n", h)outStr := fmt.Sprintf("%032b", out)fmt.Printf("out之前的二进制表示: %s\n", outStr)// 先将out整体左移一位out <<= 1// 当h为0时,进行特殊处理将num的最高位补到out的最低位if h == 0 {// 获取num的最高位highestBit := num >> 31// 将num的最高位补到out的最低位out |= highestBit} else {// 当h不为0时,直接将h补到out的最低位out |= h}outStr = fmt.Sprintf("%032b", out)fmt.Printf("out左移1次后的二进制表示: %s\n", outStr)// 同时对num也进行类似的左移操作,保持数据状态的一致性num <<= 1// 如果num超过了uint32的范围,进行处理(比如截断等,这里简单示例截断)if num > (1<<32 - 1) {num &= (1<<32 - 1)}}binary.BigEndian.PutUint32(result, out)// 将uint32转换为二进制字符串resStr := fmt.Sprintf("%08b", result)fmt.Printf("result的二进制表示: %s\n", resStr)return result
}// 2.3 PC-2置换
func PC2Permutation(c []byte, d []byte) []byte {// totalBits := 56// 这里totalBits应该是56,因为c0和d0都是4字节(32位),拼接起来就是56位// fmt.Printf("totalbits:%d\n", totalBits)// 不含奇偶校验位第 8、16、24、32、40、48、56位pc2Table := [][]int{{14, 17, 11, 24, 1, 5},{3, 28, 15, 6, 21, 10},{23, 19, 12, 4, 26, 8},{16, 7, 27, 20, 13, 2},{41, 52, 31, 37, 47, 55},{30, 40, 51, 45, 33, 48},{44, 49, 39, 56, 34, 53},{46, 42, 50, 36, 29, 32},}result := make([]byte, 6)// 将切片 d 中的所有元素逐个添加到切片 c 的末尾,然后返回一个新的切片combined := append(c, d...)fmt.Println("combined: ", combined)var bitcount = 0  //用于统计result的比特位置var bittarget = 0 //统计result放入的具体偏移量for i := 0; i < 8; i++ {for j := 0; j < 6; j++ {// 获取 表值-1 的真正的索引去cd合体的里面找bitIndex := pc2Table[i][j] - 1// 获取字节的索引byteIndex := bitIndex / 8// 根据byteIndex确定从combined中选取比特位所在的字节byteValue := combined[byteIndex]// 从combined特定字节bytevalue的特定比特位里取出 放入resultbittarget = bitcount % 8result[bitcount/8] |= (byteValue & (1 << (7 - (bitIndex % 8)))) >> (7 - (bitIndex % 8)) << bittargetbitcount++// 记录所有子密钥的步骤在上面subkey}}fmt.Println("本轮PC-2完成。")return result
}// 3. 异或
func xorBytes(a, b []byte) []byte {if len(a) != len(b) {panic("输入的两个字节切片长度不相等")}result := make([]byte, len(a))// fmt.Println(len(a), len(b))for i := range a {result[i] = a[i] ^ b[i]}return result
}// 4. S盒  6位变4位
func Sbox48To32(input []byte) []byte {if len(input) != 6 {panic("Invalid input length for SBox48To32. Expected 6 bytes (48 bits).")}output := make([]byte, 4)var out uint32 = 0// 定义一个掩码,用于提取6位数据块中的每一位const bitMask uint8 = 0x20// 遍历8个6位数据块for i := 0; i < 8; i++ {// 提取当前6位数据块var b uint8for j := 0; j < 6; j++ {// 根据当前循环的索引和位掩码,从输入字节切片中提取相应的位bitIndex := (i*6 + j) % 6// 按位或// 可以提取出 input[bitIndex] 字节中与 bitMask 对应的那一位。b |= (input[bitIndex] & bitMask) >> (7 - (j % 6))}// 计算行索引:第1位和第6位r := (b&1)<<1 + (b >> 5)// 计算列索引:第2到第5位c := ((b>>1)&1)<<3 + ((b>>2)&1)<<2 + ((b>>3)&1)<<1 + ((b >> 4) & 1)// 从对应的S盒中获取值o := src.SBoxes[i][r][c]// 将获取的值设置到输出结果中,按位或out |= uint32(o) << (i * 4)}// 将处理后的结果填充到输出切片for i := 0; i < 4; i++ {output[i] = byte((out >> (i * 8)) & 0xff)}fmt.Printf("S盒结果:%v\n", output)return output
}// 5. P盒
func PboxExchange(input []byte) []byte {Pbox := [][]int{{16, 7, 20, 21},{29, 12, 28, 17},{1, 15, 23, 26},{5, 18, 31, 10},{2, 8, 24, 14},{32, 27, 3, 9},{19, 13, 30, 6},{22, 11, 4, 25},}if len(input) != 4 {panic("Invalid input length for PboxExchange. Expected 4 bytes(32bits).")}// 将输入的字节切片转换为uint32类型var inputUint32 uint32for i, b := range input {inputUint32 |= uint32(b) << (uint32(i) * 8)}fmt.Println("转换为uint32后的输入值: ", inputUint32)var out uint32 = 0for i := 0; i < 8; i++ {for j := 0; j < 4; j++ {index := Pbox[i][j] - 1bit := (inputUint32 >> uint32(31-index)) & 1out |= bit << uint32(i*4+j)}//fmt.Printf("经过第%d行Pbox置换操作后out的值(十六进制): %x\n", i, out)}// 将置换后的结果转换为字节切片返回output := make([]byte, 4)for i := 0; i < 4; i++ {output[i] = byte((out >> (i * 8)) & 0xff)}fmt.Println("P盒output: ", output)return output
}// 解密函数,执行与加密相反的操作流程
func Decrypt(ciphertext []byte, subkeys [][]byte) []byte {fmt.Println("开始解密……")// 用于存储每组解密后的结果// 转换成字节切片组,8个字节一组var cipherGroups [][]bytefor i := 0; i < len(ciphertext); i += 8 {endIndex := i + 8if endIndex > len(ciphertext) {endIndex = len(ciphertext)}cipherGroups = append(cipherGroups, []byte(ciphertext[i:endIndex]))}// 用于存储每组解密后的结果var decryptedGroups [][]bytefor _, group := range cipherGroups {println("开始初始置换")// 3. 填充明文 初始置换//paddedGroup := PadPlaintext(group)permutedText := InitialPermutation(group)println("初始置换完毕")// 4. 拆分L0和R0//println("开始拆分L0和R0")L, R := SplitLR(permutedText)println("拆分L0和R0完毕")// 5. 进行16轮加密for i := 0; i < 16; i++ {// 扩展RexpandedR := Expand32To48(R)//println("扩展完毕")// 生成子密钥subkey := subkeys[15-i]// 异或xored := xorBytes(expandedR, subkey)//println("异或完毕")fmt.Printf("异或结果:%v\n", xored)// S盒变换sboxed := Sbox48To32(xored)//println("S盒完毕")// P盒变换pboxed := PboxExchange(sboxed)//println("P盒完毕")// 与L异或newL := xorBytes(L, pboxed)//println("二次异或完毕")L = RR = newL}// 6. 合并L16和R16plaintext := MergeLR(L, R)// 7. 逆置换finalCiphertext := ReversePermutation(plaintext)decryptedGroups = append(decryptedGroups, finalCiphertext)}// 将所有组的解密结果组合起来finalDecryptedText := []byte{}for _, decryptedGroup := range decryptedGroups {finalDecryptedText = append(finalDecryptedText, decryptedGroup...)}return finalDecryptedText
}func main() {println("请输入明文:")reader := bufio.NewReader(os.Stdin)plaintextStr, err := reader.ReadString('\n')if err != nil {fmt.Println("读取输入时出错:", err)return}// 去除字符串末尾的换行符plaintextStr = strings.TrimSpace(plaintextStr)// 转换成字节切片组,8个字节一组var plainGroups [][]bytefor i := 0; i < len(plaintextStr); i += 8 {endIndex := i + 8if endIndex > len(plaintextStr) {endIndex = len(plaintextStr)}plainGroups = append(plainGroups, []byte(plaintextStr[i:endIndex]))}// 假设主密钥为8字节(64位)println("请输入密钥:")reader = bufio.NewReader(os.Stdin)keyyy, err := reader.ReadString('\n')if err != nil {fmt.Println("读取输入时出错:", err)return}// 去除字符串末尾的换行符keyStr := strings.TrimSpace(keyyy)key := []byte(keyStr)//key := []byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}println("开始填充密钥")// 1. 填充密钥(如果需要)newKey := PadKeyWithRandom(key)//println("填充结果:", newKey)println("密钥填充结果:")for _, b := range newKey {fmt.Printf("%02x ", b)}println("\n开始生成子密钥……")// 2. 生成子密钥subkeys := GenerateSubkey(newKey)// 用于存储每组加密后的结果var encryptedGroups [][]bytefmt.Printf("明文字节组数:%d\n", len(plainGroups))for _, group := range plainGroups {//println(len(group)) 不出意外是一个8一个3 因为This is DESprintln("开始初始置换")// 3. 明文填充 + 初始置换paddedGroup := PadPlaintext(group)permutedText := InitialPermutation(paddedGroup)println("初始置换完毕")// 4. 拆分L0和R0//println("开始拆分L0和R0")L, R := SplitLR(permutedText)println("拆分L0和R0完毕")// 5. 进行16轮加密for i := 0; i < 16; i++ {// 扩展RexpandedR := Expand32To48(R)//println("扩展完毕")// 生成子密钥subkey := subkeys[i]// 异或xored := xorBytes(expandedR, subkey)//println("异或完毕")fmt.Printf("异或结果:%v\n", xored)// S盒变换sboxed := Sbox48To32(xored)//println("S盒完毕")// P盒变换pboxed := PboxExchange(sboxed)//println("P盒完毕")// 与L异或newL := xorBytes(L, pboxed)//println("二次异或完毕")L = RR = newL}// 6. 合并L16和R16ciphertext := MergeLR(L, R)// 7. 逆置换finalCiphertext := ReversePermutation(ciphertext)encryptedGroups = append(encryptedGroups, finalCiphertext)}// 将所有组的加密结果组合起来finalEncryptedText := []byte{}for _, encryptedGroup := range encryptedGroups {finalEncryptedText = append(finalEncryptedText, encryptedGroup...)}// 调用解密函数进行解密验证decryptedText := Decrypt(finalEncryptedText, subkeys)fmt.Printf("明文解析字节内容:%d\n", plainGroups)fmt.Printf("加密后的密文: %d\n", finalEncryptedText)fmt.Printf("解密后的明文: %d\n", decryptedText)/*// 示例用法data := []byte{0x12, 0x34, 0x56, 0x78}index := 3result := CircularLeftShift(data, index)fmt.Printf("原始数据: %x\n", data)fmt.Printf("循环左移后的结果: %x\n", result)*/
}

相关文章:

【Golang】手搓DES加密

代码非常长 有六百多行 参考一位博主的理论实现 通俗易懂&#xff0c;十分钟读懂DES 还有很多不足的地方 感觉只是个思路 S盒&#xff08;理论既定&#xff09; package src// 定义S - 盒的置换表 var SBoxes [8][4][16]int{{{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, …...

YouQu使用手册【元素定位】

元素定位 文章目录 前言一、气泡识别二、不依赖OpenCV的图像识别方案三、动态图像识别四、背景五、sniff(嗅探器)使用六、元素操作七、框架封装八、背景【OCR识别】九、实现原理十、使用说明十一、RPC服务端部署十二、负载均衡十三、链式调用十四、背景【相对坐标定位】十五、…...

Spark RDD sortBy算子什么情况会触发shuffle

在 Spark 的 RDD 中&#xff0c;sortBy 是一个排序算子&#xff0c;虽然它在某些场景下可能看起来是分区内排序&#xff0c;但实际上在需要全局排序时会触发 Shuffle。这里我们分析其底层逻辑&#xff0c;结合源码和原理来解释为什么会有 Shuffle 的发生。 1. 为什么 sortBy 会…...

机器视觉相机重要名词

机器视觉相机的重要名词包括&#xff1a; • 工业数字相机&#xff1a;又称工业相机&#xff0c;是机器视觉系统中的关键组件。 • 电荷偶合元件&#xff08;CCD&#xff09;&#xff1a;一种图像传感器&#xff0c;能将光学影像转换为数字信号。 • 互补金属氧化物半导体&…...

Django:从入门到精通

一、Django背景 Django是一个由Python编写的高级Web应用框架&#xff0c;以其简洁性、安全性和高效性而闻名。Django最初由Adrian Holovaty和Simon Willison于2003年开发&#xff0c;旨在简化Web应用的开发过程。作为一个开放源代码项目&#xff0c;Django迅速吸引了大量的开发…...

android viewpager2 嵌套 recyclerview 手势冲突

老规矩直接上代码&#xff0c; 不分析&#xff1a; import android.content.Context import android.util.AttributeSet import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration import android.view.ViewGroup import android.widg…...

依赖管理(go mod)

目录 各版本依赖管理的时间分布 一、GOPATH 1. GOROOT是什么 定义&#xff1a; 作用&#xff1a; 默认值&#xff1a; 是否需要手动设置&#xff1a; 查看当前的 GOROOT&#xff1a; 2. GOPATH&#xff1a;工作区目录 定义&#xff1a; 作用&#xff1a…...

Apple Vision Pro开发001-开发配置

一、Vision Pro开发硬件和软件要求 硬件要求软件要求 1、Apple Silicon Mac(M系列芯片的Mac电脑) 2、Apple vision pro-真机调试 XCode15.2及以上&#xff0c;调试开发和打包发布Unity开发者账号&&苹果开发者账号 二 、开启无线调试 1、Apple Vision Pro和Mac连接同…...

android 动画原理分析

一 android 动画分为app内的view动画和系统动画 基本原理都是监听Choreographer的doframe回调 二 app端的实现是主要通过AnimationUtils来实现具体属性的变化通过invilate来驱动 wms来进行更新。这个流程是在app进程完成 这里不是我分析的重点 直接来看下系统动画里面的本地动…...

Elasticsearch 6.8 分析器

在 Elasticsearch 中&#xff0c;分析器&#xff08;Analyzer&#xff09;是文本分析过程中的一个关键组件&#xff0c;它负责将原始文本转换为一组词汇单元&#xff08;tokens&#xff09;。 分析器由三个主要部分组成&#xff1a;分词器&#xff08;Tokenizer&#xff09;、…...

实验室资源调度系统:基于Spring Boot的创新

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…...

实验三:构建园区网(静态路由)

目录 一、实验简介 二、实验目的 三、实验需求 四、实验拓扑 五、实验任务及要求 1、任务 1&#xff1a;完成网络部署 2、任务 2&#xff1a;设计全网 IP 地址 3、任务 3&#xff1a;实现全网各主机之间的互访 六、实验步骤 1、在 eNSP 中部署网络 2、配置各主机 IP …...

3. SQL优化

SQL性能优化 在日常开发中&#xff0c;MySQL性能优化是一项必不可少的技能。本文以具体案例为主线&#xff0c;结合实际问题&#xff0c;探讨如何优化插入、排序、分组、分页、计数和更新等操作&#xff0c;帮助你实现数据库性能的飞跃。 一、索引设计原则 索引是MySQL优化的…...

web——upload-labs——第十一关——黑名单验证,双写绕过

还是查看源码&#xff0c; $file_name str_ireplace($deny_ext,"", $file_name); 该语句的作用是&#xff1a;从 $file_name 中去除所有出现在 $deny_ext 数组中的元素&#xff0c;替换为空字符串&#xff08;即删除这些元素&#xff09;。str_ireplace() 在处理时…...

AWS CLI

一、AWS CLI介绍 1、简介 AWS CLI(Amazon Web Services Command Line Interface)是一个命令行工具,它允许用户通过命令行与 Amazon Web Services(AWS)的各种云服务进行交互和管理。使用 AWS CLI,用户可以直接在终端或命令行界面中执行命令来配置、管理和自动化AWS资源,…...

springboot:责任链模式实现多级校验

责任链模式是将链中的每一个节点看作是一个对象&#xff0c;每个节点处理的请求不同&#xff0c;且内部自动维护一个下一节点对象。 当一个请求从链式的首段发出时&#xff0c;会沿着链的路径依此传递给每一个节点对象&#xff0c;直至有对象处理这个请求为止。 属于行为型模式…...

CentO7安装单节点Redis服务

本文目录 一、Redis安装与配置1.1 安装redis依赖1.2 上传压缩包并解压1.3 编译安装1.4 修改配置并启动1、复制配置文件2、修改配置文件3、启动Redis服务4、停止redis服务 1.5 redis连接使用1、 命令行客户端2、 图形界面客户端 一、Redis安装与配置 1.1 安装redis依赖 Redis是…...

FreeRTOS学习14——时间管理

时间管理 时间管理FreeRTOS 系统时钟节拍FreeRTOS 系统时钟节拍简介FreeRTOS 系统时钟节拍处理FreeRTOS 系统时钟节拍来源 FreeRTOS 任务延时函数vTaskDelay()vTaskDelayUntil() 时间管理 在前面的章节实验例程中&#xff0c;频繁地使用了 FreeRTOS 提供的延时函数&#xff0c…...

统⼀数据返回格式快速⼊⻔

为什么会有统⼀数据返回&#xff1f; 其实统一数据返回是运用了AOP&#xff08;对某一类事情的集中处理&#xff09;的思维。 优点&#xff1a; 1.⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据。 2.降低前端程序员和后端程序员的沟通成本&#xff0c;因为所有接⼝都…...

Python学习------第十天

数据容器-----元组 定义格式&#xff0c;特点&#xff0c;相关操作 元组一旦定义&#xff0c;就无法修改 元组内只有一个数据&#xff0c;后面必须加逗号 """ #元组 (1,"hello",True) #定义元组 t1 (1,"hello") t2 () t3 tuple() prin…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...