forked from shenghui0779/sdk-go
/
helper.go
252 lines (197 loc) · 4.88 KB
/
helper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package wx
import (
"bytes"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"encoding/hex"
"encoding/json"
"encoding/pem"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"path/filepath"
"strings"
"golang.org/x/crypto/pkcs12"
)
// M is a convenient alias for a map[string]interface{}.
type M map[string]interface{}
// WXML deal with xml for wechat
type WXML map[string]string
// CDATA XML CDATA section which is defined as blocks of text that are not parsed by the parser, but are otherwise recognized as markup.
type CDATA string
// MarshalXML encodes the receiver as zero or more XML elements.
func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(struct {
string `xml:",cdata"`
}{string(c)}, start)
}
// Nonce returns nonce string, param `size` better for even number.
func Nonce(size uint) string {
nonce := make([]byte, size/2)
io.ReadFull(rand.Reader, nonce)
return hex.EncodeToString(nonce)
}
// MD5 calculates the md5 hash of a string.
func MD5(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// SHA256 calculates the sha256 hash of a string.
func SHA256(s string) string {
h := sha256.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// HMacSHA256 generates a keyed sha256 hash value.
func HMacSHA256(s, key string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(s))
return hex.EncodeToString(mac.Sum(nil))
}
// FormatMap2XML format map to xml
func FormatMap2XML(m WXML) ([]byte, error) {
var builder strings.Builder
builder.WriteString("<xml>")
for k, v := range m {
builder.WriteString(fmt.Sprintf("<%s>", k))
if err := xml.EscapeText(&builder, []byte(v)); err != nil {
return nil, err
}
builder.WriteString(fmt.Sprintf("</%s>", k))
}
builder.WriteString("</xml>")
return []byte(builder.String()), nil
}
// FormatMap2XML 用于单元测试
// func FormatMap2XML(m WXML) ([]byte, error) {
// ks := make([]string, 0, len(m))
//
// for k := range m {
// ks = append(ks, k)
// }
//
// sort.Strings(ks)
//
// var builder strings.Builder
//
// builder.WriteString("<xml>")
//
// for _, k := range ks {
// builder.WriteString(fmt.Sprintf("<%s>", k))
//
// if err := xml.EscapeText(&builder, []byte(m[k])); err != nil {
// return nil, err
// }
//
// builder.WriteString(fmt.Sprintf("</%s>", k))
// }
//
// builder.WriteString("</xml>")
//
// return []byte(builder.String()), nil
// }
// ParseXML2Map parse xml to map
func ParseXML2Map(b []byte) (WXML, error) {
m := make(WXML)
xmlReader := bytes.NewReader(b)
var (
d = xml.NewDecoder(xmlReader)
tk xml.Token
depth = 0 // current xml.Token depth
key string
buf bytes.Buffer
err error
)
d.Strict = false
for {
tk, err = d.Token()
if err != nil {
if err == io.EOF {
return m, nil
}
return nil, err
}
switch v := tk.(type) {
case xml.StartElement:
depth++
switch depth {
case 2:
key = v.Name.Local
buf.Reset()
case 3:
if err = d.Skip(); err != nil {
return nil, err
}
depth--
key = "" // key == "" indicates that the node with depth==2 has children
}
case xml.CharData:
if depth == 2 && key != "" {
buf.Write(v)
}
case xml.EndElement:
if depth == 2 && key != "" {
m[key] = buf.String()
}
depth--
}
}
}
// EncodeUint32ToBytes 把整数 uint32 格式化成 4 字节的网络字节序
func EncodeUint32ToBytes(i uint32) []byte {
b := make([]byte, 4)
b[0] = byte(i >> 24)
b[1] = byte(i >> 16)
b[2] = byte(i >> 8)
b[3] = byte(i)
return b
}
// DecodeBytesToUint32 从 4 字节的网络字节序里解析出整数 uint32
func DecodeBytesToUint32(b []byte) uint32 {
if len(b) != 4 {
return 0
}
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])
}
// MarshalNoEscapeHTML marshal with no escape HTML
func MarshalNoEscapeHTML(v interface{}) ([]byte, error) {
var buf bytes.Buffer
jsonEncoder := json.NewEncoder(&buf)
jsonEncoder.SetEscapeHTML(false)
if err := jsonEncoder.Encode(v); err != nil {
return nil, err
}
b := buf.Bytes()
// 去掉 go std 给末尾加的 '\n'
// @see https://github.com/golang/go/issues/7767
if l := len(b); l != 0 && b[l-1] == '\n' {
b = b[:l-1]
}
return b, nil
}
// LoadP12Cert 通过p12(pfx)证书文件生成Pem证书
func LoadP12Cert(pfxfile, mchid string) (tls.Certificate, error) {
fail := func(err error) (tls.Certificate, error) { return tls.Certificate{}, err }
certPath, err := filepath.Abs(filepath.Clean(pfxfile))
if err != nil {
return fail(err)
}
pfxdata, err := ioutil.ReadFile(certPath)
if err != nil {
return fail(err)
}
blocks, err := pkcs12.ToPEM(pfxdata, mchid)
if err != nil {
return fail(err)
}
pemData := make([]byte, 0)
for _, b := range blocks {
pemData = append(pemData, pem.EncodeToMemory(b)...)
}
return tls.X509KeyPair(pemData, pemData)
}