vendor
This commit is contained in:
parent
53797b066c
commit
72d858539f
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,124 @@
|
|||
// Package auth
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/20 2:43 PM
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/constants"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/request"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/utils"
|
||||
"log"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var FormatISOTime = "2006-01-02T15:04:05Z"
|
||||
var DEFAULT_HEADERS_TO_SIGN []string = []string{constants.YOP_APPKEY_HEADER_KEY, constants.YOP_REQUEST_ID, constants.YOP_CONTENT_SHA256}
|
||||
|
||||
type YopSigner interface {
|
||||
// SignRequest 请求报文签名
|
||||
SignRequest(yopRequest request.YopRequest)
|
||||
|
||||
// VerifyResponse 响应报文验签
|
||||
VerifyResponse(content string, signature string, pubKey request.PlatformPubKey) bool
|
||||
}
|
||||
|
||||
type RsaSigner struct {
|
||||
}
|
||||
|
||||
func (signer *RsaSigner) SignRequest(yopRequest request.YopRequest) {
|
||||
var authString = buildAuthString(yopRequest.AppId)
|
||||
log.Println("authString:" + authString)
|
||||
|
||||
var contentHash = calculateContentHash(yopRequest)
|
||||
log.Println("contentHash:" + contentHash)
|
||||
yopRequest.Headers[constants.YOP_CONTENT_SHA256] = contentHash
|
||||
|
||||
var headerToSign = getHeaderToSign(yopRequest)
|
||||
var canonicalRequest = buildCanonicalRequest(yopRequest, authString, headerToSign)
|
||||
log.Println("canonicalRequest:" + canonicalRequest)
|
||||
|
||||
signature, _ := utils.RsaSignBase64(canonicalRequest, yopRequest.IsvPriKey.Value, crypto.SHA256)
|
||||
signature += "$" + "SHA256"
|
||||
log.Println("signature:" + signature)
|
||||
var authorizationHeader = buildAuthzHeader(authString, signature, headerToSign)
|
||||
log.Println("Authorization:" + authorizationHeader)
|
||||
yopRequest.Headers[constants.AUTHORIZATION] = authorizationHeader
|
||||
}
|
||||
|
||||
func (signer *RsaSigner) VerifyResponse(content string, signature string, pubKey request.PlatformPubKey) bool {
|
||||
re := regexp.MustCompile("[ \t\n]")
|
||||
content = re.ReplaceAllString(content, "")
|
||||
return utils.VerifySign(content, signature, pubKey.Value, crypto.SHA256)
|
||||
}
|
||||
|
||||
func calculateContentHash(yopRequest request.YopRequest) string {
|
||||
var encodedParameters = ""
|
||||
if utils.UsePayloadForQueryParameters(yopRequest) {
|
||||
encodedParameters = utils.GetCanonicalQueryString(yopRequest.Params)
|
||||
} else {
|
||||
encodedParameters = yopRequest.Content
|
||||
}
|
||||
log.Println("encodedParameters:" + encodedParameters)
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(encodedParameters)))
|
||||
}
|
||||
|
||||
func buildCanonicalRequest(yopRequest request.YopRequest, authString string, headerToSign []string) string {
|
||||
var canonicalQueryString = getCanonicalQueryString(yopRequest)
|
||||
var canonicalURI = getCanonicalURIPath(yopRequest.ApiUri)
|
||||
return authString + "\n" + yopRequest.HttpMethod + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + getCanonicalHeaders(yopRequest, headerToSign)
|
||||
}
|
||||
func buildAuthString(appId string) string {
|
||||
var t = time.Now()
|
||||
return constants.DEFAULT_YOP_PROTOCOL_VERSION + "/" + appId + "/" + t.Format(FormatISOTime) + "/" + strconv.Itoa(constants.DEFAULT_EXPIRATION_IN_SECONDS)
|
||||
}
|
||||
|
||||
func getCanonicalQueryString(yopRequest request.YopRequest) string {
|
||||
if utils.UsePayloadForQueryParameters(yopRequest) {
|
||||
return ""
|
||||
}
|
||||
return utils.GetCanonicalQueryString(yopRequest.Params)
|
||||
}
|
||||
|
||||
func getCanonicalURIPath(path string) string {
|
||||
if 0 == len(path) {
|
||||
return "/"
|
||||
} else if strings.HasPrefix(path, "/") {
|
||||
return utils.NormalizePath(path)
|
||||
} else {
|
||||
return "/" + utils.NormalizePath(path)
|
||||
}
|
||||
}
|
||||
|
||||
func getHeaderToSign(yopRequest request.YopRequest) []string {
|
||||
var result []string
|
||||
for header := range DEFAULT_HEADERS_TO_SIGN {
|
||||
var value = yopRequest.Headers[DEFAULT_HEADERS_TO_SIGN[header]]
|
||||
if 0 != len(value) {
|
||||
result = append(result, DEFAULT_HEADERS_TO_SIGN[header])
|
||||
}
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func getCanonicalHeaders(yopRequest request.YopRequest, headerToSign []string) string {
|
||||
var headerStrings []string
|
||||
for header := range headerToSign {
|
||||
headerStrings = append(headerStrings, utils.Normalize(headerToSign[header])+":"+utils.Normalize(yopRequest.Headers[headerToSign[header]]))
|
||||
}
|
||||
sort.Strings(headerStrings)
|
||||
return strings.Join(headerStrings, "\n")
|
||||
}
|
||||
|
||||
func buildAuthzHeader(authString string, signature string, headerToSign []string) string {
|
||||
return "YOP-RSA2048-SHA256" + " " + authString + "/" + strings.Join(headerToSign, ";") + "/" + signature
|
||||
}
|
183
vendor/github.com/yop-platform/yop-go-sdk/yop/client/yop_client.go
generated
vendored
Normal file
183
vendor/github.com/yop-platform/yop-go-sdk/yop/client/yop_client.go
generated
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
// Package client
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/16 3:22 PM
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/auth"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/constants"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/request"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/response"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/utils"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var DefaultClient = YopClient{&http.Client{Transport: http.DefaultTransport}}
|
||||
|
||||
type YopClient struct {
|
||||
*http.Client
|
||||
}
|
||||
|
||||
// Request 普通请求
|
||||
func (yopClient *YopClient) Request(request *request.YopRequest) (*response.YopResponse, error) {
|
||||
initRequest(request)
|
||||
var signer = auth.RsaSigner{}
|
||||
signer.SignRequest(*request)
|
||||
|
||||
httpRequest, err := buildHttpRequest(*request)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
httpResp, err := yopClient.Client.Do(&httpRequest)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
defer httpResp.Body.Close()
|
||||
body, err := ioutil.ReadAll(httpResp.Body)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
var yopResponse = response.YopResponse{Content: body}
|
||||
metaData := response.YopResponseMetadata{}
|
||||
metaData.YopSign = httpResp.Header.Get("X-Yop-Sign")
|
||||
metaData.YopRequestId = httpResp.Header.Get("X-Yop-Request-Id")
|
||||
yopResponse.Metadata = &metaData
|
||||
context := response.RespHandleContext{YopSigner: &signer, YopResponse: &yopResponse, YopRequest: *request}
|
||||
for i := range response.ANALYZER_CHAIN {
|
||||
err = response.ANALYZER_CHAIN[i].Analyze(context, httpResp)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &yopResponse, nil
|
||||
}
|
||||
func initRequest(yopRequest *request.YopRequest) {
|
||||
yopRequest.RequestId = uuid.NewV4().String()
|
||||
log.Println("requestId:" + yopRequest.RequestId)
|
||||
if 0 == len(yopRequest.ServerRoot) {
|
||||
yopRequest.HandleServerRoot()
|
||||
}
|
||||
if 0 == len(yopRequest.PlatformPubKey.Value) {
|
||||
yopRequest.PlatformPubKey.Value = request.YOP_PLATFORM_PUBLIC_KEY
|
||||
yopRequest.PlatformPubKey.CertType = request.RSA2048
|
||||
}
|
||||
addStandardHeaders(yopRequest)
|
||||
}
|
||||
func addStandardHeaders(yopRequest *request.YopRequest) {
|
||||
yopRequest.Headers = map[string]string{}
|
||||
yopRequest.Headers[constants.YOP_REQUEST_ID] = yopRequest.RequestId
|
||||
yopRequest.Headers[constants.YOP_APPKEY_HEADER_KEY] = yopRequest.AppId
|
||||
yopRequest.Headers[constants.USER_AGENT_HEADER_KEY] = buildUserAgent()
|
||||
}
|
||||
|
||||
func buildUserAgent() string {
|
||||
return "go" + "/" + constants.SDK_VERSION + "/" + runtime.GOOS + "/" + runtime.Version()
|
||||
}
|
||||
|
||||
func buildHttpRequest(yopRequest request.YopRequest) (http.Request, error) {
|
||||
if yopRequest.Timeout == 0 {
|
||||
yopRequest.Timeout = 10 * time.Second
|
||||
}
|
||||
ctx, _ := context.WithTimeout(context.Background(), yopRequest.Timeout)
|
||||
//defer cancel()
|
||||
|
||||
var uri = yopRequest.ServerRoot + yopRequest.ApiUri
|
||||
isMultiPart, err := checkForMultiPart(yopRequest)
|
||||
if nil != err {
|
||||
return http.Request{}, err
|
||||
}
|
||||
var result http.Request
|
||||
if isMultiPart {
|
||||
bodyBuf := &bytes.Buffer{}
|
||||
bodyWriter := multipart.NewWriter(bodyBuf)
|
||||
|
||||
for k, v := range yopRequest.Params {
|
||||
for i := range v {
|
||||
bodyWriter.WriteField(k, url.QueryEscape(v[i]))
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range yopRequest.Files {
|
||||
fileWriter, _ := bodyWriter.CreateFormFile(k, v.Name())
|
||||
io.Copy(fileWriter, v)
|
||||
}
|
||||
bodyWriter.Close()
|
||||
|
||||
if err != nil {
|
||||
return http.Request{}, err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", uri, bodyBuf)
|
||||
if nil != err {
|
||||
return http.Request{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
|
||||
result = *req
|
||||
} else {
|
||||
var encodedParam = utils.EncodeParameters(yopRequest.Params)
|
||||
var requestHasPayload = 0 < len(yopRequest.Content)
|
||||
var requestIsPost = 0 == strings.Compare(constants.POST_HTTP_METHOD, yopRequest.HttpMethod)
|
||||
var putParamsInUri = !requestIsPost || requestHasPayload
|
||||
if 0 < len(encodedParam) && putParamsInUri {
|
||||
uri += "?" + encodedParam
|
||||
}
|
||||
var body io.Reader = nil
|
||||
if 0 == strings.Compare(constants.POST_HTTP_METHOD, yopRequest.HttpMethod) {
|
||||
if 0 < len(yopRequest.Content) {
|
||||
body = bytes.NewBuffer([]byte(yopRequest.Content))
|
||||
} else {
|
||||
formValues := url.Values{}
|
||||
for k, v := range yopRequest.Params {
|
||||
for i := range v {
|
||||
formValues.Set(k, url.QueryEscape(v[i]))
|
||||
}
|
||||
}
|
||||
formDataStr := formValues.Encode()
|
||||
body = bytes.NewBuffer([]byte(formDataStr))
|
||||
}
|
||||
}
|
||||
httpRequest, err := http.NewRequestWithContext(ctx, yopRequest.HttpMethod, uri, body)
|
||||
if err != nil {
|
||||
return http.Request{}, err
|
||||
}
|
||||
result = *httpRequest
|
||||
result.Header.Set(constants.CONTENT_TYPE, getContentType(yopRequest))
|
||||
}
|
||||
for k, v := range yopRequest.Headers {
|
||||
result.Header.Set(k, v)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func checkForMultiPart(yopRequest request.YopRequest) (bool, error) {
|
||||
var result = nil != yopRequest.Files && 0 < len(yopRequest.Files)
|
||||
if result && 0 != strings.Compare(constants.POST_HTTP_METHOD, yopRequest.HttpMethod) {
|
||||
var errorMsg = "ContentType:multipart/form-data only support Post Request"
|
||||
log.Fatal(errorMsg)
|
||||
return false, errors.New(errorMsg)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getContentType(yopRequest request.YopRequest) string {
|
||||
if 0 == strings.Compare("POST", yopRequest.HttpMethod) && 0 < len(yopRequest.Content) {
|
||||
return constants.YOP_HTTP_CONTENT_TYPE_JSON
|
||||
}
|
||||
if 0 < len(yopRequest.Params) {
|
||||
return constants.YOP_HTTP_CONTENT_TYPE_FORM
|
||||
}
|
||||
return constants.YOP_HTTP_CONTENT_TYPE_FORM
|
||||
}
|
37
vendor/github.com/yop-platform/yop-go-sdk/yop/constants/constants.go
generated
vendored
Normal file
37
vendor/github.com/yop-platform/yop-go-sdk/yop/constants/constants.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Package constants
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/21 3:24 PM
|
||||
package constants
|
||||
|
||||
const (
|
||||
DEFAULT_YOP_PROTOCOL_VERSION = "yop-auth-v3"
|
||||
DEFAULT_EXPIRATION_IN_SECONDS = 1800
|
||||
YOP_CONTENT_SHA256 = "x-yop-content-sha256"
|
||||
AUTHORIZATION = "Authorization"
|
||||
YOP_REQUEST_ID = "x-yop-request-id"
|
||||
YOP_APPKEY_HEADER_KEY = "x-yop-appkey"
|
||||
YOP_SIGN_HEADER_KEY = "x-yop-sign"
|
||||
USER_AGENT_HEADER_KEY = "User-Agent"
|
||||
CONTENT_TYPE = "Content-Type"
|
||||
DEFAULT_USER_AGENT = ""
|
||||
YOP_SIGN = "x-yop-sign"
|
||||
YOP_SIGN_CERT_SERIAL_NO = "x-yop-sign-serial-no"
|
||||
DATE = "Date"
|
||||
YOP_HASH_CRC64ECMA = "x-yop-hash-crc64ecma"
|
||||
|
||||
YOP_HTTP_CONTENT_TYPE_JSON = "application/json"
|
||||
YOP_HTTP_CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=utf-8"
|
||||
YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM = "multipart/form-data"
|
||||
YOP_HTTP_CONTENT_TYPE_STREAM = "application/octet-stream"
|
||||
YOP_HTTP_CONTENT_TYPE_TEXT = "text/plain;charset=UTF-8"
|
||||
|
||||
POST_HTTP_METHOD = "POST"
|
||||
GET_HTTP_METHOD = "GET"
|
||||
|
||||
SC_OK = 200
|
||||
SC_NO_CONTENT = 204
|
||||
|
||||
SDK_VERSION = "4.3.6"
|
||||
)
|
175
vendor/github.com/yop-platform/yop-go-sdk/yop/request/yop_request.go
generated
vendored
Normal file
175
vendor/github.com/yop-platform/yop-go-sdk/yop/request/yop_request.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Package request
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/16 3:22 PM
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SERVER_ROOT = "https://openapi.yeepay.com/yop-center"
|
||||
YOS_SERVER_ROOT = "https://yos.yeepay.com/yop-center"
|
||||
RSA2048 = "RSA2048"
|
||||
YOP_PLATFORM_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6p0XWjscY+gsyqKRhw9MeLsEmhFdBRhT2emOck/F1Omw38ZWhJxh9kDfs5HzFJMrVozgU+SJFDONxs8UB0wMILKRmqfLcfClG9MyCNuJkkfm0HFQv1hRGdOvZPXj3Bckuwa7FrEXBRYUhK7vJ40afumspthmse6bs6mZxNn/mALZ2X07uznOrrc2rk41Y2HftduxZw6T4EmtWuN2x4CZ8gwSyPAW5ZzZJLQ6tZDojBK4GZTAGhnn3bg5bBsBlw2+FLkCQBuDsJVsFPiGh/b6K/+zGTvWyUcu+LUj2MejYQELDO3i2vQXVDk7lVi2/TcUYefvIcssnzsfCfjaorxsuwIDAQAB"
|
||||
)
|
||||
|
||||
type YopRequest struct {
|
||||
// 服务地址,一般情况无需指定
|
||||
ServerRoot string
|
||||
RequestId string
|
||||
ApiUri string
|
||||
HttpMethod string
|
||||
AppId string
|
||||
IsvPriKey IsvPriKey
|
||||
// 平台公钥,一般情况无需指定
|
||||
PlatformPubKey PlatformPubKey
|
||||
// form请求的参数
|
||||
Params map[string][]string
|
||||
// json请求参数
|
||||
Content string
|
||||
// 请求头
|
||||
Headers map[string]string
|
||||
// 文件
|
||||
Files map[string]*os.File
|
||||
|
||||
// 超时时间
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// NewYopRequest 创建请求
|
||||
func NewYopRequest(httpMethod string, apiUri string) *YopRequest {
|
||||
return &YopRequest{HttpMethod: httpMethod, ApiUri: apiUri, Timeout: 10 * time.Second}
|
||||
}
|
||||
|
||||
func (request *YopRequest) AddParam(name string, value any) {
|
||||
if nil == request.Params {
|
||||
request.Params = map[string][]string{}
|
||||
}
|
||||
var strValue = ToStringE(value)
|
||||
var paramArray = []string{strValue}
|
||||
request.Params[name] = paramArray
|
||||
}
|
||||
|
||||
func (request *YopRequest) AddFile(name string, f *os.File) {
|
||||
if nil == request.Files {
|
||||
request.Files = map[string]*os.File{}
|
||||
}
|
||||
request.Files[name] = f
|
||||
}
|
||||
|
||||
type IsvPriKey struct {
|
||||
// 密钥类型:RSA2048
|
||||
CertType string
|
||||
// 私钥值
|
||||
Value string
|
||||
}
|
||||
|
||||
type PlatformPubKey struct {
|
||||
// 密钥类型:RSA2048
|
||||
CertType string
|
||||
// 公钥值
|
||||
Value string
|
||||
}
|
||||
|
||||
func BuildYopRequest() *YopRequest {
|
||||
var isvPriKey = IsvPriKey{CertType: RSA2048}
|
||||
var platformCert = PlatformPubKey{Value: YOP_PLATFORM_PUBLIC_KEY, CertType: RSA2048}
|
||||
return &YopRequest{RequestId: uuid.NewV4().String(), IsvPriKey: isvPriKey, PlatformPubKey: platformCert, Params: map[string][]string{}, Headers: map[string]string{}, Files: map[string]*os.File{}}
|
||||
}
|
||||
|
||||
func (request *YopRequest) HandleServerRoot() {
|
||||
if 0 != len(request.ServerRoot) {
|
||||
return
|
||||
}
|
||||
|
||||
if 0 != len(request.ApiUri) || strings.HasPrefix(request.ApiUri, "/yos") {
|
||||
request.ServerRoot = YOS_SERVER_ROOT
|
||||
}
|
||||
request.ServerRoot = SERVER_ROOT
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
||||
)
|
||||
|
||||
func ToStringE(i any) string {
|
||||
i = indirectToStringerOrError(i)
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
return s
|
||||
case bool:
|
||||
return strconv.FormatBool(s)
|
||||
case float64:
|
||||
return strconv.FormatFloat(s, 'f', -1, 64)
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(s), 'f', -1, 32)
|
||||
case int:
|
||||
return strconv.Itoa(s)
|
||||
case int64:
|
||||
return strconv.FormatInt(s, 10)
|
||||
case int32:
|
||||
return strconv.Itoa(int(s))
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(s), 10)
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(s), 10)
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case json.Number:
|
||||
return s.String()
|
||||
case []byte:
|
||||
return string(s)
|
||||
case template.HTML:
|
||||
return string(s)
|
||||
case template.URL:
|
||||
return string(s)
|
||||
case template.JS:
|
||||
return string(s)
|
||||
case template.CSS:
|
||||
return string(s)
|
||||
case template.HTMLAttr:
|
||||
return string(s)
|
||||
case nil:
|
||||
return ""
|
||||
case fmt.Stringer:
|
||||
return s.String()
|
||||
case error:
|
||||
return s.Error()
|
||||
default:
|
||||
log.Fatal(fmt.Sprintf("unable to cast %#v of type %T to string", i, i))
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func indirectToStringerOrError(a any) any {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
v := reflect.ValueOf(a)
|
||||
for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v.Interface()
|
||||
}
|
85
vendor/github.com/yop-platform/yop-go-sdk/yop/response/http_response_analyzer.go
generated
vendored
Normal file
85
vendor/github.com/yop-platform/yop-go-sdk/yop/response/http_response_analyzer.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Package response
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/22 10:54 PM
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/constants"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ANALYZER_CHAIN = []HttpResponseAnalyzer{
|
||||
&YopMetadataResponseAnalyzer{},
|
||||
&YopSignatureCheckAnalyzer{},
|
||||
&YopErrorResponseAnalyzer{},
|
||||
&YopJsonResponseAnalyzer{},
|
||||
}
|
||||
|
||||
type HttpResponseAnalyzer interface {
|
||||
Analyze(context RespHandleContext, httpResponse *http.Response) error
|
||||
}
|
||||
|
||||
type YopMetadataResponseAnalyzer struct {
|
||||
}
|
||||
|
||||
func (yopMetadataResponseAnalyzer *YopMetadataResponseAnalyzer) Analyze(context RespHandleContext, httpResponse *http.Response) error {
|
||||
var metadata = YopResponseMetadata{}
|
||||
metadata.YopRequestId = httpResponse.Header.Get(constants.YOP_REQUEST_ID)
|
||||
metadata.YopContentSha256 = httpResponse.Header.Get(constants.YOP_CONTENT_SHA256)
|
||||
metadata.YopSign = httpResponse.Header.Get(constants.YOP_SIGN)
|
||||
metadata.ContentType = httpResponse.Header.Get(constants.CONTENT_TYPE)
|
||||
d, _ := time.Parse(time.RFC1123, httpResponse.Header.Get(constants.DATE))
|
||||
metadata.Date = d
|
||||
metadata.YopCertSerialNo = httpResponse.Header.Get(constants.YOP_SIGN_CERT_SERIAL_NO)
|
||||
metadata.Crc64ECMA = httpResponse.Header.Get(constants.YOP_HASH_CRC64ECMA)
|
||||
context.YopResponse.Metadata = &metadata
|
||||
return nil
|
||||
}
|
||||
|
||||
type YopSignatureCheckAnalyzer struct {
|
||||
}
|
||||
|
||||
func (yopSignatureCheckAnalyzer *YopSignatureCheckAnalyzer) Analyze(context RespHandleContext, httpResponse *http.Response) error {
|
||||
var signature = context.YopResponse.Metadata.YopSign
|
||||
if 0 < len(signature) {
|
||||
if !context.YopSigner.VerifyResponse(string(context.YopResponse.Content), signature, context.YopRequest.PlatformPubKey) {
|
||||
return errors.New("response sign verify failure")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type YopErrorResponseAnalyzer struct {
|
||||
}
|
||||
|
||||
func (yopErrorResponseAnalyzer *YopErrorResponseAnalyzer) Analyze(context RespHandleContext, httpResponse *http.Response) error {
|
||||
var statusCode = httpResponse.StatusCode
|
||||
log.Println("statusCode:" + strconv.Itoa(statusCode))
|
||||
if statusCode/100 == constants.SC_OK && statusCode != constants.SC_NO_CONTENT {
|
||||
return nil
|
||||
}
|
||||
var yopServiceError = YopServiceError{}
|
||||
json.Unmarshal(context.YopResponse.Content, &yopServiceError)
|
||||
if 0 < len(yopServiceError.Message) {
|
||||
return &yopServiceError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type YopJsonResponseAnalyzer struct {
|
||||
}
|
||||
|
||||
func (yopJsonResponseAnalyzer *YopJsonResponseAnalyzer) Analyze(context RespHandleContext, httpResponse *http.Response) error {
|
||||
if 0 < len(context.YopResponse.Content) && strings.HasPrefix(context.YopResponse.Metadata.ContentType, constants.YOP_HTTP_CONTENT_TYPE_JSON) {
|
||||
json.Unmarshal(context.YopResponse.Content, &context.YopResponse)
|
||||
}
|
||||
return nil
|
||||
}
|
37
vendor/github.com/yop-platform/yop-go-sdk/yop/response/yop_response.go
generated
vendored
Normal file
37
vendor/github.com/yop-platform/yop-go-sdk/yop/response/yop_response.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Package response
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/16 3:22 PM
|
||||
package response
|
||||
|
||||
import (
|
||||
"github.com/yop-platform/yop-go-sdk/yop/auth"
|
||||
"github.com/yop-platform/yop-go-sdk/yop/request"
|
||||
"time"
|
||||
)
|
||||
|
||||
type YopResponse struct {
|
||||
Metadata *YopResponseMetadata
|
||||
Result any
|
||||
// http请求收到的原始响应体
|
||||
// 若接口类型为文件下载,则该值为文件内容
|
||||
Content []byte
|
||||
}
|
||||
|
||||
type YopResponseMetadata struct {
|
||||
YopRequestId string
|
||||
YopContentSha256 string
|
||||
YopSign string
|
||||
ContentType string
|
||||
Date time.Time
|
||||
Server string
|
||||
YopCertSerialNo string
|
||||
Crc64ECMA string
|
||||
}
|
||||
|
||||
type RespHandleContext struct {
|
||||
auth.YopSigner
|
||||
*YopResponse
|
||||
request.YopRequest
|
||||
}
|
19
vendor/github.com/yop-platform/yop-go-sdk/yop/response/yop_service_error.go
generated
vendored
Normal file
19
vendor/github.com/yop-platform/yop-go-sdk/yop/response/yop_service_error.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Package response
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/22 11:09 PM
|
||||
package response
|
||||
|
||||
type YopServiceError struct {
|
||||
RequestId string
|
||||
Code string
|
||||
Message string
|
||||
SubCode string
|
||||
SubMessage string
|
||||
DocUrl string
|
||||
}
|
||||
|
||||
func (err *YopServiceError) Error() string {
|
||||
return err.Message + "(Error Code: " + err.Code + "; Sub Code: " + err.SubCode + "; Sub Message: " + err.SubMessage + "; Request ID: " + err.RequestId + "; docUrl: " + err.DocUrl + ")"
|
||||
}
|
77
vendor/github.com/yop-platform/yop-go-sdk/yop/utils/callback_decrypt_utils.go
generated
vendored
Normal file
77
vendor/github.com/yop-platform/yop-go-sdk/yop/utils/callback_decrypt_utils.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Package utils
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/15 2:22 PM
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DecryptCallback 解密回调通知内容
|
||||
func DecryptCallback(platformPubKey string, isvPriKey string, callBack string) (string, error) {
|
||||
cipherText := strings.Split(callBack, "$")
|
||||
if len(cipherText) != 4 {
|
||||
return "", errors.New("response invalid")
|
||||
}
|
||||
randomKey, err := RsaDecrypt(isvPriKey, cipherText[0])
|
||||
if err != nil {
|
||||
log.Println("random key rsa error ", err)
|
||||
return "", err
|
||||
}
|
||||
cipherBytes := base64Decode(cipherText[1])
|
||||
body := string(AesDecryptECB(cipherBytes, randomKey))
|
||||
dollarPosition := strings.LastIndex(body, "$")
|
||||
signature := strings.TrimSpace(body[dollarPosition+1:])
|
||||
body = body[:dollarPosition]
|
||||
|
||||
if !VerifySign(body, signature, platformPubKey, crypto.SHA256) {
|
||||
return "", errors.New("rsa sign verify fail")
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// 解析非标 base64_encode
|
||||
func base64Decode(b string) []byte {
|
||||
b = strings.Replace(b, "-", "+", -1)
|
||||
b = strings.Replace(b, "_", "/", -1)
|
||||
r, err := base64.RawStdEncoding.DecodeString(b)
|
||||
if err != nil {
|
||||
log.Println("base64 decode error ", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
|
||||
cipher, _ := aes.NewCipher(generateKey(key))
|
||||
decrypted = make([]byte, len(encrypted))
|
||||
//
|
||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
||||
}
|
||||
|
||||
trim := 0
|
||||
if len(decrypted) > 0 {
|
||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
||||
}
|
||||
|
||||
return decrypted[:trim]
|
||||
}
|
||||
|
||||
func generateKey(key []byte) (genKey []byte) {
|
||||
genKey = make([]byte, 16)
|
||||
copy(genKey, key)
|
||||
for i := 16; i < len(key); {
|
||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
||||
genKey[j] ^= key[i]
|
||||
}
|
||||
}
|
||||
return genKey
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Package utils
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/20 4:15 PM
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/yop-platform/yop-go-sdk/yop/request"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NormalizePath(path string) string {
|
||||
|
||||
return strings.ReplaceAll(Normalize(path), "%2F", "/")
|
||||
}
|
||||
|
||||
func Normalize(value string) string {
|
||||
var firstEncodeStr = url.QueryEscape(value)
|
||||
return encodeSpecialChar(firstEncodeStr)
|
||||
}
|
||||
|
||||
func encodeSpecialChar(str string) string {
|
||||
// 空格
|
||||
str = strings.ReplaceAll(str, "+", "%20")
|
||||
return str
|
||||
}
|
||||
|
||||
func EncodeParameters(params map[string][]string) string {
|
||||
if 0 == len(params) {
|
||||
return ""
|
||||
}
|
||||
var encodedNameValuePair []string
|
||||
for k, v := range params {
|
||||
for i := range v {
|
||||
encodedNameValuePair = append(encodedNameValuePair, toNameValuePair(k, v[i]))
|
||||
}
|
||||
}
|
||||
return strings.Join(encodedNameValuePair, "&")
|
||||
}
|
||||
|
||||
func toNameValuePair(paramName string, paramValue string) string {
|
||||
return Normalize(paramName) + "=" + Normalize(paramValue)
|
||||
}
|
||||
|
||||
func GetCanonicalQueryString(params map[string][]string) string {
|
||||
if 0 == len(params) {
|
||||
return ""
|
||||
}
|
||||
|
||||
var parameterStrings []string
|
||||
|
||||
for k, v := range params {
|
||||
if nil == v || 0 == len(v) {
|
||||
parameterStrings = append(parameterStrings, Normalize(k)+"=")
|
||||
} else {
|
||||
for i := range v {
|
||||
parameterStrings = append(parameterStrings, Normalize(k)+"="+Normalize(v[i]))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
sort.Strings(parameterStrings)
|
||||
return strings.Join(parameterStrings, "&")
|
||||
}
|
||||
|
||||
func UsePayloadForQueryParameters(yopRequest request.YopRequest) bool {
|
||||
var requestIsPOST = 0 == strings.Compare("POST", yopRequest.HttpMethod)
|
||||
var requestHasNoPayload = 0 == len(yopRequest.Content)
|
||||
return requestIsPOST && requestHasNoPayload
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Package utils
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/29 9:56 AM
|
||||
package utils
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
func ParseToJsonStr(params map[string]any) string {
|
||||
marshal, _ := json.Marshal(params)
|
||||
return string(marshal)
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
// Package utils
|
||||
// Copyright: Copyright (c) 2020<br>
|
||||
// Company: 易宝支付(YeePay)<br>
|
||||
// @author : yunmei.wu
|
||||
// @time : 2023/3/14 10:23 AM
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RsaSignBase64 base64UrlEncode签名
|
||||
func RsaSignBase64(content string, privateKey string, hash crypto.Hash) (string, error) {
|
||||
signature, err := Sign(content, privateKey, hash)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
|
||||
// VerifySign 验签
|
||||
func VerifySign(content string, signature string, pubKey string, hash crypto.Hash) bool {
|
||||
pubKey = FormatPemKey(pubKey, "PUBLIC KEY")
|
||||
publicKey, err := ParsePublicKey(pubKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var sig []byte
|
||||
if strings.Contains(signature, "*") || strings.Contains(signature, "+") || strings.Contains(signature, "=") {
|
||||
sig, _ = base64.StdEncoding.DecodeString(signature)
|
||||
} else {
|
||||
sig, _ = base64.RawURLEncoding.DecodeString(signature)
|
||||
}
|
||||
return Verify([]byte(content), sig, publicKey, crypto.SHA256)
|
||||
}
|
||||
|
||||
func Verify(content []byte, signature []byte, pub *rsa.PublicKey, hash crypto.Hash) bool {
|
||||
hashed := sha256.Sum256(content)
|
||||
err := rsa.VerifyPKCS1v15(pub, hash, hashed[:], signature)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Sign rsa签名
|
||||
func Sign(content string, privateKey string, hash crypto.Hash) ([]byte, error) {
|
||||
shaNew := sha256.New()
|
||||
shaNew.Write([]byte(content))
|
||||
hashed := shaNew.Sum(nil)
|
||||
|
||||
priKey, err := ParsePrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signature, err := rsa.SignPKCS1v15(rand.Reader, priKey.(*rsa.PrivateKey), hash, hashed)
|
||||
|
||||
return signature, err
|
||||
}
|
||||
|
||||
func ParsePrivateKey(privateKey string) (any, error) {
|
||||
privateKey = FormatPemKey(privateKey, "PRIVATE KEY")
|
||||
// 2、解码私钥字节,生成加密对象
|
||||
block, _ := pem.Decode([]byte(privateKey))
|
||||
if block == nil {
|
||||
return nil, errors.New("私钥信息错误!")
|
||||
}
|
||||
|
||||
// 3、解析DER编码的私钥,生成私钥对象
|
||||
priKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return priKey, nil
|
||||
}
|
||||
|
||||
func ParsePublicKey(publicKey string) (*rsa.PublicKey, error) {
|
||||
block, _ := pem.Decode([]byte(publicKey))
|
||||
if block == nil {
|
||||
return nil, errors.New("公钥信息错误!")
|
||||
}
|
||||
pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pubKey.(*rsa.PublicKey), nil
|
||||
}
|
||||
|
||||
// FormatPemKey /**
|
||||
func FormatPemKey(yopFormKey string, pemHeader string) string {
|
||||
var sb = strings.Builder{}
|
||||
sb.WriteString("-----BEGIN ")
|
||||
sb.WriteString(pemHeader)
|
||||
sb.WriteString("-----\n")
|
||||
for i := 0; i < len(yopFormKey); i++ {
|
||||
sb.WriteString(string([]rune(yopFormKey)[i]))
|
||||
if (i+1)%64 == 0 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
sb.WriteString("\n-----END ")
|
||||
sb.WriteString(pemHeader)
|
||||
sb.WriteString("-----\n")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func RsaDecrypt(priKey string, cipher string) ([]byte, error) {
|
||||
privateKey, _ := ParsePrivateKey(priKey)
|
||||
cipherBytes, _ := base64.RawURLEncoding.DecodeString(cipher)
|
||||
return rsa.DecryptPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), cipherBytes)
|
||||
}
|
Loading…
Reference in New Issue