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 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 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 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 map) throws UnsupportedEncodingException { if (null == map) { return ""; } StringBuilder result = new StringBuilder(); boolean first = true; for (Map.Entry 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 anyifyMapValue(Map map) { Map result = new HashMap<>(); if (null == map) { return null; } for (Map.Entry 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 doRequest(String action, String protocol, String method, String version, Map query, Map body, RuntimeOptions runtime) throws Exception { Map 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) 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 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 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 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); } }