chengkun
2025-06-05 4080b5997b38ca84b3b203c7101dcadb97b76925
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
package com.alipay.easysdk.kms.aliyun;
 
import com.alipay.easysdk.kms.aliyun.credentials.AccessKeyCredentials;
import com.alipay.easysdk.kms.aliyun.credentials.BasicSessionCredentials;
import com.alipay.easysdk.kms.aliyun.credentials.ICredentials;
import com.alipay.easysdk.kms.aliyun.credentials.provider.CredentialsProviderFactory;
import com.alipay.easysdk.kms.aliyun.credentials.provider.EcsRamRoleCredentialsProvider;
import com.alipay.easysdk.kms.aliyun.credentials.provider.ICredentialsProvider;
import com.alipay.easysdk.kms.aliyun.credentials.provider.RamRoleArnCredentialsProvider;
import com.alipay.easysdk.kms.aliyun.credentials.provider.StaticCredentialsProvider;
import com.alipay.easysdk.kms.aliyun.credentials.utils.CredentialType;
import com.alipay.easysdk.kms.aliyun.models.RuntimeOptions;
import com.aliyun.tea.Tea;
import com.aliyun.tea.TeaConverter;
import com.aliyun.tea.TeaException;
import com.aliyun.tea.TeaModel;
import com.aliyun.tea.TeaPair;
import com.aliyun.tea.TeaRequest;
import com.aliyun.tea.TeaResponse;
import com.aliyun.tea.TeaRetryableException;
import com.aliyun.tea.TeaUnretryableException;
import com.aliyun.tea.ValidateException;
import com.aliyun.tea.utils.StringUtils;
import com.google.gson.Gson;
import org.bouncycastle.util.encoders.Base64;
 
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.UUID;
 
public class AliyunRpcClient {
    private final String               accessKeyId;
    private final String               accessKeySecret;
    private final String               securityToken;
    private final String               roleArn;
    private final String               roleName;
    private final String               credentialType;
    private final String               policy;
    private final String               endpoint;
    private final String               format;
    private final String               signatureMethod;
    private final String               signatureVersion;
    private final ICredentialsProvider credentialsProvider;
 
    public AliyunRpcClient(Map<String, Object> config) {
        this.accessKeyId = (String) config.get("aliyunAccessKeyId");
        this.accessKeySecret = (String) config.get("aliyunAccessKeySecret");
        this.securityToken = (String) config.get("aliyunSecurityToken");
        this.roleArn = (String) config.get("aliyunRoleArn");
        this.roleName = (String) config.get("aliyunRoleName");
        this.credentialType = (String) config.get("credentialType");
        this.policy = (String) config.get("aliyunRolePolicy");
        this.endpoint = (String) config.get("kmsEndpoint");
        this.format = "json";
        this.signatureMethod = "HMAC-SHA1";
        this.signatureVersion = "1.0";
        this.credentialsProvider = getCredentialsProvider();
    }
 
    public static String percentEncode(String value) throws UnsupportedEncodingException {
        return value != null ? URLEncoder.encode(value, "UTF-8").replace("+", "%20")
                .replace("*", "%2A").replace("%7E", "~") : null;
    }
 
    private static String getSignature(Map<String, String> signedParams, String method, String secret) throws Exception {
        String[] sortedKeys = signedParams.keySet().toArray(new String[0]);
        Arrays.sort(sortedKeys);
        StringBuilder canonicalizedQueryString = new StringBuilder();
 
        for (String key : sortedKeys) {
            if (!StringUtils.isEmpty(signedParams.get(key))) {
                canonicalizedQueryString.append("&").append(percentEncode(key)).append("=").append(
                        percentEncode((String) signedParams.get(key)));
            }
        }
 
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(new SecretKeySpec((secret + "&").getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
        String stringToSign = method +
                "&" +
                percentEncode("/") +
                "&" +
                percentEncode(canonicalizedQueryString.toString().substring(1));
        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
        return Base64.toBase64String(signData);
    }
 
    public static Object parseJSON(String json) {
        return (new Gson()).fromJson(json, Map.class);
    }
 
    public static Map<String, Object> assertAsMap(Object object) {
        if (null != object && Map.class.isAssignableFrom(object.getClass())) {
            return (Map) object;
        } else {
            throw new RuntimeException("The value is not a object");
        }
    }
 
    public static byte[] readAsBytes(InputStream stream) throws IOException {
        if (null == stream) {
            return new byte[0];
        } else {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];
 
            while (true) {
                int read = stream.read(buff);
                if (read == -1) {
                    return os.toByteArray();
                }
 
                os.write(buff, 0, read);
            }
        }
    }
 
    public static String readAsString(InputStream stream) throws IOException {
        return new String(readAsBytes(stream), StandardCharsets.UTF_8);
    }
 
    public static Object readAsJSON(InputStream stream) throws IOException {
        String body = readAsString(stream);
        return parseJSON(body);
    }
 
    public static String getTimestamp() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        df.setTimeZone(new SimpleTimeZone(0, "UTC"));
        return df.format(new Date());
    }
 
    public static String getNonce() {
        StringBuilder uniqueNonce = new StringBuilder();
        UUID uuid = UUID.randomUUID();
        uniqueNonce.append(uuid.toString());
        uniqueNonce.append(System.currentTimeMillis());
        uniqueNonce.append(Thread.currentThread().getId());
        return uniqueNonce.toString();
    }
 
    public static String toFormString(Map<String, ?> map) throws UnsupportedEncodingException {
        if (null == map) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, ?> entry : map.entrySet()) {
            if (StringUtils.isEmpty(entry.getValue())) {
                continue;
            }
            if (first) {
                first = false;
            } else {
                result.append("&");
            }
            result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
            result.append("=");
            result.append(URLEncoder.encode(String.valueOf(entry.getValue()), "UTF-8"));
        }
        return result.toString();
    }
 
    public static void validateModel(TeaModel m) throws Exception {
        if (null == m) {
            throw new ValidateException("parameter is not allowed as null");
        } else {
            m.validate();
        }
    }
 
    public static Map<String, Object> anyifyMapValue(Map<String, ?> map) {
        Map<String, Object> result = new HashMap<>();
        if (null == map) {
            return null;
        }
        for (Map.Entry<String, ?> entry : map.entrySet()) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }
 
    public static boolean is4xx(Number code) {
        if (null == code) {
            return false;
        } else {
            return code.intValue() >= 400 && code.intValue() < 500;
        }
    }
 
    public static boolean is5xx(Number code) {
        if (null == code) {
            return false;
        } else {
            return code.intValue() >= 500 && code.intValue() < 600;
        }
    }
 
    public static boolean isUnset(Object object) {
        return null == object;
    }
 
    private ICredentialsProvider getCredentialsProvider() {
        CredentialsProviderFactory factory = new CredentialsProviderFactory();
        if (StringUtils.isEmpty(this.credentialType)) {
            return getAccessKeyCredentialsProvider(this.accessKeyId, this.accessKeySecret, factory);
        }
        switch (this.credentialType) {
            case CredentialType.ACCESS_KEY:
                return getAccessKeyCredentialsProvider(this.accessKeyId, this.accessKeySecret, factory);
            case CredentialType.STS:
                return getStsTokenCredentialsProvider(this.accessKeyId, this.accessKeySecret, this.securityToken, factory);
            case CredentialType.ECS_RAM_ROLE:
                return getEcsRamRoleCredentialsProvider(this.roleName, factory);
            case CredentialType.RAM_ROLE_ARN:
                return getRamRoleArnCredentialsProvider(this.accessKeyId, this.accessKeySecret, this.roleArn, this.policy, factory);
        }
        throw new IllegalArgumentException("The credentialType is not supported");
    }
 
    private ICredentialsProvider getAccessKeyCredentialsProvider(String accessKeyId, String accessKeySecret,
                                                                 CredentialsProviderFactory factory) {
        return factory.createCredentialsProvider(new StaticCredentialsProvider(new AccessKeyCredentials(accessKeyId, accessKeySecret)));
    }
 
    private ICredentialsProvider getStsTokenCredentialsProvider(String accessKeyId, String accessKeySecret, String securityToken,
                                                                CredentialsProviderFactory factory) {
        return factory.createCredentialsProvider(new StaticCredentialsProvider(new BasicSessionCredentials(accessKeyId,
                accessKeySecret, securityToken)));
    }
 
    private ICredentialsProvider getEcsRamRoleCredentialsProvider(String roleName, CredentialsProviderFactory factory) {
        if (StringUtils.isEmpty(roleName)) {
            throw new IllegalArgumentException("The roleName is empty");
        }
        return factory.createCredentialsProvider(new EcsRamRoleCredentialsProvider(roleName));
    }
 
    private ICredentialsProvider getRamRoleArnCredentialsProvider(String accessKeyId, String accessKeySecret, String roleArn,
                                                                  String policy, CredentialsProviderFactory factory) {
        if (StringUtils.isEmpty(accessKeyId) || StringUtils.isEmpty(accessKeySecret)) {
            throw new IllegalArgumentException("The accessKeyId or accessKeySecret is empty");
        }
        if (StringUtils.isEmpty(roleArn)) {
            throw new IllegalArgumentException("The roleArn is empty");
        }
        return factory.createCredentialsProvider(new RamRoleArnCredentialsProvider(accessKeyId, accessKeySecret, roleArn, policy));
    }
 
    public Map<String, Object> doRequest(String action, String protocol, String method, String version, Map<String, Object> query,
                                         Map<String, Object> body, RuntimeOptions runtime) throws Exception {
        Map<String, Object> runtime_ = TeaConverter.buildMap(
                new TeaPair("readTimeout", runtime.readTimeout),
                new TeaPair("connectTimeout", runtime.connectTimeout),
                new TeaPair("retry", TeaConverter.buildMap(
                        new TeaPair("maxAttempts", runtime.maxAttempts)
                )),
                new TeaPair("backoff", TeaConverter.buildMap(
                        new TeaPair("policy", runtime.backoffPolicy),
                        new TeaPair("period", runtime.backoffPeriod)
                )),
                new TeaPair("ignoreSSL", runtime.ignoreSSL)
        );
 
        TeaRequest _lastRequest = null;
        long _now = System.currentTimeMillis();
        int _retryTimes = 0;
        while (Tea.allowRetry((java.util.Map<String, Object>) runtime_.get("retry"), _retryTimes, _now)) {
            if (_retryTimes > 0) {
                int backoffTime = Tea.getBackoffTime(runtime_.get("backoff"), _retryTimes);
                if (backoffTime > 0) {
                    Tea.sleep(backoffTime);
                }
            }
            _retryTimes = _retryTimes + 1;
            try {
                TeaRequest request_ = new TeaRequest();
                request_.protocol = protocol;
                request_.method = method;
                request_.pathname = "/";
                request_.query = TeaConverter.merge(String.class,
                        TeaConverter.buildMap(
                                new TeaPair("Action", action),
                                new TeaPair("Format", this.format),
                                new TeaPair("Timestamp", getTimestamp()),
                                new TeaPair("Version", version),
                                new TeaPair("SignatureNonce", getNonce())
                        ),
                        query
                );
                request_.headers = TeaConverter.buildMap(
                        new TeaPair("host", this.endpoint)
                );
                if (!isUnset(body)) {
                    java.util.Map<String, Object> tmp = anyifyMapValue(body);
                    request_.body = Tea.toReadable(toFormString(tmp));
                    request_.headers.put("content-type", "application/x-www-form-urlencoded");
                }
 
                ICredentials credentials = this.credentialsProvider.getCredentials();
                if (credentials == null) {
                    throw new TeaRetryableException();
                }
 
                request_.query.put("SignatureMethod", this.signatureMethod);
                request_.query.put("SignatureVersion", this.signatureVersion);
                request_.query.put("AccessKeyId", credentials.getAccessKeyId());
                if (!StringUtils.isEmpty(credentials.getSecurityToken())) {
                    request_.query.put("SecurityToken", credentials.getSecurityToken());
                }
                java.util.Map<String, String> signedParam = TeaConverter.merge(String.class,
                        request_.query,
                        body
                );
                request_.query.put("Signature", getSignature(signedParam, request_.method, credentials.getAccessKeySecret()));
 
                _lastRequest = request_;
                TeaResponse response_ = Tea.doAction(request_, runtime_);
 
                Object obj = readAsJSON(response_.body);
                java.util.Map<String, Object> res = assertAsMap(obj);
                if (is4xx(response_.statusCode) || is5xx(response_.statusCode)) {
                    throw new TeaException(TeaConverter.buildMap(
                            new TeaPair("message", res.get("Message")),
                            new TeaPair("data", res),
                            new TeaPair("code", res.get("Code"))
                    ));
                }
 
                return res;
            } catch (Exception e) {
                if (Tea.isRetryable(e)) {
                    continue;
                }
                throw e;
            }
        }
 
        throw new TeaUnretryableException(_lastRequest);
    }
}