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);
|
}
|
}
|