增加vendor
This commit is contained in:
+332
@@ -0,0 +1,332 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 微信签名算法方式
|
||||
const (
|
||||
SignTypeMD5 = `MD5`
|
||||
SignTypeHMACSHA256 = `HMAC-SHA256`
|
||||
)
|
||||
|
||||
// EncryptMsg 加密消息
|
||||
func EncryptMsg(random, rawXMLMsg []byte, appID, aesKey string) (encrtptMsg []byte, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("panic error: err=%v", e)
|
||||
return
|
||||
}
|
||||
}()
|
||||
var key []byte
|
||||
key, err = aesKeyDecode(aesKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ciphertext := AESEncryptMsg(random, rawXMLMsg, appID, key)
|
||||
encrtptMsg = []byte(base64.StdEncoding.EncodeToString(ciphertext))
|
||||
return
|
||||
}
|
||||
|
||||
// AESEncryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
|
||||
// 参考:github.com/chanxuehong/wechat.v2
|
||||
func AESEncryptMsg(random, rawXMLMsg []byte, appID string, aesKey []byte) (ciphertext []byte) {
|
||||
const (
|
||||
BlockSize = 32 // PKCS#7
|
||||
BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
|
||||
)
|
||||
|
||||
appIDOffset := 20 + len(rawXMLMsg)
|
||||
contentLen := appIDOffset + len(appID)
|
||||
amountToPad := BlockSize - contentLen&BlockMask
|
||||
plaintextLen := contentLen + amountToPad
|
||||
|
||||
plaintext := make([]byte, plaintextLen)
|
||||
|
||||
// 拼接
|
||||
copy(plaintext[:16], random)
|
||||
encodeNetworkByteOrder(plaintext[16:20], uint32(len(rawXMLMsg)))
|
||||
copy(plaintext[20:], rawXMLMsg)
|
||||
copy(plaintext[appIDOffset:], appID)
|
||||
|
||||
// PKCS#7 补位
|
||||
for i := contentLen; i < plaintextLen; i++ {
|
||||
plaintext[i] = byte(amountToPad)
|
||||
}
|
||||
|
||||
// 加密
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, aesKey[:16])
|
||||
mode.CryptBlocks(plaintext, plaintext)
|
||||
|
||||
ciphertext = plaintext
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptMsg 消息解密
|
||||
func DecryptMsg(appID, encryptedMsg, aesKey string) (random, rawMsgXMLBytes []byte, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("panic error: err=%v", e)
|
||||
return
|
||||
}
|
||||
}()
|
||||
var encryptedMsgBytes, key, getAppIDBytes []byte
|
||||
encryptedMsgBytes, err = base64.StdEncoding.DecodeString(encryptedMsg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
key, err = aesKeyDecode(aesKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
random, rawMsgXMLBytes, getAppIDBytes, err = AESDecryptMsg(encryptedMsgBytes, key)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("消息解密失败,%v", err)
|
||||
return
|
||||
}
|
||||
if appID != string(getAppIDBytes) {
|
||||
err = fmt.Errorf("消息解密校验APPID失败")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func aesKeyDecode(encodedAESKey string) (key []byte, err error) {
|
||||
if len(encodedAESKey) != 43 {
|
||||
err = fmt.Errorf("the length of encodedAESKey must be equal to 43")
|
||||
return
|
||||
}
|
||||
key, err = base64.StdEncoding.DecodeString(encodedAESKey + "=")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(key) != 32 {
|
||||
err = fmt.Errorf("encodingAESKey invalid")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AESDecryptMsg ciphertext = AES_Encrypt[random(16B) + msg_len(4B) + rawXMLMsg + appId]
|
||||
// 参考:github.com/chanxuehong/wechat.v2
|
||||
func AESDecryptMsg(ciphertext []byte, aesKey []byte) (random, rawXMLMsg, appID []byte, err error) {
|
||||
const (
|
||||
BlockSize = 32 // PKCS#7
|
||||
BlockMask = BlockSize - 1 // BLOCK_SIZE 为 2^n 时, 可以用 mask 获取针对 BLOCK_SIZE 的余数
|
||||
)
|
||||
|
||||
if len(ciphertext) < BlockSize {
|
||||
err = fmt.Errorf("the length of ciphertext too short: %d", len(ciphertext))
|
||||
return
|
||||
}
|
||||
if len(ciphertext)&BlockMask != 0 {
|
||||
err = fmt.Errorf("ciphertext is not a multiple of the block size, the length is %d", len(ciphertext))
|
||||
return
|
||||
}
|
||||
|
||||
plaintext := make([]byte, len(ciphertext)) // len(plaintext) >= BLOCK_SIZE
|
||||
|
||||
// 解密
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, aesKey[:16])
|
||||
mode.CryptBlocks(plaintext, ciphertext)
|
||||
|
||||
// PKCS#7 去除补位
|
||||
amountToPad := int(plaintext[len(plaintext)-1])
|
||||
if amountToPad < 1 || amountToPad > BlockSize {
|
||||
err = fmt.Errorf("the amount to pad is incorrect: %d", amountToPad)
|
||||
return
|
||||
}
|
||||
plaintext = plaintext[:len(plaintext)-amountToPad]
|
||||
|
||||
// 反拼接
|
||||
// len(plaintext) == 16+4+len(rawXMLMsg)+len(appId)
|
||||
if len(plaintext) <= 20 {
|
||||
err = fmt.Errorf("plaintext too short, the length is %d", len(plaintext))
|
||||
return
|
||||
}
|
||||
rawXMLMsgLen := int(decodeNetworkByteOrder(plaintext[16:20]))
|
||||
if rawXMLMsgLen < 0 {
|
||||
err = fmt.Errorf("incorrect msg length: %d", rawXMLMsgLen)
|
||||
return
|
||||
}
|
||||
appIDOffset := 20 + rawXMLMsgLen
|
||||
if len(plaintext) <= appIDOffset {
|
||||
err = fmt.Errorf("msg length too large: %d", rawXMLMsgLen)
|
||||
return
|
||||
}
|
||||
|
||||
random = plaintext[:16:20]
|
||||
rawXMLMsg = plaintext[20:appIDOffset:appIDOffset]
|
||||
appID = plaintext[appIDOffset:]
|
||||
return
|
||||
}
|
||||
|
||||
// 把整数 n 格式化成 4 字节的网络字节序
|
||||
func encodeNetworkByteOrder(orderBytes []byte, n uint32) {
|
||||
orderBytes[0] = byte(n >> 24)
|
||||
orderBytes[1] = byte(n >> 16)
|
||||
orderBytes[2] = byte(n >> 8)
|
||||
orderBytes[3] = byte(n)
|
||||
}
|
||||
|
||||
// 从 4 字节的网络字节序里解析出整数
|
||||
func decodeNetworkByteOrder(orderBytes []byte) (n uint32) {
|
||||
return uint32(orderBytes[0])<<24 |
|
||||
uint32(orderBytes[1])<<16 |
|
||||
uint32(orderBytes[2])<<8 |
|
||||
uint32(orderBytes[3])
|
||||
}
|
||||
|
||||
// CalculateSign 计算签名
|
||||
func CalculateSign(content, signType, key string) (string, error) {
|
||||
var h hash.Hash
|
||||
if signType == SignTypeHMACSHA256 {
|
||||
h = hmac.New(sha256.New, []byte(key))
|
||||
} else {
|
||||
h = md5.New()
|
||||
}
|
||||
|
||||
if _, err := h.Write([]byte(content)); err != nil {
|
||||
return ``, err
|
||||
}
|
||||
return strings.ToUpper(hex.EncodeToString(h.Sum(nil))), nil
|
||||
}
|
||||
|
||||
// ParamSign 计算所传参数的签名
|
||||
func ParamSign(p map[string]string, key string) (string, error) {
|
||||
bizKey := "&key=" + key
|
||||
str := OrderParam(p, bizKey)
|
||||
|
||||
var signType string
|
||||
switch p["sign_type"] {
|
||||
case SignTypeMD5, SignTypeHMACSHA256:
|
||||
signType = p["sign_type"]
|
||||
case ``:
|
||||
signType = SignTypeMD5
|
||||
default:
|
||||
return ``, errors.New(`invalid sign_type`)
|
||||
}
|
||||
|
||||
return CalculateSign(str, signType, key)
|
||||
}
|
||||
|
||||
// ECB provides confidentiality by assigning a fixed ciphertext block to each plaintext block.
|
||||
// See NIST SP 800-38A, pp 08-09
|
||||
// reference: https://codereview.appspot.com/7860047/patch/23001/24001
|
||||
type ecb struct {
|
||||
b cipher.Block
|
||||
blockSize int
|
||||
}
|
||||
|
||||
func newECB(b cipher.Block) *ecb {
|
||||
return &ecb{
|
||||
b: b,
|
||||
blockSize: b.BlockSize(),
|
||||
}
|
||||
}
|
||||
|
||||
// ECBEncryptor -
|
||||
type ECBEncryptor ecb
|
||||
|
||||
// NewECBEncryptor returns a BlockMode which encrypts in electronic code book mode, using the given Block.
|
||||
func NewECBEncryptor(b cipher.Block) cipher.BlockMode {
|
||||
return (*ECBEncryptor)(newECB(b))
|
||||
}
|
||||
|
||||
// BlockSize implement BlockMode.BlockSize
|
||||
func (x *ECBEncryptor) BlockSize() int {
|
||||
return x.blockSize
|
||||
}
|
||||
|
||||
// CryptBlocks implement BlockMode.CryptBlocks
|
||||
func (x *ECBEncryptor) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
panic("crypto/cipher: input not full blocks")
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
}
|
||||
for len(src) > 0 {
|
||||
x.b.Encrypt(dst, src[:x.blockSize])
|
||||
src = src[x.blockSize:]
|
||||
dst = dst[x.blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
// ECBDecryptor -
|
||||
type ECBDecryptor ecb
|
||||
|
||||
// NewECBDecryptor returns a BlockMode which decrypts in electronic code book mode, using the given Block.
|
||||
func NewECBDecryptor(b cipher.Block) cipher.BlockMode {
|
||||
return (*ECBDecryptor)(newECB(b))
|
||||
}
|
||||
|
||||
// BlockSize implement BlockMode.BlockSize
|
||||
func (x *ECBDecryptor) BlockSize() int {
|
||||
return x.blockSize
|
||||
}
|
||||
|
||||
// CryptBlocks implement BlockMode.CryptBlocks
|
||||
func (x *ECBDecryptor) CryptBlocks(dst, src []byte) {
|
||||
if len(src)%x.blockSize != 0 {
|
||||
panic("crypto/cipher: input not full blocks")
|
||||
}
|
||||
if len(dst) < len(src) {
|
||||
panic("crypto/cipher: output smaller than input")
|
||||
}
|
||||
for len(src) > 0 {
|
||||
x.b.Decrypt(dst, src[:x.blockSize])
|
||||
src = src[x.blockSize:]
|
||||
dst = dst[x.blockSize:]
|
||||
}
|
||||
}
|
||||
|
||||
// AesECBDecrypt will decrypt data with PKCS5Padding
|
||||
func AesECBDecrypt(ciphertext []byte, aesKey []byte) ([]byte, error) {
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
// ECB mode always works in whole blocks.
|
||||
if len(ciphertext)%aes.BlockSize != 0 {
|
||||
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
NewECBDecryptor(block).CryptBlocks(ciphertext, ciphertext)
|
||||
return PKCS5UnPadding(ciphertext), nil
|
||||
}
|
||||
|
||||
// PKCS5Padding -
|
||||
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padText...)
|
||||
}
|
||||
|
||||
// PKCS5UnPadding -
|
||||
func PKCS5UnPadding(origData []byte) []byte {
|
||||
length := len(origData)
|
||||
unPadding := int(origData[length-1])
|
||||
return origData[:(length - unPadding)]
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// CommonError 微信返回的通用错误json
|
||||
type CommonError struct {
|
||||
ErrCode int64 `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
// DecodeWithCommonError 将返回值按照CommonError解析
|
||||
func DecodeWithCommonError(response []byte, apiName string) (err error) {
|
||||
var commError CommonError
|
||||
err = json.Unmarshal(response, &commError)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if commError.ErrCode != 0 {
|
||||
return fmt.Errorf("%s Error , errcode=%d , errmsg=%s", apiName, commError.ErrCode, commError.ErrMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeWithError 将返回值按照解析
|
||||
func DecodeWithError(response []byte, obj interface{}, apiName string) error {
|
||||
err := json.Unmarshal(response, obj)
|
||||
if err != nil {
|
||||
return fmt.Errorf("json Unmarshal Error, err=%v", err)
|
||||
}
|
||||
responseObj := reflect.ValueOf(obj)
|
||||
if !responseObj.IsValid() {
|
||||
return fmt.Errorf("obj is invalid")
|
||||
}
|
||||
commonError := responseObj.Elem().FieldByName("CommonError")
|
||||
if !commonError.IsValid() || commonError.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("commonError is invalid or not struct")
|
||||
}
|
||||
errCode := commonError.FieldByName("ErrCode")
|
||||
errMsg := commonError.FieldByName("ErrMsg")
|
||||
if !errCode.IsValid() || !errMsg.IsValid() {
|
||||
return fmt.Errorf("errcode or errmsg is invalid")
|
||||
}
|
||||
if errCode.Int() != 0 {
|
||||
return fmt.Errorf("%s Error , errcode=%d , errmsg=%s", apiName, errCode.Int(), errMsg.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/pkcs12"
|
||||
)
|
||||
|
||||
// HTTPGet get 请求
|
||||
func HTTPGet(uri string) ([]byte, error) {
|
||||
return HTTPGetContext(context.Background(), uri)
|
||||
}
|
||||
|
||||
// HTTPGetContext get 请求
|
||||
func HTTPGetContext(ctx context.Context, uri string) ([]byte, error) {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := http.DefaultClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(response.Body)
|
||||
}
|
||||
|
||||
// HTTPPost post 请求
|
||||
func HTTPPost(uri string, data string) ([]byte, error) {
|
||||
return HTTPPostContext(context.Background(), uri, data)
|
||||
}
|
||||
|
||||
// HTTPPostContext post 请求
|
||||
func HTTPPostContext(ctx context.Context, uri string, data string) ([]byte, error) {
|
||||
body := bytes.NewBuffer([]byte(data))
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, uri, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := http.DefaultClient.Do(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(response.Body)
|
||||
}
|
||||
|
||||
// PostJSON post json 数据请求
|
||||
func PostJSON(uri string, obj interface{}) ([]byte, error) {
|
||||
jsonBuf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(jsonBuf)
|
||||
enc.SetEscapeHTML(false)
|
||||
err := enc.Encode(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := http.Post(uri, "application/json;charset=utf-8", jsonBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(response.Body)
|
||||
}
|
||||
|
||||
// PostJSONWithRespContentType post json数据请求,且返回数据类型
|
||||
func PostJSONWithRespContentType(uri string, obj interface{}) ([]byte, string, error) {
|
||||
jsonBuf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(jsonBuf)
|
||||
enc.SetEscapeHTML(false)
|
||||
err := enc.Encode(obj)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
response, err := http.Post(uri, "application/json;charset=utf-8", jsonBuf)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, "", fmt.Errorf("http get error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||
}
|
||||
responseData, err := ioutil.ReadAll(response.Body)
|
||||
contentType := response.Header.Get("Content-Type")
|
||||
return responseData, contentType, err
|
||||
}
|
||||
|
||||
// PostFile 上传文件
|
||||
func PostFile(fieldname, filename, uri string) ([]byte, error) {
|
||||
fields := []MultipartFormField{
|
||||
{
|
||||
IsFile: true,
|
||||
Fieldname: fieldname,
|
||||
Filename: filename,
|
||||
},
|
||||
}
|
||||
return PostMultipartForm(fields, uri)
|
||||
}
|
||||
|
||||
// MultipartFormField 保存文件或其他字段信息
|
||||
type MultipartFormField struct {
|
||||
IsFile bool
|
||||
Fieldname string
|
||||
Value []byte
|
||||
Filename string
|
||||
}
|
||||
|
||||
// PostMultipartForm 上传文件或其他多个字段
|
||||
func PostMultipartForm(fields []MultipartFormField, uri string) (respBody []byte, err error) {
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
|
||||
for _, field := range fields {
|
||||
if field.IsFile {
|
||||
fileWriter, e := bodyWriter.CreateFormFile(field.Fieldname, field.Filename)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("error writing to buffer , err=%v", e)
|
||||
return
|
||||
}
|
||||
|
||||
fh, e := os.Open(field.Filename)
|
||||
if e != nil {
|
||||
err = fmt.Errorf("error opening file , err=%v", e)
|
||||
return
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
if _, err = io.Copy(fileWriter, fh); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
partWriter, e := bodyWriter.CreateFormField(field.Fieldname)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
valueReader := bytes.NewReader(field.Value)
|
||||
if _, err = io.Copy(partWriter, valueReader); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentType := bodyWriter.FormDataContentType()
|
||||
bodyWriter.Close()
|
||||
|
||||
resp, e := http.Post(uri, contentType, bodyBuf)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, err
|
||||
}
|
||||
respBody, err = ioutil.ReadAll(resp.Body)
|
||||
return
|
||||
}
|
||||
|
||||
// PostXML perform a HTTP/POST request with XML body
|
||||
func PostXML(uri string, obj interface{}) ([]byte, error) {
|
||||
xmlData, err := xml.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body := bytes.NewBuffer(xmlData)
|
||||
response, err := http.Post(uri, "application/xml;charset=utf-8", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http code error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(response.Body)
|
||||
}
|
||||
|
||||
// httpWithTLS CA证书
|
||||
func httpWithTLS(rootCa, key string) (*http.Client, error) {
|
||||
var client *http.Client
|
||||
certData, err := ioutil.ReadFile(rootCa)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to find cert path=%s, error=%v", rootCa, err)
|
||||
}
|
||||
cert := pkcs12ToPem(certData, key)
|
||||
config := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: config,
|
||||
DisableCompression: true,
|
||||
}
|
||||
client = &http.Client{Transport: tr}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// pkcs12ToPem 将Pkcs12转成Pem
|
||||
func pkcs12ToPem(p12 []byte, password string) tls.Certificate {
|
||||
blocks, err := pkcs12.ToPEM(p12, password)
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
log.Print(x)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var pemData []byte
|
||||
for _, b := range blocks {
|
||||
pemData = append(pemData, pem.EncodeToMemory(b)...)
|
||||
}
|
||||
cert, err := tls.X509KeyPair(pemData, pemData)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return cert
|
||||
}
|
||||
|
||||
// PostXMLWithTLS perform a HTTP/POST request with XML body and TLS
|
||||
func PostXMLWithTLS(uri string, obj interface{}, ca, key string) ([]byte, error) {
|
||||
xmlData, err := xml.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body := bytes.NewBuffer(xmlData)
|
||||
client, err := httpWithTLS(ca, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := client.Post(uri, "application/xml;charset=utf-8", body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("http code error : uri=%v , statusCode=%v", uri, response.StatusCode)
|
||||
}
|
||||
return ioutil.ReadAll(response.Body)
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// OrderParam order params
|
||||
func OrderParam(p map[string]string, bizKey string) (returnStr string) {
|
||||
keys := make([]string, 0, len(p))
|
||||
for k := range p {
|
||||
if k == "sign" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
var buf bytes.Buffer
|
||||
for _, k := range keys {
|
||||
if p[k] == "" {
|
||||
continue
|
||||
}
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
buf.WriteString(k)
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(p[k])
|
||||
}
|
||||
buf.WriteString(bizKey)
|
||||
returnStr = buf.String()
|
||||
return
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RSADecrypt 数据解密
|
||||
func RSADecrypt(privateKey string, ciphertext []byte) ([]byte, error) {
|
||||
block, _ := pem.Decode([]byte(privateKey))
|
||||
if block == nil {
|
||||
return nil, errors.New("PrivateKey format error")
|
||||
}
|
||||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
oldErr := err
|
||||
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ParsePKCS1PrivateKey error: %s, ParsePKCS8PrivateKey error: %s", oldErr.Error(), err.Error())
|
||||
}
|
||||
switch t := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
priv = key.(*rsa.PrivateKey)
|
||||
default:
|
||||
return nil, fmt.Errorf("ParsePKCS1PrivateKey error: %s, ParsePKCS8PrivateKey error: Not supported privatekey format, should be *rsa.PrivateKey, got %T", oldErr.Error(), t)
|
||||
}
|
||||
}
|
||||
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
|
||||
}
|
||||
|
||||
// RSADecryptBase64 Base64解码后再次进行RSA解密
|
||||
func RSADecryptBase64(privateKey string, cryptoText string) ([]byte, error) {
|
||||
encryptedData, err := base64.StdEncoding.DecodeString(cryptoText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return RSADecrypt(privateKey, encryptedData)
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Signature sha1签名
|
||||
func Signature(params ...string) string {
|
||||
sort.Strings(params)
|
||||
h := sha1.New()
|
||||
for _, s := range params {
|
||||
_, _ = io.WriteString(h, s)
|
||||
}
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RandomStr 随机生成字符串
|
||||
func RandomStr(length int) string {
|
||||
str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
bytes := []byte(str)
|
||||
result := []byte{}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := 0; i < length; i++ {
|
||||
result = append(result, bytes[r.Intn(len(bytes))])
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
// GetCurrTS return current timestamps
|
||||
func GetCurrTS() int64 {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package util
|
||||
|
||||
// SliceChunk 用于将字符串切片分块
|
||||
func SliceChunk(src []string, chunkSize int) (chunks [][]string) {
|
||||
total := len(src)
|
||||
chunks = make([][]string, 0)
|
||||
if chunkSize < 1 {
|
||||
chunkSize = 1
|
||||
}
|
||||
if total == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
chunkNum := total / chunkSize
|
||||
if total%chunkSize != 0 {
|
||||
chunkNum++
|
||||
}
|
||||
|
||||
chunks = make([][]string, chunkNum)
|
||||
|
||||
for i := 0; i < chunkNum; i++ {
|
||||
for j := 0; j < chunkSize; j++ {
|
||||
offset := i*chunkSize + j
|
||||
if offset >= total {
|
||||
return
|
||||
}
|
||||
|
||||
if chunks[i] == nil {
|
||||
actualChunkSize := chunkSize
|
||||
if i == chunkNum-1 && total%chunkSize != 0 {
|
||||
actualChunkSize = total % chunkSize
|
||||
}
|
||||
chunks[i] = make([]string, actualChunkSize)
|
||||
}
|
||||
|
||||
chunks[i][j] = src[offset]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user