Go中使用加密算法的方法

目录
  • 哈希算法
  • 加密模式
  • 对称加密
  • ⾮对称加密
  • 椭圆曲线加密算法ECC
  • 数字签名
  • 字符编码/解码
  • 巨人的肩膀

哈希算法

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 加密算法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Go实现基于RSA加密算法的接口鉴权

    基于 RSA 加密算法的接口鉴权方案 假设接口调用者是客户端,接口提供方是服务端,则此方案存在以下规则: 客户端需要使用 RSA 算法(1024 位长度的私钥)生成公私钥,并将公钥下发给服务端: 客户端使用私钥对请求内容加签,然后需要同时将请求内容和签名一并发给服务端: 服务端收到请求后,使用客户端给的公钥对请求内容和签名进行验签,以确定请求是来自客户端的. 生成公私钥 # 生成 1024 位长度的私钥 openssl genrsa -out private-key.pem 1024 # 生成公

  • Go语言实现AzDG可逆加密算法实例

    本文实例讲述了Go语言实现AzDG可逆加密算法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: package main import (  "fmt"  "crypto/md5"  "encoding/base64"  "time" ) var cipher = "密鑰" var h = md5.New() func cipherEncode(sourceText string) stri

  • Django自带的加密算法及加密模块详解

    Django 内置的User类提供了用户密码的存储.验证.修改等功能,可以很方便你的给用户提供密码服务. 默认的Ddjango使用pbkdf2_sha256方式来存储和管理用的密码,当然是可以自定义的. Django 通过PASSWORD_HASHERS 设置选择要使用的算法. 下面有一个列表,列出了Django 支持的哈希算法类.列表的第一个元素 (即settings.PASSWORD_HASHERS[0]) 会用于储存密码, 所有其它元素都是用于验证的哈希值,它们可以用于检查现有的密码.意思

  • 简单介绍django提供的加密算法

    导包 from django.contrib.auth.hashers import make_password, check_password 加密 # 原密码 1234 password = '1234' # 加密 make_password(password) # pbkdf2_sha256$120000$S92tuv6RM7Ct$SwDIx5MYxahhSCFWf/OmA650rZTvqbW7QcbNLw/Oq/I= 解密 # 加密后的密码 pwd = 'pbkdf2_sha256$12

  • Go中使用加密算法的方法

    目录 哈希算法 加密模式 对称加密 ⾮对称加密 椭圆曲线加密算法ECC 数字签名 字符编码/解码 巨人的肩膀 哈希算法 md5 128bit,16字节 如:md5 ("hello world!") = fc3ff98e8c6a0d3087d515c0473f8677 // 32位16进制数字 func Test(t *testing.T) { //方法一 str := "hello world!" has := md5.Sum([]byte(str)) md5str

  • iOS中MD5加密算法的介绍和使用

    前言 软件开发过程中,对数据进行加密是保证数据安全的重要手段,常见的加密有Base64加密和MD5加密.Base64加密是可逆的,MD5加密目前来说一般是不可逆的. MD5生成的是固定的128bit,即128个0和1的二进制位,而在实际应用开发中,通常是以16进制输出的,所以正好就是32位的16进制,说白了也就是32个16进制的数字. MD5主要特点是 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样(也不是绝对的,但基本是不能一样的). MD5算法还具有以下性质: 1.压缩性:任意

  • 在Angular中使用JWT认证方法示例

    本文介绍了在Angular中使用JWT认证方法示例,分享给大家,具体如下: 项目地址: grading-system 基于session的认证和基于token的认证的方式已经被广泛使用.在session认证中,服务端会存储一份用户登录信息,这份登录信息会在响应时传递给浏览器并保存为Cookie,在下次请求时,会带上这份登录信息,这样就能识别请求来自哪个用户. 在基于session的认证中,每个用户都要生成一份session,这份session通常保存在内存中,随着用户量的增加,服务端的开销会增大

  • ThinkPHP5框架中使用JWT的方法示例

    本文实例讲述了ThinkPHP5框架中使用JWT的方法.分享给大家供大家参考,具体如下: JWT下载地址:https://jwt.io 可以直接去github上下载,也可以使用composer 使用composer的话要确保你的电脑上安装了composer,进入项目根目录下载即了,自动会放在vendor目录下 创建文件 我是放在common目录下 使用教程 github都有的 贴源码 JWTAuth.php <?php namespace app\common\Auth; use \Lcobuc

  • Golang中常见加密算法的总结

    目录 1.md5 加密——不可逆 2.hmacsha 加密——不可逆 hmac-md5加密 hamacsha1 加密 hamacsha 256 加密 hmacsha512加密 hamasha 调用 3.Sha 加密——不可逆 sha1 sha256 sha512 sha调用 4.base 加密 解密 加密 解密 base64 调用 5.AES 加密 CBC方式 ECB方式 CFB 方式 6.RSA加密 RSA加密 RSA分段加密 7.DES加密 内置库完成 使用第三方库 8.3DES加密算法 如

  • AngularJs中route的使用方法和配置

    angular是Google开发的一个单页面应用框架,是现在比较主流的单页面应用框架之一.该强大的地方有很多,比如双向数据绑定,应用了后端的MVC模式到前端,自定义指令等. 既然是单页面应用,肯定离不开页面的切换.我们首先来说一下angular的路由. angular实现页面切换时用了route. <script src="js/plugins/angular/angular.min.js"></script> <script src="js/p

  • 深入学习JavaScript的AngularJS框架中指令的使用方法

    指令(Directives)是所有AngularJS应用最重要的部分.尽管AngularJS已经提供了非常丰富的指令,但还是经常需要创建应用特定的指令.这篇教程会为你讲述如何自定义指令,以及介绍如何在实际项目中使用.在这篇文章的最后(第二部分),我会指导你如何使用Angular指令来创建一个简单的记事本应用. 概述 一个指令用来引入新的HTML语法.指令是DOM元素上的标记,使元素拥有特定的行为.举例来说,静态的HTML不知道如何来创建和展现一个日期选择器控件.让HTML能识别这个语法,我们需要

  • AngularJS入门教程二:在路由中传递参数的方法分析

    本文实例讲述了AngularJS在路由中传递参数的方法.分享给大家供大家参考,具体如下: 我们不仅可以在控制器中直接定义属性的值,比如: app.controller('listController',function($scope){ $scope.name="ROSE"; }); AngularJS还提供了传递参数的功能,目前我接触到的一种方式是从视图中传参: <!--首页html--> <li><a href="#/user/18"

  • Java中synchronized关键字修饰方法同步的用法详解

    Java的最基本的同步方式,即使用synchronized关键字来控制一个方法的并发访问. 每一个用synchronized关键字声明的方法都是临界区.在Java中,同一个对象的临界区,在同一时间只有一个允许被访问. 静态方法则有不同的行为.用synchronized关键字声明的静态方法,同时只能够被一个执行线程访问,但是其他线程可以访问这个对象的非静态的synchronized方法.必须非常谨慎这一点,因为两个线程可以同时访问一个对象的两个不同的synchronized方法,即其中一个是静态s

  • python编程之requests在网络请求中添加cookies参数方法详解

    哎,好久没有学习爬虫了,现在想要重新拾起来.发现之前学习爬虫有些粗糙,竟然连requests中添加cookies都没有掌握,惭愧.废话不宜多,直接上内容. 我们平时使用requests获取网络内容很简单,几行代码搞定了,例如: import requests res=requests.get("https://cloud.flyme.cn/browser/index.jsp") print res.content 你没有看错,真的只有三行代码.但是简单归简单,问题还是不少的. 首先,这

随机推荐