package comm import ( "crypto/aes" "crypto/cipher" "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "hash" "io" "math" "crypto/rand" mrand "math/rand" "strconv" "strings" "time" ) // 实现php的sha1()方法,返回的是byte转换的字符串,如果需要转成16进制,可以使用hex.EncodeToString func GetSha1(str string) string { h := sha1.New() io.WriteString(h, str) return string(h.Sum(nil)) } // 右边补全字符串实现方法,主要实现php的str_pad()方法 func StrPadRight(input string, padLength int, padString string) string { output := "" inputLen := len(input) if inputLen >= padLength { return input } ll := padLength - inputLen for i := 1; i <= ll; i = i + len(padString) { output += padString } return input + output } // url安全模式decode字符串 func UrlSafeB64decode(str string) (result []byte) { str = strings.Replace(str, "-", "+", -1) str = strings.Replace(str, "_", "/", -1) mod4 := len(str) % 4 if mod4 != 0 { str = str + "===="[0:mod4] } result, _ = base64.StdEncoding.DecodeString(str) return result } // base64字符串,替换转换为安全模式 func UrlSafeB64encode(str string) (result string) { str = strings.Replace(str, "+", "-", -1) str = strings.Replace(str, "/", "_", -1) return str } // 使用aes-256-gcm方式解密字符串,主要针对php的openssl_decrypt()方法,注意在php7.1后增加了tag和add参数 func DecodeAesGcm(encryptData string, hex_key string, hex_add string) ([]byte, error) { tagSize := 16 //nonceSize,tag的长度,用于open时候生成tag,默认12 key := []byte(hex_key) add := []byte(hex_add) block, err := aes.NewCipher(key) //生成加解密用的block if err != nil { return []byte(""), err } //根据不同加密算法,也有不同tag长度的方法设定和调用,比如NewGCMWithTagSize、newGCMWithNonceAndTagSize aesgcm, err := cipher.NewGCMWithNonceSize(block, tagSize) if err != nil { return []byte(""), err } decodeEncryptStr := UrlSafeB64decode(encryptData) ciphertext := decodeEncryptStr if len(ciphertext) <= aesgcm.NonceSize() { // 长度应该>iv return []byte(""), errors.New("string: too short") //解密失败 } iv := ciphertext[:aesgcm.NonceSize()] //分离出IV ciphertext = ciphertext[aesgcm.NonceSize():] // 密文,tag是调用open方法时候通过密文和前面new时候传的size来进行截取的 plaintext, err := aesgcm.Open(nil, iv, ciphertext, add) return plaintext, err } // 使用aes-256-gcm加密数据,主要针对php的openssl_encrypt()方法,注意在php7.1后增加了tag和add参数 func EncodeAesGcm(data string, hex_key string, hex_add string) (result string, error error) { tagSize := 16 //nonceSize,tag的长度,用于open时候生成tag,默认12 key := []byte(hex_key) add := []byte(hex_add) block, err := aes.NewCipher(key) //生成加解密用的block if err != nil { return result, err } //根据不同加密算法,也有不同tag长度的方法设定和调用,比如NewGCMWithTagSize、newGCMWithNonceAndTagSize aesgcm, err := cipher.NewGCMWithNonceSize(block, tagSize) if err != nil { return result, err } plaintext := []byte(data) iv := make([]byte, tagSize) // NonceSize=12 rand.Read(iv) //获取随机值 ciphertext := aesgcm.Seal(iv, iv, plaintext, add) //加密,密文为:iv+密文+tag result = base64.StdEncoding.EncodeToString(ciphertext) result = UrlSafeB64encode(result) return result, nil // 生成的BS64 } func GenSecretKey(appId string, appSecret string) string { key := appId + "&" + appSecret sha1_str := GetSha1(key) str10 := "0" pack64, _ := strconv.ParseUint(str10, 10, 32) //对应php的pack()方法,字符串转换为uint类型 pack32 := uint32(pack64) str_pad := StrPadRight(sha1_str, 32, fmt.Sprintf("%d", pack32)) return base64.StdEncoding.EncodeToString([]byte(str_pad)) } // 加密 func Encode(secretKey string, data []byte) (string, error) { decodeSecretKeyByte, _ := base64.StdEncoding.DecodeString(secretKey) c, e := EncodeAesGcm(string(data), string(decodeSecretKeyByte), "") if e != nil { return "", e } return c, nil } // 加密 func Decode(secretKey string, encryptData string) ([]byte, error) { decodeSecretKeyByte, _ := base64.StdEncoding.DecodeString(secretKey) decodeSecretKey := string(decodeSecretKeyByte) //$data = openssl_decrypt(substr($decodeEncrypt, 16, -16), 'aes-256-gcm', $decodeSecret, 1, substr($decodeEncrypt, 0, 16), substr($decodeEncrypt, -16, 16)); p, e := DecodeAesGcm(encryptData, decodeSecretKey, "") if e != nil { return nil, e } return p, nil } func decryptOAEP(pritKey []byte, cipherdata []byte, label []byte) ([]byte, error) { block, _ := pem.Decode(pritKey) if block == nil { err := fmt.Errorf("failed to parse certificate PEM") return nil, err } priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) // ASN.1 PKCS#1 DER encoded form. if err != nil { return nil, err } h := sha256.New() return DecryptOAEPLong(h, rand.Reader, priv, cipherdata, label) } func EncryptOAEPLong(hash hash.Hash, random io.Reader, public *rsa.PublicKey, msg []byte, label []byte) ([]byte, error) { msgLen := len(msg) step := public.Size() - 2*hash.Size() - 2 var encryptedBytes []byte for start := 0; start < msgLen; start += step { finish := start + step if finish > msgLen { finish = msgLen } encryptedBlockBytes, err := rsa.EncryptOAEP(hash, random, public, msg[start:finish], label) if err != nil { return nil, err } encryptedBytes = append(encryptedBytes, encryptedBlockBytes...) } return encryptedBytes, nil } func DecryptOAEPLong(hash hash.Hash, random io.Reader, private *rsa.PrivateKey, msg []byte, label []byte) ([]byte, error) { msgLen := len(msg) step := private.PublicKey.Size() var decryptedBytes []byte for start := 0; start < msgLen; start += step { finish := start + step if finish > msgLen { finish = msgLen } decryptedBlockBytes, err := rsa.DecryptOAEP(hash, random, private, msg[start:finish], label) if err != nil { return nil, err } decryptedBytes = append(decryptedBytes, decryptedBlockBytes...) } return decryptedBytes, nil } func encryptOAEP(pubkey []byte, msg []byte, label []byte) ([]byte, error) { block, _ := pem.Decode(pubkey) if block == nil { err := fmt.Errorf("failed to parse certificate PEM") return nil, err } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } rsaPublicKey := pub.(*rsa.PublicKey) h := sha256.New() // sha1.New() or md5.New() return EncryptOAEPLong(h, rand.Reader, rsaPublicKey, msg, label) } func radomInt() int32 { mrand.Seed(time.Now().UnixNano()) return mrand.Int31n(math.MaxInt32) }