首页 > 其他分享 >go加解密学习笔记

go加解密学习笔记

时间:2023-02-06 23:56:17浏览次数:60  
标签:origData func fmt 加解密 笔记 key go byte block

1、概念

加解密分为以下几种

  1. 对称加密, 加解密都使用的是同一个密钥, 其中的代表就是AES、DES
  2. 非对加解密, 加解密使用不同的密钥, 其中的代表就是RSA
  3. 签名算法, 如MD5、SHA1、HMAC等, 主要用于验证,防止信息被修改, 如:文件校验、数字签名、鉴权协议

Base64不是加密算法,它是一种数据编码方式,虽然是可逆的,但是它的编码方式是公开的,无所谓加密。本文也对Base64编码方式做了简要介绍

2、AES

AES,即高级加密标准(Advanced Encryption Standard),是一个对称分组密码算法,旨在取代DES成为广泛使用的标准。AES中常见的有三种解决方案,分别为AES-128、AES-192和AES-256。
AES加密过程涉及到4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。加解密中每轮的密钥分别由初始密钥扩展得到。算法中16字节的明文、密文和轮密钥都以一个4x4的矩阵表示。
AES 有五种加密模式

电码本模式(Electronic Codebook Book (ECB))
密码分组链接模式(Cipher Block Chaining (CBC))
计算器模式(Counter (CTR))
密码反馈模式(Cipher FeedBack (CFB))
输出反馈模式(Output FeedBack (OFB))

2.1、ECB模式

出于安全考虑,golang默认并不支持ECB模式。

package main

//ECB模式
import (
   "crypto/aes"
   "fmt"
)

func AESEncrypt(src []byte, key []byte) (encrypted []byte) {
   cipher, _ := aes.NewCipher(generateKey(key))
   length := (len(src) + aes.BlockSize) / aes.BlockSize
   plain := make([]byte, length*aes.BlockSize)
   copy(plain, src)
   pad := byte(len(plain) - len(src))
   for i := len(src); i < len(plain); i++ {
      plain[i] = pad
   }
   encrypted = make([]byte, len(plain))
   // 分组分块加密
   for bs, be := 0, cipher.BlockSize(); bs <= len(src); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
      cipher.Encrypt(encrypted[bs:be], plain[bs:be])
   }

   return encrypted
}

func AESDecrypt(encrypted []byte, key []byte) (decrypted []byte) {
   cipher, _ := aes.NewCipher(generateKey(key))
   decrypted = make([]byte, len(encrypted))
   //
   for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
      cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
   }

   trim := 0
   if len(decrypted) > 0 {
      trim = len(decrypted) - int(decrypted[len(decrypted)-1])
   }

   return decrypted[:trim]
}

func generateKey(key []byte) (genKey []byte) {
   genKey = make([]byte, 16)
   copy(genKey, key)
   for i := 16; i < len(key); {
      for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
         genKey[j] ^= key[i]
      }
   }
   return genKey
}
func main() {

   source := "hello world"
   fmt.Println("原字符:", source)
   //16byte密钥
   key := "1443flfsaWfdas"
   encryptCode := AESEncrypt([]byte(source), []byte(key))
   fmt.Println("密文:", string(encryptCode))

   decryptCode := AESDecrypt(encryptCode, []byte(key))

   fmt.Println("解密:", string(decryptCode))
}

image-20230206214458701

2.2、CBC模式

package main

import (
   "bytes"
   "crypto/aes"
   "crypto/cipher"
   "encoding/base64"
   "fmt"
)

func main() {
   orig := "hello world"
   key := "0123456789012345"
   fmt.Println("原文:", orig)
   encryptCode := AesEncrypt(orig, key)
   fmt.Println("密文:", encryptCode)
   decryptCode := AesDecrypt(encryptCode, key)
   fmt.Println("解密结果:", decryptCode)
}
func AesEncrypt(orig string, key string) string {
   // 转成字节数组
   origData := []byte(orig)
   k := []byte(key)
   // 分组秘钥
   // NewCipher该函数限制了输入k的长度必须为16, 24或者32
   block, _ := aes.NewCipher(k)
   // 获取秘钥块的长度
   blockSize := block.BlockSize()
   // 补全码
   origData = PKCS7Padding(origData, blockSize)
   // 加密模式
   blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
   // 创建数组
   cryted := make([]byte, len(origData))
   // 加密
   blockMode.CryptBlocks(cryted, origData)
   return base64.StdEncoding.EncodeToString(cryted)
}
func AesDecrypt(cryted string, key string) string {
   // 转成字节数组
   crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
   k := []byte(key)
   // 分组秘钥
   block, _ := aes.NewCipher(k)
   // 获取秘钥块的长度
   blockSize := block.BlockSize()
   // 加密模式
   blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
   // 创建数组
   orig := make([]byte, len(crytedByte))
   // 解密
   blockMode.CryptBlocks(orig, crytedByte)
   // 去补全码
   orig = PKCS7UnPadding(orig)
   return string(orig)
}

//补码
//AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
   padding := blocksize - len(ciphertext)%blocksize
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(ciphertext, padtext...)
}

//去码
func PKCS7UnPadding(origData []byte) []byte {
   length := len(origData)
   unpadding := int(origData[length-1])
   return origData[:(length - unpadding)]
}

image-20230206214702343

2.3、CRT模式

package main

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

//AEC加密和解密(CRT模式)
func aesCtrCrypt(plainText []byte, key []byte) ([]byte, error) {

   //指定加密、解密算法为AES,返回一个AES的Block接口对象
   block, err := aes.NewCipher(key)
   if err != nil {
      panic(err)
   }
   //指定计数器,长度必须等于block的块尺寸
   count := []byte("12345678abcdefgh")
   //指定分组模式
   blockMode := cipher.NewCTR(block, count)
   //执行加密、解密操作
   message := make([]byte, len(plainText))
   blockMode.XORKeyStream(message, plainText)
   
   //返回明文或密文
   return message, nil
}

func main() {
   source := "hello world"
   fmt.Println("原字符:", source)

   key := "1234567812345678"
   encryptCode, _ := aesCtrCrypt([]byte(source), []byte(key))
   fmt.Println("密文:", string(encryptCode))

   decryptCode, _ := aesCtrCrypt(encryptCode, []byte(key))

   fmt.Println("解密:", string(decryptCode))
}

2.4、CFB模式

package main

import (
   "crypto/aes"
   "crypto/cipher"
   "crypto/rand"
   "encoding/hex"
   "fmt"
   "io"
)

func AesEncryptCFB(origData []byte, key []byte) (encrypted []byte) {
   block, err := aes.NewCipher(key)
   if err != nil {
      //panic(err)
      fmt.Println("err:", err)
   }
   encrypted = make([]byte, aes.BlockSize+len(origData))
   iv := encrypted[:aes.BlockSize]
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {
      //panic(err)
   }
   stream := cipher.NewCFBEncrypter(block, iv)
   stream.XORKeyStream(encrypted[aes.BlockSize:], origData)
   return encrypted
}
func AesDecryptCFB(encrypted []byte, key []byte) (decrypted []byte) {
   block, _ := aes.NewCipher(key)
   if len(encrypted) < aes.BlockSize {
      panic("ciphertext too short")
   }
   iv := encrypted[:aes.BlockSize]
   encrypted = encrypted[aes.BlockSize:]

   stream := cipher.NewCFBDecrypter(block, iv)
   stream.XORKeyStream(encrypted, encrypted)
   return encrypted
}
func main() {
   source := "hello world"
   fmt.Println("原字符:", source)
   key := "ABCDEFGHIJKLMNO1" //16位
   encryptCode := AesEncryptCFB([]byte(source), []byte(key))
   fmt.Println("密文:", hex.EncodeToString(encryptCode))
   decryptCode := AesDecryptCFB(encryptCode, []byte(key))

   fmt.Println("解密:", string(decryptCode))
}

image-20230206214824368

2.5、OFB模式

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
)

func aesEncryptOFB(data []byte, key []byte) ([]byte, error) {
	data = PKCS7Padding(data, aes.BlockSize)
	block, _ := aes.NewCipher([]byte(key))
	out := make([]byte, aes.BlockSize+len(data))
	iv := out[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}

	stream := cipher.NewOFB(block, iv)
	stream.XORKeyStream(out[aes.BlockSize:], data)
	return out, nil
}

func aesDecryptOFB(data []byte, key []byte) ([]byte, error) {
	block, _ := aes.NewCipher([]byte(key))
	iv := data[:aes.BlockSize] //aes.BlockSize=16 偏移量
	data = data[aes.BlockSize:]
	if len(data)%aes.BlockSize != 0 {
		return nil, fmt.Errorf("data is not a multiple of the block size")
	}

	out := make([]byte, len(data))
	mode := cipher.NewOFB(block, iv)
	mode.XORKeyStream(out, data)

	out = PKCS7UnPadding(out)
	return out, nil
}

//补码
//AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
	padding := blocksize - len(ciphertext)%blocksize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

//去码
func PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func main() {
	source := "hello world"
	fmt.Println("原字符:", source)
	key := "1111111111111111" //16位  32位均可
	encryptCode, _ := aesEncryptOFB([]byte(source), []byte(key))
	fmt.Println("密文:", hex.EncodeToString(encryptCode))
	decryptCode, _ := aesDecryptOFB(encryptCode, []byte(key))

	fmt.Println("解密:", string(decryptCode))
}

3、DES

DES是一种对称加密算法,又称为美国数据加密标准。DES加密时以64位分组对数据进行加密,加密和解密都使用的是同一个长度为64位的密钥,实际上只用到了其中的56位,密钥中的第8、16…64位用来作奇偶校验。DES有ECB(电子密码本)和CBC(加密块)等加密模式。DES算法的安全性很高,目前除了穷举搜索破解外, 尚无更好的的办法来破解。其密钥长度越长,破解难度就越大。
填充和去填充函数。

本代码采用CBC加密模式,填充方式采用PKCS5Padding

package main

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"fmt"
)

//DES加密方法
func MyDESEncrypt(origData, key []byte) {
	//将字节秘钥转换成block快
	block, _ := des.NewCipher(key)
	//对明文先进行补码操作
	origData = PKCS5Padding(origData, block.BlockSize())
	//设置加密方式
	blockMode := cipher.NewCBCEncrypter(block, key)
	//创建明文长度的字节数组
	crypted := make([]byte, len(origData))
	//加密明文,加密后的数据放到数组中
	blockMode.CryptBlocks(crypted, origData)
	//将字节数组转换成字符串
	fmt.Println(base64.StdEncoding.EncodeToString(crypted))

}

//实现明文的补码
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	//计算出需要补多少位
	padding := blockSize - len(ciphertext)%blockSize
	//Repeat()函数的功能是把参数一 切片复制 参数二count个,然后合成一个新的字节切片返回
	// 需要补padding位的padding值
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	//把补充的内容拼接到明文后面
	return append(ciphertext, padtext...)
}

//解密
func MyDESDecrypt(data string, key []byte) {
	//倒叙执行一遍加密方法
	//将字符串转换成字节数组
	crypted, _ := base64.StdEncoding.DecodeString(data)
	//将字节秘钥转换成block快
	block, _ := des.NewCipher(key)
	//设置解密方式
	blockMode := cipher.NewCBCDecrypter(block, key)
	//创建密文大小的数组变量
	origData := make([]byte, len(crypted))
	//解密密文到数组origData中
	blockMode.CryptBlocks(origData, crypted)
	//去补码
	origData = PKCS5UnPadding(origData)
	//打印明文
	fmt.Println(string(origData))
}

//去除补码
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	//解密去补码时需取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文
	return origData[:(length - unpadding)]
}

func main() {
	//定义明文
	data := []byte("hello world")
	//密钥
	key := []byte("12345678")
	//加密
	MyDESEncrypt(data, key)
	//解密
	MyDESDecrypt("CyqS6B+0nOGkMmaqyup7gQ==", key)
}

4、3DES

对比DES,3DES只是换了NewTripleDESCipher。不过,需要注意的是,密钥长度必须24byte

package main

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

//DES 和 3DES加密区别
//前者 加密  密钥必须是8byte
//后者加密 解密 再加密  密钥必须是24byte
func main() {
   //定义密钥,必须是24byte
   key := []byte("123456789012345678901234")
   //定义明文
   origData := []byte("hello world")

   //加密
   en := ThriDESEnCrypt(origData, key)
   fmt.Println("密文:", string(en))
   //解密
   de := ThriDESDeCrypt(en, key)
   fmt.Println("解密:", string(de))
}

//解密
func ThriDESDeCrypt(crypted, key []byte) []byte {
   //获取block块
   block, _ := des.NewTripleDESCipher(key)
   //创建切片
   context := make([]byte, len(crypted))

   //设置解密方式
   blockMode := cipher.NewCBCDecrypter(block, key[:8])
   //解密密文到数组
   blockMode.CryptBlocks(context, crypted)

   //去补码
   context = PKCSUnPadding(context)
   return context
}

//去补码
func PKCSUnPadding(origData []byte) []byte {
   length := len(origData)
   unpadding := int(origData[length-1])
   return origData[:length-unpadding]
}

//加密
func ThriDESEnCrypt(origData, key []byte) []byte {
   //获取block块
   block, _ := des.NewTripleDESCipher(key)
   //补码
   origData = PKCSPadding(origData, block.BlockSize())
   //设置加密方式为 3DES  使用3条56位的密钥对数据进行三次加密
   blockMode := cipher.NewCBCEncrypter(block, key[:8])

   //创建明文长度的数组
   crypted := make([]byte, len(origData))

   //加密明文
   blockMode.CryptBlocks(crypted, origData)

   return crypted

}

//补码
func PKCSPadding(origData []byte, blockSize int) []byte {
   //计算需要补几位数
   padding := blockSize - len(origData)%blockSize
   //在切片后面追加char数量的byte(char)
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)

   return append(origData, padtext...)
}

5、RSA

这是一个非对称加密算法,一般通过公钥加密,私钥解密。在加解密过程中,使用openssl生产密钥。执行如下操作:

5.1、创建秘钥

密钥长度,1024觉得不够安全的话可以用2048,但是代价也相应增大

openssl genrsa -out private.pem 1024  

image-20230206225109672

5.2、创建公钥

这样便生产了密钥。

openssl rsa -in private.pem -pubout -out public.pem 

image-20230206225135029

package main

import (
   "crypto/rand"
   "crypto/rsa"
   "crypto/x509"
   "encoding/pem"
   "errors"
   "fmt"
)

const privateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQD*****************rIfZtAscC/4/Nt4\n-----END RSA PRIVATE KEY-----"//修改为自己的私钥

const publicKey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCyO17QHp25mr*****************************lq+Oa5fRNQIDAQAB\n-----END PUBLIC KEY-----"//修改为自己的公钥

//加密
func RsaEncrypt(origData []byte) ([]byte, error) {
   block, _ := pem.Decode([]byte(publicKey))
   if block == nil {
      return nil, errors.New("public key error")
   }
   pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
   if err != nil {
      return nil, err
   }
   pub := pubInterface.(*rsa.PublicKey)
   return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}

//解密
func RsaDecrypt(ciphertext []byte) ([]byte, error) {
   block, _ := pem.Decode([]byte(privateKey))
   if block == nil {
      return nil, errors.New("private key error!")
   }
   priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
   if err != nil {
      return nil, err
   }
   return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}
func main() {
   data, err := RsaEncrypt([]byte("hello world"))
   if err != nil {
      panic(err)
   }
   origData, err := RsaDecrypt(data)
   if err != nil {
      panic(err)
   }
   fmt.Println(string(origData))
}

6、MD5

MD5的全称是Message-DigestAlgorithm 5,可以产生出一个128位(16进制,32个字符)的散列值(hash value),用于确保信息传输完整一致

func GetMd5String(s string) string {
   h := md5.New()
   h.Write([]byte(s))
   return hex.EncodeToString(h.Sum(nil))
}

func main() {
   text := GetMd5String("hello word")
   fmt.Println(text)
}

7、sha1

SHA-1可以生成一个被称为消息摘要的160(20字节)散列值,散列值通常的呈现形式为40个十六进制数。

func GetSha1String(s string) string {
   //产生一个散列值得方式是 sha1.New(),sha1.Write(bytes),然后 sha1.Sum([]byte{})。这里我们从一个新的散列开始。
   h := sha1.New()
   //写入要处理的字节。如果是一个字符串,需要使用[]byte(s) 来强制转换成字节数组。
   h.Write([]byte(s))
   //这个用来得到最终的散列值的字符切片。Sum 的参数可以用来都现有的字符切片追加额外的字节切片:一般不需要要。
   bs := h.Sum(nil)
   //SHA1 值经常以 16 进制输出,例如在 git commit 中。使用%x 来将散列结果格式化为 16 进制字符串。

   return hex.EncodeToString(bs)

}

func main() {
   text := GetSha1String("hello word")
   fmt.Println(text)
}

8、bcrypt

需要先下载该包(已配置goproxy和gomodule)

go get golang.org/x/crypto/bcrypt
package main

import (
	"fmt"
	"golang.org/x/crypto/bcrypt"
)

func main() {
	password := "test"
	hash, _ := bcrypt.GenerateFromPassword([]byte(password), 0)
	fmt.Println("密文:", string(hash))

	// 密码如果校验成功会返回Nil
	fmt.Println(bcrypt.CompareHashAndPassword(hash, []byte("test")))
}

9、hmac

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,

它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中。

和我们自定义的加salt算法不同,Hmac算法针对所有哈希算法都通用,无论是MD5还是SHA-1。采用Hmac替代我们自己的salt算法,可以使程序算法更标准化,也更安全。

//key随意设置 data 要加密数据
func Hmac(key, data string) string {
   hash := hmac.New(md5.New, []byte(key)) // 创建对应的md5哈希加密算法
   hash.Write([]byte(data))
   return hex.EncodeToString(hash.Sum([]byte("")))
}
func HmacSha256(key, data string) string {
   hash := hmac.New(sha256.New, []byte(key)) //创建对应的sha256哈希加密算法
   hash.Write([]byte(data))
   return hex.EncodeToString(hash.Sum([]byte("")))
}

func main() {

   key := "123456"
   data := GetMd5String("text")
   fmt.Println("加salt前:", data)
   en := Hmac(key, data)
   fmt.Println("加salt1后:", en)
   en1 := HmacSha256(key, data)
   fmt.Println("加salt2后:", en1)
}

10、base64

Base64是一种任意二进制到文本字符串的编码方法,常用于在URL、Cookie、网页中传输少量二进制数据。
首先使用Base64编码需要一个含有64个字符的表,这个表由大小写字母、数字、+和/组成。采用Base64编码处理数据时,会把每三个字节共24位作为一个处理单元,再分为四组,每组6位,查表后获得相应的字符即编码后的字符串。编码后的字符串长32位,这样,经Base64编码后,原字符串增长1/3。如果要编码的数据不是3的倍数,最后会剩下一到两个字节,Base64编码中会采用\x00在处理单元后补全,编码后的字符串最后会加上一到两个 = 表示补了几个字节。

const (
   base64Table = "IJjkKLMNO567PQX12RVW3YZaDEFGbcdefghiABCHlSTUmnopqrxyz04stuvw89+/"
)

var coder = base64.NewEncoding(base64Table)

func Base64Encode(src []byte) []byte { //编码
   return []byte(coder.EncodeToString(src))
}

func Base64Decode(src []byte) ([]byte, error) { //解码
   return coder.DecodeString(string(src))
}

func main() {
   en := Base64Encode([]byte("hello world"))
   fmt.Println(string(en))
   text, _ := Base64Decode(en)
   fmt.Println(string(text))

image-20230206233017255

参考链接

https://blog.csdn.net/wade3015/article/details/84454836

https://www.cnblogs.com/you-men/p/14160439.html

标签:origData,func,fmt,加解密,笔记,key,go,byte,block
From: https://www.cnblogs.com/akka1/p/17097062.html

相关文章

  • 工厂模式-go语言实现
    一、理论知识工厂模式的作用就是用来创建对象,细分为三种:简单工厂、工厂方法、抽象工厂。1.1应用场景工厂模式一般用于对于不同的场景,需要创建不同的对象,但是这些对象实......
  • Spring IOC官方文档学习笔记(九)之基于注解的容器配置
    1.基于注解的配置与基于xml的配置(1)在xml配置文件中,使用context:annotation-config</context:annotation-config>标签即可开启基于注解的配置,如下所示,该标签会隐式的向......
  • OpenMMLab AI实战营 第四课笔记
    OpenMMLabAI实战营第四课笔记目录OpenMMLabAI实战营第四课笔记目标检测与MMDetection1.什么是目标检测1.1目标检测的应用1.1.1目标检测in人脸识别1.1.2目标检......
  • 前端面试题学习-个人总结笔记 Day 4 JS
    前端面试题学习-个人总结笔记Day4JS这是看别人总结的基础上再度总结的,总结的链接如下链接1.对闭包的理解+严格模式+判断对象是否属于某个类+map某个案例2.......
  • 《分布式技术原理与算法解析》学习笔记Day03
    分布式互斥方法什么是分布式互斥?对于同一个共享资源,当一个程序正在使用的时候,不希望被其他程序打扰,这种排他性的资源访问方式,叫做分布式互斥,被互斥访问的共享资源被称作......
  • 谷歌关键词是怎么排名的?google关键词优化
    本文主要分享关于谷歌排名算法的一些分析以及谷歌关键词优化的细节。本文由光算创作,有可能会被修改和剽窃,我们佛系对待这种行为吧。谷歌关键词排名是指确定网站在搜索引擎结......
  • 关于Kubernetes 中通过 Kustomize 实现YAML资源文件组合与继承的一些笔记
    写在前面分享一些通过Kustomize实现YAML资源文件组合与继承的笔记官方文档里叫做组织和定制,这里这么叫方便理解博文内容基本为官网文档内容理解不足小伙伴帮忙指正......
  • [思路笔记] 线段树合并与你
    明天再来补最后一题的思路。CF208EBloodCousins题目大意给一棵\(n\)个点的树,点编号为\(1\)到\(n\)。共\(m\)次询问,每次询问给出一对整数\(v\)和\(p\),求有多......
  • 前端面试题学习-个人总结笔记 Day 3 JS
    前端面试题学习-个人总结笔记Day3JS这是看别人总结的基础上再度总结的,总结的链接如下链接1.JS基本数据类型+内部属性[[Class]]+内置对象2.内置对象3.JS......
  • 【ctf权威竞赛指南笔记】1.CTF
    赛事介绍赛事起源CTF(CaptureTheFlag)中文译作夺旗赛,原为西方传统运动,两队人马互相前往对方的基地夺取旗帜。在网络空间安全领域被用来指代技术人员之间进行技术竞技的比......