Completed
Push — master ( 721c87...eab9b9 )
by wannanbigpig
02:40
created

Helpers   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 243
Duplicated Lines 0 %

Test Coverage

Coverage 36.26%

Importance

Changes 0
Metric Value
eloc 94
dl 0
loc 243
ccs 33
cts 91
cp 0.3626
rs 9.76
c 0
b 0
f 0
wmc 33

8 Methods

Rating   Name   Duplication   Size   Complexity  
A checkEmpty() 0 18 6
A sign() 0 29 5
A getSignContentUrlencode() 0 20 5
A checkResponseSign() 0 20 3
A parserSignSource() 0 13 3
A verify() 0 32 5
A getSignContent() 0 20 5
A generateSign() 0 3 1
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 EasyAlipay\Kernel\Traits;
12
13
use EasyAlipay\Kernel\Exceptions\InvalidSignException;
14
use WannanBigPig\Supports\Exceptions\InvalidArgumentException;
15
16
/**
17
 * Class Helpers
18
 *
19
 * @author   liuml  <[email protected]>
20
 * @DateTime 2019-07-18  17:00
21
 */
22
trait Helpers
23
{
24
    /**
25
     * generateSign.
26
     *
27
     * @param        $data
28
     * @param string $signType
29
     *
30
     * @return string
31
     *
32
     * @throws \WannanBigPig\Supports\Exceptions\InvalidArgumentException
33
     */
34 4
    public function generateSign($data, $signType = 'RSA2')
35
    {
36 4
        return $this->sign($this->getSignContent($data), $signType);
37
    }
38
39
    /**
40
     * getSignContent.
41
     *
42
     * @param $params
43
     *
44
     * @return string
45
     */
46 4
    public function getSignContent($params)
47
    {
48 4
        ksort($params);
49
50 4
        $stringToBeSigned = "";
51 4
        $i = 0;
52 4
        foreach ($params as $k => $v) {
53 4
            if (false === $this->checkEmpty($v) && "@" !== substr($v, 0, 1)) {
54 4
                if ($i === 0) {
55 4
                    $stringToBeSigned .= "$k"."="."$v";
56
                } else {
57 4
                    $stringToBeSigned .= "&"."$k"."="."$v";
58
                }
59 4
                $i++;
60
            }
61
        }
62
63 4
        unset($k, $v);
64
65 4
        return $stringToBeSigned;
66
    }
67
68
    /**
69
     * This method does URLEncode for values.
70
     *
71
     * @param $params
72
     *
73
     * @return string
74
     */
75
    public function getSignContentUrlencode($params)
76
    {
77
        ksort($params);
78
79
        $stringToBeSigned = "";
80
        $i = 0;
81
        foreach ($params as $k => $v) {
82
            if (false === $this->checkEmpty($v) && "@" !== substr($v, 0, 1)) {
83
                if ($i === 0) {
84
                    $stringToBeSigned .= "$k"."=".urlencode($v);
85
                } else {
86
                    $stringToBeSigned .= "&"."$k"."=".urlencode($v);
87
                }
88
                $i++;
89
            }
90
        }
91
92
        unset($k, $v);
93
94
        return $stringToBeSigned;
95
    }
96
97
    /**
98
     * sign.
99
     *
100
     * @param        $data
101
     * @param string $signType
102
     *
103
     * @return string
104
     *
105
     * @throws \WannanBigPig\Supports\Exceptions\InvalidArgumentException
106
     */
107 4
    protected function sign($data, $signType = "RSA2")
108
    {
109 4
        $rsaPrivateKeyFilePath = $this->app['config']->get('private_key_path');
110 4
        if ($this->checkEmpty($rsaPrivateKeyFilePath)) {
111
            $priKey = $this->app['config']['private_key'];
112
            $res = "-----BEGIN RSA PRIVATE KEY-----\n".
113
                wordwrap($priKey, 64, "\n", true).
114
                "\n-----END RSA PRIVATE KEY-----";
115
        } else {
116 4
            $priKey = file_get_contents($rsaPrivateKeyFilePath);
117 4
            $res = openssl_get_privatekey($priKey);
118
        }
119
120 4
        if ($res === false) {
121
            throw new InvalidArgumentException('Invalid private_key configuration');
122
        }
123
124 4
        if ("RSA2" === $signType) {
125 4
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
126
        } else {
127
            openssl_sign($data, $sign, $res);
128
        }
129
130 4
        if (!$this->checkEmpty($rsaPrivateKeyFilePath)) {
131 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

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

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