Go中使用加密算法的方法

 更新时间:2021年8月14日 16:00  点击:2127

哈希算法

 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 {
		log.Println("Decimal to hexadecimal error: the argument must be greater than zero.")
		return ""
	}
	if n == 0 {
		return "0"
	}

	hex := map[int64]int64{10: 65, 11: 66, 12: 67, 13: 68, 14: 69, 15: 70}
	s := ""
	for q := n; q > 0; q = q / 16 {
		m := q % 16
		if m > 9 && m < 16 {
			m = hex[m]
			s = fmt.Sprintf("%v%v", string(m), s)
			continue
		}
		s = fmt.Sprintf("%v%v", m, s)
	}
	return s
}

//验证签名1
func VerifySig(pubKey, message []byte, r, s *big.Int) bool {
	curve := elliptic.P256()
	//公钥的⻓度
	keyLen := len(pubKey)
	//前⼀半为x轴坐标,后⼀半为y轴坐标
	x := big.Int{}
	y := big.Int{}
	x.SetBytes(pubKey[:(keyLen / 2)])
	y.SetBytes(pubKey[(keyLen / 2):])
	rawPubKey := ecdsa.PublicKey{curve, &x, &y}
	//根据交易哈希、公钥、数字签名验证成功。ecdsa.Verify func Verify(pub *PublicKey, hash[] byte, r * big.Int, s * big.Int) bool
	res := ecdsa.Verify(&rawPubKey, message, r, s)
	return res
}

//验证签名2
func VerifySignature(pubKey, message []byte, r, s string) bool {
	curve := elliptic.P256()
	//公钥的⻓度
	keyLen := len(pubKey)
	//前⼀半为x轴坐标,后⼀半为y轴坐标
	x := big.Int{}
	y := big.Int{}
	x.SetBytes(pubKey[:(keyLen / 2)])
	y.SetBytes(pubKey[(keyLen / 2):])
	rawPubKey := ecdsa.PublicKey{curve, &x, &y}
	//根据交易哈希、公钥、数字签名验证成功。ecdsa.Verify func Verify(pub *PublicKey, hash[] byte, r * big.Int, s * big.Int) bool
	rint := big.Int{}
	sint := big.Int{}
	rByte, _ := hex.DecodeString(r)
	sByte, _ := hex.DecodeString(s)
	rint.SetBytes(rByte)
	sint.SetBytes(sByte)
	//fmt.Println("------", rint.SetBytes(rByte))
	//fmt.Println("------", sint.SetBytes(sByte))
	res := ecdsa.Verify(&rawPubKey, message, &rint, &sint)
	return res
}

//验证过程
func Test(t *testing.T) {
	//1、⽣成签名
	fmt.Println("1、⽣成签名-------------------------------")
	//调⽤函数⽣成私钥与公钥
	privKey, pubKey := NewKeyPair()
	//信息的哈希
	msg := sha256.Sum256([]byte("hello world"))
	//根据私钥和信息的哈希进⾏数字签名,产⽣r和s
	r, s, _ := ecdsa.Sign(rand.Reader, &privKey, msg[:])
	//⽣成r、s字符串
	fmt.Println("-------------------------------")
	strSigR := fmt.Sprintf("%x", r)
	strSigS := fmt.Sprintf("%x", s)
	fmt.Println("r、s的10进制分别为:", r, s)
	fmt.Println("r、s的16进制分别为:", strSigR, strSigS)
	//r和s拼接在⼀起,形成数字签名的der格式
	signatureDer := MakeSignatureDerString(strSigR, strSigS)
	//打印数字签名的16进制显示
	fmt.Println("数字签名DER格式为:", signatureDer)
	fmt.Println()
	//2、签名验证过程
	fmt.Println("2、签名验证过程-------------------------------")
	res := VerifySig(pubKey, msg[:], r, s)
	fmt.Println("签名验证结果:", res)
	res = VerifySignature(pubKey, msg[:], strSigR, strSigS)
	fmt.Println("签名验证结果:", res)
}

字符编码/解码

Base64

Base64就是⼀种基于64个可打印字符来表示⼆进制数据的⽅法。Base64使⽤了26个⼩写字⺟、26个⼤写字⺟、10个数字以及两个符号(例如“+”和“/”),⽤于在电⼦邮件这样的基于⽂本的媒介中传输⼆进制数据。

Base64字符集:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
在这里插入图片描述


Base64的步骤

将每个字符转成ASCII编码(10进制)将10进制编码转成2进制编码将2进制编码按照6位⼀组进⾏平分将6位⼀组的2进制数⾼位补零,然后转成10进制数将10进制数作为索引,从Base64编码表中查找字符每3个字符的⽂本将编码为4个字符⻓度(38=46)
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中使用加密算法的方法的文章就介绍到这了,更多相关go 加密算法内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

[!--infotagslink--]

相关文章

  • 图解PHP使用Zend Guard 6.0加密方法教程

    有时为了网站安全和版权问题,会对自己写的php源码进行加密,在php加密技术上最常用的是zend公司的zend guard 加密软件,现在我们来图文讲解一下。 下面就简单说说如何...2016-11-25
  • Go应用中优雅处理Error的技巧总结

    在程序员中,尤其是go新手,经常听到的一个讨论话题是:如何处理错误,这篇文章主要给大家介绍了关于Go应用中优雅处理Error的一些相关技巧,需要的朋友可以参考下...2021-09-08
  • C#几种排序算法

    作者:Sabine 【导读】本文介绍了C#的四种排序算法:冒泡排序、选择排序、插入排序和希尔排序  冒泡排序 using System; namespace BubbleSorter { public class Bubb...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • vue接口请求加密实例

    这篇文章主要介绍了vue接口请求加密实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
  • Django def clean()函数对表单中的数据进行验证操作

    这篇文章主要介绍了Django def clean()函数对表单中的数据进行验证操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-09
  • AES加密解密的例子小结

    关于AES加密的算法我们就不说了,这里主要给各位演示了三个关于AES算法实现的加密例子,希望本文章能给你带来帮助。 话不多说,先放上代码,一共有两个文件:AES.php(aes算...2016-11-25
  • node.JS md5加密中文与php结果不一致怎么办

    这次文章要给大家介绍的是node.JS md5加密中文与php结果不一致怎么办,不知道具体解决办法的下面跟小编一起来看看。 因项目需要,需要Node.js与PHP做接口调用,发现nod...2017-07-06
  • NODE.JS加密模块CRYPTO常用方法介绍

    使用require('crypto')调用加密模块。加密模块需要底层系统提供OpenSSL的支持。它提供了一种安全凭证的封装方式,可以用于HTTPS安全网络以及普通HTTP连接。该模块还提供了一套针对OpenSSL的hash(哈希),hmac(密钥哈希),cipher...2014-06-07
  • C#连接加密的Sqlite数据库的方法

    对数据加密分两种,一种是对数据库本身进行加密,另一种是对数据表中的数据进行加密,下面通过本文给大家介绍C#连接加密的Sqlite数据库的方法,感兴趣的朋友一起看看吧...2020-06-25
  • C#实现对文件进行加密解密的方法

    这篇文章主要介绍了C#实现对文件进行加密解密的方法,涉及C#加密与解密的技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C#中实现任意List的全组合算法代码

    这篇文章主要是介绍了.net C# 实现任意List的全组合算法实现代码,需要的朋友可以参考下...2020-06-25
  • 同时兼容JS和C#的RSA加密解密算法详解(对web提交的数据加密传输)

    这篇文章主要给大家介绍了关于同时兼容JS和C#的RSA加密解密算法,通过该算法可以对web提交的数据进行加密传输,文中通过图文及示例代码介绍的非常详细,需要的朋友们可以参考借鉴,下面来一起看看吧。...2020-06-25
  • golang官方嵌入文件到可执行程序的示例详解

    这篇文章主要介绍了golang官方嵌入文件到可执行程序,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-20
  • go浮点数转字符串保留小数点后N位的完美解决方法

    这篇文章主要介绍了go浮点数转字符串保留小数点后N位解决办法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-05-11
  • 图文详解Heap Sort堆排序算法及JavaScript的代码实现

    这篇文章以图文详解Heap Sort堆排序算法及JavaScript的代码实现,堆排序算法基于类二叉树的堆数据结构,需要的朋友可以参考下...2016-05-05
  • php使用异或实现的加解密的方法

    若a=b ^ c;则b=a ^ c (^是异或的意思),php在处理异或的字符时先把字符转化为二进制的ascii值,对这些值进行异或,获取结果后在将ascii值转化为字符...2013-09-26
  • Go语言使用读写OPC详解

    这篇文章主要介绍了Go语言使用读写OPC详解,图文讲解的很清晰,有感兴趣的同学可以学习下...2021-03-05
  • go语言使用RC4加密的方法

    这篇文章主要介绍了go语言使用RC4加密的方法,实例分析了RC4加密的技巧与实现方法,具有一定参考借鉴价值,需要的朋友可以参考下...2020-05-07
  • C#常用数据结构和算法总结

    这篇文章主要介绍了C#常用数据结构和算法,这里我们总结了一些知识点,可以帮助大家理解这些概念。...2020-06-25