📌Golang📌应用📌encoding编码和hash哈希.txt
hex也称为base16,使用16个可见字符来表示一个二进制数组,编码后数据大小变成原来的2倍。
base32使用32个可见字符来表示一个二进制数组,编码后数据大小变成原来的8/5,如果不足8个字符默认会填充=。
base64使用64个可见字符来表示一个二进制数组,编码后数据大小变成原来的4/3,如果不足4个字符默认会填充=。
base64是具有比较高的空间效率的。hex和base32编码不区分大小写,base64区分大小写。

"encoding/hex"包实现了16进制字符表示的编解码。

"encoding/base32"包实现了RFC-4648规定的base32编码。
预定义常量:
	StdPadding rune = '=' // Standard padding character
	NoPadding  rune = -1  // No padding
	encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
	encodeHex = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
func NewEncoding(encoder string) *Encoding
使用给定的字符集创建一个编码规则,字符集长度必须为32。
func (enc Encoding) WithPadding(padding rune) *Encoding
创建一个指定填充符号的编码规则,padding不能是换行符或大于\xff,不要填充可设为预定义常量NoPadding。
预定义编码规则:
	StdEncoding //RFC-4648定义的标准base32编码字符集。
	HexEncoding //RFC-4648定义的扩展Hex字符集,用于DNS。
	
"encoding/base64"包实现了RFC-4648规定的base64编码。
预定义常量:
	StdPadding rune = '=' // Standard padding character
	NoPadding  rune = -1  // No padding
	encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
	encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
func NewEncoding(encoder string) *Encoding
使用给定的字符集创建一个编码规则,字符集长度必须为64且不能包含换行符。
func (enc Encoding) WithPadding(padding rune) *Encoding
创建一个指定填充符号的编码规则,padding不能是换行符或大于\xff,不要填充可设为预定义常量NoPadding。
预定义编码规则:
	StdEncoding    //RFC-4648定义的标准base64编码字符集。
	RawStdEncoding //StdEncoding的无填充版本
	URLEncoding    //RFC-4648定义的另一base64编码字符集,用于URL和文件名。
	RawURLEncoding //URLEncoding的无填充版本

"encoding/pem"包实现了PEM数据编码(源自保密增强邮件协议)。目前PEM编码主要用于TLS密钥和证书。
	func Decode(data []byte) (p *Block, rest []byte)
	func Encode(out io.Writer, b *Block) error
	func EncodeToMemory(b *Block) []byte

========== ========== ========== ========== ==========

	var src = []byte(`?a=1.\/{[<@ #>]}`)

	e1 := hex.EncodeToString(src)
	d1, _ := hex.DecodeString(e1)
	fmt.Println(e1, bytes.Equal(d1, src)) //3f613d312e5c2f7b5b3c4020233e5d7d true

	e2 := base32.HexEncoding.EncodeToString(src)
	d2, _ := base32.HexEncoding.DecodeString(e2)
	fmt.Println(e2, bytes.Equal(d2, src)) //7TGJQC9EBGNNMMPS80G26FITFK====== true

	e3 := base32.StdEncoding.EncodeToString(src)
	d3, _ := base32.StdEncoding.DecodeString(e3)
	fmt.Println(e3, bytes.Equal(d3, src)) //H5QT2MJOLQXXWWZ4IAQCGPS5PU====== true

	e4 := base64.StdEncoding.EncodeToString(src)
	d4, _ := base64.StdEncoding.DecodeString(e4)
	fmt.Println(e4, bytes.Equal(d4, src)) //P2E9MS5cL3tbPEAgIz5dfQ== true

	e5 := base64.URLEncoding.EncodeToString(src)
	d5, _ := base64.URLEncoding.DecodeString(e5)
	fmt.Println(e5, bytes.Equal(d5, src)) //P2E9MS5cL3tbPEAgIz5dfQ== true

	block := &pem.Block{
		Type:    "My Type",
		Headers: map[string]string{"a": "one", "b": "two"},
		Bytes:   []byte{49, 108, 178, 125, 95, 35, 126, 41, 129, 229, 48, 6, 94, 69, 20},
	}
	code := pem.EncodeToMemory(block)
	fmt.Println(string(code))
	/*
	-----BEGIN My Type-----
	a: one
	b: two

	MWyyfV8jfimB5TAGXkUU
	-----END My Type-----
	*/

	block2, _ := pem.Decode(code)
	if block2 == nil {
		fmt.Println("block nil")
	} else {
		fmt.Println(block2.Bytes) //[49 108 178 125 95 35 126 41 129 229 48 6 94 69 20]
	}

========== ========== ========== ========== ==========

"crypto/md5"包实现了MD5哈希算法。"crypto/sha1"包实现了SHA1哈希算法。
"crypto/sha256"包实现了SHA224和SHA256哈希算法。"crypto/sha512"包实现了SHA512/224、SHA512/256、SHA384、SHA512哈希算法。
"hash/adler32"、"hash/crc32"、"hash/crc64"、"hash/fnv"包还提供了其他不常用hash算法。
"golang.org/x/crypto/"扩展包还提供了以下hash算法:
md4、ripemd160、sha3.224、sha3.256、sha3.384、sha3.512、
blake2s.128、blake2s.256、blake2b.256、blake2b.384、blake2b.512
"crypto/hmac"包实现了加密哈希信息认证码。HMAC是使用key标记信息的加密hash。接收者使用相同的key运算来认证hash。
哈希计算的结果通常使用hex进行编码,如业务上有额外约定亦可使用其他编码方式。

"hash"包定义了以下三个接口:
type Hash interface {
	io.Writer
	Sum(b []byte) []byte //将当前哈希追加到b并返回结果切片。
	Reset()
	Size() int //返回Sum将返回的字节数。
	BlockSize() int
}
type Hash32 interface {
	Hash
	Sum32() uint32
}
type Hash64 interface {
	Hash
	Sum64() uint64
}

常用哈希方法:
"crypto/md5":
	func New() hash.Hash
	func Sum(data []byte) [16]byte
"crypto/sha1":
	func New() hash.Hash
	func Sum(data []byte) [20]byte
"crypto/sha256":
	func New() hash.Hash
	func New224() hash.Hash
	func Sum256(data []byte) [32]byte
	func Sum224(data []byte) [28]byte
"crypto/sha512":
	func New() hash.Hash
	func New384() hash.Hash
	func New512_256() hash.Hash
	func New512_224() hash.Hash
	func Sum512(data []byte) [64]byte
	func Sum384(data []byte) [48]byte
	func Sum512_256(data []byte) [32]byte
	func Sum512_224(data []byte) [28]byte
"crypto/hmac":
	func New(h func() hash.Hash, key []byte) hash.Hash
	func Equal(mac1, mac2 []byte) bool

========== ========== ========== ========== ==========

{
	//多次write用法
	h := md5.New()
	h.Write([]byte("abc"))
	h.Write([]byte("123"))
	s := hex.EncodeToString(h.Sum(nil))
	fmt.Println(s) // e99a18c428cb38d5f260853678922e03

	//一次Sum用法
	sum := md5.Sum([]byte("abc123"))
	ss := hex.EncodeToString(sum[:])
	fmt.Println(ss) // e99a18c428cb38d5f260853678922e03

	//hmac用法
	mac := hmac.New(md5.New, []byte("key"))
	mac.Write([]byte("abc"))
	mac.Write([]byte("123"))
	ms := hex.EncodeToString(mac.Sum(nil))
	fmt.Println(ms) // e33cf8cd1f571c0b6d900fad52ce9a7e 长度取决于使用的hash
}

为方便使用,也可以统一封装成支持任意多个字符串或字节数组的函数

type stream interface{ []byte | string }

func hashes[T stream](h hash.Hash, src []T) string {
	for _, v := range src {
		h.Write([]byte(v))
	}
	return hex.EncodeToString(h.Sum(nil))
}

func MD5[T stream](src ...T) string    { return hashes(md5.New(), src) }
func SHA1[T stream](src ...T) string   { return hashes(sha1.New(), src) }

func HmacMD5[K, T stream](key K, src ...T) string  { return hashes(hmac.New(md5.New, []byte(key)), src) }
func HmacSHA1[K, T stream](key K, src ...T) string { return hashes(hmac.New(sha1.New, []byte(key)), src) }

{
	s1 := MD5("abc", "123")
	s2 := MD5("abc123")
	s3 := HmacMD5("key", "abc", "123")
	s4 := HmacMD5("key", "abc", "123")
	fmt.Println(s1 == s2, s1) // true e99a18c428cb38d5f260853678922e03
	fmt.Println(s3 == s4, s3) // true e33cf8cd1f571c0b6d900fad52ce9a7e
}