Completed
Push — master ( 8787ba...721c87 )
by wannanbigpig
05:01 queued 11s
created

Helpers::checkResponseSign()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 12
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 20
ccs 0
cts 12
cp 0
crap 12
rs 9.8666
1
<?php
2
/*
3
 * This file is part of the wannanbigpig/alipay.
4
 *
5
 * (c) wannanbigpig <[email protected]>
6
 *
7
 * This source file is subject to the MIT license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace WannanBigPig\Alipay\Kernel\Traits;
12
13
use Exception;
14
use WannanBigPig\Alipay\Kernel\Exceptions\InvalidSignException;
15
use WannanBigPig\Supports\Exceptions\InvalidArgumentException;
16
17
/**
18
 * Class Helpers
19
 *
20
 * @author   liuml  <[email protected]>
21
 * @DateTime 2019-07-18  17:00
22
 */
23
trait Helpers
24
{
25
    /**
26
     * generateSign.
27
     *
28
     * @param        $data
29
     * @param string $signType
30
     *
31
     * @return string
32
     *
33
     * @throws \WannanBigPig\Supports\Exceptions\InvalidArgumentException
34
     */
35 4
    public function generateSign($data, $signType = 'RSA2')
36
    {
37 4
        return $this->sign($this->getSignContent($data), $signType);
38
    }
39
40
    /**
41
     * getSignContent.
42
     *
43
     * @param $params
44
     *
45
     * @return string
46
     */
47 4
    public function getSignContent($params)
48
    {
49 4
        ksort($params);
50
51 4
        $stringToBeSigned = "";
52 4
        $i = 0;
53 4
        foreach ($params as $k => $v) {
54 4
            if (false === $this->checkEmpty($v) && "@" !== substr($v, 0, 1)) {
55 4
                if ($i === 0) {
56 4
                    $stringToBeSigned .= "$k"."="."$v";
57
                } else {
58 4
                    $stringToBeSigned .= "&"."$k"."="."$v";
59
                }
60 4
                $i++;
61
            }
62
        }
63
64 4
        unset($k, $v);
65
66 4
        return $stringToBeSigned;
67
    }
68
69
    /**
70
     * This method does URLEncode for values.
71
     *
72
     * @param $params
73
     *
74
     * @return string
75
     */
76
    public function getSignContentUrlencode($params)
77
    {
78
        ksort($params);
79
80
        $stringToBeSigned = "";
81
        $i = 0;
82
        foreach ($params as $k => $v) {
83
            if (false === $this->checkEmpty($v) && "@" !== substr($v, 0, 1)) {
84
                if ($i === 0) {
85
                    $stringToBeSigned .= "$k"."=".urlencode($v);
86
                } else {
87
                    $stringToBeSigned .= "&"."$k"."=".urlencode($v);
88
                }
89
                $i++;
90
            }
91
        }
92
93
        unset($k, $v);
94
95
        return $stringToBeSigned;
96
    }
97
98
    /**
99
     * sign.
100
     *
101
     * @param        $data
102
     * @param string $signType
103
     *
104
     * @return string
105
     *
106
     * @throws \WannanBigPig\Supports\Exceptions\InvalidArgumentException
107
     */
108 4
    protected function sign($data, $signType = "RSA2")
109
    {
110 4
        $rsaPrivateKeyFilePath = $this->app['config']->get('private_key_path');
111 4
        if ($this->checkEmpty($rsaPrivateKeyFilePath)) {
112
            $priKey = $this->app['config']['private_key'];
113
            $res = "-----BEGIN RSA PRIVATE KEY-----\n".
114
                wordwrap($priKey, 64, "\n", true).
115
                "\n-----END RSA PRIVATE KEY-----";
116
        } else {
117 4
            $priKey = file_get_contents($rsaPrivateKeyFilePath);
118 4
            $res = openssl_get_privatekey($priKey);
119
        }
120
121 4
        if ($res === false) {
122
            throw new InvalidArgumentException('Invalid private_key configuration');
123
        }
124
125 4
        if ("RSA2" === $signType) {
126 4
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
127
        } else {
128
            openssl_sign($data, $sign, $res);
129
        }
130
131 4
        if (!$this->checkEmpty($rsaPrivateKeyFilePath)) {
132 4
            openssl_free_key($res);
0 ignored issues
show
Bug introduced by
It seems like $res can also be of type string; however, parameter $key_identifier of openssl_free_key() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
            openssl_free_key(/** @scrutinizer ignore-type */ $res);
Loading history...
133
        }
134 4
        $sign = base64_encode($sign);
135
136 4
        return $sign;
137
    }
138
139
    /**
140
     * checkEmpty.
141
     *
142
     * @param $value
143
     *
144
     * @return bool
145
     */
146 4
    protected function checkEmpty($value)
147
    {
148 4
        if ($value === null) {
149 4
            return true;
150
        }
151
152 4
        if (is_string($value)) {
153 4
            if (trim($value) === "") {
154 4
                return true;
155
            }
156
        } elseif (is_array($value)) {
157
            if (count($value) < 1) {
158
                return true;
159
            }
160
        }
161
162
163 4
        return false;
164
    }
165
166
    /**
167
     * parserSignSource.
168
     *
169
     * @param array       $response
170
     * @param string|null $apiMethod
171
     *
172
     * @return array|mixed
173
     */
174
    public function parserSignSource(array $response, string $apiMethod = null)
175
    {
176
        $response_suffix = '_response';
177
        $error_respones = 'error_response';
178
        $apiMethod = $this->app['config']['api_method'] ?? $apiMethod;
179
        $rootNodeName = str_replace(".", "_", $apiMethod).$response_suffix;
180
181
        if (isset($response[$rootNodeName])) {
182
            return $response[$rootNodeName];
183
        } elseif (isset($response[$error_respones])) {
184
            return $response[$error_respones];
185
        } else {
186
            return $response;
187
        }
188
    }
189
190
    /**
191
     * verify sign.
192
     *
193
     * @param string $data
194
     * @param        $sign
195
     * @param string $signType
196
     *
197
     * @return bool
198
     *
199
     * @throws \WannanBigPig\Supports\Exceptions\InvalidArgumentException
200
     */
201
    public function verify(string $data, $sign, $signType = 'RSA2')
202
    {
203
        $alipayPublicKeyPath = $this->app['config']->get('alipay_public_Key_path');
204
        if ($this->checkEmpty($alipayPublicKeyPath)) {
205
            $pubKey = $this->app['config']->get('alipay_public_Key');
206
            $res = "-----BEGIN PUBLIC KEY-----\n".
207
                wordwrap($pubKey, 64, "\n", true).
208
                "\n-----END PUBLIC KEY-----";
209
        } else {
210
            // Read public key file
211
            $pubKey = file_get_contents($alipayPublicKeyPath);
212
            // Convert to openssl format key
213
            $res = openssl_get_publickey($pubKey);
214
        }
215
216
        if ($res === false) {
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
217
            throw new InvalidArgumentException('Invalid alipay_public_Key configuration');
218
        }
219
220
        // Call openssl built-in method checksum, return bool value
221
        if ("RSA2" === $signType) {
222
            $result = (openssl_verify($data, base64_decode($sign, true), $res, OPENSSL_ALGO_SHA256) === 1);
223
        } else {
224
            $result = (openssl_verify($data, base64_decode($sign, true), $res) === 1);
225
        }
226
227
        if (!$this->checkEmpty($alipayPublicKeyPath)) {
228
            // Release resources
229
            openssl_free_key($res);
0 ignored issues
show
Bug introduced by
It seems like $res can also be of type string; however, parameter $key_identifier of openssl_free_key() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

229
            openssl_free_key(/** @scrutinizer ignore-type */ $res);
Loading history...
230
        }
231
232
        return $result;
233
    }
234
235
    /**
236
     * checkResponseSign.
237
     *
238
     * @param             $content
239
     * @param string|null $sign
240
     *
241
     * @return bool
242
     *
243
     * @throws \WannanBigPig\Alipay\Kernel\Exceptions\InvalidSignException
244
     * @throws \WannanBigPig\Supports\Exceptions\InvalidArgumentException
245
     */
246
    public function checkResponseSign($content, string $sign = null)
247
    {
248
        if (!is_null($sign)) {
249
            $result = $this->verify(
250
                $content,
251
                $sign,
252
                $this->app['config']->get('sign_type', 'RSA2')
253
            );
254
        } else {
255
            throw new InvalidSignException('check sign Fail! The reason : sign data is Empty', 0, $content);
256
        }
257
258
        if (!$result) {
259
            throw new InvalidSignException(sprintf(
260
                '"%s" method responds to parameter validation signature error',
261
                $this->app['config']['api_method']
262
            ));
263
        }
264
265
        return $result;
266
    }
267
}
268