Passed
Push — master ( a6a7ae...bf6fd0 )
by i
03:53
created

SecurityTrait::getKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 10
ccs 0
cts 6
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Nilnice\Payment\Alipay\Traits;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
use Nilnice\Payment\Constant;
8
use Nilnice\Payment\Exception\InvalidKeyException;
9
10
trait SecurityTrait
11
{
12
    /**
13
     * Generate signature.
14
     *
15
     * @param array $array
16
     * @param null  $key
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $key is correct as it would always require null to be passed?
Loading history...
17
     *
18
     * @return string
19
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
20
     */
21 3
    public static function generateSign(array $array, $key = null) : string
22
    {
23 3
        if ($key === null) {
24 3
            throw new InvalidKeyException(
25 3
                'Invalid Alipay [private key] configuration.',
26 3
                Constant::ALI_PAY_PRIVATE_KEY_INVALID
27
            );
28
        }
29
30
        if (Str::endsWith($key, '.pem')) {
31
            $key = openssl_pkey_get_private($key);
32
        } else {
33
            $key = self::getKey($key, false);
34
        }
35
36
        $data = self::getSignContent($array);
37
        openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA256);
38
39
        return base64_encode($sign);
40
    }
41
42
    /**
43
     * Get signature content.
44
     *
45
     * @param array $array
46
     * @param bool  $isVerify
47
     *
48
     * @return string
49
     */
50
    public static function getSignContent(
51
        array $array,
52
        $isVerify = false
53
    ) : string {
54
        $to = $array['charset'] ?? 'GB2312';
55
        $array = self::toEncoding($array, $to);
56
        ksort($array);
57
58
        $string = '';
59
        foreach ($array as $key => $val) {
60
            if ($isVerify && $key !== 'sign' && $key !== 'sign_type') {
61
                $string .= $key . '=' . $val . '&';
62
            }
63
            if (! $isVerify
64
                && $val !== ''
65
                && null !== $val
66
                && $key !== 'sign'
67
                && 0 !== strpos($val, '@')
68
            ) {
69
                $string .= $key . '=' . $val . '&';
70
            }
71
        }
72
73
        return rtrim($string, '&');
74
    }
75
76
    /**
77
     * Verify signature.
78
     *
79
     * @param array       $array
80
     * @param string|null $key
81
     * @param bool        $isSync
82
     * @param string|null $sign
83
     *
84
     * @return bool
85
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
86
     */
87
    public static function verifySign(
88
        array $array,
89
        $key = null,
90
        $isSync = false,
91
        $sign = null
92
    ) : bool {
93
        if ($key === null) {
94
            throw new InvalidKeyException(
95
                'Invalid Alipay [public key] configuration.',
96
                Constant::ALI_PAY_PUBLIC_KEY_INVALID
97
            );
98
        }
99
100
        if (Str::endsWith($key, '.pem')) {
101
            $key = openssl_pkey_get_public($key);
102
        } else {
103
            $key = self::getKey($key);
104
        }
105
106
        $sign = $sign ?? Arr::get($array, 'sign');
107
        $data = $isSync
108
            ? mb_convert_encoding(
109
                json_encode($array, JSON_UNESCAPED_UNICODE),
110
                'GB2312',
111
                'UTF-8'
112
            )
113
            : self::getSignContent($array, true);
114
        $sign = base64_decode($sign);
115
        $result = openssl_verify($data, $sign, $key, OPENSSL_ALGO_SHA256) === 1;
116
117
        return $result === 1;
118
    }
119
120
    /**
121
     * To encoding.
122
     *
123
     * @param array  $array
124
     * @param string $to
125
     * @param string $from
126
     *
127
     * @return array
128
     */
129
    public static function toEncoding(
130
        array $array,
131
        $to = 'GB2312',
132
        $from = 'UTF-8'
133
    ) : array {
134
        $data = [];
135
        foreach ($array as $key => $val) {
136
            if (\is_array($val)) {
137
                $data[$key] = self::toEncoding((array)$val, $to, $from);
138
            } else {
139
                $data[$key] = mb_convert_encoding($val, $to, $from);
140
            }
141
        }
142
143
        return $data;
144
    }
145
146
    /**
147
     * Get key.
148
     *
149
     * @param string $key
150
     * @param bool   $isPublicKey
151
     *
152
     * @return string
153
     */
154
    public static function getKey(
155
        string $key,
156
        bool $isPublicKey = true
157
    ) : string {
158
        $char = $isPublicKey ? 'PUBLIC' : 'RSA PRIVATE';
159
        $format = "-----BEGIN %s KEY-----\n%s\n-----END %s KEY-----";
160
        $key = wordwrap($key, 64, PHP_EOL, true);
161
        $string = sprintf($format, $char, $key, $char);
162
163
        return $string;
164
    }
165
}
166