Issues (9)

src/Alipay/Traits/SecurityTrait.php (1 issue)

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
     *
20
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
21
     */
22 3
    public static function generateSign(array $array, $key = null) : string
23
    {
24 3
        if ($key === null) {
25 3
            throw new InvalidKeyException(
26 3
                'Invalid Alipay [private key] configuration.',
27 3
                Constant::ALI_PAY_PRIVATE_KEY_INVALID
28
            );
29
        }
30
31
        if (Str::endsWith($key, '.pem')) {
32
            $key = openssl_pkey_get_private($key);
33
        } else {
34
            $key = self::getKey($key, false);
35
        }
36
37
        $data = self::getSignContent($array);
38
        openssl_sign($data, $sign, $key, OPENSSL_ALGO_SHA256);
39
40
        return base64_encode($sign);
41
    }
42
43
    /**
44
     * Get signature content.
45
     *
46
     * @param array $array
47
     * @param bool  $isVerify
48
     *
49
     * @return string
50
     */
51
    public static function getSignContent(
52
        array $array,
53
        $isVerify = false
54
    ) : string {
55
        $to = $array['charset'] ?? 'GB2312';
56
        $array = self::toEncoding($array, $to);
57
        ksort($array);
58
59
        $string = '';
60
        foreach ($array as $key => $val) {
61
            if ($isVerify && $key !== 'sign' && $key !== 'sign_type') {
62
                $string .= $key . '=' . $val . '&';
63
            }
64
            if (! $isVerify
65
                && $val !== ''
66
                && null !== $val
67
                && $key !== 'sign'
68
                && 0 !== strpos($val, '@')
69
            ) {
70
                $string .= $key . '=' . $val . '&';
71
            }
72
        }
73
74
        return rtrim($string, '&');
75
    }
76
77
    /**
78
     * Verify signature.
79
     *
80
     * @param array       $array
81
     * @param string|null $key
82
     * @param bool        $isSync
83
     * @param string|null $sign
84
     *
85
     * @return bool
86
     *
87
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
88
     */
89
    public static function verifySign(
90
        array $array,
91
        $key = null,
92
        $isSync = false,
93
        $sign = null
94
    ) : bool {
95
        if ($key === null) {
96
            throw new InvalidKeyException(
97
                'Invalid Alipay [public key] configuration.',
98
                Constant::ALI_PAY_PUBLIC_KEY_INVALID
99
            );
100
        }
101
102
        if (Str::endsWith($key, '.pem')) {
103
            $key = openssl_pkey_get_public($key);
104
        } else {
105
            $key = self::getKey($key);
106
        }
107
108
        $sign = $sign ?? Arr::get($array, 'sign');
109
        $data = $isSync
110
            ? mb_convert_encoding(
111
                json_encode($array, JSON_UNESCAPED_UNICODE),
112
                'GB2312',
113
                'UTF-8')
114
            : self::getSignContent($array, true);
115
        $sign = base64_decode($sign);
116
117
        return openssl_verify($data, $sign, $key, OPENSSL_ALGO_SHA256) === 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