Go中使用加密算法的方法
来源:脚本之家
时间:2022-12-29 18:43:31 254浏览 收藏
怎么入门Golang编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《Go中使用加密算法的方法》,涉及到算法、go加密,有需要的可以收藏一下
哈希算法
md5
128bit,16字节
如:md5 (“hello world!”) = fc3ff98e8c6a0d3087d515c0473f8677 // 32位16进制数字
func Test(t *testing.T) { //方法一 str := "hello world!" has := md5.Sum([]byte(str)) md5str1 := fmt.Sprintf("%x", has) //将[]byte转成16进制 t.Log(md5str1) //方法二 w := md5.New() io.WriteString(w, str) md5str2 := fmt.Sprintf("%x", w.Sum(nil)) t.Log(md5str2) }
SHA1
160bit,20字节
如:SHA1 (“hello world!”) = 430ce34d020724ed75a196dfc2ad67c77772d169 // 40位16进制数字
func Test(t *testing.T) { str := "hello world!" //产生一个散列值得方式是 sha1.New(),sha1.Write(bytes),然后 sha1.Sum([]byte{})。 h := sha1.New() //写入要处理的字节。 h.Write([]byte(str)) //SHA1 值经常以 16 进制输出,例如在 git commit 中。 t.Log(hex.EncodeToString(h.Sum(nil))) }
RIPEMD-160
160bit,20字节
如:RIPEMD-160 (“hello world!”) = dffd03137b3a333d5754813399a5f437acd694e5 // 40位16进制数字
func Test(t *testing.T) { str := "hello world!" h := ripemd160.New() h.Write([]byte(str)) t.Log(hex.EncodeToString(h.Sum(nil))) }
SHA256
256bit,32字节
如:SHA256 (“hello world!”) = 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 // 64位16进制数字
func Test(t *testing.T) { str := "hello world!" // 第一种调用方法 sum := sha256.Sum256([]byte(str)) t.Logf("%x\n", sum) // 第二种调用方法 h := sha256.New() io.WriteString(h,str) t.Log(hex.EncodeToString(h.Sum(nil))) }
SHA256实现原理
附加填充⽐特
SHA-256算法输⼊报⽂的最⼤⻓度不超过2^64 bit,输⼊按512bit分组进⾏处理,产⽣的输出是⼀个256bit的报⽂摘要。
SHA256算法包括以下⼏步:
对报⽂进⾏填充,使报⽂⻓度与448 模512 同余(⻓度=448 mod512),填充的⽐特数范围是1 到512,填充⽐特串的最⾼位为1,其余位为0。就是先在报⽂后⾯加⼀个 1,再加很多个0,直到⻓度满⾜mod512=448。为什么是448,因为448+64=512。第⼆步会加上⼀个64bit的原始报⽂的 ⻓度信息。附加⻓度值
将⽤64bit 表示的初始报⽂(填充前)的位⻓度附加在步骤1的结果后(低位字节优先)初始化缓存
使⽤⼀个256bit 的缓存来存放该散列函数的中间及最终结果。该缓存表示为:
A=0x6A09E667
B=0xBB67AE85
C=0x3C6EF372
D=0xA54FF53A
E=0x510E527F
F=0x9B05688C
G=0x1F83D9AB
H=0x5BE0CD19处理512bit(16 个字)报⽂分组序列
该算法使⽤了六种基本逻辑函数,由64 步迭代运算组成。每步都以256bit 缓存ABCDEFGH 为输⼊,然后更新缓存内容。每步使⽤⼀个32bit 常数值Kt 和⼀个32bit Wt。
SHA512
512bit,64字节
如:SHA512 (“hello world!”) = db9b1cd3262dee37756a09b9064973589847caa8e53d31a9d142ea2701b1b28abd97838bb9a27068ba305dc8d04a45a1fcf079de54d607666996b3cc54f6b67c // 128位16进制数字
func Test(t *testing.T) { str := "hello world!" // 第一种调用方法 sum := sha512.Sum512([]byte(str)) t.Logf("%x\n", sum) // 第二种调用方法 h := sha512.New() io.WriteString(h,str) t.Log(hex.EncodeToString(h.Sum(nil))) }
加密模式
加密一般分为对称加密(Symmetric Key Encryption)和非对称加密(Asymmetric Key Encryption)。
对称加密又分为分组加密和序列密码。
分组密码,也叫块加密(block cyphers),一次加密明文中的一个块。是将明文按一定的位长分组,明文组经过加密运算得到密文组,密文组经过解密运算(加密运算的逆运算),还原成明文组。
序列密码,也叫流加密(stream cyphers),一次加密明文中的一个位。是指利用少量的密钥(制乱元素)通过某种复杂的运算(密码算法)产生大量的伪随机位流,用于对明文位流的加密。
解密是指用同样的密钥和密码算法及与加密相同的伪随机位流,用以还原明文位流。
分组加密算法中,有ECB,CBC,CFB,OFB这几种算法模式。
加密模式 | 解释 |
---|---|
ECB | 最基本的加密模式,也就是通常理解的加密,相同的明⽂将永远加密成相同的密⽂,⽆初始向量,容易受到密码本重放攻击,⼀般情况下很少⽤ |
CBC | 明⽂被加密前要与前⾯的密⽂进⾏异或运算后再加密,因此只要选择不同的初始向量,相同的密⽂加密后会形成不同的密⽂,这是⽬前应⽤最⼴泛的模式。CBC加密后的密⽂是上下⽂相关的,但明⽂的错误不会传递到后续分组,但如果⼀个分组丢失,后⾯的分组将全部作废(同步错误) |
CFB | 类似于⾃同步序列密码,分组加密后,按8位分组将密⽂和明⽂进⾏移位异或后得到输出同时反馈回移位寄存器,优点最⼩可以按字节进⾏加解密,也可以是n位的,CFB也是上下⽂相关的,CFB模式下,明⽂的⼀个错误会影响后⾯的密⽂(错误扩散)。 |
OFB | 将分组密码作为同步序列密码运⾏,和CFB相似,不过OFB⽤的是前⼀个n位密⽂输出分组反馈回移位寄存器,OFB没有错误扩散问题 |
对称加密
最常用的对称加密算法DES、3DES(TripleDES)和AES,常采用的填充⽅式是NoPadding(不填充)、Zeros填充(0填充)、PKCS5Padding填充。
加密算法要求明文需要按一定长度对齐,叫做块大小(BlockSize),比如8字节,那么对于一段任意的数据,加密前需要对最后一个块填充到8 字节,解密后需要删除掉填充的数据。
填充⽅式 | 解释 |
---|---|
ZeroPadding | 数据长度不对齐时使用0填充,否则不填充 |
PKCS7Padding | 假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小 |
PKCS5Padding | PKCS7Padding的子集,块大小固定为8字节 |
由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。
对称加密需要的填充函数
func PKCS5Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padtext := bytes.Repeat([]byte{byte(padding)}, padding) return append(data, padtext...) } func PKCS5UnPadding(data []byte) []byte { length := len(data) // 去掉最后⼀个字节 unpadding 次 unpadding := int(data[length-1]) return data[:(length - unpadding)] } func ZeroPadding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padtext := bytes.Repeat([]byte{0}, padding) return append(data, padtext...) } func ZeroUnPadding(data []byte) []byte { return bytes.TrimRightFunc(data, func(r rune) bool { return r == rune(0) }) }
DES
//DES加密字节数组,返回字节数组 func DesEncrypt(originalBytes, key []byte) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { return nil, err } originalBytes = PKCS5Padding(originalBytes, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, key) cipherArr := make([]byte, len(originalBytes)) blockMode.CryptBlocks(cipherArr, originalBytes) return cipherArr, nil } //DES解密字节数组,返回字节数组 func DesDecrypt(cipherBytes, key []byte) ([]byte, error) { block, err := des.NewCipher(key) if err != nil { return nil, err } blockMode := cipher.NewCBCDecrypter(block, key) originalText := make([]byte, len(cipherBytes)) blockMode.CryptBlocks(originalText, cipherBytes) originalText = PKCS5UnPadding(originalText) return originalText, nil } //DES加密⽂本,返回加密后⽂本 func DesEncryptString(originalText string, key []byte) (string, error) { cipherArr, err := DesEncrypt([]byte(originalText), key) if err != nil { return "", err } base64str := base64.StdEncoding.EncodeToString(cipherArr) return base64str, nil } //对加密⽂本进⾏DES解密,返回解密后明⽂ func DesDecryptString(cipherText string, key []byte) (string, error) { cipherArr, _ := base64.StdEncoding.DecodeString(cipherText) cipherArr, err := DesDecrypt(cipherArr, key) if err != nil { return "", err } return string(cipherArr), nil }
3DES
// 3DES加密字节数组,返回字节数组 func TripleDesEncrypt(originalBytes, key []byte) ([]byte, error) { block, err := des.NewTripleDESCipher(key) if err != nil { return nil, err } originalBytes = PKCS5Padding(originalBytes, block.BlockSize()) // originalBytes = ZeroPadding(originalBytes, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, key[:8]) cipherArr := make([]byte, len(originalBytes)) blockMode.CryptBlocks(cipherArr, originalBytes) return cipherArr, nil } // 3DES解密字节数组,返回字节数组 func TripleDesDecrypt(cipherBytes, key []byte) ([]byte, error) { block, err := des.NewTripleDESCipher(key) if err != nil { return nil, err } blockMode := cipher.NewCBCDecrypter(block, key[:8]) originalArr := make([]byte, len(cipherBytes)) blockMode.CryptBlocks(originalArr, cipherBytes) originalArr = PKCS5UnPadding(originalArr) // origData = ZeroUnPadding(origData) return originalArr, nil } // 3DES加密字符串,返回base64处理后字符串 func TripleDesEncrypt2Str(originalText string, key []byte) (string, error) { block, err := des.NewTripleDESCipher(key) if err != nil { return "", err } originalData := PKCS5Padding([]byte(originalText), block.BlockSize()) // originalData = ZeroPadding(originalData, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, key[:8]) cipherArr := make([]byte, len(originalData)) blockMode.CryptBlocks(cipherArr, originalData) cipherText := base64.StdEncoding.EncodeToString(cipherArr) return cipherText, nil } // 3DES解密base64处理后的加密字符串,返回明⽂字符串 func TripleDesDecrypt2Str(cipherText string, key []byte) (string, error) { cipherArr, _ := base64.StdEncoding.DecodeString(cipherText) block, err := des.NewTripleDESCipher(key) if err != nil { return "", err } blockMode := cipher.NewCBCDecrypter(block, key[:8]) originalArr := make([]byte, len(cipherArr)) blockMode.CryptBlocks(originalArr, cipherArr) originalArr = PKCS5UnPadding(originalArr) // origData = ZeroUnPadding(origData) return string(originalArr), nil }
AES
//AES加密字节数组,返回字节数组 func AesEncrypt(originalBytes, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() originalBytes = PKCS5Padding(originalBytes, blockSize) // originalBytes = ZeroPadding(originalBytes, block.BlockSize()) blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) cipherBytes := make([]byte, len(originalBytes)) // 根据CryptBlocks⽅法的说明,如下⽅式初始化crypted也可以 // crypted := originalBytes blockMode.CryptBlocks(cipherBytes, originalBytes) return cipherBytes, nil } //AES解密字节数组,返回字节数组 func AesDecrypt(cipherBytes, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) originalBytes := make([]byte, len(cipherBytes)) // origData := cipherBytes blockMode.CryptBlocks(originalBytes, cipherBytes) originalBytes = PKCS5UnPadding(originalBytes) // origData = ZeroUnPadding(origData) return originalBytes, nil } //AES加密⽂本,返回对加密后字节数组进⾏base64处理后字符串 func AesEncryptString(originalText string, key []byte) (string, error) { cipherBytes, err := AesEncrypt([]byte(originalText), key) if err != nil { return "", err } base64str := base64.StdEncoding.EncodeToString(cipherBytes) return base64str, nil } //AES解密⽂本,对Base64处理后的加密⽂本进⾏AES解密,返回解密后明⽂ func AesDecryptString(cipherText string, key []byte) (string, error) { cipherBytes, _ := base64.StdEncoding.DecodeString(cipherText) cipherBytes, err := AesDecrypt(cipherBytes, key) if err != nil { return "", err } return string(cipherBytes), nil }
⾮对称加密
RSA算法也是一个块加密算法( block cipher algorithm),总是在一个固定长度的块上进行操作。但跟AES等不同的是,block length是跟key length有关的。
每次RSA加密的明文的长度是受RSA填充模式限制的,但是RSA每次加密的块长度就是key length。
填充⽅式 | 密文长度 |
---|---|
PKCS1Padding | 必须 比 RSA 秘钥模长(modulus) 短至少11个字节, 也就是RSA_SIZE(rsa) – 11 |
OAEPPadding | RSA_SIZE(rsa) – 41 |
NOPadding | 可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充 |
在不同的padding模式下,使用相同长度的密钥可以加密的数据最大长度不同在不同密钥长度下,使用相同的padding模式可以加密的数据最大长度也不同
因此,脱离了密钥长度而讨论padding模式可以加密的最大长度是不严谨的。常用的密钥长度有1024bits,2048bits等,理论上1024bits的密钥可以加密的数据最大长度为1024bits(即1024/8 = 128bytes)。2048bits的密钥可以加密的数据最大长度为2048bits(2048/8 = 256bytes),但是RSA在实际应用中不可能使用这种“教科书式的RSA”系统。实际应用中RSA经常与填充技术(padding)一起使用,可以增加RSA的安全性。
PKCS1
// 加密字节数组,返回字节数组 func RsaEncrypt(publicKey, origData []byte) ([]byte, error) { block, _ := pem.Decode(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(privateKey, ciphertext []byte) ([]byte, error) { block, _ := pem.Decode(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) } // 加密字符串,返回base64处理的字符串 func RsaEncryptString(publicKey []byte, origData string) (string, error) { block, _ := pem.Decode(publicKey) if block == nil { return "", errors.New("public key error") } pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return "", err } pub := pubInterface.(*rsa.PublicKey) cipherArr, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(origData)) if err != nil { return "", err } else { return base64.StdEncoding.EncodeToString(cipherArr), nil } } // 解密经过base64处理的加密字符串,返回加密前的明⽂ func RsaDecryptString(privateKey []byte, cipherText string) (string, error) { block, _ := pem.Decode(privateKey) if block == nil { return "", errors.New("private key error!") } priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", err } cipherArr, _ := base64.StdEncoding.DecodeString(cipherText) originalArr, err := rsa.DecryptPKCS1v15(rand.Reader, priv, cipherArr) if err != nil { return "", err } else { return string(originalArr), nil } }
OAEP
// 加密 func EncryptOAEP(publicKey []byte, text string) (string, error) { block, _ := pem.Decode(publicKey) if block == nil { return "", errors.New("public key error") } pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return "", err } pub := pubInterface.(*rsa.PublicKey) secretMessage := []byte(text) rng := rand.Reader cipherdata, err := rsa.EncryptOAEP(sha1.New(), rng, pub, secretMessage, nil) if err != nil { return "", err } ciphertext := base64.StdEncoding.EncodeToString(cipherdata) return ciphertext, nil } // 解密 func DecryptOAEP(privateKey []byte, ciphertext string) (string, error) { block, _ := pem.Decode(privateKey) if block == nil { return "", errors.New("private key error!") } priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { return "", err } cipherdata, _ := base64.StdEncoding.DecodeString(ciphertext) rng := rand.Reader plaintext, err := rsa.DecryptOAEP(sha1.New(), rng, priv, cipherdata, nil) if err != nil { return "", err } return string(plaintext), nil }
椭圆曲线加密算法ECC
椭圆曲线密码学(Elliptic curve cryptography,缩写为 ECC),是基于椭圆曲线数学理论实现的⼀种⾮对称加密算法。
ECC与RSA算法的优势对⽐
椭圆曲线公钥系统是代替RSA的强有⼒的竞争者。
与经典的RSA、DSA等公钥密码体制相⽐,椭圆密码体制有以下优点:
(1)安全性能更⾼(ECC可以使⽤更短的密钥):
160位ECC加密算法的安全强度相当于1024位RSA加密;
210位ECC加密算法的安全强度相当于2048位RSA加密。
(2)处理速度快:计算量⼩,处理速度快 在私钥的处理速度上(解密和签名),ECC远 ⽐RSA、DSA快得多。
(3)存储空间占⽤⼩: ECC的密钥尺⼨和系统参数与RSA、DSA相⽐要⼩得多, 所以占⽤的存储空间⼩得多。
(4)带宽要求低使得ECC具有⼴泛的应⽤前景。ECC的这些特点使它必将取代RSA,成为通⽤的公钥加密算法。
//生成ECC椭圆曲线密钥对 func GenerateECCKey() (*ecdsa.PublicKey, *ecdsa.PrivateKey, error) { privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { return nil, nil, err } publicKey := &privateKey.PublicKey return publicKey, privateKey, nil } //对消息的散列值生成数字签名 func SignECC(msg []byte) ([]byte, []byte) { //取得私钥 _, privateKey, err := GenerateECCKey() if err != nil { panic(err) } //计算哈希值 hash := sha256.New() //填入数据 hash.Write(msg) b := hash.Sum(nil) //对哈希值生成数字签名 r, s, err := ecdsa.Sign(rand.Reader, privateKey, b) if err != nil { panic(err) } rtext, _ := r.MarshalText() stext, _ := s.MarshalText() return rtext, stext } //验证数字签名 func VerifySignECC(msg []byte, rtext, stext []byte) bool { //取得公钥 publicKey, _, err := GenerateECCKey() if err != nil { panic(err) } //计算哈希值 hash := sha256.New() hash.Write(msg) b := hash.Sum(nil) //验证数字签名 var r, s big.Int if err := r.UnmarshalText(rtext); err != nil { panic(err) } if err := s.UnmarshalText(stext); err != nil { panic(err) } verify := ecdsa.Verify(publicKey, b, &r, &s) return verify } //测试 func Test(t *testing.T) { //模拟发送者 //要发送的消息 msg := []byte("hello world") //生成数字签名 rtext, stext := SignECC(msg) //模拟接受者 //接受到的消息 acceptmsg := []byte("hello world") //接收到的签名 acceptrtext := rtext acceptstext := stext //验证签名 verifySignECC := VerifySignECC(acceptmsg, acceptrtext, acceptstext) fmt.Println("验证结果:", verifySignECC) }
数字签名
数字签名的概念
1、签名不可伪造性;
2、签名不可抵赖的;
3、签名可信性,签名的识别和应⽤相对容易,任何⼈都可以验证签名的有效
性;
4、签名是不可复制的,签名与原⽂是不可分割的整体;
5、签名消息不可篡改,因为任意⽐特数据被篡改,其签名便被随之改变,那么
任何⼈可以验证⽽拒绝接受此签名。
椭圆曲线数字签名算法ECDSA
//⽣成私钥和公钥,⽣成的私钥为结构体ecdsa.PrivateKey的指针 func NewKeyPair() (ecdsa.PrivateKey, []byte) { //⽣成secp256椭圆曲线 curve := elliptic.P256() //产⽣的是⼀个结构体指针,结构体类型为ecdsa.PrivateKey private, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { log.Panic(err) } fmt.Printf("私钥:%x\n", private) fmt.Printf("私钥X:%x\n", private.X.Bytes()) fmt.Printf("私钥Y:%x\n", private.Y.Bytes()) fmt.Printf("私钥D:%x\n", private.D.Bytes()) //x坐标与y坐标拼接在⼀起,⽣成公钥 pubKey := append(private.X.Bytes(), private.Y.Bytes()...) //打印公钥,公钥⽤16进制打印出来⻓度为128,包含了x轴坐标与y轴坐标。 fmt.Printf("公钥:%x \n", pubKey) return *private, pubKey } //⽣成签名的DER格式 func MakeSignatureDerString(r, s string) string { // 获取R和S的⻓度 lenSigR := len(r) / 2 lenSigS := len(s) / 2 // 计算DER序列的总⻓度 lenSequence := lenSigR + lenSigS + 4 // 将10进制⻓度转16进制字符串 strLenSigR := DecimalToHex(int64(lenSigR)) strLenSigS := DecimalToHex(int64(lenSigS)) strLenSequence := DecimalToHex(int64(lenSequence)) // 拼凑DER编码 derString := "30" + strLenSequence derString = derString + "02" + strLenSigR + r derString = derString + "02" + strLenSigS + s derString = derString + "01" return derString } func DecimalToHex(n int64) string { if n 0; q = q / 16 { m := q % 16 if m > 9 && m字符编码/解码
Base64
Base64就是⼀种基于64个可打印字符来表示⼆进制数据的⽅法。Base64使⽤了26个⼩写字⺟、26个⼤写字⺟、10个数字以及两个符号(例如“+”和“/”),⽤于在电⼦邮件这样的基于⽂本的媒介中传输⼆进制数据。
Base64字符集:
将每个字符转成ASCII编码(10进制)将10进制编码转成2进制编码将2进制编码按照6位⼀组进⾏平分将6位⼀组的2进制数⾼位补零,然后转成10进制数将10进制数作为索引,从Base64编码表中查找字符每3个字符的⽂本将编码为4个字符⻓度(38=46)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
Base64的步骤
a. 若⽂本为3个字符,则正好编码为4个字符⻓度;
b. 若⽂本为2个字符,则编码为3个字符,由于不⾜4个字符,则在尾部⽤⼀个“=”补⻬;
c. 若⽂本为1个字符,则编码为2个字符,由于不⾜4个字符,则在尾部⽤两个“=”补⻬。巨人的肩膀
从他人的工作中汲取经验来避免自己的错误重复,正如我们是站在巨人的肩膀上才能做出更好的成绩。
https://github.com/rubyhan1314/Golang-100-Days
https://blog.csdn.net/luckydog612/article/details/80547758
https://www.cnblogs.com/yanzi-meng/p/9640578.html
https://www.cnblogs.com/starwolf/p/3365834.html
https://blog.csdn.net/u013073067/article/details/87086562
https://www.cnblogs.com/Terry-Wu/p/10314315.html
http://blog.studygolang.com/2013/01/go%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%E4%B9%8Bdes/
https://blog.csdn.net/kikajack/article/details/78329567以上就是《Go中使用加密算法的方法》的详细内容,更多关于golang的资料请关注golang学习网公众号!
-
398 收藏
-
327 收藏
-
319 收藏
-
272 收藏
-
130 收藏
-
188 收藏
-
317 收藏
-
430 收藏
-
451 收藏
-
311 收藏
-
408 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习