diff --git a/pom.xml b/pom.xml index 7b318f95..e6bd1454 100644 --- a/pom.xml +++ b/pom.xml @@ -212,6 +212,12 @@ ${org.springframework} + + org.springframework + spring-context + ${org.springframework} + + org.bouncycastle bcprov-jdk15on diff --git a/yop-java-sdk-apache/src/main/java/com/yeepay/yop/sdk/http/impl/apache/YopHttpClient.java b/yop-java-sdk-apache/src/main/java/com/yeepay/yop/sdk/http/impl/apache/YopHttpClient.java index be4f0432..ea764bba 100644 --- a/yop-java-sdk-apache/src/main/java/com/yeepay/yop/sdk/http/impl/apache/YopHttpClient.java +++ b/yop-java-sdk-apache/src/main/java/com/yeepay/yop/sdk/http/impl/apache/YopHttpClient.java @@ -1,7 +1,9 @@ package com.yeepay.yop.sdk.http.impl.apache; import com.yeepay.yop.sdk.client.ClientConfiguration; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.*; import com.yeepay.yop.sdk.internal.MultiPartFile; import com.yeepay.yop.sdk.internal.Request; @@ -53,6 +55,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.yeepay.yop.sdk.YopConstants.*; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; /** * title: Yop http客户端
@@ -170,7 +173,7 @@ private HttpClientConnectionManager createHttpClientConnectionManager() { SSLContext sslContext = getSSLContext(); sslSocketFactory = new SSLConnectionSocketFactory(sslContext, HOSTNAME_VERIFIER_INSTANCE); } catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new YopClientException("EnvProblem, Fail to Create SSLConnectionSocketFactory, ex:", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "EnvProblem, Fail to Create SSLConnectionSocketFactory, ex:", e); } RegistryBuilder registryBuilder = RegistryBuilder.create().register(Protocol.HTTPS.toString(), sslSocketFactory) .register(Protocol.HTTP.toString(), PlainConnectionSocketFactory.getSocketFactory()); @@ -302,7 +305,7 @@ private HttpRequestBase createHttpRequest(Request request) { } else if (request.getHttpMethod() == HttpMethodName.HEAD) { httpRequest = new HttpHead(uri); } else { - throw new YopClientException("ReqParam Illegal, HttpMethod, name:" + request.getHttpMethod()); + throw new IllegalParamFormatException("httpMethod", "ReqParam Illegal, HttpMethod, name:" + request.getHttpMethod()); } } diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/constants/ExceptionConstants.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/constants/ExceptionConstants.java new file mode 100644 index 00000000..a622bd2d --- /dev/null +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/constants/ExceptionConstants.java @@ -0,0 +1,64 @@ +/* + * Copyright: Copyright (c)2011 + * Company: 易宝支付(YeePay) + */ + +package com.yeepay.yop.sdk.constants; + +/** + * title: YOP-SDK异常码定义
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2025/2/24 + */ +public interface ExceptionConstants { + + /** + * 错误描述:SDK项目运行时依赖配置异常 + * 解决方案:检查pom.xml文件与类加载环境,确保按官方文档引入相关依赖 + */ + String SDK_CONFIG_RUNTIME_DEPENDENCY = "sdk.config.runtime.dependency"; + + /** + * 错误描述:SDK配置项格式错误 + * 解决方案:检查yop_sdk_config_xxx.json文件与YopFileSdkConfig配置类的属性,确保类型与格式正确 + */ + String SDK_CONFIG_PARAM_FORMAT = "sdk.config.param.format"; + + /** + * 错误描述:SDK请求参数格式错误 + * 解决方案:检查接口文档的参数描述与xxxRequest设置的实际参数,确保类型与格式正确 + */ + String SDK_INVOKE_REQUEST_PARAM_FORMAT_PREFIX = "sdk.invoke.check.request-param.format"; + + /** + * 错误描述:SDK请求域名解析异常 + * 解决方案: + * 1.检查客户端代理与服务端请求域名,确认网络配置是否正常,是否有网络波动 + * 2.可通过查单接口或等待结果回调通知,获取最终业务处理状态,避免重复交易 + */ + String SDK_INVOKE_IO_EXCEPTION_PREFIX = "sdk.invoke.io"; + + /** + * 错误描述:SDK请求域名熔断 + * 解决方案: + * 1.检查报错详细堆栈,确定熔断原因 + * 2.默认情况下,YOP-SDK会自动切换域名重试,重试次数为3次 + * 3.可通过查单接口或等待结果回调通知,获取最终业务处理状态,避免重复交易 + */ + String SDK_INVOKE_HOST_BLOCKED_EXCEPTION = "sdk.invoke.host.blocked"; + + /** + * 错误描述:SDK请求未知异常 + * 解决方案: + * 1.检查报错详细堆栈,确定异常原因 + * 2.可通过查单接口或等待结果回调通知,获取最终业务处理状态,避免重复交易 + */ + String SDK_INVOKE_UNEXPECTED_EXCEPTION = "sdk.invoke.unexpected"; + + +} diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopClientBizException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopClientBizException.java index 4981de85..749800dc 100644 --- a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopClientBizException.java +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopClientBizException.java @@ -1,5 +1,9 @@ package com.yeepay.yop.sdk.exception; +import com.yeepay.yop.sdk.utils.YopTraceUtils; + +import static com.yeepay.yop.sdk.constants.CharacterConstants.COMMA; + /** * title:
* description:
@@ -10,23 +14,39 @@ * @version 1.0.0 * @since 2018/8/17 16:45 */ -public class YopClientBizException extends YopClientException { +public class YopClientBizException extends YopClientException implements YopTracedException { private static final long serialVersionUID = -1L; private final String errorCode; + private final String requestId; + + public YopClientBizException(String errorCode, String message) { super(message); + this.requestId = YopTraceUtils.getCurrentRequestId(); this.errorCode = errorCode; } public YopClientBizException(String errorCode, String message, Throwable cause) { super(message, cause); + this.requestId = YopTraceUtils.getCurrentRequestId(); this.errorCode = errorCode; } + @Override public String getErrorCode() { return errorCode; } + + @Override + public String getMessage() { + return getErrorCode() + COMMA + getRequestId() + COMMA + super.getMessage(); + } + + @Override + public String getRequestId() { + return requestId; + } } diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopHostBlockException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopHostBlockException.java index 1e8b557e..9d4965df 100644 --- a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopHostBlockException.java +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopHostBlockException.java @@ -4,6 +4,10 @@ */ package com.yeepay.yop.sdk.exception; +import static com.yeepay.yop.sdk.constants.CharacterConstants.COMMA; +import static com.yeepay.yop.sdk.constants.CharacterConstants.EMPTY; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_INVOKE_HOST_BLOCKED_EXCEPTION; + /** * title: Yop域名熔断异常
* description: 该异常会触发当笔切换dns重试
@@ -14,7 +18,7 @@ * @version 1.0.0 * @since 2023/3/27 */ -public class YopHostBlockException extends YopBlockException { +public class YopHostBlockException extends YopBlockException implements YopTracedException { private static final long serialVersionUID = -1L; public YopHostBlockException(String message) { @@ -24,4 +28,20 @@ public YopHostBlockException(String message) { public YopHostBlockException(String message, Throwable cause) { super(message, cause); } + + @Override + public String getErrorCode() { + return SDK_INVOKE_HOST_BLOCKED_EXCEPTION; + } + + @Override + public String getRequestId() { + // 熔断异常,请求标识无意义 + return EMPTY; + } + + @Override + public String getMessage() { + return getErrorCode() + COMMA + super.getMessage(); + } } diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopServiceException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopServiceException.java index 824b397d..0e06e523 100644 --- a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopServiceException.java +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopServiceException.java @@ -10,7 +10,7 @@ * about the error and why it occurred. In particular, the errorType field can be used to determine if the caller's * request was invalid, or the service encountered an error on the server side while processing it. */ -public class YopServiceException extends YopClientException { +public class YopServiceException extends YopClientException implements YopTracedException { private static final long serialVersionUID = 1483785729559154396L; /** diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopTracedException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopTracedException.java new file mode 100644 index 00000000..a1c5dc6a --- /dev/null +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopTracedException.java @@ -0,0 +1,35 @@ +/* + * Copyright: Copyright (c)2011 + * Company: 易宝支付(YeePay) + */ + +package com.yeepay.yop.sdk.exception; + +/** + * title:
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2025/2/24 + */ +public interface YopTracedException { + + /** + * 获取错误码 + * + * @return String + */ + String getErrorCode(); + + /** + * 获取请求标识 + * + * @return String + */ + String getRequestId(); + + +} diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopUnknownException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopUnknownException.java index 6194e0fc..bdabc0fb 100644 --- a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopUnknownException.java +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/YopUnknownException.java @@ -4,6 +4,9 @@ */ package com.yeepay.yop.sdk.exception; +import static com.yeepay.yop.sdk.constants.CharacterConstants.COMMA; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_INVOKE_UNEXPECTED_EXCEPTION; + /** * title: Yop未知异常
* description: 该异常不会重试
@@ -14,9 +17,11 @@ * @version 1.0.0 * @since 2023/3/27 */ -public class YopUnknownException extends RuntimeException { +public class YopUnknownException extends RuntimeException implements YopTracedException { private static final long serialVersionUID = -1L; + private String requestId; + public YopUnknownException(String message) { super(message); } @@ -24,4 +29,24 @@ public YopUnknownException(String message) { public YopUnknownException(String message, Throwable cause) { super(message, cause); } + + public YopUnknownException(String message, Throwable cause, String requestId) { + super(message, cause); + this.requestId = requestId; + } + + @Override + public String getErrorCode() { + return SDK_INVOKE_UNEXPECTED_EXCEPTION; + } + + @Override + public String getRequestId() { + return this.requestId; + } + + @Override + public String getMessage() { + return getErrorCode() + COMMA + getRequestId() + COMMA + super.getMessage(); + } } diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/io/YopIOException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/io/YopIOException.java new file mode 100644 index 00000000..8a1a1863 --- /dev/null +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/io/YopIOException.java @@ -0,0 +1,191 @@ +/* + * Copyright: Copyright (c)2011 + * Company: 易宝支付(YeePay) + */ + +package com.yeepay.yop.sdk.exception.io; + +import com.yeepay.yop.sdk.exception.YopHttpException; +import com.yeepay.yop.sdk.exception.YopTracedException; +import com.yeepay.yop.sdk.utils.YopTraceUtils; + +import java.net.NoRouteToHostException; +import java.net.UnknownHostException; + +import static com.yeepay.yop.sdk.constants.CharacterConstants.*; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_INVOKE_IO_EXCEPTION_PREFIX; + +/** + * title:
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2025/2/24 + */ +public class YopIOException extends YopHttpException implements YopTracedException { + + private static final String CONNECTION_REFUSED = "Connection refused"; + private static final String CONNECTION_TIMEOUT = "Connection timed out"; + private static final String CONNECTION_TIMEOUT2 = "connect timed out"; + private static final String CONNECTION_TIMEOUT3 = "Timeout waiting for connection from pool"; + private static final String READ_TIMEOUT = "Read timed out"; + private static final String CONNECTION_RESET = "Connection reset"; + private static final String NO_ROUTE_TO_HOST = "No route to host"; + + private final IOExceptionEnum ioException; + private final String errorCode; + private final String errorMsg; + private final String requestId; + private final boolean isRetryable; + + public YopIOException(String message) { + super(message); + this.requestId = YopTraceUtils.getCurrentRequestId(); + this.ioException = parse(message, null); + this.errorCode = SDK_INVOKE_IO_EXCEPTION_PREFIX + DOT + ioException.getCode(); + this.errorMsg = EMPTY; + this.isRetryable = ioException.isRetryable(); + } + + public YopIOException(String message, IOExceptionEnum ioException) { + super(message); + this.requestId = YopTraceUtils.getCurrentRequestId(); + this.ioException = ioException; + this.errorCode = SDK_INVOKE_IO_EXCEPTION_PREFIX + DOT + ioException.getCode(); + this.errorMsg = EMPTY; + this.isRetryable = ioException.isRetryable(); + } + + public YopIOException(String message, Throwable cause) { + super(message, cause); + this.requestId = YopTraceUtils.getCurrentRequestId(); + this.ioException = parse(message, cause); + this.errorCode = SDK_INVOKE_IO_EXCEPTION_PREFIX + DOT + ioException.getCode(); + this.errorMsg = EMPTY; + this.isRetryable = ioException.isRetryable(); + } + + public YopIOException(String message, Throwable cause, IOExceptionEnum ioException) { + super(message, cause); + this.requestId = YopTraceUtils.getCurrentRequestId(); + this.ioException = ioException; + this.errorCode = SDK_INVOKE_IO_EXCEPTION_PREFIX + DOT + ioException.getCode(); + this.errorMsg = EMPTY; + this.isRetryable = ioException.isRetryable(); + } + + public YopIOException(String message, Throwable cause, String errorMsg) { + super(message, cause); + this.requestId = YopTraceUtils.getCurrentRequestId(); + this.ioException = parse(message, cause); + this.errorCode = SDK_INVOKE_IO_EXCEPTION_PREFIX + DOT + ioException.getCode(); + this.errorMsg = errorMsg; + this.isRetryable = ioException.isRetryable(); + } + + @Override + public String getErrorCode() { + return this.errorCode; + } + + @Override + public String getRequestId() { + return this.requestId; + } + + @Override + public String getMessage() { + String exMsg = this.errorMsg; + if (isEmpty(exMsg)) { + exMsg = super.getMessage(); + } else if (!isEmpty(super.getMessage())) { + exMsg = exMsg + COMMA + super.getMessage(); + } + return getErrorCode() + COMMA + getRequestId() + COMMA + exMsg; + } + + public IOExceptionEnum getIoException() { + return this.ioException; + } + + public boolean isRetryable() { + return this.isRetryable; + } + + private IOExceptionEnum parse(String message, Throwable cause) { + IOExceptionEnum ioException = IOExceptionEnum.UNKNOWN; + if (isEmpty(message) && null == cause) { + return ioException; + } + if (cause instanceof UnknownHostException) { + return IOExceptionEnum.UNKNOWN_HOST; + } + + if (cause instanceof NoRouteToHostException) { + return IOExceptionEnum.NO_ROUTE_TO_HOST; + } + + if (isEmpty(message) && null != cause.getMessage()) { + message = cause.getMessage(); + } + + if (isEmpty(message)) { + return ioException; + } + + if (message.contains(CONNECTION_REFUSED)) { + ioException = IOExceptionEnum.CONNECTION_REFUSED; + } else if (message.contains(CONNECTION_TIMEOUT) + || message.contains(CONNECTION_TIMEOUT2) + || message.contains(CONNECTION_TIMEOUT3)) { + ioException = IOExceptionEnum.CONNECTION_TIMEOUT; + } else if (message.contains(READ_TIMEOUT)) { + ioException = IOExceptionEnum.READ_TIMEOUT; + } else if (message.contains(CONNECTION_RESET)) { + ioException = IOExceptionEnum.CONNECTION_RESET; + } else if (message.contains(NO_ROUTE_TO_HOST)) { + ioException = IOExceptionEnum.NO_ROUTE_TO_HOST; + } + return ioException; + } + + private static boolean isEmpty(String str) { + return str == null || str.trim().isEmpty(); + } + + public enum IOExceptionEnum { + + UNKNOWN_HOST("unknown-host", true), + CONNECTION_TIMEOUT("connection-timeout", true), + READ_TIMEOUT("read-timeout"), + CONNECTION_RESET("connection-reset"), + CONNECTION_REFUSED("connection-refused", true), + NO_ROUTE_TO_HOST("no-route-to-host", true), + UNKNOWN("unknown"); + + private final String code; + private final boolean isRetryable; + + private IOExceptionEnum(String code) { + this.code = code; + this.isRetryable = false; + } + + IOExceptionEnum(String code, boolean isRetryable) { + this.code = code; + this.isRetryable = isRetryable; + } + + public String getCode() { + return code; + } + + public boolean isRetryable() { + return isRetryable; + } + } + +} diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/param/IllegalParamFormatException.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/param/IllegalParamFormatException.java new file mode 100644 index 00000000..a987580b --- /dev/null +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/exception/param/IllegalParamFormatException.java @@ -0,0 +1,32 @@ +/* + * Copyright: Copyright (c)2011 + * Company: 易宝支付(YeePay) + */ + +package com.yeepay.yop.sdk.exception.param; + +import com.yeepay.yop.sdk.exception.YopClientBizException; + +import static com.yeepay.yop.sdk.constants.CharacterConstants.COLON; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_INVOKE_REQUEST_PARAM_FORMAT_PREFIX; + +/** + * title: 请求参数非法
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2025/2/24 + */ +public class IllegalParamFormatException extends YopClientBizException { + + public IllegalParamFormatException(String field, String message) { + super(SDK_INVOKE_REQUEST_PARAM_FORMAT_PREFIX + COLON + field, message); + } + + public IllegalParamFormatException(String field, String message, Throwable cause) { + super(SDK_INVOKE_REQUEST_PARAM_FORMAT_PREFIX + COLON + field, message, cause); + } +} diff --git a/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/utils/YopTraceUtils.java b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/utils/YopTraceUtils.java new file mode 100644 index 00000000..94694bac --- /dev/null +++ b/yop-java-sdk-api/src/main/java/com/yeepay/yop/sdk/utils/YopTraceUtils.java @@ -0,0 +1,34 @@ +/* + * Copyright: Copyright (c)2011 + * Company: 易宝支付(YeePay) + */ + +package com.yeepay.yop.sdk.utils; + +/** + * title:
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2025/2/24 + */ +public class YopTraceUtils { + + // 当前线程上下文中的requestId + private static ThreadLocal CURRENT_REQUEST_ID = new ThreadLocal<>(); + + public static String getCurrentRequestId() { + return CURRENT_REQUEST_ID.get(); + } + + public static void setCurrentRequestId(String requestId) { + CURRENT_REQUEST_ID.set(requestId); + } + + public static void removeCurrentRequestId() { + CURRENT_REQUEST_ID.remove(); + } +} diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/Region.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/Region.java index a6a4a040..55fce37c 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/Region.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/Region.java @@ -1,5 +1,7 @@ package com.yeepay.yop.sdk; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; + import java.util.Arrays; import java.util.List; @@ -55,6 +57,6 @@ public static Region fromValue(String regionId) { return region; } } - throw new IllegalArgumentException("Cannot create region from " + regionId); + throw new IllegalConfigFormatException("region", "Cannot create region from " + regionId); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/YopConstants.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/YopConstants.java index 1ec60045..e74abcb7 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/YopConstants.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/YopConstants.java @@ -86,6 +86,7 @@ public interface YopConstants { */ String YOP_ENCRYPT_V1 = "yop-encrypt-v1"; String SM4_CBC_PKCS5PADDING = "SM4/CBC/PKCS5Padding"; + String SM4_ECB_PKCS5PADDING = "SM4/ECB/PKCS5Padding"; String SM2 = "SM2"; String AES = "AES"; String AES_ECB_PKCS5PADDING = "AES/ECB/PKCS5Padding"; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBaseCredentialsProvider.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBaseCredentialsProvider.java index 1c803c5f..977b0a17 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBaseCredentialsProvider.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBaseCredentialsProvider.java @@ -10,7 +10,7 @@ import com.yeepay.yop.sdk.auth.credentials.YopPKICredentials; import com.yeepay.yop.sdk.auth.credentials.provider.YopCredentialsProvider; import com.yeepay.yop.sdk.base.config.YopAppConfig; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -36,14 +36,17 @@ public abstract class YopBaseCredentialsProvider implements YopCredentialsProvid protected YopCredentials buildCredentials(YopAppConfig appConfig, String credentialType) { CertTypeEnum certType; - if (null == appConfig || StringUtils.isEmpty(credentialType) || + if (null == appConfig) { + throw new IllegalConfigFormatException("YopAppConfig", "YopAppConfig is required"); + } + if (StringUtils.isEmpty(credentialType) || (null == (certType = CertTypeEnum.parse(credentialType)))) { - throw new YopClientException("ConfigProblem, credentialType:" + credentialType + ", appConfig:" + appConfig); + throw new IllegalConfigFormatException("credentialType", "illegal value: " + credentialType); } PrivateKey privateKey = appConfig.loadPrivateKey(certType); if (null == privateKey) { - throw new YopClientException("ConfigProblem, IsvPrivateCert NotFound, appKey:" + appConfig.getAppKey() + ", certType:" + certType); + throw new IllegalConfigFormatException("isv_private_key", "appKey:" + appConfig.getAppKey() + ", certType:" + certType + "not found"); } PKICredentialsItem pkiCredentialsItem = new PKICredentialsItem(privateKey, certType); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBasePlatformCredentialsProvider.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBasePlatformCredentialsProvider.java index 32669dd2..8919833b 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBasePlatformCredentialsProvider.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/credentials/provider/YopBasePlatformCredentialsProvider.java @@ -10,7 +10,8 @@ import com.yeepay.yop.sdk.auth.credentials.YopPlatformCredentialsHolder; import com.yeepay.yop.sdk.auth.credentials.provider.YopPlatformCredentialsProvider; import com.yeepay.yop.sdk.base.cache.YopCertificateCache; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.utils.X509CertUtils; import org.apache.commons.collections4.CollectionUtils; @@ -66,14 +67,14 @@ private String cacheKey(String provider, String env, String appKey, String seria @Override public YopPlatformCredentials getCredentials(String provider, String env, String appKey, String serialNo, String serverRoot) { if (StringUtils.isBlank(serialNo)) { - throw new YopClientException("ReqParam Illegal, PlatformCert SerialNo NotSpecified"); + throw new IllegalParamFormatException("serialNo", "ReqParam Illegal, PlatformCert SerialNo NotSpecified"); } String key = cacheKey(provider, env, appKey, serialNo); YopPlatformCredentials foundCredentials = credentialsMap.computeIfAbsent(key, p -> { if (serialNo.equals(YOP_RSA_PLATFORM_CERT_DEFAULT_SERIAL_NO)) { PublicKey rsaPublicKey = loadLocalRsaKey(provider, env, appKey, serialNo); if (null == rsaPublicKey) { - throw new YopClientException("ConfigProblem, LocalRsaCert NotFound, " + + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, LocalRsaCert NotFound, " + "provider:" + provider + ",env:" + env + ",serialNo:" + serialNo); } return convertCredentials(appKey, CertTypeEnum.RSA2048.getValue(), rsaPublicKey, YOP_RSA_PLATFORM_CERT_DEFAULT_SERIAL_NO); @@ -82,7 +83,7 @@ public YopPlatformCredentials getCredentials(String provider, String env, String if (null == localCredentials) { X509Certificate remoteCert = loadRemoteSm2Cert(provider, env, appKey, serialNo, serverRoot); if (null == remoteCert) { - throw new YopClientException("ConfigProblem, LocalRsaCert NotFound, " + + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, LocalRsaCert NotFound, " + "provider:" + provider + ",env:" + env + ",serialNo:" + serialNo); } return storeCredentials(provider, env, appKey, CertTypeEnum.SM2.name(), remoteCert); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopOauth2Signer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopOauth2Signer.java index 5a83a3d8..76d6dc6a 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopOauth2Signer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopOauth2Signer.java @@ -9,7 +9,7 @@ import com.yeepay.yop.sdk.auth.credentials.YopCredentials; import com.yeepay.yop.sdk.auth.credentials.YopCredentialsWithoutSign; import com.yeepay.yop.sdk.auth.credentials.YopOauth2Credentials; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.internal.Request; import com.yeepay.yop.sdk.model.BaseRequest; @@ -19,8 +19,6 @@ import java.util.List; -import static com.google.common.base.Preconditions.checkNotNull; - /** * title: YopOauth2Signer
* description: 描述
@@ -45,13 +43,13 @@ public List supportSignerAlg() { @Override public void sign(Request request, YopCredentials credentials, SignOptions options) { if (null == request) { - throw new YopClientException("ReqParam Illegal, Request IsNull"); + throw new IllegalParamFormatException("request", "request is required"); } if (credentials == null || credentials instanceof YopCredentialsWithoutSign) { return; } if (!(credentials instanceof YopOauth2Credentials)) { - throw new YopClientException("ReqParam Illegal, YopCredentials IsNotYopOauth2Credentials, type:" + credentials.getClass().getSimpleName()); + throw new IllegalParamFormatException("YopCredentials", "ReqParam Illegal, YopCredentials IsNotYopOauth2Credentials, type:" + credentials.getClass().getSimpleName()); } String secretKey = (String) credentials.getCredential(); String authorizationHeader = AUTHORIZATION_PREFIX + secretKey; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopSignerFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopSignerFactory.java index 3e90119a..e118fda8 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopSignerFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/YopSignerFactory.java @@ -5,12 +5,14 @@ package com.yeepay.yop.sdk.base.auth.signer; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.security.SignerTypeEnum; import java.util.Map; import java.util.ServiceLoader; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: YopSignerFactory
* description:
@@ -46,7 +48,7 @@ public static void registerSigner(SignerTypeEnum signerType, YopSigner signer) { public static YopSigner getSigner(String signerType) { final YopSigner yopSigner = YOP_SIGNER_MAP.get(signerType); if (null == yopSigner) { - throw new YopClientException("ConfigProblem, YopSigner NotFound, signerType:" + signerType); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, YopSigner NotFound, signerType:" + signerType); } return yopSigner; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/process/YopSignProcessorFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/process/YopSignProcessorFactory.java index 9b61c889..5da07573 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/process/YopSignProcessorFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/auth/signer/process/YopSignProcessorFactory.java @@ -6,11 +6,13 @@ import com.google.common.collect.Maps; import com.yeepay.yop.sdk.auth.signer.process.YopSignProcessor; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import java.util.Map; import java.util.ServiceLoader; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: YopSignProcessorFactory
* description: 描述
@@ -35,7 +37,7 @@ public class YopSignProcessorFactory { public static YopSignProcessor getSignProcessor(String certType) { final YopSignProcessor yopSignProcessor = YOP_SIGN_PROCESSOR_MAP.get(certType); if (null == yopSignProcessor) { - throw new YopClientException("ConfigProblem, YopSignProcessor NotFound, certType:" + certType); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, YopSignProcessor NotFound, certType:" + certType); } return yopSignProcessor; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/EncryptOptionsCache.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/EncryptOptionsCache.java index 0dd39a49..00699fa8 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/EncryptOptionsCache.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/EncryptOptionsCache.java @@ -10,7 +10,9 @@ import com.yeepay.yop.sdk.base.security.encrypt.RsaEnhancer; import com.yeepay.yop.sdk.base.security.encrypt.Sm2Enhancer; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorFactory; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.security.encrypt.EncryptOptionsEnhancer; import com.yeepay.yop.sdk.security.encrypt.YopEncryptor; @@ -27,6 +29,7 @@ import static com.yeepay.yop.sdk.YopConstants.*; import static com.yeepay.yop.sdk.constants.CharacterConstants.COMMA; import static com.yeepay.yop.sdk.constants.CharacterConstants.EMPTY; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; /** * title: 加密选项缓存
@@ -48,7 +51,7 @@ public class EncryptOptionsCache { /** * 初始化加密选项,并缓存 * - * @param appKey 应用 + * @param appKey 应用 * @param encryptAlg 加解密算法 * @return Future */ @@ -72,14 +75,14 @@ public static Future loadEncryptOptions(String provider, String try { return ENCRYPT_OPTIONS_CACHE.get(getCacheKey(provider, env, appKey, encryptAlg, serverRoot)); } catch (ExecutionException e) { - throw new YopClientException("ConfigProblem, LoadEncryptOptions Fail, appKey:" + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, LoadEncryptOptions Fail, appKey:" + appKey + ", encryptAlg:" + encryptAlg + ", ex:", e); } } /** * 刷新加密选项,并缓存(异步操作,不立即生效) - * + *

* Loads a new value for key, possibly asynchronously. * While the new value is loading the previous value (if any) will continue to be returned by get(key) unless it is evicted. * If the new value is loaded successfully it will replace the previous value in the cache; @@ -94,7 +97,7 @@ public static void refreshEncryptOptions(String appKey, String encryptAlg) { /** * 刷新加密选项,并缓存(异步操作,不立即生效) - * + *

* Loads a new value for key, possibly asynchronously. * While the new value is loading the previous value (if any) will continue to be returned by get(key) unless it is evicted. * If the new value is loaded successfully it will replace the previous value in the cache; @@ -112,7 +115,7 @@ public static void refreshEncryptOptions(String provider, String env, String app try { ENCRYPT_OPTIONS_CACHE.refresh(getCacheKey(provider, env, appKey, encryptAlg, serverRoot)); } catch (Exception e) { - throw new YopClientException("ConfigProblem, RefreshEncryptOptions Fail, appKey:" + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, RefreshEncryptOptions Fail, appKey:" + appKey + ", encryptAlg:" + encryptAlg + ", ex:", e); } } @@ -142,7 +145,7 @@ public static void invalidateEncryptOptions(String provider, String env, String try { ENCRYPT_OPTIONS_CACHE.invalidate(getCacheKey(provider, env, appKey, encryptAlg, serverRoot)); } catch (Exception e) { - throw new YopClientException("ConfigProblem, InvalidateEncryptOptions Fail, appKey:" + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, InvalidateEncryptOptions Fail, appKey:" + appKey + ", encryptAlg:" + encryptAlg + ", ex:", e); } } @@ -150,7 +153,7 @@ public static void invalidateEncryptOptions(String provider, String env, String /** * 失效并同步加载新的加密选项,并缓存 * - * @param appKey 应用 + * @param appKey 应用 * @param encryptAlg 加解密算法 * @return Future */ @@ -174,7 +177,7 @@ public static Future reloadEncryptOptions(String provider, Strin try { ENCRYPT_OPTIONS_CACHE.invalidate(getCacheKey(provider, env, appKey, encryptAlg, serverRoot)); } catch (Exception e) { - throw new YopClientException("ConfigProblem, InvalidateEncryptOptions Fail, appKey:" + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, InvalidateEncryptOptions Fail, appKey:" + appKey + ", encryptAlg:" + encryptAlg + ", ex:", e); } return loadEncryptOptions(provider, env, appKey, encryptAlg, serverRoot); @@ -201,9 +204,7 @@ public Future load(String cacheKey) throws Exception { Future encryptOptions = null; try { String[] split = StringUtils.splitPreserveAllTokens(cacheKey, COMMA); - String provider = split[0], env = split[1] - ,appKey = split[2], encryptAlg = split[3] - , serverRoot = split.length > 4 ? split[4]: null; + String provider = split[0], env = split[1], appKey = split[2], encryptAlg = split[3], serverRoot = split.length > 4 ? split[4] : null; YopEncryptor encryptor = YopEncryptorFactory.getEncryptor(encryptAlg); List enhancers = Collections.singletonList(getEnhancer(provider, env, appKey, encryptAlg, serverRoot)); encryptOptions = encryptor.initOptions(encryptAlg, enhancers); @@ -226,7 +227,7 @@ private static EncryptOptionsEnhancer getEnhancer(String provider, String env, case AES_ECB_PKCS5PADDING: return new RsaEnhancer(provider, env, appKey); default: - throw new YopClientException("not supported encryptAlg:" + encryptAlg); + throw new IllegalParamFormatException("encryptAlg", "not supported encryptAlg:" + encryptAlg); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/YopCertificateCache.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/YopCertificateCache.java index fc36ddc7..851d618e 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/YopCertificateCache.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/cache/YopCertificateCache.java @@ -16,6 +16,7 @@ import com.yeepay.yop.sdk.config.enums.CertStoreType; import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.model.cert.YopPlatformCertQueryResult; import com.yeepay.yop.sdk.model.cert.YopPlatformPlainCert; import com.yeepay.yop.sdk.security.CertTypeEnum; @@ -101,7 +102,7 @@ private static X509Certificate loadRootCertFromLocal(String provider, String env } catch (Exception e) { LOGGER.error("error when load sm2 root cert, ex:", e); } - throw new YopClientException("Config Error, platform sm2 root cert not found, provider:" + throw new IllegalConfigFormatException(DEFAULT_CFCA_ROOT_FILE, "Config Error, platform sm2 root cert not found, provider:" + provider + ", env:" + env + ", appKey:" + appKey); } @@ -138,7 +139,7 @@ private static X509Certificate loadInterCertFromLocal(String provider, String en } catch (Exception e) { LOGGER.error("error when load sm2 inter certs, ex:", e); } - throw new YopClientException("Config Error, platform sm2 inter cert not found, provider:" + throw new IllegalConfigFormatException(DEFAULT_YOP_INTER_FILE, "Config Error, platform sm2 inter cert not found, provider:" + provider + ", env:" + env + ", appKey:" + appKey); } @@ -151,7 +152,7 @@ private static X509Certificate loadPlatformCertFromLocal(String provider, String return cert; } } - throw new YopClientException("Config Error, platform cert not found, provider:" + throw new IllegalConfigFormatException("yop_public_key|yop_cert_store", "Config Error, platform cert not found, provider:" + provider + ", env:" + env + ", appKey:" + appKey + ", certType:" + certType + ", certFile:" + certFile); } @@ -216,7 +217,7 @@ private static PublicKey loadPlatformRsaKeyFromLocal(String provider, String env } catch (Exception e) { LOGGER.warn("error when load yop rsa certs, ex:", e); } - throw new YopClientException("Config Error, platform rsa certs not found, provider:" + throw new IllegalConfigFormatException("yop_public_key", "Config Error, platform rsa certs not found, provider:" + provider + ", env:" + env + ", appKey:" + appKey + ", serialNo:" + serialNo); } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/YopSdkConfigProviderRegistry.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/YopSdkConfigProviderRegistry.java index d31ab141..64202267 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/YopSdkConfigProviderRegistry.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/YopSdkConfigProviderRegistry.java @@ -2,7 +2,9 @@ import com.yeepay.yop.sdk.base.config.provider.file.YopFileSdkConfigProvider; import com.yeepay.yop.sdk.config.provider.YopSdkConfigProvider; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; + +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; /** * title: SDK 配置中心
@@ -27,7 +29,7 @@ public class YopSdkConfigProviderRegistry { */ public static void registerProvider(YopSdkConfigProvider provider) { if (provider == null) { - throw new YopClientException("SetUpProblem, Custom YopSdkConfigProvider IsNull."); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "SetUpProblem, Custom YopSdkConfigProvider IsNull."); } customProvider = provider; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/file/YopFileSdkConfigProvider.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/file/YopFileSdkConfigProvider.java index efd8168a..770bb5f1 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/file/YopFileSdkConfigProvider.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/config/provider/file/YopFileSdkConfigProvider.java @@ -5,7 +5,7 @@ import com.yeepay.yop.sdk.base.config.provider.YopFixedSdkConfigProvider; import com.yeepay.yop.sdk.config.YopSdkConfig; import com.yeepay.yop.sdk.config.provider.file.YopFileSdkConfig; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.utils.BeanUtils; import com.yeepay.yop.sdk.utils.JsonUtils; import com.yeepay.yop.sdk.utils.StreamUtils; @@ -145,7 +145,7 @@ private YopFileSdkConfig doLoadYopFileSdkConfig(String provider, String currentE } if (null == customSdkConfig) { - throw new YopClientException("ConfigProblem, Can't load config, file:" + configFile); + throw new IllegalConfigFormatException("yop_sdk_config_xxx.json", "ConfigProblem, Can't load config, file:" + configFile); } return customSdkConfig; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/X509CertSupportFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/X509CertSupportFactory.java index b89e9447..e78bd8c2 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/X509CertSupportFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/X509CertSupportFactory.java @@ -5,11 +5,13 @@ package com.yeepay.yop.sdk.base.security.cert; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import java.util.Map; import java.util.ServiceLoader; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: x509证书支持类工厂
* description: 描述
@@ -46,7 +48,7 @@ public class X509CertSupportFactory { public static X509CertSupport getSupport(String certType) { final X509CertSupport x509CertSupport = X509_CERT_GENERATOR_MAP.get(certType); if (null == x509CertSupport) { - throw new YopClientException("ConfigProblem, X509CertSupport NotFound, certType:" + certType); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, X509CertSupport NotFound, certType:" + certType); } return x509CertSupport; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/AbstractYopPublicKeyParser.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/AbstractYopPublicKeyParser.java index d2d7096b..06f3f843 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/AbstractYopPublicKeyParser.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/AbstractYopPublicKeyParser.java @@ -6,6 +6,7 @@ import com.yeepay.yop.sdk.base.security.cert.X509CertSupportFactory; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.utils.FileUtils; import com.yeepay.yop.sdk.utils.StreamUtils; @@ -40,7 +41,7 @@ protected X509Certificate getX509Cert(String certPath, CertTypeEnum certType) th try { certStream = FileUtils.getResourceAsStream(certPath); if (null == certStream) { - throw new YopClientException("ConfigProblem, X509Cert NotFound, path:" + certPath + ", type:" + certType); + throw new IllegalConfigFormatException("yop_cert_store", "ConfigProblem, X509Cert NotFound, path:" + certPath + ", type:" + certType); } return X509CertSupportFactory.getSupport(certType.getValue()).generate(certStream); } finally { diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/YopCertParserFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/YopCertParserFactory.java index 4884865c..29a077cd 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/YopCertParserFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/cert/parser/YopCertParserFactory.java @@ -5,15 +5,17 @@ package com.yeepay.yop.sdk.base.security.cert.parser; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.security.cert.YopCertCategory; -import com.yeepay.yop.sdk.exception.YopClientException; -import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.constants.CharacterConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.security.CertTypeEnum; +import com.yeepay.yop.sdk.security.cert.YopCertCategory; import org.apache.commons.lang3.StringUtils; import java.util.Map; import java.util.ServiceLoader; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: 密钥解析器工厂类
* description: 描述
@@ -60,7 +62,7 @@ public static void registerParser(String parserId, YopCertParser certParser) { public static YopCertParser getCertParser(String parserId) { final YopCertParser yopCertParser = YOP_CERT_PARSER_MAP.get(parserId); if (null == yopCertParser) { - throw new YopClientException("ConfigProblem, YopCertParser NotFound, parserId:" + parserId); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, YopCertParser NotFound, parserId:" + parserId); } return yopCertParser; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/digest/YopDigesterFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/digest/YopDigesterFactory.java index a2849df3..4a04a408 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/digest/YopDigesterFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/digest/YopDigesterFactory.java @@ -5,12 +5,14 @@ package com.yeepay.yop.sdk.base.security.digest; import com.google.common.collect.Maps; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.security.digest.YopDigester; -import com.yeepay.yop.sdk.exception.YopClientException; import java.util.Map; import java.util.ServiceLoader; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: 摘要器工厂类
* description: 描述
@@ -58,7 +60,7 @@ public static void registerDigester(String alg, YopDigester digester) { public static YopDigester getDigester(String alg) { final YopDigester yopDigester = YOP_DIGESTER_MAP.get(alg); if (null == yopDigester) { - throw new YopClientException("ConfigProblem, YopDigester NotFound, alg:" + alg); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, YopDigester NotFound, alg:" + alg); } return yopDigester; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/AbstractEncryptOptionsEnhancer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/AbstractEncryptOptionsEnhancer.java index 9280517f..a37de25f 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/AbstractEncryptOptionsEnhancer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/AbstractEncryptOptionsEnhancer.java @@ -5,7 +5,7 @@ package com.yeepay.yop.sdk.base.security.encrypt; import com.yeepay.yop.sdk.auth.credentials.YopSymmetricCredentials; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.security.encrypt.EncryptOptionsEnhancer; @@ -23,7 +23,7 @@ public abstract class AbstractEncryptOptionsEnhancer implements EncryptOptionsEn protected boolean checkForEnhance(EncryptOptions source) { if (null == source.getCredentials()) { - throw new YopClientException("ParamProblem, EncryptOptionsCredentials IsNull, value:" + source); + throw new IllegalConfigFormatException("yop_public_key", "ParamProblem, EncryptOptionsCredentials IsNull, value:" + source); } if (source.getCredentials() instanceof YopSymmetricCredentials) { return true; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm2Enhancer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm2Enhancer.java index dbf5ff28..174900f0 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm2Enhancer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm2Enhancer.java @@ -9,6 +9,7 @@ import com.yeepay.yop.sdk.auth.credentials.YopSymmetricCredentials; import com.yeepay.yop.sdk.auth.credentials.provider.YopPlatformCredentialsProvider; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.utils.Encodes; @@ -84,7 +85,7 @@ public EncryptOptions enhance(EncryptOptions source) { .getLatestCredentials(provider, env, appKey, CertTypeEnum.SM2.getValue(), serverRoot); } if (null == platformCredentials) { - throw new YopClientException("ConfigProblem, YopPlatformCredentials NotFound to Enhance EncryptOptions, appKey:" + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, YopPlatformCredentials NotFound to Enhance EncryptOptions, appKey:" + appKey + ", serialNo:" + serialNo); } source.setEncryptedCredentials(YopEncryptorFactory.getEncryptor(YOP_CREDENTIALS_ENCRYPT_ALG_SM2).encryptToBase64(credentialBytes, diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm4Enhancer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm4Enhancer.java index 9f003b89..d6a5de1d 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm4Enhancer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/Sm4Enhancer.java @@ -7,7 +7,7 @@ import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.auth.credentials.YopSymmetricCredentials; import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.utils.ClientUtils; import com.yeepay.yop.sdk.utils.Encodes; @@ -53,7 +53,7 @@ public EncryptOptions enhance(EncryptOptions source) { List mainKeys = ClientUtils.getCurrentCredentialsProvider() .getIsvEncryptKey(provider, env, appKey); if (CollectionUtils.isEmpty(mainKeys)) { - throw new YopClientException("ConfigProblem, IsvEncryptKey NotFound, appKey:" + appKey); + throw new IllegalConfigFormatException("isv_encrypt_key", "ConfigProblem, IsvEncryptKey NotFound, appKey:" + appKey); } EncryptOptions mainKeyOptions = source.copy(); String mainCredential = mainKeys.get(0).getValue(); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptProtocol.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptProtocol.java index 27d65a1f..ea7986b0 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptProtocol.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptProtocol.java @@ -8,7 +8,8 @@ import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.auth.credentials.YopCredentials; import com.yeepay.yop.sdk.auth.credentials.YopSymmetricCredentials; -import com.yeepay.yop.sdk.exception.YopCallbackException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; +import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.security.encrypt.BigParamEncryptMode; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.utils.Encodes; @@ -87,7 +88,7 @@ public static Set parseEncryptItems(String itemStr, boolean base64) { } return Collections.emptySet(); } catch (UnsupportedEncodingException e) { - throw new YopCallbackException("error when parse encrypt protocol, ex:", e); + throw new IllegalParamFormatException(Headers.YOP_ENCRYPT, "error when parse encrypt protocol, ex:", e); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorAdaptor.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorAdaptor.java index d4c4d922..ef9f3c33 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorAdaptor.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorAdaptor.java @@ -7,7 +7,9 @@ import com.google.common.collect.Queues; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.yeepay.yop.sdk.YopConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.YopUnknownException; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.security.encrypt.EncryptOptionsEnhancer; import com.yeepay.yop.sdk.security.encrypt.YopEncryptor; @@ -25,6 +27,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: 加解密适配器
* description: 描述
@@ -69,8 +73,8 @@ public String encryptToBase64(String plain, EncryptOptions options) { try { return Encodes.encodeUrlSafeBase64(encrypt(plain.getBytes(YopConstants.DEFAULT_ENCODING), options)); } catch (UnsupportedEncodingException e) { - throw new YopClientException("UnexpectedError, EncryptFail, value:" - + plain + ",options:" + options + ",ex:", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "UnexpectedError, EncryptFail, value:" + + plain + ",options:" + options + ", cause:" + e.getMessage(), e); } } @@ -89,7 +93,7 @@ public String decryptToPlain(byte[] cipher, EncryptOptions options) { try { return new String(decrypt(cipher, options), YopConstants.DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { - throw new YopClientException("UnexpectedError, DecryptFail, options:" + options + ",ex:", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "UnexpectedError, DecryptFail, options:" + options + ", cause:" + e.getMessage(), e); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorFactory.java index 132783ec..b465dbce 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/base/security/encrypt/YopEncryptorFactory.java @@ -5,13 +5,15 @@ package com.yeepay.yop.sdk.base.security.encrypt; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.security.encrypt.YopEncryptor; import java.util.List; import java.util.Map; import java.util.ServiceLoader; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: 加解密器工厂类
* description: 描述
@@ -80,7 +82,7 @@ public static void registerEncryptor(Map encryptors) { public static YopEncryptor getEncryptor(String encryptAlg) { final YopEncryptor yopEncryptor = YOP_ENCRYPTOR_MAP.get(encryptAlg); if (null == yopEncryptor) { - throw new YopClientException("ConfigProblem, YopEncryptor NotFound, encryptAlg:" + encryptAlg); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "ConfigProblem, YopEncryptor NotFound, encryptAlg:" + encryptAlg); } return yopEncryptor; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/ClientHandlerImpl.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/ClientHandlerImpl.java index c8daafbb..520ed244 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/ClientHandlerImpl.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/ClientHandlerImpl.java @@ -23,6 +23,8 @@ import com.yeepay.yop.sdk.config.provider.YopSdkConfigProvider; import com.yeepay.yop.sdk.config.provider.file.YopCircuitBreakerConfig; import com.yeepay.yop.sdk.exception.*; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.ExecutionContext; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.YopHttpClient; @@ -40,6 +42,7 @@ import com.yeepay.yop.sdk.security.encrypt.YopEncryptor; import com.yeepay.yop.sdk.sentinel.YopSph; import com.yeepay.yop.sdk.utils.ClientUtils; +import com.yeepay.yop.sdk.utils.YopTraceUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; @@ -194,7 +197,8 @@ public Output execute(R invoker.addException(invoker.getExceptionAnalyzer().analyze(hostBlockException)); throw hostBlockException; } - throw new YopUnknownException("UnExpected Error, ", ex); + // ClientHandlerImpl.doExecute封装后,理论上不会到这里 + throw new YopUnknownException("UnExpectedError, cause:" + ex.getMessage(), ex, request.getRequestId()); } finally { if (null != uriResource && null != uriResource.getCallback()) { uriResource.getCallback().notify(successInvoked); @@ -230,9 +234,9 @@ private Output doExecut } catch (YopHttpException httpException) {//HTTP调用异常 throwable = httpException; throw httpException; - } catch (Throwable ex) {// 非预期异常 + } catch (Throwable ex) {// 非预期异常,com.yeepay.yop.sdk.http.YopHttpClient.execute包装后,理论上不会到这里 throwable = ex; - throw new YopUnknownException("UnExpected Error, ", ex); + throw new YopUnknownException("UnExpectedError, cause:" + ex.getMessage(), ex, request.getRequestId()); } finally { String result = "success"; if (null != throwable) { @@ -267,17 +271,22 @@ public YopInvoker(ClientExecutionParams executionParams, @Override public Output invoke() { - // 准备http参数 - Request request = getInput().getRequestMarshaller().marshall(getInput().getInput()); - request.setEndpoint(getUriResource().getResource()); - request.addHeader(Headers.YOP_SDK_SOURCE, sdkSource); - - // 发起http调用 - if (isCircuitBreakerEnable() && null != circuitBreaker && !BooleanUtils.isFalse(request.getOriginalRequestObject() - .getRequestConfig().getEnableCircuitBreaker())) { - return circuitBreaker.execute(request, this); - } else { - return doExecute(request, this); + try { + // 准备http参数 + Request request = getInput().getRequestMarshaller().marshall(getInput().getInput()); + YopTraceUtils.setCurrentRequestId(request.getRequestId()); + request.setEndpoint(getUriResource().getResource()); + request.addHeader(Headers.YOP_SDK_SOURCE, sdkSource); + + // 发起http调用 + if (isCircuitBreakerEnable() && null != circuitBreaker && !BooleanUtils.isFalse(request.getOriginalRequestObject() + .getRequestConfig().getEnableCircuitBreaker())) { + return circuitBreaker.execute(request, this); + } else { + return doExecute(request, this); + } + } finally { + YopTraceUtils.removeCurrentRequestId(); } } } @@ -286,7 +295,7 @@ private ExecutionContex ClientExecutionParams executionParams) { AuthorizationReq authorizationReq = getAuthorizationReq(executionParams.getInput()); if (authorizationReq == null) { - throw new YopClientException("no authenticate req defined"); + throw new IllegalParamFormatException("AuthorizationReq", "no authenticate req defined"); } else { YopRequestConfig requestConfig = executionParams.getInput().getRequestConfig(); YopCredentials credential = getCredentials(provider, env, requestConfig, authorizationReq); @@ -345,7 +354,7 @@ private AuthorizationReq computeSecurityReq(List availableCerts, L return authorizationReq; } } - throw new YopClientException("can not computeSecurityReq, please check your cert config"); + throw new IllegalParamFormatException("AuthorizationReq", "can not computeSecurityReq, please check your cert config"); } private List checkAuthReqsByApi(String operationId) { @@ -360,7 +369,7 @@ private List checkAuthReqsByApi(String operationId) { private AuthorizationReq checkCustomSecurityReq(String customSecurityReq) { AuthorizationReq authorizationReq = AuthorizationReqSupport.getAuthorizationReq(customSecurityReq); if (authorizationReq == null) { - throw new YopClientException("unsupported customSecurityReq:" + customSecurityReq); + throw new IllegalParamFormatException("CustomAuthorizationReq", "unsupported customSecurityReq:" + customSecurityReq); } return authorizationReq; } @@ -368,7 +377,7 @@ private AuthorizationReq checkCustomSecurityReq(String customSecurityReq) { private List checkAvailableCerts(String appKey, String provider, String env) { List configPrivateCerts = yopCredentialsProvider.getSupportCertTypes(provider, env, appKey); if (CollectionUtils.isEmpty(configPrivateCerts)) { - throw new YopClientException("can not find private key for provider:" + throw new IllegalConfigFormatException("isv_private_key", "can not find private key for provider:" + provider + ",env:" + env + ",appKey:" + appKey); } return configPrivateCerts; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/router/SimpleGateWayRouter.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/router/SimpleGateWayRouter.java index 81e0857f..6afff963 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/router/SimpleGateWayRouter.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/client/router/SimpleGateWayRouter.java @@ -9,7 +9,11 @@ import com.yeepay.yop.sdk.client.metric.report.host.YopHostStatusChangePayload; import com.yeepay.yop.sdk.client.metric.report.host.YopHostStatusChangeReport; import com.yeepay.yop.sdk.constants.CharacterConstants; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.internal.Request; import com.yeepay.yop.sdk.invoke.model.UriResource; import com.yeepay.yop.sdk.model.YopRequestConfig; @@ -121,7 +125,7 @@ private boolean recordMainServer(URI serverRoot, ServerRootType serverRootType, private boolean recordMainServer(URI serverRoot, ServerRootType serverRootType, Map mainServers, boolean force) { if (null == serverRoot) { - throw new YopClientException("Config Error, No ServerRoot Found, type:" + serverRootType); + throw new IllegalConfigFormatException("server_root", "Config Error, No ServerRoot Found, type:" + serverRootType); } final URI oldMain = mainServers.putIfAbsent(serverRootType, serverRoot); if (LOGGER.isDebugEnabled()) { @@ -210,7 +214,7 @@ public UriResource route(String appKey, Request request, List excludeSer if (StringUtils.isNotBlank(requestConfig.getServerRoot())) { URI serverRoot = CheckUtils.checkServerRoot(requestConfig.getServerRoot()); if (isExcludeServerRoots(serverRoot, excludeServerRoots)) { - throw new YopClientException("RequestConfig Error, serverRoot excluded:" + serverRoot); + throw new IllegalParamFormatException("serverRoot", "RequestConfig Error, serverRoot excluded:" + serverRoot); } collectServerRootType(space.getProvider(), space.getEnv(), serverRoot, serverRootType); return new UriResource(serverRoot); @@ -220,7 +224,7 @@ public UriResource route(String appKey, Request request, List excludeSer if (independentApiGroups.contains(apiGroup)) { final URI independentServerRoot = independentServerRoot(apiGroup, request); if (isExcludeServerRoots(independentServerRoot, excludeServerRoots)) { - throw new YopClientException("Config Error, ServerRoot excluded:" + independentServerRoot); + throw new IllegalConfigFormatException("serverRoot", "Config Error, ServerRoot excluded:" + independentServerRoot); } return new UriResource(independentServerRoot); } @@ -228,7 +232,7 @@ public UriResource route(String appKey, Request request, List excludeSer // 主域名准备 URI mainServer = this.serverRootRouting.getMainServers().get(serverRootType); if (null == mainServer) { - throw new YopClientException("Config Error, Main ServerRoot NotFound" + serverRootType); + throw new IllegalConfigFormatException("server_root", "Config Error, Main ServerRoot NotFound" + serverRootType); } // 主域名正常 @@ -263,7 +267,7 @@ private URI independentServerRoot(String apiGroup, Request request) { getIndependentApiGroupHost(apiGroup, serverRoot.getHost(), request.isYosRequest()), serverRoot.getPort(), serverRoot.getPath(), serverRoot.getQuery(), serverRoot.getFragment()); } catch (Exception ex) { - throw new YopClientException("Route Request Failure, ex:", ex); + throw new IllegalParamFormatException("serverRoot", "Route Request Failure, ex:", ex); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpClient.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpClient.java index 144e1e85..53ded2a2 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpClient.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpClient.java @@ -16,6 +16,8 @@ import com.yeepay.yop.sdk.exception.YopClientException; import com.yeepay.yop.sdk.exception.YopHttpException; import com.yeepay.yop.sdk.exception.YopServiceException; +import com.yeepay.yop.sdk.exception.io.YopIOException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.internal.Request; import com.yeepay.yop.sdk.model.BaseRequest; import com.yeepay.yop.sdk.model.BaseResponse; @@ -90,9 +92,9 @@ public Output execute(R throw e; } catch (Exception e) { ex = e; - throw new YopHttpException("Unable to execute HTTP request, requestId:" + throw new YopIOException("", e, "Unable to execute HTTP request, requestId:" + request.getRequestId() + ", apiUri:" + request.getResourcePath() - + ", serverHost:" + request.getEndpoint(), e); + + ", serverHost:" + request.getEndpoint()); } finally { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Analyzed Response: {}, elapsed:{}ms", analyzedResponse, @@ -267,7 +269,7 @@ private void signRequest(Request request, Exe protected boolean checkForMultiPart(Request request) throws YopClientException { boolean result = request.getMultiPartFiles() != null && request.getMultiPartFiles().size() > 0; if (result && !HttpMethodName.POST.equals(request.getHttpMethod())) { - throw new YopClientException("ReqParam Illegal, ContentType:multipart/form-data only support Post Request"); + throw new IllegalParamFormatException("httpMethod", "ReqParam Illegal, ContentType:multipart/form-data only support Post Request"); } return result; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpResponse.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpResponse.java index 1134f884..408b0b3a 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpResponse.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/AbstractYopHttpResponse.java @@ -6,7 +6,7 @@ import com.google.common.collect.Maps; import com.yeepay.yop.sdk.YopConstants; -import com.yeepay.yop.sdk.exception.YopHttpException; +import com.yeepay.yop.sdk.exception.io.YopIOException; import com.yeepay.yop.sdk.utils.DateUtils; import com.yeepay.yop.sdk.utils.StreamUtils; import org.apache.commons.io.IOUtils; @@ -67,7 +67,8 @@ public String readContent() { contentStr = IOUtils.toString(this.content, YopConstants.DEFAULT_ENCODING); return contentStr; } catch (IOException ex) { - throw new YopHttpException("ResponseError, Read Content Fail, headers:" + this.headers + ", ex:", ex); + throw new YopIOException("ResponseError, Read Content Fail, headers:" + this.headers, ex, + YopIOException.IOExceptionEnum.UNKNOWN); } finally { StreamUtils.closeQuietly(this.content); } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/YopHttpClientFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/YopHttpClientFactory.java index c19393ab..7eeac367 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/YopHttpClientFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/YopHttpClientFactory.java @@ -5,7 +5,7 @@ import com.yeepay.yop.sdk.client.ClientConfiguration; import com.yeepay.yop.sdk.client.support.ClientConfigurationSupport; import com.yeepay.yop.sdk.config.provider.YopSdkConfigProvider; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import org.apache.commons.lang3.StringUtils; import java.util.Map; @@ -14,6 +14,7 @@ import static com.yeepay.yop.sdk.YopConstants.YOP_DEFAULT_ENV; import static com.yeepay.yop.sdk.YopConstants.YOP_DEFAULT_PROVIDER; import static com.yeepay.yop.sdk.constants.CharacterConstants.COLON; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; /** * title: YopHttpClient工厂
@@ -37,7 +38,7 @@ public class YopHttpClientFactory { httpClientProviderMap.put(yopHttpClientProvider.name(), yopHttpClientProvider); } if (httpClientProviderMap.isEmpty()) { - throw new YopClientException("SetUpProblem,YopHttpClientProvider NotFound!"); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "SetUpProblem,YopHttpClientProvider NotFound!"); } } @@ -61,7 +62,7 @@ public static YopHttpClient getDefaultClient(String provider, String env, YopSdk public static YopHttpClient getClient(ClientConfiguration clientConfig) { final YopHttpClientProvider yopHttpClientProvider = httpClientProviderMap.get(clientConfig.getClientImpl()); if (null == yopHttpClientProvider) { - throw new YopClientException("SetUpProblem, YopHttpClientProvider NotFound, name:" + clientConfig.getClientImpl()); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "SetUpProblem, YopHttpClientProvider NotFound, name:" + clientConfig.getClientImpl()); } return yopHttpClientProvider.get(clientConfig); } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopErrorResponseAnalyzer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopErrorResponseAnalyzer.java index 0cc2b1b8..62baab32 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopErrorResponseAnalyzer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopErrorResponseAnalyzer.java @@ -1,7 +1,7 @@ package com.yeepay.yop.sdk.http.analyzer; -import com.yeepay.yop.sdk.exception.YopHttpException; import com.yeepay.yop.sdk.exception.YopServiceException; +import com.yeepay.yop.sdk.exception.io.YopIOException; import com.yeepay.yop.sdk.http.HttpResponseAnalyzer; import com.yeepay.yop.sdk.http.HttpResponseHandleContext; import com.yeepay.yop.sdk.http.HttpStatus; @@ -9,11 +9,10 @@ import com.yeepay.yop.sdk.model.BaseResponse; import com.yeepay.yop.sdk.model.YopErrorResponse; import com.yeepay.yop.sdk.utils.JsonUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.yeepay.yop.sdk.constants.CharacterConstants.SLASH; - /** * title:YopErrorResponseAnalyzer
* description: HTTP error response handler for YOP responses.
@@ -49,37 +48,53 @@ public boolean analysis(HttpResponseHandleContext conte String resource = context.getOriginRequest().getEndpoint() + context.getOriginRequest().getResourcePath(); // 5xx if (statusCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR && statusCode != HttpStatus.SC_BAD_GATEWAY) { - YopServiceException yse = null; - String content = httpResponse.readContent(); - if (null != content) { - YopErrorResponse yopErrorResponse = null; - try { - yopErrorResponse = JsonUtils.loadFrom(content, YopErrorResponse.class); - } catch (Exception ex) { - LOGGER.warn("Response Illegal, YopErrorResponse ParseFail, content:" + content, ex); - } - if (yopErrorResponse != null && yopErrorResponse.getMessage() != null) { - yse = new YopServiceException(yopErrorResponse.getMessage()); - yse.setErrorCode(yopErrorResponse.getCode()); - yse.setSubErrorCode(yopErrorResponse.getSubCode()); - yse.setSubMessage(yopErrorResponse.getSubMessage()); - yse.setRequestId(yopErrorResponse.getRequestId()); - yse.setDocUrl(yopErrorResponse.getDocUrl()); - } - } - if (yse == null) { - yse = new YopServiceException(httpResponse.getStatusText()); + YopServiceException yse = buildServiceException(httpResponse.readContent(), httpResponse, resource); + if (StringUtils.isBlank(yse.getRequestId())) { yse.setRequestId(response.getMetadata().getYopRequestId()); } - yse.setStatusCode(httpResponse.getStatusCode()); yse.setErrorType(YopServiceException.ErrorType.Service); throw yse; } else if (statusCode == HttpStatus.SC_BAD_GATEWAY || statusCode == HttpStatus.SC_NOT_FOUND) { - throw new YopHttpException("ResponseError, Unexpected Response, statusCode:" + statusCode + ", resource:" + resource); + throw new YopIOException("ResponseError, Unexpected Response, statusCode:" + statusCode + + ", resource:" + resource, YopIOException.IOExceptionEnum.UNKNOWN); + } else if (statusCode == 429){// 429 + final YopServiceException invokeEx = buildServiceException(httpResponse.readContent(), httpResponse, resource); + invokeEx.setErrorType(YopServiceException.ErrorType.Client); + throw invokeEx; } else {// 4xx final YopServiceException invokeEx = new YopServiceException("ReqParam Illegal, Bad Request, statusCode:" + statusCode + ", resource:" + resource); + invokeEx.setStatusCode(statusCode); invokeEx.setErrorType(YopServiceException.ErrorType.Client); throw invokeEx; } } + + private YopServiceException buildServiceException(String content, YopHttpResponse httpResponse, String resource) { + YopServiceException yse = null; + if (null != content) { + YopErrorResponse yopErrorResponse = null; + try { + yopErrorResponse = JsonUtils.loadFrom(content, YopErrorResponse.class); + } catch (Exception ex) { + LOGGER.warn("Response Illegal, YopErrorResponse ParseFail, content:" + content, ex); + } + if (yopErrorResponse != null) { + if (StringUtils.isNotBlank(yopErrorResponse.getMessage())) { + yse = new YopServiceException(yopErrorResponse.getMessage()); + } else { + yse = new YopServiceException(httpResponse.getStatusText() + "ResponseError, resource:" + resource); + } + yse.setErrorCode(yopErrorResponse.getCode()); + yse.setSubErrorCode(yopErrorResponse.getSubCode()); + yse.setSubMessage(yopErrorResponse.getSubMessage()); + yse.setRequestId(yopErrorResponse.getRequestId()); + yse.setDocUrl(yopErrorResponse.getDocUrl()); + } + } + if (yse == null) { + yse = new YopServiceException(httpResponse.getStatusText() + "ResponseError, resource:" + resource); + } + yse.setStatusCode(httpResponse.getStatusCode()); + return yse; + } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopJsonResponseAnalyzer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopJsonResponseAnalyzer.java index d96fa858..efee9096 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopJsonResponseAnalyzer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopJsonResponseAnalyzer.java @@ -1,6 +1,6 @@ package com.yeepay.yop.sdk.http.analyzer; -import com.yeepay.yop.sdk.exception.YopHttpException; +import com.yeepay.yop.sdk.exception.io.YopIOException; import com.yeepay.yop.sdk.http.HttpResponseAnalyzer; import com.yeepay.yop.sdk.http.HttpResponseHandleContext; import com.yeepay.yop.sdk.model.BaseResponse; @@ -34,9 +34,10 @@ protected YopJsonResponseAnalyzer() { public boolean analysis(HttpResponseHandleContext context, T response) throws Exception { String content = context.getResponse().readContent(); if (!isJsonResponse(response.getMetadata().getContentType())) { - throw new YopHttpException("ResponseError, Need Json, contentType:" + response.getMetadata().getContentType() + throw new YopIOException("ResponseError, Need Json, contentType:" + response.getMetadata().getContentType() + ", content:" + content + ", resource:" - + context.getOriginRequest().getEndpoint() + SLASH + context.getOriginRequest().getResourcePath()); + + context.getOriginRequest().getEndpoint() + SLASH + context.getOriginRequest().getResourcePath(), + YopIOException.IOExceptionEnum.UNKNOWN); } if (null != content) { JsonUtils.load(content, response); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopSignatureCheckAnalyzer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopSignatureCheckAnalyzer.java index 64f5d693..e5e2fda3 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopSignatureCheckAnalyzer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/http/analyzer/YopSignatureCheckAnalyzer.java @@ -3,6 +3,7 @@ import com.yeepay.yop.sdk.auth.SignOptions; import com.yeepay.yop.sdk.auth.credentials.YopPlatformCredentials; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.http.HttpResponseAnalyzer; import com.yeepay.yop.sdk.http.HttpResponseHandleContext; import com.yeepay.yop.sdk.model.BaseResponse; @@ -60,7 +61,7 @@ public boolean analysis(HttpResponseHandleContext conte .withExpirationInSeconds(reqOptions.getExpirationInSeconds()) .withUrlSafe(!StringUtils.containsAny(metadata.getYopSign(), '+', '/', '='))); } else { - throw new YopClientException("ConfigProblem, YopPlatformCredentials NotFound, appKey:" + context.getAppKey() + ", serialNo:" + metadata.getYopCertSerialNo()); + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, YopPlatformCredentials NotFound, appKey:" + context.getAppKey() + ", serialNo:" + metadata.getYopCertSerialNo()); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("response sign verify success, requestId:{}, sign:{}", metadata.getYopRequestId(), metadata.getYopSign()); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/DefaultRequest.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/DefaultRequest.java index f161cf0e..96781d39 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/DefaultRequest.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/DefaultRequest.java @@ -3,7 +3,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.yeepay.yop.sdk.constants.CharacterConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.HttpMethodName; import com.yeepay.yop.sdk.http.YopContentType; @@ -267,7 +267,7 @@ public void addMultiPartFile(String name, File file) { try { files.add(new MultiPartFile(file)); } catch (Exception ex) { - throw new YopClientException("ReqParam Illegal, FileParam, name:" + name + ", value:" + file, ex); + throw new IllegalParamFormatException(name, "ReqParam Illegal, FileParam, value:" + file, ex); } } @@ -281,7 +281,7 @@ public void addMultiPartFile(String name, InputStream in) { try { files.add(new MultiPartFile(in)); } catch (Exception ex) { - throw new YopClientException("ReqParam Illegal, InputStreamParam, name:" + name + ", ex:", ex); + throw new IllegalParamFormatException(name, "ReqParam Illegal, InputStreamParam, ex:", ex); } } @@ -295,7 +295,7 @@ public void addMultiPartFile(String name, FileParam fileParam) { try { files.add(new MultiPartFile(fileParam.getFileExtName(), fileParam.getFileStream())); } catch (Exception ex) { - throw new YopClientException("ReqParam Illegal, InputStreamParam, name:" + name + ", ex:", ex); + throw new IllegalParamFormatException(name, "ReqParam Illegal, InputStreamParam, ex:", ex); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestAnalyzer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestAnalyzer.java index db4ff7c2..2605b334 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestAnalyzer.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestAnalyzer.java @@ -10,7 +10,8 @@ import com.yeepay.yop.sdk.auth.req.AuthorizationReq; import com.yeepay.yop.sdk.base.cache.YopCredentialsCache; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorFactory; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.model.YopRequestConfig; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.encrypt.YopEncryptor; @@ -50,7 +51,7 @@ public static YopCredentials getCredentials(String provider, String env, YopR , authorizationReq.getCredentialType()); } if (null == credential) { - throw new YopClientException("No credentials specified"); + throw new IllegalConfigFormatException("isv_private_key", "No credentials specified"); } // 缓存最新调用凭证 YopCredentialsCache.put(provider, env, credential.getAppKey(), credential); @@ -90,7 +91,7 @@ public static YopEncryptor getEncryptor(YopRequestConfig requestConfig) { } YopEncryptor encryptor = YopEncryptorFactory.getEncryptor(requestConfig.getEncryptAlg()); if (null == encryptor) { - throw new YopClientException("not supported the encryptAlg: " + requestConfig.getEncryptAlg()); + throw new IllegalParamFormatException("encryptAlg", "not supported the encryptAlg: " + requestConfig.getEncryptAlg()); } return encryptor; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestEncryptor.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestEncryptor.java index 8daede30..6f51dd89 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestEncryptor.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RequestEncryptor.java @@ -12,7 +12,10 @@ import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.base.cache.EncryptOptionsCache; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptProtocol; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.YopContentType; import com.yeepay.yop.sdk.model.BaseRequest; @@ -202,7 +205,7 @@ private static void encryptContent(YopEncryptor encryptor, Set finalEncr request.setContent(encryptor.encrypt(request.getContent(), encryptOptions)); finalEncryptParams.add(DOLLAR); } else { - throw new YopClientException("body content is not supported, contentType:" + request.getContentType()); + throw new IllegalParamFormatException("contentType", "body content is not supported, contentType:" + request.getContentType()); } } @@ -246,7 +249,7 @@ private static byte[] encryptJsonParams(YopEncryptor encryptor, Set fina LOGGER.debug("json request encrypted, source:{}, target:{}, options:{}", originJson, encryptedJson, encryptOptions); return encryptedJson.getBytes(YopConstants.DEFAULT_ENCODING); } catch (IOException e) { - throw new YopClientException("error happened when encrypt json", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "error happened when encrypt json", e); } } @@ -260,7 +263,7 @@ private static void encryptMultiPartParams(YopEncryptor encryptor, Set f encryptedValues.add(new MultiPartFile( encryptor.encrypt(value.getInputStream(), encryptOptions), value.getFileName())); } catch (IOException e) { - throw new YopClientException("error happened when encrypt MultiPartFile", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "error happened when encrypt MultiPartFile", e); } } multiPartFiles.put(name, encryptedValues); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableFileInputStream.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableFileInputStream.java index ee2c3eaf..96408e2d 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableFileInputStream.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableFileInputStream.java @@ -12,7 +12,7 @@ */ package com.yeepay.yop.sdk.internal; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import java.io.File; import java.io.FileInputStream; @@ -42,7 +42,7 @@ public void restart() { this.input.close(); this.input = new FileInputStream(this.file); } catch (IOException e) { - throw new YopClientException("Fail to restart.", e); + throw new IllegalParamFormatException(file.getName(), "Fail to restart.", e); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableNonResettableInputStream.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableNonResettableInputStream.java index c80839d9..a78c140a 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableNonResettableInputStream.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableNonResettableInputStream.java @@ -14,6 +14,7 @@ import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import java.io.IOException; import java.io.InputStream; @@ -46,7 +47,7 @@ public RestartableNonResettableInputStream(InputStream input, int bufferSize) { try { count = this.input.read(this.buffer, this.length, bufferSize - this.length); } catch (IOException e) { - throw new YopClientException("Fail to read data from input.", e); + throw new IllegalParamFormatException("InputStream", "Fail to read data from input.", e); } if (count < 0) { this.eof = true; @@ -59,7 +60,7 @@ public RestartableNonResettableInputStream(InputStream input, int bufferSize) { @Override public void restart() { if (this.buffer == null) { - throw new IllegalStateException("Fail to restart. Input buffer exhausted."); + throw new IllegalParamFormatException("InputStream", "Fail to restart. Input buffer exhausted."); } this.offset = 0; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableResettableInputStream.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableResettableInputStream.java index e9a42d12..caddbc52 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableResettableInputStream.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/internal/RestartableResettableInputStream.java @@ -13,7 +13,7 @@ package com.yeepay.yop.sdk.internal; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import java.io.IOException; import java.io.InputStream; @@ -38,7 +38,7 @@ public void restart() { try { this.input.reset(); } catch (IOException e) { - throw new YopClientException("Fail to reset the underlying input stream.", e); + throw new IllegalParamFormatException("InputStream", "Fail to reset the underlying input stream.", e); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/BaseRequest.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/BaseRequest.java index c96166ed..3f52bf27 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/BaseRequest.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/BaseRequest.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import org.apache.commons.lang3.StringUtils; @@ -71,13 +71,13 @@ public Map getHeaders() { protected void validateParameter(String name, Object value) { if (StringUtils.isEmpty(name)) { - throw new YopClientException("ReqParam Illegal, ParamName IsEmpty, name:" + name + ",value:" + value); + throw new IllegalParamFormatException("EmptyField", "ReqParam Illegal, ParamName IsEmpty, name:" + name + ",value:" + value); } if (value == null) { - throw new YopClientException("ReqParam Illegal, ParamValue IsNull, name:" + name); + throw new IllegalParamFormatException(name, "ReqParam Illegal, ParamValue IsNull" ); } if (value instanceof File && !((File) value).exists()) { - throw new YopClientException("ReqParam Illegal. FileParam NotExist, name:" + name + ", value:" + value); + throw new IllegalParamFormatException(name, "ReqParam Illegal. FileParam NotExist, value:" + value); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/transform/AbstractYopRequestMarshaller.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/transform/AbstractYopRequestMarshaller.java index a7e6cfc9..b0dcc607 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/transform/AbstractYopRequestMarshaller.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/model/transform/AbstractYopRequestMarshaller.java @@ -2,7 +2,7 @@ import com.google.common.collect.Lists; import com.yeepay.yop.sdk.YopConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.HttpMethodName; import com.yeepay.yop.sdk.http.YopContentType; @@ -59,7 +59,7 @@ public Request marshall(YopRequest request) { resetStreamIfNecessary(((FileParam) value).getFileStream()); internalRequest.addMultiPartFile(name, (FileParam) value); } else { - throw new YopClientException("ReqParam Illegal, FileParamType NotSupport, name:" + name + ", type:" + value.getClass() + "."); + throw new IllegalParamFormatException(name, "ReqParam Illegal, FileParamType NotSupport, type:" + value.getClass() + "."); } } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopCallbackEngine.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopCallbackEngine.java index faa104b9..7a443d03 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopCallbackEngine.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopCallbackEngine.java @@ -16,6 +16,7 @@ import com.yeepay.yop.sdk.base.auth.signer.process.YopSignProcessorFactory; import com.yeepay.yop.sdk.base.cache.EncryptOptionsCache; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.YopContentType; import com.yeepay.yop.sdk.internal.Request; @@ -71,7 +72,7 @@ public static YopCallbackRequest build(String provider, String env, YopRequest r Request marshalled = YopCallbackRequestMarshaller.getInstance().marshall(request); AuthorizationReq authorizationReq = AuthorizationReqSupport.getAuthorizationReq(request.getRequestConfig().getSecurityReq()); if (null == authorizationReq) { - throw new YopClientException("no authenticate req defined"); + throw new IllegalParamFormatException("YopRequestConfig.AuthorizationReq", "no authenticate req defined"); } else { YopRequestConfig requestConfig = request.getRequestConfig(); YopCredentials credential = getCredentials(provider, env, requestConfig, authorizationReq); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopClientImpl.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopClientImpl.java index 2a59d4cc..96dcc5e1 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopClientImpl.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/YopClientImpl.java @@ -1,7 +1,7 @@ package com.yeepay.yop.sdk.service.common; import com.yeepay.yop.sdk.client.*; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.HttpResponseAnalyzerSupport; import com.yeepay.yop.sdk.http.HttpResponseHandler; import com.yeepay.yop.sdk.http.handler.DefaultHttpResponseHandler; @@ -33,7 +33,7 @@ public class YopClientImpl implements YopClient { @Override public YopResponse request(YopRequest request) { if (request == null) { - throw new YopClientException("request is required."); + throw new IllegalParamFormatException("request", "request is required"); } RequestMarshaller requestMarshaller = YopRequestMarshaller.getInstance(); HttpResponseHandler responseHandler = new DefaultHttpResponseHandler(YopResponse.class, @@ -47,7 +47,7 @@ public YopResponse request(YopRequest request) { @Override public YosDownloadResponse download(YopRequest request) { if (request == null) { - throw new YopClientException("request is required."); + throw new IllegalParamFormatException("request", "request is required"); } RequestMarshaller requestMarshaller = YopRequestMarshaller.getInstance(); HttpResponseHandler responseHandler = new DefaultHttpResponseHandler(YosDownloadResponse.class, @@ -61,10 +61,10 @@ public YosDownloadResponse download(YopRequest request) { @Override public YosUploadResponse upload(YopRequest request) { if (request == null) { - throw new YopClientException("request is required."); + throw new IllegalParamFormatException("request", "request is required"); } if (request.getMultipartFiles().isEmpty() && request.getContent() == null) { - throw new YopClientException("request.multiPartFiles and request.content both are empty."); + throw new IllegalParamFormatException("multiPartFiles|content", "request multiPartFiles or content is required"); } RequestMarshaller requestMarshaller = YopRequestMarshaller.getInstance(); HttpResponseHandler responseHandler = new DefaultHttpResponseHandler(YosUploadResponse.class, diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/handler/impl/YopWildCardCallbackHandler.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/handler/impl/YopWildCardCallbackHandler.java index cb88aacd..0651150d 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/handler/impl/YopWildCardCallbackHandler.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/handler/impl/YopWildCardCallbackHandler.java @@ -1,6 +1,7 @@ package com.yeepay.yop.sdk.service.common.callback.handler.impl; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.service.common.callback.YopCallback; import com.yeepay.yop.sdk.service.common.callback.handler.YopCallbackHandler; import org.slf4j.Logger; @@ -30,7 +31,7 @@ public String getType() { @Override public void handle(YopCallback callback) { LOGGER.warn("you need to handle the new yop callback, received: {}.", callback); - throw new YopClientException("no YopCallbackHandler found"); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "no YopCallbackHandler found"); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopCallbackProtocolFactory.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopCallbackProtocolFactory.java index db747015..19a67300 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopCallbackProtocolFactory.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopCallbackProtocolFactory.java @@ -7,6 +7,7 @@ import com.google.common.collect.Maps; import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.service.common.callback.YopCallbackRequest; @@ -62,7 +63,7 @@ private static YopCallbackProtocol deprecatedProtocol(YopCallbackRequest request params = toParams(request.getParams()); break; default: - throw new YopClientException("unsupported content Type for YopCallback, type:" + request.getContentType()); + throw new IllegalParamFormatException("contentType", "unsupported content Type for YopCallback, type:" + request.getContentType()); } String appKey = params.get("customerIdentification"); diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm2CallbackProtocol.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm2CallbackProtocol.java index 2bf3df55..4fa72948 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm2CallbackProtocol.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm2CallbackProtocol.java @@ -18,7 +18,7 @@ import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptProtocol; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorFactory; import com.yeepay.yop.sdk.constants.CharacterConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.protocol.AuthenticateProtocolVersion; import com.yeepay.yop.sdk.security.CertTypeEnum; @@ -36,6 +36,7 @@ import java.util.*; import static com.yeepay.yop.sdk.constants.CharacterConstants.*; +import static com.yeepay.yop.sdk.http.Headers.YOP_ENCRYPT; import static com.yeepay.yop.sdk.utils.HttpUtils.useEmptyAsCanonicalQueryString; /** @@ -160,7 +161,7 @@ private String decryptBizContent() { templateOptions.setCredentialsAlg(certType.getValue()); YopEncryptProtocol.Inst parsedEncryptProtocol = parseEncryptProtocol(yopEncrypt, yopCredentials, templateOptions); if (null == parsedEncryptProtocol) { - throw new YopClientException("illegal YopSm2CallbackProtocol, request:" + originRequest); + throw new IllegalParamFormatException(YOP_ENCRYPT, "illegal YopSm2CallbackProtocol, request:" + originRequest); } if (LOGGER.isDebugEnabled()) { @@ -199,10 +200,10 @@ private void initialize(YopCallbackRequest request) { } platformSerialNo = X509CertUtils.parseToHex(platformSerialNo); platformServerRoot = request.getPlatformServerRoot(); - yopEncrypt = headers.get(Headers.YOP_ENCRYPT); + yopEncrypt = headers.get(YOP_ENCRYPT); yopRequestId = headers.get(Headers.YOP_REQUEST_ID); } catch (Exception e) { - throw new YopClientException("error initialize YopSm2CallbackProtocol, ex:", e); + throw new IllegalParamFormatException("YopCallbackRequest", "error initialize YopSm2CallbackProtocol, ex:", e); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm4CallbackProtocol.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm4CallbackProtocol.java index 2fc64cff..d27a69e8 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm4CallbackProtocol.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/callback/protocol/YopSm4CallbackProtocol.java @@ -8,7 +8,8 @@ import com.yeepay.yop.sdk.auth.credentials.provider.YopCredentialsProviderRegistry; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorFactory; import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.encrypt.BigParamEncryptMode; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; @@ -73,7 +74,7 @@ public YopCallback parse() { final List isvEncryptKeys = YopCredentialsProviderRegistry.getProvider() .getIsvEncryptKey(originRequest.getProvider(), originRequest.getEnv(), customerIdentification); if (CollectionUtils.isEmpty(isvEncryptKeys)) { - throw new YopClientException("no isvEncryptKeys found for appKey:" + customerIdentification); + throw new IllegalConfigFormatException("isv_encrypt_key", "no isvEncryptKeys found for appKey:" + customerIdentification); } for (YopCertConfig isvEncryptKey : isvEncryptKeys) { @@ -91,7 +92,7 @@ public YopCallback parse() { LOGGER.warn("fail to parse YopSm4CallbackProtocol with key:" + isvEncryptKey.getValue() + ", ex:", e); } } - throw new YopClientException("fail to parse YopSm4CallbackProtocol, appKey:" + customerIdentification); + throw new IllegalParamFormatException("YopCallback", "fail to parse YopSm4CallbackProtocol, appKey:" + customerIdentification + ", cipherText:" + cipherText); } public String getAlgorithm() { diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/request/YopRequest.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/request/YopRequest.java index 700460f2..1cdae3c1 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/request/YopRequest.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/request/YopRequest.java @@ -3,6 +3,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.internal.RestartableInputStream; import com.yeepay.yop.sdk.model.BaseRequest; import com.yeepay.yop.sdk.model.FileParam; @@ -167,7 +168,7 @@ private InputStream restartStream(InputStream inputStream) { baos.flush(); return RestartableInputStream.wrap(baos.toByteArray()); } catch (IOException e) { - throw new YopClientException("ReqParam Illegal, InputStreamParam Cant Restart, ex:", e); + throw new IllegalParamFormatException("InputStream", "ReqParam Illegal, InputStreamParam Cant Restart, ex:", e); } } @@ -185,7 +186,7 @@ public YopRequest addEncryptMultiPartFile(String name, InputStream inputStream, public YopRequest setContent(String content) { if (StringUtils.isEmpty(content)) { - throw new YopClientException("ReqParam Illegal, RequestContent Is Empty"); + throw new IllegalParamFormatException("content", "ReqParam Illegal, RequestContent Is Empty"); } this.content = content; return this; @@ -207,7 +208,7 @@ public YopRequest setEncryptContent(String content, Set jsonPaths) { public YopRequest setStream(InputStream inputStream) { if (inputStream == null) { - throw new YopClientException("ReqParam Illegal, InputStreamParam IsNull."); + throw new IllegalParamFormatException("InputStream", "ReqParam Illegal, InputStreamParam IsNull."); } this.content = restartStream(inputStream); return this; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YopResponse.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YopResponse.java index af9ec2d3..25cc4001 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YopResponse.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YopResponse.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.model.BaseResponse; import com.yeepay.yop.sdk.utils.JsonUtils; import com.yeepay.yop.sdk.utils.json.KeepAsRawStringDeserializer; @@ -50,7 +51,7 @@ public void setStringResult(String stringResult) { try { this.result = JsonUtils.getObjectMapper().readValue(stringResult, Object.class); } catch (IOException e) { - throw new YopClientException("unable to deserialize stringResult:" + stringResult); + throw new IllegalParamFormatException("result", "unable to deserialize stringResult:" + stringResult); } } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YosUploadResponse.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YosUploadResponse.java index 413b7740..4197b596 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YosUploadResponse.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/service/common/response/YosUploadResponse.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.model.yos.BaseYosUploadResponse; import com.yeepay.yop.sdk.utils.JsonUtils; import com.yeepay.yop.sdk.utils.json.KeepAsRawStringDeserializer; @@ -50,7 +51,7 @@ public void setStringResult(String stringResult) { try { this.result = JsonUtils.getObjectMapper().readValue(stringResult, Object.class); } catch (IOException e) { - throw new YopClientException("unable to deserialize stringResult:" + stringResult); + throw new IllegalParamFormatException("result", "unable to deserialize stringResult:" + stringResult); } } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/CheckUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/CheckUtils.java index d3ad2d66..7cc458d8 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/CheckUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/CheckUtils.java @@ -4,6 +4,7 @@ import com.yeepay.yop.sdk.exception.YopClientException; import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.exception.config.MissingConfigException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import org.apache.commons.lang3.StringUtils; import java.net.MalformedURLException; @@ -65,7 +66,7 @@ public static void checkApiUri(String apiUri) { if (StringUtils.isNotBlank(apiUri) && API_URI_PATTERN.matcher(apiUri).matches()) { return; } - throw new YopClientException("ReqParam Illegal, ApiUri, value:" + apiUri); + throw new IllegalParamFormatException("apiUri", "ReqParam Illegal, ApiUri, value:" + apiUri); } /** @@ -77,7 +78,7 @@ public static void checkCallbackUri(String callbackUri) { if (StringUtils.isNotBlank(callbackUri)) { return; } - throw new YopClientException("callbackUri is illegal, param:" + callbackUri); + throw new IllegalParamFormatException("callbackUri", "callbackUri is illegal, param:" + callbackUri); } /** @@ -93,10 +94,10 @@ public static URI checkServerRoot(String serverRoot) { try { uri = new URI(serverRoot); } catch (Exception e) { - throw new YopClientException("request serverRoot is illegal, value:" + serverRoot); + throw new IllegalParamFormatException("serverRoot", "request serverRoot is illegal, value:" + serverRoot); } if (!StringUtils.equalsAny(uri.getScheme(), "http", "https")) { - throw new YopClientException("unsupported request scheme, value:" + uri.getScheme()); + throw new IllegalParamFormatException("httpScheme", "unsupported request scheme, value:" + uri.getScheme()); } return uri; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/DigitalEnvelopeUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/DigitalEnvelopeUtils.java index bbe2aaba..9262fb7b 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/DigitalEnvelopeUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/DigitalEnvelopeUtils.java @@ -15,6 +15,8 @@ import com.yeepay.yop.sdk.constants.CharacterConstants; import com.yeepay.yop.sdk.exception.VerifySignFailedException; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.DigestAlgEnum; import com.yeepay.yop.sdk.security.SymmetricEncryptAlgEnum; @@ -106,7 +108,7 @@ public static String decrypt(String cipherText, String appKey, PrivateKey privat //分解参数 String[] args = cipherText.split("\\" + SEPARATOR); if (args.length != 4) { - throw new RuntimeException("source invalid : " + cipherText); + throw new IllegalParamFormatException("cipherText", "source invalid : " + cipherText); } String encryptedRandomKeyToBase64 = args[0]; String encryptedDataToBase64 = args[1]; @@ -138,7 +140,7 @@ public static String decrypt(String cipherText, String appKey, PrivateKey privat final YopSignProcessor yopSignProcess = YopSignProcessorFactory.getSignProcessor(SIGNER_MAP.get(digestAlg)); boolean verifySign = yopSignProcess.verify(sourceData, signToBase64, platformCredentials.getCredential()); if (!verifySign) { - throw new YopClientException("verifySign fail!"); + throw new IllegalConfigFormatException("yop_public_key", "verifySign fail!"); } //返回源数据 return sourceData; diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Encodes.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Encodes.java index 77e6806a..caee6877 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Encodes.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Encodes.java @@ -1,6 +1,6 @@ package com.yeepay.yop.sdk.utils; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; @@ -12,6 +12,8 @@ import java.net.URLEncoder; import java.security.Key; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: 封装各种格式的编码解码工具类
* description:
@@ -57,7 +59,7 @@ public static byte[] decodeHex(String input) { try { return Hex.decodeHex(input.toCharArray()); } catch (DecoderException e) { - throw new YopClientException("Decode Hex fails.", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "Decode Hex fails, cause:" + e.getMessage(), e); } } @@ -128,7 +130,7 @@ public static String urlEncode(String part) { try { return URLEncoder.encode(part, DEFAULT_URL_ENCODING); } catch (UnsupportedEncodingException e) { - throw new YopClientException("Url encode fails.", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "Url encode fails, cause:" + e.getMessage(), e); } } @@ -140,7 +142,7 @@ public static String urlDecode(String part) { try { return URLDecoder.decode(part, DEFAULT_URL_ENCODING); } catch (UnsupportedEncodingException e) { - throw new YopClientException("Url Decode fails.", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "Url Decode fails, cause:" + e.getMessage(), e); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/FileUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/FileUtils.java index b9708cdc..22e45143 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/FileUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/FileUtils.java @@ -2,7 +2,7 @@ import com.google.common.collect.Maps; import com.yeepay.yop.sdk.constants.CharacterConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.model.yos.YosDownloadInputStream; import com.yeepay.yop.sdk.model.yos.YosDownloadResponse; import org.apache.commons.io.IOUtils; @@ -25,6 +25,7 @@ import java.nio.file.Files; import static com.yeepay.yop.sdk.YopConstants.FILE_PROTOCOL_PREFIX; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; /** * title:
@@ -174,7 +175,7 @@ public static File saveFile(YosDownloadResponse yosDownloadResponse) { return tmpFile; } catch (Throwable ex) { LOGGER.error("fail to save file, response:" + yosDownloadResponse, ex); - throw new YopClientException("fail to save file, response:" + yosDownloadResponse); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "fail to save file, response:" + yosDownloadResponse); } finally { StreamUtils.closeQuietly(yosDownloadInputStream); } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Holder.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Holder.java index 94507d7f..9c45a657 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Holder.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/Holder.java @@ -1,6 +1,6 @@ package com.yeepay.yop.sdk.utils; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -8,6 +8,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** * title: 单例holder
* description:
@@ -33,10 +35,10 @@ public V getValue() { try { return futureTask.get(); } catch (InterruptedException e) { - throw new YopClientException("SystemError, ThreadInterrupted, ex:", e); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "SystemError, ThreadInterrupted, ex:", e); } catch (ExecutionException e) { Throwable cause = e.getCause(); - throw new YopClientException("UnexpectedError, ex:", cause); + throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, "UnexpectedError, ex:", cause); } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/HttpUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/HttpUtils.java index e188bc68..f5b68a19 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/HttpUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/HttpUtils.java @@ -6,7 +6,7 @@ import com.google.common.collect.Sets; import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.constants.CharacterConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.HttpMethodName; import com.yeepay.yop.sdk.http.Protocol; @@ -123,7 +123,7 @@ public static String normalize(String value) { } return builder.toString(); } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); + throw new IllegalParamFormatException("field", value + "encode fail", e); } } @@ -253,7 +253,7 @@ public static URI appendUri(URI baseUri, String... pathComponents) { try { return new URI(builder.toString()); } catch (URISyntaxException e) { - throw new RuntimeException("Unexpected error", e); + throw new IllegalParamFormatException("serverRoot|apiUri", "Unexpected error", e); } } @@ -293,7 +293,7 @@ public static String encodeParameters(Request request, Bo try { encodedNameValuePair.add(toNameValuePair(forSignature, paramName, paramValue)); } catch (UnsupportedEncodingException e) { - throw new YopClientException("ReqParam Illegal, Charset NotSuppport, name:" + paramName + ",value:" + paramValue, e); + throw new IllegalParamFormatException(paramName, "ReqParam Illegal, Charset NotSuppport, name:" + paramName + ",value:" + paramValue, e); } } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/JsonUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/JsonUtils.java index 586ac9c9..95cd63ec 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/JsonUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/JsonUtils.java @@ -17,7 +17,9 @@ import com.jayway.jsonpath.spi.json.JsonProvider; import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.MappingProvider; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.utils.json.joda.DatetimeModule; import org.apache.commons.collections4.CollectionUtils; import org.slf4j.Logger; @@ -89,7 +91,7 @@ public static String toJsonString(Object value) { try { return writer.writeValueAsString(value); } catch (Exception e) { - throw new IllegalStateException(e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "json", e); } } @@ -104,7 +106,7 @@ public static T fromJsonString(String json, Class clazz) { try { return OBJECT_MAPPER.readValue(json, clazz); } catch (Exception e) { - throw new YopClientException("Unable to parse Json String.", e); + throw new IllegalParamFormatException("json", "Unable to parse Json String.", e); } } @@ -119,10 +121,8 @@ public static JsonGenerator jsonGeneratorOf(Writer writer) throws IOException { public static T loadFrom(File file, Class clazz) throws IOException { try { return OBJECT_MAPPER.readValue(file, clazz); - } catch (IOException e) { - throw e; } catch (Exception e) { - throw new IllegalStateException(e); + throw new IllegalParamFormatException(null != file ? file.getName() : "file", "Unable to load json file" ,e); } } @@ -162,7 +162,7 @@ public static boolean isTotalEncrypt(Set jsonPaths) { return true; } if (jsonPaths.size() > 1 && !CollectionUtils.intersection(jsonPaths, JSON_PATH_ROOT).isEmpty()) { - throw new YopClientException("illegal json paths:" + jsonPaths); + throw new IllegalParamFormatException("jsonPath", "illegal json paths:" + jsonPaths); } return false; } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSdkInitUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSdkInitUtils.java index fafb2f69..3d831180 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSdkInitUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSdkInitUtils.java @@ -4,6 +4,10 @@ */ package com.yeepay.yop.sdk.utils; +import com.yeepay.yop.sdk.service.common.YopClientBuilder; +import com.yeepay.yop.sdk.service.common.YopClientImpl; +import com.yeepay.yop.sdk.service.common.request.YopRequest; +import com.yeepay.yop.sdk.service.common.response.YopResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,4 +67,13 @@ private static void doLoadSpiClass(String spiClass) { } } + public static void main(String[] args) { + YopClientImpl yopClient = YopClientBuilder.builder().build(); + final YopRequest yopRequest = new YopRequest("/rest/v2.0/yop/platform/certs", "GET"); + yopRequest.getRequestConfig().setAppKey("您的国密appKey"); + yopRequest.addParameter("certType", "SM2"); + final YopResponse response = yopClient.request(yopRequest); + assert null != response; + } + } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSignUtils.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSignUtils.java index e0705127..b4e7bbbd 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSignUtils.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/YopSignUtils.java @@ -14,7 +14,10 @@ import com.yeepay.yop.sdk.auth.credentials.provider.YopPlatformCredentialsProviderRegistry; import com.yeepay.yop.sdk.auth.signer.process.YopSignProcessor; import com.yeepay.yop.sdk.base.auth.signer.process.YopSignProcessorFactory; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import org.apache.commons.lang3.StringUtils; @@ -78,7 +81,7 @@ public static void verify(String data, String signature, String appKey, String s if (null != yopPlatformCredentials) { verify(data, signature, yopPlatformCredentials.getCredential()); } else { - throw new YopClientException("can not load platform cert"); + throw new IllegalConfigFormatException("yop_public_key", "can not load platform cert"); } } @@ -107,11 +110,12 @@ public static void verify(String data, String signature, CredentialsItem credent validSignature(signature); YopSignProcessor yopSignProcessor = YopSignProcessorFactory.getSignProcessor(credentialsItem.getCertType().name()); if (null == yopSignProcessor) { - throw new YopClientException("unsupported certType"); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "unsupported certType"); } String args[] = signature.split("\\$"); if (!yopSignProcessor.verify(data, args[0], credentialsItem)) { - throw new YopClientException("verify fail!"); + throw new IllegalParamFormatException("data", "verify fail!, data:" + data + + ", signature:" + signature + ", publicKey:" + credentialsItem); } } @@ -145,7 +149,7 @@ public static String sign(String data, String certType, String appKey, String pr public static String sign(String data, String certType, PrivateKey privateKey) { YopSignProcessor yopSignProcessor = YopSignProcessorFactory.getSignProcessor(certType); if (null == yopSignProcessor) { - throw new YopClientException("unsupported certType"); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "unsupported certType:" + certType); } PKICredentialsItem pkiCredentialsItem = new PKICredentialsItem(privateKey, CertTypeEnum.parse(certType)); return yopSignProcessor.sign(data, pkiCredentialsItem) + SPLIT_CHAR + yopSignProcessor.getDigestAlg(); @@ -154,11 +158,11 @@ public static String sign(String data, String certType, PrivateKey privateKey) { private static void validSignature(String signature) { String args[] = signature.split("\\$"); if ((args.length != 2 && args.length != 4)) { - throw new YopClientException("illegal signature"); + throw new IllegalParamFormatException("signature", "illegal signature"); } CertTypeEnum certType = digestAlgANdCertTypeMap.get(args[1]); if (certType == null) { - throw new YopClientException("illegal signature"); + throw new IllegalParamFormatException("digestAlg", "illegal certType:" + args[1]); } } } diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/JacksonJsonMarshaller.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/JacksonJsonMarshaller.java index 5ac70d78..7e41c73a 100644 --- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/JacksonJsonMarshaller.java +++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/JacksonJsonMarshaller.java @@ -6,12 +6,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopClientBizException; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY; + /** *

  *    将响应对象流化成JSON。 {@link ObjectMapper}是线程安全的。
@@ -43,7 +45,7 @@ public static  T unmarshal(String content, Class objectType) {
         try {
             return objectMapper.readValue(content, objectType);
         } catch (IOException e) {
-            throw new YopClientException(e.getMessage(), e);
+            throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, e.getMessage(), e);
         }
     }
 
@@ -51,7 +53,7 @@ public static  T unmarshal(InputStream content, Class objectType) {
         try {
             return objectMapper.readValue(content, objectType);
         } catch (IOException e) {
-            throw new YopClientException(e.getMessage(), e);
+            throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, e.getMessage(), e);
         }
     }
 
@@ -59,7 +61,7 @@ public static void load(String content, Object obj) {
         try {
             objectMapper.readerForUpdating(obj).readValue(content);
         } catch (IOException ex) {
-            throw new YopClientException(ex.getMessage(), ex);
+            throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, ex.getMessage(), ex);
         }
     }
 
@@ -67,7 +69,7 @@ public static String marshal(Object content) {
         try {
             return objectMapper.writeValueAsString(content);
         } catch (JsonProcessingException e) {
-            throw new YopClientException(e.getMessage(), e);
+            throw new YopClientBizException(SDK_CONFIG_RUNTIME_DEPENDENCY, e.getMessage(), e);
         }
     }
 
diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/KeepAsRawStringDeserializer.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/KeepAsRawStringDeserializer.java
index c1b047cb..bb185477 100644
--- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/KeepAsRawStringDeserializer.java
+++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/json/KeepAsRawStringDeserializer.java
@@ -4,6 +4,7 @@
 import com.fasterxml.jackson.core.JsonToken;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,7 +54,7 @@ private String getStringFromSource(Object sourceRef) {
         if (sourceRef instanceof StringReader) {
             return getStringViaReflection((StringReader) sourceRef);
         }
-        throw new IllegalArgumentException("source ref of json is not an instance of String/StringReader,can't use KeepAsRawStringDeserializer here!");
+        throw new IllegalParamFormatException("json", "source ref of json is not an instance of String/StringReader,can't use KeepAsRawStringDeserializer here!");
     }
 
     /**
diff --git a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/support/CashierSupport.java b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/support/CashierSupport.java
index b3bcaf4c..79fb4184 100644
--- a/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/support/CashierSupport.java
+++ b/yop-java-sdk-base/src/main/java/com/yeepay/yop/sdk/utils/support/CashierSupport.java
@@ -8,6 +8,7 @@
 import com.google.common.collect.Maps;
 import com.yeepay.yop.sdk.auth.credentials.PKICredentialsItem;
 import com.yeepay.yop.sdk.base.auth.signer.process.YopSignProcessorFactory;
+import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException;
 import com.yeepay.yop.sdk.security.CertTypeEnum;
 
 import java.security.PrivateKey;
@@ -71,7 +72,8 @@ public static String getPayUrl(PrivateKey privateKey, String appKey, String merc
     // 获取请求收银台的sign
     private static String signature(String plainText, PrivateKey privateKey) {
         CertTypeEnum certType = resolveCertType(privateKey);
-        return YopSignProcessorFactory.getSignProcessor(certType.name()).sign(plainText, new PKICredentialsItem(privateKey, certType));
+        return YopSignProcessorFactory.getSignProcessor(certType.name()).sign(plainText, new PKICredentialsItem(privateKey, certType))
+                + "$" + digestAlgByCertType(certType);
     }
 
     private static CertTypeEnum resolveCertType(PrivateKey privateKey) {
@@ -81,7 +83,18 @@ private static CertTypeEnum resolveCertType(PrivateKey privateKey) {
             case "EC":
                 return CertTypeEnum.SM2;
             default:
-                throw new RuntimeException("sign algorithm(" + privateKey.getAlgorithm() + ") not supported");
+                throw new IllegalParamFormatException("PrivateKey", "sign algorithm(" + privateKey.getAlgorithm() + ") not supported");
+        }
+    }
+
+    private static String digestAlgByCertType(CertTypeEnum certType) {
+        switch (certType) {
+            case RSA2048:
+                return "SHA256";
+            case SM2:
+                return "SM3";
+            default:
+                throw new RuntimeException("sign algorithm(" + certType + ") not supported");
         }
     }
 
diff --git a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/PKICredentialsItem.java b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/PKICredentialsItem.java
index 599b3242..85420ca3 100644
--- a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/PKICredentialsItem.java
+++ b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/PKICredentialsItem.java
@@ -8,6 +8,7 @@
 
 import java.security.PrivateKey;
 import java.security.PublicKey;
+import java.util.Base64;
 
 /**
  * title: 
@@ -56,4 +57,8 @@ public CertTypeEnum getCertType() { return certType; } + @Override + public String toString() { + return null != publicKey ? "pubKey:" + Base64.getEncoder().encodeToString(publicKey.getEncoded()) : super.toString(); + } } diff --git a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopCredentialsProviderRegistry.java b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopCredentialsProviderRegistry.java index 617b1157..0cfb5c13 100644 --- a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopCredentialsProviderRegistry.java +++ b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopCredentialsProviderRegistry.java @@ -1,6 +1,7 @@ package com.yeepay.yop.sdk.auth.credentials.provider; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import java.util.ServiceLoader; @@ -27,7 +28,7 @@ public class YopCredentialsProviderRegistry { public static void registerProvider(YopCredentialsProvider yopCredentialsProvider) { if (yopCredentialsProvider == null) { - throw new YopClientException("SetUpProblem, Custom YopCredentialsProvider IsNull."); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "SetUpProblem, Custom YopCredentialsProvider IsNull."); } CUSTOM_PROVIDER = yopCredentialsProvider; } diff --git a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopPlatformCredentialsProviderRegistry.java b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopPlatformCredentialsProviderRegistry.java index 09daf76a..54facde9 100644 --- a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopPlatformCredentialsProviderRegistry.java +++ b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/credentials/provider/YopPlatformCredentialsProviderRegistry.java @@ -4,6 +4,8 @@ */ package com.yeepay.yop.sdk.auth.credentials.provider; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; import java.util.ServiceLoader; @@ -31,7 +33,7 @@ public class YopPlatformCredentialsProviderRegistry { public static void registerProvider(YopPlatformCredentialsProvider yopCredentialsProvider) { if (yopCredentialsProvider == null) { - throw new YopClientException("SetUpProblem, Custom YopPlatformCredentialsProvider IsNull."); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "SetUpProblem, Custom YopPlatformCredentialsProvider IsNull."); } CUSTOM_PROVIDER = yopCredentialsProvider; } diff --git a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/signer/process/YopSignProcessor.java b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/signer/process/YopSignProcessor.java index 72aeaa50..d71e08b4 100644 --- a/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/signer/process/YopSignProcessor.java +++ b/yop-java-sdk-crypto-api/src/main/java/com/yeepay/yop/sdk/auth/signer/process/YopSignProcessor.java @@ -7,6 +7,7 @@ import com.yeepay.yop.sdk.auth.SignOptions; import com.yeepay.yop.sdk.auth.credentials.CredentialsItem; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; /** * title: YopSignProcessor
@@ -29,7 +30,7 @@ public interface YopSignProcessor { */ default String sign(String content, CredentialsItem credentialsItem) { if (!isSupport(credentialsItem)) { - throw new YopClientException("ConfigProblem, CredentialsItem Type NotSupport, type:" + credentialsItem.getClass().getSimpleName()); + throw new IllegalParamFormatException("CredentialsItem", "ConfigProblem, CredentialsItem Type NotSupport, type:" + credentialsItem.getClass().getSimpleName()); } return doSign(content, credentialsItem); } @@ -60,7 +61,7 @@ default boolean verify(String content, String signature, CredentialsItem credent if (isSupport(credentialsItem)) { return doVerify(content, signature, credentialsItem); } - throw new YopClientException("ConfigProblem, CredentialsItem Type NotSupport, type:" + credentialsItem.getClass().getSimpleName()); + throw new IllegalParamFormatException("CredentialsItem", "ConfigProblem, CredentialsItem Type NotSupport, type:" + credentialsItem.getClass().getSimpleName()); } default boolean doVerify(String content, String signature, CredentialsItem credentialsItem) { diff --git a/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/cert/parser/YopSm2PublicKeyParser.java b/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/cert/parser/YopSm2PublicKeyParser.java index 2ee57476..535b35ca 100644 --- a/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/cert/parser/YopSm2PublicKeyParser.java +++ b/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/cert/parser/YopSm2PublicKeyParser.java @@ -6,6 +6,7 @@ import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; import com.yeepay.yop.sdk.base.security.cert.parser.AbstractYopPublicKeyParser; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.security.cert.YopCertCategory; import com.yeepay.yop.sdk.base.security.cert.parser.YopCertParser; import com.yeepay.yop.sdk.security.cert.YopPublicKey; @@ -34,23 +35,25 @@ public class YopSm2PublicKeyParser extends AbstractYopPublicKeyParser implements @Override public YopPublicKey parse(YopCertConfig certConfig) { if (null == certConfig.getStoreType()) { - throw new YopClientException("ConfigProblem, YopPublicKey StoreType IsNull, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, YopPublicKey StoreType IsNull, certConfig:" + certConfig); } switch (certConfig.getStoreType()) { case STRING: try { return new YopPublicKey(SmUtils.string2PublicKey(certConfig.getValue())); } catch (Exception ex) { - throw new YopClientException("ConfigProblem, YopPublicKey Value Illegal, certConfig:" + certConfig, ex); + throw new IllegalConfigFormatException("value", "ConfigProblem, YopPublicKey Value Illegal, certConfig:" + + certConfig + ", cause:" + ex.getMessage(), ex); } case FILE_CER: try { return new YopPublicKey(getX509Cert(certConfig.getValue(), CertTypeEnum.SM2)); } catch (Exception e) { - throw new YopClientException("ConfigProblem, YopPublicKey Value Illegal, certConfig:" + certConfig, e); + throw new IllegalConfigFormatException("value", "ConfigProblem, YopPublicKey Value Illegal, certConfig:" + + certConfig + ", cause" + e.getMessage(), e); } default: - throw new YopClientException("ConfigProblem, YopPublicKey StoreType Illegal, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, YopPublicKey StoreType Illegal, certConfig:" + certConfig); } } diff --git a/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/digest/YopSm3Digester.java b/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/digest/YopSm3Digester.java index 0a6b1e24..8982a610 100644 --- a/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/digest/YopSm3Digester.java +++ b/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/security/digest/YopSm3Digester.java @@ -5,7 +5,8 @@ package com.yeepay.yop.sdk.gm.base.security.digest; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.gm.base.utils.SmUtils; import com.yeepay.yop.sdk.security.DigestAlgEnum; import com.yeepay.yop.sdk.security.digest.YopDigester; @@ -54,7 +55,8 @@ public byte[] digest(InputStream input, String alg) { while (digestInputStream.read(buffer) > -1) {} return digestInputStream.getMessageDigest().digest(); } catch (Exception e) { - throw new YopClientException("UnexpectedError, MessageDigest Fail, alg:" + alg + ", ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "UnexpectedError, MessageDigest Fail, alg:" + alg + ", ex:", e); } } diff --git a/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/utils/SmUtils.java b/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/utils/SmUtils.java index 52a7966c..5f7d7d3f 100644 --- a/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/utils/SmUtils.java +++ b/yop-java-sdk-crypto-gm-base/src/main/java/com/yeepay/yop/sdk/gm/base/utils/SmUtils.java @@ -4,7 +4,7 @@ */ package com.yeepay.yop.sdk.gm.base.utils; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.utils.Encodes; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; @@ -62,7 +62,8 @@ public static PublicKey string2PublicKey(String pubKey) { KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); return kf.generatePublic(eks); } catch (Exception e) { - throw new YopClientException("ConfigProblem, YopPublicKey Convert Fail, value:" + pubKey, e); + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, YopPublicKey Convert Fail, value:" + + pubKey + ", cause:" + e.getMessage(), e); } } diff --git a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/cert/parser/YopSm2PrivateKeyParser.java b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/cert/parser/YopSm2PrivateKeyParser.java index b5da01b0..c6fcedc9 100644 --- a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/cert/parser/YopSm2PrivateKeyParser.java +++ b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/cert/parser/YopSm2PrivateKeyParser.java @@ -8,7 +8,7 @@ import com.yeepay.yop.sdk.base.security.cert.parser.YopCertParser; import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; import com.yeepay.yop.sdk.constants.CharacterConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.gm.base.utils.SmUtils; import com.yeepay.yop.sdk.gm.utils.Sm2Utils; import com.yeepay.yop.sdk.security.CertTypeEnum; @@ -40,23 +40,25 @@ public class YopSm2PrivateKeyParser extends AbstractYopPrivateKeyParser implemen @Override public PrivateKey parse(YopCertConfig certConfig) { if (null == certConfig.getStoreType()) { - throw new YopClientException("ConfigProblem, IsvPrivateKey StoreType IsNull, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, IsvPrivateKey StoreType IsNull, certConfig:" + certConfig); } switch (certConfig.getStoreType()) { case STRING: try { return Sm2Utils.string2PrivateKey(certConfig.getValue()); } catch (Exception ex) { - throw new YopClientException("ConfigProblem, IsvPrivateKey Value Illegal, certConfig:" + certConfig, ex); + throw new IllegalConfigFormatException("value", "ConfigProblem, IsvPrivateKey Value Illegal, certConfig:" + + certConfig + ", cause:" + ex.getMessage(), ex); } case FILE_P12: try { return parsePrivateKey(certConfig.getPassword(), certConfig.getValue()); } catch (Exception ex) { - throw new YopClientException("ConfigProblem, IsvPrivateKey Password Illegal, certConfig:" + certConfig, ex); + throw new IllegalConfigFormatException("value", "ConfigProblem, IsvPrivateKey Password Illegal, certConfig:" + + certConfig + ", cause:" + ex.getMessage(), ex); } default: - throw new YopClientException("ConfigProblem, IsvPrivateKey StoreType NotSupport, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, IsvPrivateKey StoreType NotSupport, certConfig:" + certConfig); } } diff --git a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm2Encryptor.java b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm2Encryptor.java index fef9e72b..60ba9508 100644 --- a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm2Encryptor.java +++ b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm2Encryptor.java @@ -8,7 +8,8 @@ import com.yeepay.yop.sdk.auth.credentials.YopPKICredentials; import com.yeepay.yop.sdk.auth.credentials.YopPlatformCredentials; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorAdaptor; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.gm.base.utils.SmUtils; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import org.bouncycastle.crypto.engines.SM2Engine; @@ -75,7 +76,8 @@ public byte[] encrypt(byte[] plain, EncryptOptions options) { engine.init(true, pwr); return engine.processBlock(plain, 0, plain.length); } catch (Throwable e) { - throw new YopClientException("SystemError, Encrypt Fail, options:" + options, e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Encrypt Fail, options:" + options + ", cause:" + e.getMessage(), e); } } @@ -94,7 +96,8 @@ public byte[] decrypt(byte[] cipher, EncryptOptions options) { engine.init(false, priKeyParameters); return engine.processBlock(cipher, 0, cipher.length); } catch (Throwable e) { - throw new YopClientException("SystemError, Decrypt Fail, options:" + options, e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Decrypt Fail, options:" + options + ", cause:" + e.getMessage(), e); } } diff --git a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm4Encryptor.java b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm4Encryptor.java index c815ce9e..31a87dc0 100644 --- a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm4Encryptor.java +++ b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/security/encrypt/YopSm4Encryptor.java @@ -8,7 +8,9 @@ import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.auth.credentials.YopSymmetricCredentials; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorAdaptor; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.gm.base.utils.SmUtils; import com.yeepay.yop.sdk.gm.utils.Sm4Utils; import com.yeepay.yop.sdk.security.encrypt.BigParamEncryptMode; @@ -30,6 +32,7 @@ import java.util.Map; import static com.yeepay.yop.sdk.YopConstants.SM4_CBC_PKCS5PADDING; +import static com.yeepay.yop.sdk.YopConstants.SM4_ECB_PKCS5PADDING; /** * title: 对称加密器
@@ -61,9 +64,10 @@ protected Map initialValue() { Map map = Maps.newHashMap(); try { map.put(SM4_CBC_PKCS5PADDING, Cipher.getInstance(SM4_CBC_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME)); + map.put(SM4_ECB_PKCS5PADDING, Cipher.getInstance(SM4_ECB_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME)); map.put(ALGORITHM_NAME_GCM_NOPADDING, Cipher.getInstance(ALGORITHM_NAME_GCM_NOPADDING, BouncyCastleProvider.PROVIDER_NAME)); } catch (Exception e) { - throw new YopClientException("SystemError, YopSm4Encryptor InitFail, ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "SystemError, YopSm4Encryptor InitFail, ex:", e); } return map; } @@ -71,7 +75,7 @@ protected Map initialValue() { @Override public List supportedAlgs() { - return Arrays.asList(SM4_CBC_PKCS5PADDING, ALGORITHM_NAME_GCM_NOPADDING); + return Arrays.asList(SM4_CBC_PKCS5PADDING, SM4_ECB_PKCS5PADDING, ALGORITHM_NAME_GCM_NOPADDING); } @Override @@ -90,7 +94,8 @@ public byte[] encrypt(byte[] plain, EncryptOptions options) { Cipher initializedCipher = getInitializedCipher(ENCRYPT_MODE, options); return initializedCipher.doFinal(plain); } catch (Throwable t) { - throw new YopClientException("SystemError, Encrypt Fail, options:" + options + ", ex:", t); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Encrypt Fail, options:" + options + ", cause:" + t.getMessage(), t); } } @@ -98,7 +103,8 @@ public byte[] encrypt(byte[] plain, EncryptOptions options) { public InputStream encrypt(InputStream plain, EncryptOptions options) { // TODO 支持chunked加密 if (BigParamEncryptMode.chunked.equals(options.getBigParamEncryptMode())) { - throw new YopClientException("SystemError, Encrypt Chunked NotSupport, options:" + options); + throw new IllegalParamFormatException("BigParamEncryptMode", + "SystemError, Encrypt Chunked NotSupport, options:" + options); } return new CipherInputStream(plain, getInitializedCipher(ENCRYPT_MODE, options, false)); } @@ -109,7 +115,8 @@ public byte[] decrypt(byte[] cipher, EncryptOptions options) { Cipher initializedCipher = getInitializedCipher(DECRYPT_MODE, options); return initializedCipher.doFinal(cipher); } catch (Throwable t) { - throw new YopClientException("SystemError, Decrypt Fail, options:" + options, t); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Decrypt Fail, options:" + options + ", cause:" + t.getMessage(), t); } } @@ -117,7 +124,8 @@ public byte[] decrypt(byte[] cipher, EncryptOptions options) { public InputStream decrypt(InputStream cipher, EncryptOptions options) { // TODO 支持chunked加密 if (BigParamEncryptMode.chunked.equals(options.getBigParamEncryptMode())) { - throw new YopClientException("SystemError, Encrypt Chunked NotSupport, options:" + options); + throw new IllegalParamFormatException("BigParamEncryptMode", + "SystemError, Encrypt Chunked NotSupport, options:" + options); } return new CipherInputStream(cipher, getInitializedCipher(DECRYPT_MODE, options, false)); } @@ -151,7 +159,8 @@ private Cipher getInitializedCipher(int mode, EncryptOptions encryptOptions, boo cipher.init(mode, sm4Key); return cipher; } catch (Throwable throwable) { - throw new YopClientException("SystemError, InitCipher Fail, mode:" + mode + + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, InitCipher Fail, mode:" + mode + ", options:" + encryptOptions + ", ex:", throwable); } } diff --git a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm2Utils.java b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm2Utils.java index 8294b06d..0416cf8e 100644 --- a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm2Utils.java +++ b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm2Utils.java @@ -6,7 +6,10 @@ import com.google.common.base.Charsets; import com.yeepay.yop.sdk.auth.SignOptions; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.gm.base.utils.SmUtils; import com.yeepay.yop.sdk.utils.Encodes; import org.bouncycastle.asn1.*; @@ -73,7 +76,7 @@ public static KeyPair generateKeyPair() { KeyPair kp = kpGen.generateKeyPair(); return kp; } catch (Exception e) { - throw new RuntimeException(e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, "maybe missing bouncycastle jars", e); } } @@ -93,7 +96,7 @@ public static PublicKey string2PublicKey(String pubKey) { KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); return kf.generatePublic(eks); } catch (Exception e) { - throw new YopClientException("ConfigProblem, YopPublicKey Illegal, value:" + pubKey + ", ex:", e); + throw new IllegalConfigFormatException("yop_public_key", "ConfigProblem, YopPublicKey Illegal, value:" + pubKey + ", ex:", e); } } @@ -113,7 +116,7 @@ public static PrivateKey string2PrivateKey(String priKey) { KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); return kf.generatePrivate(peks); } catch (Exception e) { - throw new YopClientException("ConfigProblem, IsvPrivateKey Illegal, value:" + priKey + ", ex:" + e); + throw new IllegalConfigFormatException("isv_private_key", "ConfigProblem, IsvPrivateKey Illegal, value:" + priKey + ", ex:" + e); } } @@ -155,7 +158,8 @@ public static String sign(String data, BCECPrivateKey priKey, SignOptions option } return Encodes.encodeUrlSafeBase64(sign(priKey, dataByte)); } catch (CryptoException e) { - throw new YopClientException("UnexpectedError, Sign Fail, data:" + data + ", key:" + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "UnexpectedError, Sign Fail, data:" + data + ", key:" + priKey + ", options:" + options + ", ex:", e); } @@ -175,7 +179,26 @@ public static boolean verifySign(String data, String signature, BCECPublicKey pu byte[] dataByte = data.getBytes(Charsets.UTF_8); return verify(publicKey, dataByte, encodeSM2SignToDER(signByte)); } catch (IOException e) { - throw new YopClientException("UnexpectedError, VerifySign Fail, data:" + + throw new IllegalParamFormatException("data|signature", "UnexpectedError, VerifySign Fail, data:" + + data + ", sign:" + signature + ", key:" + publicKey + ", ex:", e); + } + } + + /** + * sm2密钥进行签名验证-withId + * + * @param data + * @param signature + * @param publicKey + * @return + */ + public static boolean verifySignWithId(String data, String signature, BCECPublicKey publicKey, byte[] withId) { + try { + byte[] signByte = Encodes.decodeBase64(signature); + byte[] dataByte = data.getBytes(Charsets.UTF_8); + return verifyWithId(publicKey, dataByte, encodeSM2SignToDER(signByte), withId); + } catch (IOException e) { + throw new IllegalParamFormatException("data|signature", "UnexpectedError, VerifySign Fail, data:" + data + ", sign:" + signature + ", key:" + publicKey + ", ex:", e); } } @@ -232,6 +255,35 @@ public static byte[] sign(BCECPrivateKey priKey, byte[] srcData) throws CryptoEx return rawSign; } + /** + * 签名-withId + * + * @param priKey 私钥 + * @param srcData 原文 + * @return 64字节的纯R+S字节流 + * @throws CryptoException + */ + public static byte[] signWithId(BCECPrivateKey priKey, byte[] srcData, byte[] withId) throws CryptoException { + ECParameterSpec parameterSpec = priKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + ECPrivateKeyParameters priKeyParameters = new ECPrivateKeyParameters(priKey.getD(), domainParameters); + //der编码后的签名值 + byte[] derSign = sign(priKeyParameters, withId, srcData); + + //der解码过程 + ASN1Sequence as = DERSequence.getInstance(derSign); + byte[] rBytes = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray(); + byte[] sBytes = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray(); + //由于大数的补0规则,所以可能会出现33个字节的情况,要修正回32个字节 + rBytes = fixToCurveLengthBytes(rBytes); + sBytes = fixToCurveLengthBytes(sBytes); + byte[] rawSign = new byte[rBytes.length + sBytes.length]; + System.arraycopy(rBytes, 0, rawSign, 0, rBytes.length); + System.arraycopy(sBytes, 0, rawSign, rBytes.length, sBytes.length); + return rawSign; + } + /** * 签名 * @@ -272,6 +324,22 @@ public static boolean verify(BCECPublicKey pubKey, byte[] srcData, byte[] sign) return verify(pubKeyParameters, null, srcData, sign); } + /** + * 验签 + * + * @param pubKey 公钥 + * @param srcData 原文 + * @param sign DER编码的签名值 + * @return + */ + public static boolean verifyWithId(BCECPublicKey pubKey, byte[] srcData, byte[] sign, byte[] withId) { + ECParameterSpec parameterSpec = pubKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + ECPublicKeyParameters pubKeyParameters = new ECPublicKeyParameters(pubKey.getQ(), domainParameters); + return verify(pubKeyParameters, withId, srcData, sign); + } + /** * 验签 * diff --git a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm4Utils.java b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm4Utils.java index 891d9c26..c2c35a55 100644 --- a/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm4Utils.java +++ b/yop-java-sdk-crypto-gm/src/main/java/com/yeepay/yop/sdk/gm/utils/Sm4Utils.java @@ -5,7 +5,8 @@ package com.yeepay.yop.sdk.gm.utils; import com.google.common.base.Charsets; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.gm.base.utils.SmUtils; import com.yeepay.yop.sdk.utils.Encodes; import org.apache.commons.lang3.StringUtils; @@ -53,7 +54,8 @@ public static byte[] generateKey() { try { return generateKey(DEFAULT_KEY_SIZE); } catch (Exception e) { - throw new YopClientException("UnexpectedError, Generate Sm4Key Fail, ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "UnexpectedError, Generate Sm4Key Fail, ex:", e); } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PrivateKeyParser.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PrivateKeyParser.java index 000acb38..ed7a1286 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PrivateKeyParser.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PrivateKeyParser.java @@ -8,7 +8,7 @@ import com.yeepay.yop.sdk.base.security.cert.parser.YopCertParser; import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; import com.yeepay.yop.sdk.constants.CharacterConstants; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.inter.utils.RSAKeyUtils; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.cert.YopCertCategory; @@ -33,23 +33,26 @@ public class YopRsa2048PrivateKeyParser extends AbstractYopPrivateKeyParser impl @Override public PrivateKey parse(YopCertConfig certConfig) { if (null == certConfig.getStoreType()) { - throw new YopClientException("ConfigProblem, IsvPrivateKey StoreType IsNull, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, IsvPrivateKey StoreType IsNull, certConfig:" + certConfig); } switch (certConfig.getStoreType()) { case STRING: try { return RSAKeyUtils.string2PrivateKey(certConfig.getValue()); } catch (Exception ex) { - throw new YopClientException("ConfigProblem, IsvPrivateKey Parse Fail, certConfig:" + certConfig, ex); + throw new IllegalConfigFormatException("value", + "ConfigProblem, IsvPrivateKey Parse Fail, certConfig:" + + certConfig + ", cause:" + ex.getMessage(), ex); } case FILE_P12: try { return parsePrivateKey(certConfig.getPassword(), certConfig.getValue()); } catch (Exception ex) { - throw new YopClientException("ConfigProblem, IsvPrivateKey Parse Fail, certConfig:" + certConfig, ex); + throw new IllegalConfigFormatException("value", "ConfigProblem, IsvPrivateKey Parse Fail, certConfig:" + + certConfig + ", cause:" + ex.getMessage(), ex); } default: - throw new YopClientException("ConfigProblem, IsvPrivateKey StoreType NotSupport, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, IsvPrivateKey StoreType NotSupport, certConfig:" + certConfig); } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PublicKeyParser.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PublicKeyParser.java index ee930719..be9d828e 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PublicKeyParser.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/cert/parser/YopRsa2048PublicKeyParser.java @@ -9,6 +9,7 @@ import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; import com.yeepay.yop.sdk.constants.CharacterConstants; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.inter.utils.RSAKeyUtils; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.cert.YopCertCategory; @@ -30,23 +31,24 @@ public class YopRsa2048PublicKeyParser extends AbstractYopPublicKeyParser implem @Override public YopPublicKey parse(YopCertConfig certConfig) { if (null == certConfig.getStoreType()) { - throw new YopClientException("ConfigProblem, YopPublicKey StoreType IsNull, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, YopPublicKey StoreType IsNull, certConfig:" + certConfig); } switch (certConfig.getStoreType()) { case STRING: try { return new YopPublicKey(RSAKeyUtils.string2PublicKey(certConfig.getValue())); } catch (Exception ex) { - throw new YopClientException("ConfigProblem, YopPublicKey Parse Fail, certConfig:" + certConfig + ", ex", ex); + throw new IllegalConfigFormatException("value", "ConfigProblem, YopPublicKey Value Illegal, certConfig:" + certConfig + ", cause:" + ex.getMessage(), ex); } case FILE_CER: try { return new YopPublicKey(getX509Cert(certConfig.getValue(), CertTypeEnum.RSA2048)); } catch (Exception e) { - throw new YopClientException("ConfigProblem, YopPublicKey Parse Fail, certConfig:" + certConfig + ", ex", e); + throw new IllegalConfigFormatException("value", "ConfigProblem, YopPublicKey Value Illegal, certConfig:" + + certConfig + ", cause" + e.getMessage(), e); } default: - throw new YopClientException("ConfigProblem, YopPublicKey StoreType NotSupport, certConfig:" + certConfig); + throw new IllegalConfigFormatException("store_type", "ConfigProblem, YopPublicKey StoreType Illegal, certConfig:" + certConfig); } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/digest/YopShaDigester.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/digest/YopShaDigester.java index 76d84972..91cd3cf8 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/digest/YopShaDigester.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/digest/YopShaDigester.java @@ -6,9 +6,10 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.yeepay.yop.sdk.security.digest.YopDigester; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.security.DigestAlgEnum; +import com.yeepay.yop.sdk.security.digest.YopDigester; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +50,8 @@ public byte[] digest(InputStream input, String alg) { while (digestInputStream.read(buffer) > -1) {} return digestInputStream.getMessageDigest().digest(); } catch (Exception e) { - throw new YopClientException("SystemError, Digest Fail, alg:" + alg + ", ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Digest Fail, alg:" + alg + ", cause:" + e.getMessage(), e); } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopAesEncryptor.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopAesEncryptor.java index 4b4f6174..efc6a19b 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopAesEncryptor.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopAesEncryptor.java @@ -5,7 +5,9 @@ import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.auth.credentials.YopSymmetricCredentials; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorAdaptor; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.encrypt.BigParamEncryptMode; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; import com.yeepay.yop.sdk.utils.Encodes; @@ -43,7 +45,8 @@ protected Map initialValue() { map.put(AES_ECB_PKCS5PADDING, Cipher.getInstance(AES_ECB_PKCS5PADDING)); map.put(AES, Cipher.getInstance(AES)); } catch (Exception e) { - throw new YopClientException("SystemError, InitCipher Fail, ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, InitCipher Fail, cause:" + e.getMessage(), e); } return map; } @@ -79,7 +82,8 @@ public byte[] encrypt(byte[] plain, EncryptOptions options) { Cipher initializedCipher = getInitializedCipher(Cipher.ENCRYPT_MODE, options); return initializedCipher.doFinal(plain); } catch (Throwable t) { - throw new YopClientException("SystemError, Encrypt Fail, options:" + options + ", ex:", t); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Encrypt Fail, options:" + options + ", cause:" + t.getMessage(), t); } } @@ -87,7 +91,8 @@ public byte[] encrypt(byte[] plain, EncryptOptions options) { public InputStream encrypt(InputStream plain, EncryptOptions options) { // TODO 支持chunked加密 if (BigParamEncryptMode.chunked.equals(options.getBigParamEncryptMode())) { - throw new YopClientException("SystemError, Encrypt Chunked NotSupport, options:" + options); + throw new IllegalParamFormatException("BigParamEncryptMode", + "SystemError, Encrypt Chunked NotSupport, options:" + options); } return new CipherInputStream(plain, getInitializedCipher(Cipher.ENCRYPT_MODE, options, false)); } @@ -98,7 +103,8 @@ public byte[] decrypt(byte[] cipher, EncryptOptions options) { Cipher initializedCipher = getInitializedCipher(Cipher.DECRYPT_MODE, options); return initializedCipher.doFinal(cipher); } catch (Throwable t) { - throw new YopClientException("SystemError, Decrypt Fail, options:" + options + ", ex:", t); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Decrypt Fail, options:" + options + ", cause:" + t.getMessage(), t); } } @@ -106,7 +112,8 @@ public byte[] decrypt(byte[] cipher, EncryptOptions options) { public InputStream decrypt(InputStream cipher, EncryptOptions options) { // TODO 支持chunked加密 if (BigParamEncryptMode.chunked.equals(options.getBigParamEncryptMode())) { - throw new YopClientException("SystemError, Decrypt Chunked NotSupport, options:" + options); + throw new IllegalParamFormatException("BigParamEncryptMode", + "SystemError, Decrypt Chunked NotSupport, options:" + options); } return new CipherInputStream(cipher, getInitializedCipher(Cipher.DECRYPT_MODE, options, false)); } @@ -124,7 +131,8 @@ private Cipher getInitializedCipher(int mode, EncryptOptions encryptOptions, boo cipher.init(mode, secretKey); return cipher; } catch (Throwable throwable) { - throw new YopClientException("error happened when initialize cipher", throwable); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "error happened when initialize cipher, cause:" + throwable.getMessage(), throwable); } } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopRsaEncryptor.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopRsaEncryptor.java index dc7b24da..acde2816 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopRsaEncryptor.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/security/encrypt/YopRsaEncryptor.java @@ -9,6 +9,8 @@ import com.yeepay.yop.sdk.auth.credentials.YopPKICredentials; import com.yeepay.yop.sdk.auth.credentials.YopPlatformCredentials; import com.yeepay.yop.sdk.base.security.encrypt.YopEncryptorAdaptor; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; import com.yeepay.yop.sdk.inter.utils.RSA; import com.yeepay.yop.sdk.security.encrypt.EncryptOptions; @@ -64,7 +66,8 @@ public byte[] encrypt(byte[] plain, EncryptOptions options) { return RSA.encrypt(plain, ((PKICredentialsItem) ((YopPlatformCredentials) options.getCredentials()).getCredential()).getPublicKey()); } catch (Throwable e) { - throw new YopClientException("SystemError, Encrypt Fail, options:" + options + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Encrypt Fail, options:" + options + ", cause:" + e.getMessage(), e); } } @@ -79,7 +82,8 @@ public byte[] decrypt(byte[] cipher, EncryptOptions options) { try { return RSA.decrypt(cipher, ((YopPKICredentials) options.getCredentials()).getCredential().getPrivateKey()); } catch (Throwable e) { - throw new YopClientException("SystemError, Decrypt Fail, options:" + options + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Decrypt Fail, options:" + options + ", cause:" + e.getMessage(), e); } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSA.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSA.java index 9d79ddf2..9950246c 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSA.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSA.java @@ -4,7 +4,9 @@ import com.google.common.collect.Maps; import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.auth.SignOptions; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.constants.ExceptionConstants; +import com.yeepay.yop.sdk.exception.YopClientBizException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.DigestAlgEnum; import com.yeepay.yop.sdk.utils.Encodes; @@ -13,6 +15,7 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; +import java.util.Base64; import java.util.Map; /** @@ -49,7 +52,9 @@ public static boolean verifySign(byte[] data, byte[] sign, PublicKey publicKey, signature.update(data); return signature.verify(sign); } catch (Exception e) { - throw new YopClientException("SystemError, Sign Fail, key:" + publicKey + ", digestAlg" + digestAlg + "ex:", e); + throw new IllegalParamFormatException("YopPublicKey", "SystemError, Sign Fail, pubKey:" + + (null != publicKey ? Base64.getEncoder().encodeToString(publicKey.getEncoded()) : null) + + ", digestAlg" + digestAlg + ", cause:" + e.getMessage(), e); } } @@ -83,7 +88,8 @@ public static byte[] sign(byte[] data, PrivateKey key, DigestAlgEnum digestAlg) signature.update(data); return signature.sign(); } catch (Exception e) { - throw new YopClientException("SystemError, Sign Fail, key:" + key + ", digestAlg" + digestAlg + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Sign Fail, key:" + key + ", digestAlg" + digestAlg + ", cause:" + e.getMessage(), e); } } @@ -130,7 +136,8 @@ public static byte[] encrypt(byte[] data, Key key) { cipher.init(Cipher.ENCRYPT_MODE, key); return cipher.doFinal(data); } catch (Exception e) { - throw new YopClientException("SystemError, Encrypt Fail, key:" + key + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Encrypt Fail, key:" + key + ", cause:" + e.getMessage(), e); } } @@ -145,7 +152,8 @@ public static String encryptToBase64(String data, Key key) { try { return Encodes.encodeUrlSafeBase64(encrypt(data.getBytes(Charsets.UTF_8), key)); } catch (Exception e) { - throw new YopClientException("SystemError, Encrypt Fail, data:" + data + ", key:" + key + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Encrypt Fail, data:" + data + ", key:" + key + ", cause:" + e.getMessage(), e); } } @@ -162,7 +170,8 @@ public static byte[] decrypt(byte[] data, Key key) { cipher.init(Cipher.DECRYPT_MODE, key); return cipher.doFinal(data); } catch (Exception e) { - throw new YopClientException("SystemError, Decrypt Fail, key:" + key + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Decrypt Fail, key:" + key + ", cause:" + e.getMessage(), e); } } @@ -177,7 +186,8 @@ public static String decryptFromBase64(String data, Key key) { try { return new String(decrypt(Encodes.decodeBase64(data), key), Charsets.UTF_8); } catch (Exception e) { - throw new YopClientException("SystemError, Decrypt Fail, data:" + data + ", key:" + key + "ex:", e); + throw new YopClientBizException(ExceptionConstants.SDK_CONFIG_RUNTIME_DEPENDENCY, + "SystemError, Decrypt Fail, data:" + data + ", key:" + key + ", cause:" + e.getMessage(), e); } } } diff --git a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSAKeyUtils.java b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSAKeyUtils.java index ad3e54f6..f77e0155 100644 --- a/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSAKeyUtils.java +++ b/yop-java-sdk-crypto-inter/src/main/java/com/yeepay/yop/sdk/inter/utils/RSAKeyUtils.java @@ -1,7 +1,7 @@ package com.yeepay.yop.sdk.inter.utils; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; import com.yeepay.yop.sdk.utils.Encodes; import java.security.KeyFactory; @@ -37,7 +37,8 @@ public static PublicKey string2PublicKey(String pubKey) { return KeyFactory.getInstance(RSA).generatePublic( new X509EncodedKeySpec(Encodes.decodeBase64(pubKey))); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new YopClientException("ConfigProblem, YopPublicKey ParseFail, value:" + pubKey + ", ex:", e); + throw new IllegalConfigFormatException("yop_public_key", + "ConfigProblem, YopPublicKey ParseFail, value:" + pubKey + ", ex:", e); } } @@ -52,7 +53,8 @@ public static PrivateKey string2PrivateKey(String priKey) { return KeyFactory.getInstance(RSA).generatePrivate( new PKCS8EncodedKeySpec(Encodes.decodeBase64(priKey))); } catch (Exception e) { - throw new YopClientException("ConfigProblem, IsvPrivateKey ParseFail, value:" + priKey + ", ex:", e); + throw new IllegalConfigFormatException("isv_private_key", + "ConfigProblem, IsvPrivateKey ParseFail, value:" + priKey + ", ex:", e); } } diff --git a/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriResourceRouteInvokerWrapper.java b/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriResourceRouteInvokerWrapper.java index 971154b2..4d7e65b2 100644 --- a/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriResourceRouteInvokerWrapper.java +++ b/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriResourceRouteInvokerWrapper.java @@ -5,7 +5,9 @@ package com.yeepay.yop.sdk.invoke; import com.google.common.collect.Lists; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopTracedException; import com.yeepay.yop.sdk.exception.YopUnknownException; import com.yeepay.yop.sdk.invoke.model.AnalyzedException; import com.yeepay.yop.sdk.invoke.model.RetryContext; @@ -18,6 +20,8 @@ import java.net.URI; import java.util.List; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_CONFIG_PARAM_FORMAT; + /** * title: 基于UriResource路由的调用器实现
* description: 分析&封装异常、路由、重试等功能
@@ -69,7 +73,7 @@ public Output invoke() { currentEx = throwable; // 路由异常,客户端配置问题 if (null == lastServerRoot || null == lastServerRoot.getResource()) { - throw new YopClientException("Config Error, No ServerRoot Found", throwable); + throw new YopClientBizException(SDK_CONFIG_PARAM_FORMAT, "No ServerRoot Found", throwable); } // 客户端异常、业务异常,直接抛给上层 @@ -110,7 +114,10 @@ private RuntimeException handleUnExpectedError(Throwable ex) { if (ex instanceof YopUnknownException) { return (YopUnknownException) ex; } - return new YopUnknownException("UnExpected Error, ", ex); + if (ex instanceof YopTracedException) { + throw (RuntimeException) ex; + } + return new YopUnknownException("UnExpectedError, cause:" + ex.getMessage(), ex, ""); } @Override diff --git a/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriRouteInvokerWrapper.java b/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriRouteInvokerWrapper.java index 21c5ae3d..4af6fc39 100644 --- a/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriRouteInvokerWrapper.java +++ b/yop-java-sdk-invoke-base/src/main/java/com/yeepay/yop/sdk/invoke/UriRouteInvokerWrapper.java @@ -5,7 +5,9 @@ package com.yeepay.yop.sdk.invoke; import com.google.common.collect.Lists; +import com.yeepay.yop.sdk.exception.YopClientBizException; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.YopTracedException; import com.yeepay.yop.sdk.exception.YopUnknownException; import com.yeepay.yop.sdk.invoke.model.AnalyzedException; import com.yeepay.yop.sdk.invoke.model.RetryContext; @@ -16,6 +18,8 @@ import java.net.URI; import java.util.List; +import static com.yeepay.yop.sdk.constants.ExceptionConstants.SDK_INVOKE_UNEXPECTED_EXCEPTION; + /** * title: 基于uri路由的调用器实现
* description: 分析&封装异常、路由、重试等功能
@@ -70,6 +74,9 @@ public Output invoke() { // 其他异常,须具体分析 final Exception analyzedEx = invoker.getLastException(); + if (null == analyzedEx) { + handleUnExpectedError(throwable); + } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Fail ServerRoot, {}, exDetail:{}, elapsed:{}", invoker.getUri(), analyzedEx.getExDetail(), System.currentTimeMillis() - start); @@ -93,7 +100,7 @@ public Output invoke() { } // 非预期的客户端异常 - throw new YopClientException("Client Error, ex:", throwable); + throw new YopClientBizException(SDK_INVOKE_UNEXPECTED_EXCEPTION, "Client Error", throwable); } } @@ -111,7 +118,10 @@ private void handleUnExpectedError(Throwable ex) { if (ex instanceof YopUnknownException) { throw (YopUnknownException) ex; } - throw new YopUnknownException("UnExpected Error, ", ex); + if (ex instanceof YopTracedException) { + throw (RuntimeException) ex; + } + throw new YopUnknownException("UnExpectedError, cause:" + ex.getMessage(), ex, ""); } @Override diff --git a/yop-java-sdk-okhttp/src/main/java/com/yeepay/yop/sdk/http/impl/ok/YopHttpClient.java b/yop-java-sdk-okhttp/src/main/java/com/yeepay/yop/sdk/http/impl/ok/YopHttpClient.java index f47c6356..b2efd124 100644 --- a/yop-java-sdk-okhttp/src/main/java/com/yeepay/yop/sdk/http/impl/ok/YopHttpClient.java +++ b/yop-java-sdk-okhttp/src/main/java/com/yeepay/yop/sdk/http/impl/ok/YopHttpClient.java @@ -7,6 +7,7 @@ import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.client.ClientConfiguration; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.http.AbstractYopHttpClient; import com.yeepay.yop.sdk.http.Headers; import com.yeepay.yop.sdk.http.YopHttpResponse; @@ -182,7 +183,7 @@ private okhttp3.Request buildHttpRequest(Request + + org.springframework + spring-context + test + junit junit @@ -44,6 +49,12 @@ jmh-generator-annprocess test + + cn.hutool + hutool-all + 5.1.5 + test + \ No newline at end of file diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/config/provider/db/YopDbCredentialsProvider.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/config/provider/db/YopDbCredentialsProvider.java new file mode 100644 index 00000000..b1086d89 --- /dev/null +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/config/provider/db/YopDbCredentialsProvider.java @@ -0,0 +1,99 @@ +/* + * Copyright: Copyright (c)2011 + * Company: 易宝支付(YeePay) + */ + +package com.yeepay.yop.sdk.config.provider.db; + +import com.yeepay.yop.sdk.auth.credentials.YopCredentials; +import com.yeepay.yop.sdk.auth.credentials.provider.YopCredentialsProviderRegistry; +import com.yeepay.yop.sdk.base.auth.credentials.provider.YopBaseCredentialsProvider; +import com.yeepay.yop.sdk.base.config.YopAppConfig; +import com.yeepay.yop.sdk.config.enums.CertStoreType; +import com.yeepay.yop.sdk.config.provider.file.YopCertConfig; +import com.yeepay.yop.sdk.security.CertTypeEnum; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.*; + +/** + * title: 示例:从数据库加载密钥配置
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2024/12/12 + */ +@Component// 该注解依赖spring包, Maven坐标: org.springframework:spring-context:xxx +public class YopDbCredentialsProvider extends YopBaseCredentialsProvider { + + // 1、表结构简单设计 + // |-------id--------|appKey-------|-----certType-----|-----certValue-----| + // | 1 |app_001|RSA2048|xxxxxxxxxx| + // | 2 |app_002|SM2|xxxxxxxxxx| + + // 2、此处模拟数据库存储 + Map mockDbStore = new HashMap<>(); + { + mockDbStore.put("app_001", new String[]{"1", "app_001", "RSA2048", "app_001的密钥串xxxxxx"}); + mockDbStore.put("app_002", new String[]{"2", "app_002", "SM2", "app_002的密钥串xxxxxx"}); + } + + // 3、借助spring 方式, 在provider构造好后,保证系统启动时注册该自定义provider为单例 + @PostConstruct + public void init() { + YopCredentialsProviderRegistry.registerProvider(this); + } + + // 4、指定默认的appKey,当请求时没指定appKey时,会使用该appKey + @Override + public String getDefaultAppKey() { + // 此处简单硬编码,可以根据自身情况指定 + return "app_001"; + } + + // 5、模拟从数据库查询 + private YopCertConfig mockFindCertConfigByAppKey(String appKey) { + YopCertConfig yopCertConfig = new YopCertConfig(); + String[] dbRow = mockDbStore.get(appKey); + yopCertConfig.setStoreType(CertStoreType.STRING); + yopCertConfig.setCertType(CertTypeEnum.parse(dbRow[2])); + yopCertConfig.setValue(dbRow[3]); + return yopCertConfig; + } + + // 6、实现根据appKey和certType,构造调用身份 + @Override + public YopCredentials getCredentials(String appKey, String credentialType) { + // 兼容默认appKey的场景 + appKey = useDefaultIfBlank(appKey); + + // 构造调用身份 + YopAppConfig yopAppConfig = new YopAppConfig(); + yopAppConfig.setAppKey(appKey); + + // 从数据库加载密钥配置 + YopCertConfig yopCertConfig = mockFindCertConfigByAppKey(appKey); + + // 装载调用身份 + List isvPrivateKeys = new LinkedList<>(); + isvPrivateKeys.add(yopCertConfig); + yopAppConfig.setIsvPrivateKey(isvPrivateKeys); + return buildCredentials(yopAppConfig, credentialType); + } + + + // 7、实现支持的密钥类型,此处根据实际情况返回即可 + @Override + public List getSupportCertTypes(String appKey) { + Set result = new HashSet<>(); + // 从数据库加载支持的密钥类型列表 + for (Map.Entry appCert : mockDbStore.entrySet()) { + result.add(CertTypeEnum.parse(appCert.getValue()[2])); + } + return new ArrayList<>(result); + } +} diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/doc/ExampleTest.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/doc/ExampleTest.java index a3fcde0a..6b0fc034 100644 --- a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/doc/ExampleTest.java +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/doc/ExampleTest.java @@ -17,10 +17,14 @@ import com.yeepay.yop.sdk.service.common.request.YopRequest; import com.yeepay.yop.sdk.service.common.response.YopResponse; import com.yeepay.yop.sdk.service.common.response.YosUploadResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.Ignore; import org.junit.Test; import java.security.PrivateKey; +import java.util.Arrays; +import java.util.stream.Collectors; /** * title:
@@ -84,13 +88,26 @@ appKey, new PKICredentialsItem(getPrivateKey( "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg/WsUu5NQDTDJjjaXWLlNfBNZhamXAqCLcyLPSHDSD4qgCgYIKoEcz1UBgi2hRANCAAQUC8TdvSHnCXGlQzm62w+sqHK8wt/ZDXmuhyU4qOEJ8jRMiTzQWoX8BC0fB7ggzWIobHrJouBgnEm3AxVhShpZ", CertTypeEnum.SM2) , CertTypeEnum.SM2))); - request.addParameter("parentMerchantNo", "1234321"); - request.addParameter("orderId", "1234321"); - request.addParameter("orderAmount", "100.05"); - request.addParameter("notifyUrl", "https://xxx.com/notify"); + try { + request.addParameter("parentMerchantNo", "1234321"); + request.addParameter("orderId", "1234321"); + request.addParameter("orderAmount", "100.05"); + request.addParameter("notifyUrl", "https://xxx.com/notify"); + } catch (Exception e) { + throw new RuntimeException(e); + } assertTheRequest(yopClient, request); } + @Test + public void testCaused() { + try { + throw new RuntimeException("1", new RuntimeException("2", new RuntimeException("3"))); + } catch (RuntimeException e) { + System.out.println(Arrays.stream(ExceptionUtils.getThrowables(e)).map(t -> t.getClass().getCanonicalName() + ":" + t.getMessage()).collect(Collectors.joining(","))); + } + } + @Test public void singleUpload() { System.setProperty("yop.sdk.http", "true");//2021-12-08T11:59:16Z,d48782ac-93c1-466e-b417-f7a71e4965f0 diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/auth/credentials/provider/MockEncryptorCredentialsProvider.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/auth/credentials/provider/MockEncryptorCredentialsProvider.java index 211580a6..c4fad049 100644 --- a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/auth/credentials/provider/MockEncryptorCredentialsProvider.java +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/auth/credentials/provider/MockEncryptorCredentialsProvider.java @@ -17,6 +17,8 @@ import com.yeepay.yop.sdk.config.provider.file.YopFileSdkConfig; import com.yeepay.yop.sdk.base.config.provider.file.YopFileSdkConfigProvider; import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.exception.config.IllegalConfigFormatException; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.utils.Encodes; import org.apache.commons.collections4.CollectionUtils; @@ -50,7 +52,8 @@ protected final YopCredentials buildCredentials(YopAppConfig appConfig, Strin CertTypeEnum certType; if (null == appConfig || StringUtils.isEmpty(credentialType) || (null == (certType = CertTypeEnum.parse(credentialType)))) { - throw new YopClientException("ConfigProblem, IsvPrivateCert NotFound, certType:" + credentialType + ", config:" + appConfig); + throw new IllegalConfigFormatException("isv_private_key", + "ConfigProblem, IsvPrivateCert NotFound, certType:" + credentialType + ", config:" + appConfig); } switch (certType) { case RSA2048: @@ -58,7 +61,8 @@ protected final YopCredentials buildCredentials(YopAppConfig appConfig, Strin MockEncryptorCredentialsItem credentialsItem = new MockEncryptorCredentialsItem(Encodes.encodeKey(appConfig.loadPrivateKey(certType)), certType); return new MockEncryptorCredentials(appConfig.getAppKey(), credentialsItem); default: - throw new YopClientException("ConfigProblem, IsvPrivateCert Type NotSupport, certType:" + certType + ", config:" + appConfig); + throw new IllegalParamFormatException("credentialType", + "ConfigProblem, IsvPrivateCert Type NotSupport, certType:" + certType + ", config:" + appConfig); } } diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/signer/process/MockEncryptorSignProcessor.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/signer/process/MockEncryptorSignProcessor.java index 797d6a46..055f2b4c 100644 --- a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/signer/process/MockEncryptorSignProcessor.java +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/encryptor/signer/process/MockEncryptorSignProcessor.java @@ -4,15 +4,15 @@ */ package com.yeepay.yop.sdk.encryptor.signer.process; -import com.yeepay.yop.sdk.encryptor.auth.credentials.MockEncryptorCredentialsItem; import com.google.common.collect.ImmutableMap; import com.yeepay.yop.sdk.auth.SignOptions; import com.yeepay.yop.sdk.auth.credentials.CredentialsItem; import com.yeepay.yop.sdk.auth.credentials.PKICredentialsItem; -import com.yeepay.yop.sdk.inter.auth.signer.process.YopRsaSignProcessor; import com.yeepay.yop.sdk.auth.signer.process.YopSignProcessor; +import com.yeepay.yop.sdk.encryptor.auth.credentials.MockEncryptorCredentialsItem; +import com.yeepay.yop.sdk.exception.param.IllegalParamFormatException; import com.yeepay.yop.sdk.gm.auth.signer.process.YopSm2SignProcessor; -import com.yeepay.yop.sdk.exception.YopClientException; +import com.yeepay.yop.sdk.inter.auth.signer.process.YopRsaSignProcessor; import com.yeepay.yop.sdk.security.CertTypeEnum; import com.yeepay.yop.sdk.security.DigestAlgEnum; @@ -45,7 +45,7 @@ public String doSign(String content, CredentialsItem credentialsItem, SignOption if (certType == CertTypeEnum.RSA2048) { return "mock sign, to be impl by yourself"; } - throw new YopClientException("UnSupported cert type:" + certType); + throw new IllegalParamFormatException("CredentialsItem", "UnSupported cert type:" + certType + ",credentialsItem:" + credentialsItem); } @Override @@ -53,7 +53,7 @@ public boolean verify(String content, String signature, CredentialsItem credenti if (credentialsItem instanceof PKICredentialsItem) { return doVerify(content, signature, credentialsItem); } - throw new YopClientException("UnSupported credentialsItem type:" + credentialsItem.getClass().getSimpleName()); + throw new IllegalParamFormatException("CredentialsItem", "UnSupported credentialsItem type:" + credentialsItem.getClass().getSimpleName()); } @Override diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopRsaEncryptExample.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopRsaEncryptExample.java index 3650f012..12b0ec8f 100644 --- a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopRsaEncryptExample.java +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopRsaEncryptExample.java @@ -4,45 +4,50 @@ */ package com.yeepay.yop.sdk.example; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Joiner; -import com.google.common.collect.*; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; import org.apache.http.Header; import org.apache.http.HttpEntity; -import org.apache.http.HttpStatus; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.methods.RequestBuilder; -import org.apache.http.client.utils.HttpClientUtils; -import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; import java.io.*; +import java.net.URL; import java.net.URLEncoder; import java.nio.file.Files; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.text.SimpleDateFormat; import java.util.*; -import static com.google.common.base.Preconditions.checkNotNull; - /** * title: rsa 加密示例
* description: 描述
@@ -55,7 +60,13 @@ */ public class YopRsaEncryptExample { + private static final Logger LOGGER = LoggerFactory.getLogger(YopRsaEncryptExample.class); + + //须引入第三方包 org.apache.httpcomponents:httpclient private static final CloseableHttpClient httpClient; + //须引入第三方包 com.fasterxml.jackson.core:jackson-databind + private static final ObjectMapper OBJECT_MAPPER; + static { // 自定义连接池大小、默认超时时间 httpClient = HttpClients.custom() @@ -63,11 +74,23 @@ public class YopRsaEncryptExample { .setMaxConnTotal(200) .setDefaultRequestConfig( RequestConfig.custom() - .setConnectionRequestTimeout(3000) - .setConnectTimeout(3000) - .setSocketTimeout(30000) - .build()) + .setConnectionRequestTimeout(3000) + .setConnectTimeout(3000) + .setSocketTimeout(30000) + .build()) .build(); + OBJECT_MAPPER = new ObjectMapper(); + OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_COMMENTS, true); + OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + OBJECT_MAPPER.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); + OBJECT_MAPPER.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + // 小数都用BigDecimal,默认的是Double + OBJECT_MAPPER.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS); + OBJECT_MAPPER.setNodeFactory(JsonNodeFactory.withExactBigDecimals(true)); } private static final String APP_KEY = "sandbox_rsa_10080041523"; @@ -89,206 +112,6 @@ public class YopRsaEncryptExample { private static final String RSA = "RSA"; private static final String DEFAULT_DIGEST_ALG = "SHA-256"; private static final String DEFAULT_SIGN_ALG = "SHA256withRSA"; - private static final Joiner QUERY_STRING_JOINER = Joiner.on('&'); - - public static void main(String[] args) throws Exception { - // 加密会话密钥,可每笔调用都生成,也可以多笔公用一个,建议定时更换 - String encryptKey = encodeUrlSafeBase64(generateRandomKey()); - -// // get请求,form参数 - getFormExample(YopRequestMethod.GET, "/rest/v1.0/test/errorcode2", - YopRequestContentType.FORM_URL_ENCODE, encryptKey); - - // post请求,form参数 - postFormExample(YopRequestMethod.POST, "/rest/v1.0/test/old-api-mgr/find-api-by-uri", - YopRequestContentType.FORM_URL_ENCODE, encryptKey); - - // post请求,json参数 - postJsonExample(YopRequestMethod.POST, "/rest/v1.0/test-wdc/test/http-json/test", - YopRequestContentType.JSON, encryptKey); - - // get请求,form参数,下载文件 - downloadExample(YopRequestMethod.GET, "/yos/v1.0/test/test/ceph-download", - YopRequestContentType.FORM_URL_ENCODE, encryptKey); - } - - private static void getFormExample(YopRequestMethod requestMethod, String requestUri, - YopRequestContentType requestContentType, - String encryptKey) throws Exception { - // 请求参数 - String paramKey = "errorCode"; - String paramPlainValue = "000027", - paramEncryptValue = encryptParam(encryptKey, paramPlainValue); - Multimap formParams = ArrayListMultimap.create(); - // 参数不加密 -// formParams.put(paramKey, paramPlainValue); - - // 参数加密 - formParams.put(paramKey, paramEncryptValue); - Set encryptHeaders = Collections.emptySet(); - Set encryptParams = Sets.newHashSet(); - encryptParams.add(paramKey); - - // 请求头 - Map headers = buildHeaders(requestMethod, requestUri, formParams, - requestContentType, "", encryptKey, encryptHeaders, encryptParams); - - // 构造http请求 - HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, - requestMethod, headers, formParams, null); - - // 发起http调用 - CloseableHttpResponse response = null; - try { - response = httpClient.execute(request); - handleResponse(YopRequestType.WEB, response, encryptKey); - } finally { - HttpClientUtils.closeQuietly(response); - } - } - - private static Map buildHeaders(YopRequestMethod httpMethod, String apiUri, - Multimap params, - YopRequestContentType contentType, String content, - String encryptKey, Set encryptHeaders, Set encryptParams) throws Exception { - Map headers = new HashMap<>(); - headers.put(YOP_SDK_LANGS, "java"); - headers.put(YOP_SDK_VERSION, "4.3.0"); - headers.put(YOP_APPKEY, APP_KEY); - headers.put(YOP_REQUEST_ID, UUID.randomUUID().toString()); - - // 加密头:请求参数不加密的情况, 也需设置加密头,用于响应结果加密 - headers.put(YOP_ENCRYPT, buildEncryptHeader(encryptHeaders, encryptParams, encryptKey)); - - // 摘要头 - headers.put(YOP_CONTENT_SHA256, calculateContentHash(httpMethod, contentType, params, content)); - - // 签名头 - headers.put(AUTHORIZATION, signRequest(httpMethod, apiUri, headers, params)); - return headers; - } - - private static void postFormExample(YopRequestMethod requestMethod, String requestUri, - YopRequestContentType requestContentType, - String encryptKey) throws Exception { - - // 请求参数 - String paramKey = "apiUri"; - String paramPlainValue = "/rest/v1.0/test/product/find/lookatdoc", - paramEncryptValue = encryptParam(encryptKey, paramPlainValue); - Multimap params = ArrayListMultimap.create(); - // 参数不加密 -// params.put(paramKey, paramPlainValue); - - // 参数加密 - params.put(paramKey, paramEncryptValue); - Set encryptHeaders = Collections.emptySet(); - Set encryptParams = Sets.newHashSet(); - encryptParams.add(paramKey); - - // 请求头 - Map headers = buildHeaders(requestMethod, requestUri, params, - requestContentType, "", encryptKey, encryptHeaders, encryptParams); - - // 构造http请求 - HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, - requestMethod, headers, params, null); - - // 发起http调用 - CloseableHttpResponse response = null; - try { - response = httpClient.execute(request); - handleResponse(YopRequestType.WEB, response, encryptKey); - } finally { - HttpClientUtils.closeQuietly(response); - } - } - - private static void postJsonExample(YopRequestMethod requestMethod, String requestUri, - YopRequestContentType requestContentType, - String encryptKey) throws Exception { - // 请求参数 - String plainJsonContent = ("{\n" + - " \"arg1\" : {\n" + - " \"appId\" : \"app_1111111111\",\n" + - " \"customerNo\" : \"333333333\"\n" + - " },\n" + - " \"arg0\" : {\n" + - " \"string\" : \"hello\",\n" + - " \"array\" : [ \"test\" ]\n" + - " }\n" + - "}"), - encryptJsonContent = encryptParam(encryptKey, plainJsonContent); - - String finalJsonContent - // 不加密 -// = plainJsonContent; - // 加密 - = encryptJsonContent; - Set encryptHeaders = Collections.emptySet(); - Set encryptParams = Sets.newHashSet(); - encryptParams.add("$");// 目前json推荐整体加密 - - // 请求头 - // 目前不允许json接口带有form参数,置空即可 - final ArrayListMultimap params = ArrayListMultimap.create(); - Map headers = buildHeaders(requestMethod, requestUri, params, - requestContentType, finalJsonContent, encryptKey, encryptHeaders, encryptParams); - - // 构造http请求 - HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, - requestMethod, headers, params, finalJsonContent); - - // 发起http调用 - CloseableHttpResponse response = null; - try { - response = httpClient.execute(request); - handleResponse(YopRequestType.WEB, response, encryptKey); - } finally { - HttpClientUtils.closeQuietly(response); - } - } - - private static void downloadExample(YopRequestMethod requestMethod, String requestUri, - YopRequestContentType requestContentType, - String encryptKey) throws Exception { - // 根据实际情况来,也可能是post方法,请求报文可能是form,也可能是json,可参考其他方式的入参处理 - // 此处仅演示文件响应流的处理 - // 请求参数 - String paramKey = "fileName"; - String paramPlainValue = "wym-test.txt", - paramEncryptValue = encryptParam(encryptKey, paramPlainValue); - Multimap params = ArrayListMultimap.create(); - // 不加密 -// params.put(paramKey, paramPlainValue); - - // 加密 - params.put(paramKey, paramEncryptValue); - Set encryptHeaders = Collections.emptySet(); - Set encryptParams = Sets.newHashSet(); - encryptParams.add(paramKey); - - // 请求头 - Map headers = buildHeaders(requestMethod, requestUri, params, - requestContentType, "", encryptKey, encryptHeaders, encryptParams); - - // 构造http请求 - HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, - requestMethod, headers, params, ""); - - // 发起请求 - CloseableHttpResponse response = null; - try { - response = httpClient.execute(request); - handleResponse(YopRequestType.FILE_DOWNLOAD, response, encryptKey); - } finally { - HttpClientUtils.closeQuietly(response); - } - } - - private static final Set DEFAULT_HEADERS_TO_SIGN = Sets.newHashSet(); - private static final Joiner HEADER_JOINER = Joiner.on('\n'); - private static final Joiner SIGNED_HEADER_STRING_JOINER = Joiner.on(';'); private static final String YOP_SDK_VERSION = "x-yop-sdk-version"; private static final String YOP_SDK_LANGS = "x-yop-sdk-langs"; @@ -296,455 +119,510 @@ private static void downloadExample(YopRequestMethod requestMethod, String reque private static final String YOP_APPKEY = "x-yop-appkey"; private static final String YOP_CONTENT_SHA256 = "x-yop-content-sha256"; private static final String YOP_ENCRYPT = "x-yop-encrypt"; + private static final String YOP_SIGN = "x-yop-sign"; private static final String AUTHORIZATION = "Authorization"; private static final String CONTENT_DISPOSITION = "Content-Disposition"; private static final String CONTENT_TYPE = "Content-Type"; - static { - DEFAULT_HEADERS_TO_SIGN.add(YOP_REQUEST_ID); - DEFAULT_HEADERS_TO_SIGN.add(YOP_APPKEY); - DEFAULT_HEADERS_TO_SIGN.add(YOP_CONTENT_SHA256); - DEFAULT_HEADERS_TO_SIGN.add(YOP_ENCRYPT); - } + public static void main(String[] args) throws Exception { + // get请求,form参数,返回json + getFormExample(); - private static String signRequest(YopRequestMethod requestMethod, String requestUri, - Map headers, Multimap params) throws Exception { - // A.构造认证字符串 - String authString = buildAuthString(); + // 文件下载接口,返回文件流 + downloadExample(); - // B.获取规范请求串 - SortedMap headersToSign = getHeadersToSign(headers, DEFAULT_HEADERS_TO_SIGN); - String canonicalRequest = buildCanonicalRequest(requestMethod, requestUri, params, authString, headersToSign); + // post请求,form参数,返回json + postFormExample(); - // C.计算签名 - String signature = encodeUrlSafeBase64(sign(canonicalRequest.getBytes(DEFAULT_ENCODING))) + "$" + "SHA256"; + // post请求,json参数,返回json + postJsonExample(); - // D.添加认证头 - return buildAuthHeader(authString, headersToSign, signature); + // post请求,文件上传,返回json + uploadExample(); } - private static String buildAuthHeader(String authString, - SortedMap headersToSign, - String signature) { - String signedHeaders = SIGNED_HEADER_STRING_JOINER.join(headersToSign.keySet()); - signedHeaders = signedHeaders.trim().toLowerCase(); - return DEFAULT_AUTH_PREFIX_RSA2048 + " " + authString + "/" + signedHeaders + "/" + signature; + /** + * HTTP GET 请求 + * + * @param serverRoot 请求端点地址 + * @param apiUri 请求接口地址 + * @param params 查询参数Map + * @param encryptParams 指定待加密参数名 + * @param yopPublicKey 平台公钥 + * @param isvPrivateKey 商户私钥 + * @return ApiResponse + * @throws Exception + */ + public static ApiResponse get(String serverRoot, + String apiUri, + Map params, + Set encryptParams, + String yopPublicKey, + String isvPrivateKey) throws Exception { + // 请求类型,具体查看枚举注释 + YopRequestType requestType = YopRequestType.WEB; + + // 请求方法 + YopRequestMethod httpMethod = YopRequestMethod.GET; + + // 请求内容类型 + YopRequestContentType contentType = YopRequestContentType.FORM_URL_ENCODE; + + // GET 请求不支持http-body参数,留空即可 + Map bodyParams = null; + + return request(serverRoot, requestType, httpMethod, apiUri, contentType, params, bodyParams, + encryptParams, yopPublicKey, isvPrivateKey); } - private static byte[] sign(byte[] data) { - try { - Signature signature = Signature.getInstance(DEFAULT_SIGN_ALG); - signature.initSign(string2PrivateKey(ISV_PRIVATE_KEY)); - signature.update(data); - return signature.sign(); - } catch (Exception e) { - throw new RuntimeException("SystemError, Sign Fail, key:" + ISV_PRIVATE_KEY + ", ex:", e); + /** + * HTTP POST 请求 + * + * @param serverRoot 请求端点地址 + * @param apiUri 请求接口地址 + * @param params 业务参数Map + * @param contentType 内容类型(form/json) + * @param encryptParams 指定待加密参数名 + * @param yopPublicKey 平台公钥 + * @param isvPrivateKey 商户私钥 + * @return ApiResponse + * @throws Exception + */ + public static ApiResponse post(String serverRoot, + String apiUri, YopRequestContentType contentType, + Map params, + Set encryptParams, + String yopPublicKey, + String isvPrivateKey) throws Exception { + if (null == params || params.isEmpty()) { + throw new IllegalArgumentException("biz params must not be null or empty"); } - } - private static PrivateKey string2PrivateKey(String priKey) { - try { - return KeyFactory.getInstance(RSA).generatePrivate( - new PKCS8EncodedKeySpec(decodeBase64(priKey))); - } catch (Exception e) { - throw new RuntimeException("ConfigProblem, IsvPrivateKey ParseFail, value:" + priKey + ", ex:", e); + // 纠正内容类型,具体查看枚举注释 + for (Object value : params.values()) { + if (isFileParam(value)) { + return upload(serverRoot, apiUri, params, encryptParams, yopPublicKey, isvPrivateKey); + } } - } - private static byte[] decodeBase64(String input) { - return Base64.decodeBase64(input); + // 请求类型 + YopRequestType requestType = YopRequestType.WEB; + + // 请求方法 + YopRequestMethod httpMethod = YopRequestMethod.POST; + + // http-body参数,留空即可 + Map bodyParams = params; + + return request(serverRoot, requestType, httpMethod, apiUri, contentType, null, bodyParams, + encryptParams, yopPublicKey, isvPrivateKey); } - private static String buildCanonicalRequest(YopRequestMethod httpMethod, String apiUri, Multimap params, - String authString, - SortedMap headersToSign) { - String canonicalQueryString; - if (YopRequestMethod.GET.equals(httpMethod) && null != params) { - canonicalQueryString = getCanonicalQueryString(params, true); - } else { - canonicalQueryString = "";// post from 与json时,均为空,此处先简单处理 + /** + * HTTP POST 请求 + * + * @param serverRoot 请求端点地址 + * @param apiUri 请求接口地址 + * @param params 业务参数Map + * @param encryptParams 指定待加密参数名 + * @param yopPublicKey 平台公钥 + * @param isvPrivateKey 商户私钥 + * @return ApiResponse + * @throws Exception + */ + public static ApiResponse upload(String serverRoot, + String apiUri, + Map params, + Set encryptParams, + String yopPublicKey, + String isvPrivateKey) throws Exception { + if (null == params || params.isEmpty()) { + throw new IllegalArgumentException("biz params must not be null or empty"); } - String canonicalHeaders = getCanonicalHeaders(headersToSign); - String canonicalURI = getCanonicalURIPath(apiUri); - return authString + "\n" - + httpMethod.name() + "\n" - + canonicalURI + "\n" - + canonicalQueryString + "\n" - + canonicalHeaders; + YopRequestContentType contentType = YopRequestContentType.MULTIPART_FORM; + + // 请求类型 + YopRequestType requestType = YopRequestType.WEB; + + // 请求方法 + YopRequestMethod httpMethod = YopRequestMethod.POST; + + // http-body参数,留空即可 + Map bodyParams = params; + + return request(serverRoot, requestType, httpMethod, apiUri, contentType, null, bodyParams, + encryptParams, yopPublicKey, isvPrivateKey); } - private static String getCanonicalURIPath(String path) { - if (path == null) { - return "/"; - } else if (path.startsWith("/")) { - return normalizePath(path); + /** + * HTTP 文件下载 请求 + * + * @param serverRoot 请求端点地址 + * @param apiUri 请求接口地址 + * @param httpMethod 请求方法 + * @param contentType 内容类型 + * @param params 参数Map + * @param encryptParams 指定待加密参数名 + * @param yopPublicKey 平台公钥 + * @param isvPrivateKey 商户私钥 + * @return + * @throws Exception + */ + public static ApiResponse download(String serverRoot, + String apiUri, + YopRequestMethod httpMethod, + YopRequestContentType contentType, + Map params, + Set encryptParams, + String yopPublicKey, + String isvPrivateKey) throws Exception { + // 请求类型,具体查看枚举注释 + YopRequestType requestType = YopRequestType.FILE_DOWNLOAD; + + // 请求不支持http-body参数,留空即可 + Map queryParams = null, bodyParams = null; + + if (YopRequestMethod.GET == httpMethod) { + queryParams = params; } else { - return "/" + normalizePath(path); + bodyParams = params; } - } - private static String normalizePath(String path) { - return normalize(path).replace("%2F", "/"); + return request(serverRoot, requestType, httpMethod, apiUri, contentType, queryParams, bodyParams, + encryptParams, yopPublicKey, isvPrivateKey); } - private static SortedMap getHeadersToSign(Map headers, Set headersToSign) { - SortedMap ret = Maps.newTreeMap(); - if (headersToSign != null) { - Set tempSet = Sets.newHashSet(); - for (String header : headersToSign) { - tempSet.add(header.trim().toLowerCase()); - } - headersToSign = tempSet; - } - for (Map.Entry entry : headers.entrySet()) { - String key = entry.getKey(); - if (entry.getValue() != null && !entry.getValue().isEmpty()) { - if ((headersToSign != null && headersToSign.contains(key.toLowerCase()) - && !AUTHORIZATION.equalsIgnoreCase(key))) { - ret.put(key, entry.getValue()); - } - } - } - return ret; - } + private static void downloadExample() throws Exception { + // 接口地址 + String apiUri = "/rest/v1.0/test-wdc/test-param-parse/input-stream-result"; - private static String getCanonicalHeaders(SortedMap headers) { - if (headers.isEmpty()) { - return ""; - } + // TODO 请求方法,根据接口文档,实际情况来选择GET/POST + YopRequestMethod httpMethod = YopRequestMethod.GET; - List headerStrings = Lists.newArrayList(); - for (Map.Entry entry : headers.entrySet()) { - String key = entry.getKey(); - if (key == null) { - continue; - } - String value = entry.getValue(); - if (value == null) { - value = ""; - } - headerStrings.add(normalize(key.trim().toLowerCase()) + ':' + normalize(value.trim())); - } - Collections.sort(headerStrings); + // TODO 请求内容类型,根据接口文档,实际情况来选择form/json + YopRequestContentType contentType = YopRequestContentType.FORM_URL_ENCODE; - return HEADER_JOINER.join(headerStrings); - } + // TODO 构建业务参数,根据接口文档填充接口参数 + Map params = new HashMap<>(); + params.put("strParam", "xxx"); - private static final DateTimeFormatter alternateIso8601DateFormat = - ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC); + // TODO 根据接口需要选择示例1或示例2,如果不涉及敏数据,建议走示例1 + // 示例1、参数不加密 + ApiResponse apiResponse1 = download(SERVER_ROOT, apiUri, httpMethod, contentType, + params, null, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse1); - private static String buildAuthString() { - Date timestamp = new Date(); - return DEFAULT_YOP_PROTOCOL_VERSION + "/" - + APP_KEY + "/" - + alternateIso8601DateFormat.print(new DateTime(timestamp)) + "/" - + "1800"; + // 示例2、参数加密,填充敏感字段名 + Set encryptParams = new HashSet<>(); + encryptParams.add("strParam"); + ApiResponse apiResponse2 = download(SERVER_ROOT, apiUri, httpMethod, contentType, + params, encryptParams, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse2); } - private static String calculateContentHash(String jsonContent) throws Exception { - InputStream contentStream = getContentStream(jsonContent); - return Hex.encodeHexString((digest(contentStream))); - } + private static void getFormExample() throws Exception { + // 接口地址 + String apiUri = "/rest/v1.0/test/errorcode2"; - private static String calculateContentHash(YopRequestMethod requestMethod, YopRequestContentType requestContentType, - Multimap params, String content) throws Exception { - String digestSource; + // TODO 构建业务参数,根据接口文档填充接口参数 + Map params = new HashMap<>(); + params.put("errorCode", "000027"); - if (requestMethod.equals(YopRequestMethod.GET)) { - digestSource = ""; - } else if (requestMethod.equals(YopRequestMethod.POST) - && requestContentType.equals(YopRequestContentType.JSON)) { - digestSource = content; - } else { - digestSource = getCanonicalQueryString(params, true); - } - InputStream contentStream = getContentStream(digestSource); - return Hex.encodeHexString((digest(contentStream))); + // TODO 根据接口需要选择示例1或示例2,如果不涉及敏数据,建议走示例1 + // 示例1、参数不加密 + ApiResponse apiResponse1 = get(SERVER_ROOT, apiUri, params, null, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse1); + + // 示例2、参数加密,填充敏感字段名 + Set encryptParams = new HashSet<>(); + encryptParams.add("errorCode"); + ApiResponse apiResponse2 = get(SERVER_ROOT, apiUri, params, encryptParams, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse2); } - private static ByteArrayInputStream getContentStream(Multimap params) throws Exception { - return getContentStream(getCanonicalQueryString(params, true)); + private static void postFormExample() throws Exception { + // 接口地址 + String apiUri = "/rest/v1.0/frontcashier/agreement/sign/confirm"; + + // 请求内容类型 + YopRequestContentType contentType = YopRequestContentType.FORM_URL_ENCODE; + + // POST 请求http-body参数 + Map params = new HashMap<>(); + params.put("parentMerchant", "10080041523"); + params.put("merchantNo", "10080041523"); + params.put("smsCode", "11111131"); + params.put("merchantFlowId", "111111"); + + // TODO 根据接口需要选择示例1或示例2,如果不涉及敏数据,建议走示例1 + // 示例1、参数不加密 + ApiResponse apiResponse1 = post(SERVER_ROOT, apiUri, contentType, + params, null, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse1); + + // 示例2、参数加密,填充敏感字段名 + Set encryptParams = new HashSet<>(); + encryptParams.add("smsCode"); + ApiResponse apiResponse2 = post(SERVER_ROOT, apiUri, contentType, + params, encryptParams, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse2); } - private static ByteArrayInputStream getContentStream(String paramStr) throws Exception { - byte[] bytes; - if (StringUtils.isEmpty(paramStr)) { - bytes = new byte[0]; - } else { - bytes = paramStr.getBytes(DEFAULT_ENCODING); - } - return new ByteArrayInputStream(bytes); + private static void uploadExample() throws Exception { + // 接口地址 + String apiUri = "/yos/v1.0/sys/merchant/qual/upload"; + + // POST 请求http-body参数 + Map params = new HashMap<>(); + params.put("clientId", APP_KEY); + params.put("merQual", new FileParam(new URL("https://open.yeepay.com/apis/docs/apis/common/ALL.json").openStream(), "abc.json")); + + // TODO 根据接口需要选择示例1或示例2,如果不涉及敏数据,建议走示例1 + // 示例1、参数不加密 + ApiResponse apiResponse1 = upload(SERVER_ROOT, apiUri, + params, null, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse1); + + // 示例2、参数加密,填充敏感字段名 +// Set encryptParams = new HashSet<>(); +// encryptParams.add("clientId"); +// ApiResponse apiResponse2 = upload(SERVER_ROOT, apiUri, +// params, encryptParams, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); +// LOGGER.info("apiResponse:{}", apiResponse2); } - public static String getCanonicalQueryString(Multimap params, boolean forSignature) { - Map> parameters; - if (null == params || (parameters = params.asMap()).isEmpty()) { - return ""; - } + private static void postJsonExample() throws Exception { + // 接口地址 + String apiUri = "/rest/v1.0/test-wdc/test/http-json/test"; + + // 请求内容类型 + YopRequestContentType contentType = YopRequestContentType.JSON; + + // POST-json请求支持http-body参数,此处为嵌套复杂对象示例 + Map params = new HashMap<>(); + Map arg0 = new HashMap<>(); + arg0.put("string","hello world"); + arg0.put("array", Arrays.asList("test")); + params.put("arg0", arg0); + Map arg1 = new HashMap<>(); + arg1.put("appId","app_1111111111"); + arg1.put("customerNo","333333333"); + params.put("arg1", arg1); + + // TODO 根据接口需要选择示例1或示例2,如果不涉及敏数据,建议走示例1 + // 示例1、参数不加密 + ApiResponse apiResponse1 = post(SERVER_ROOT, apiUri, contentType, + params, null, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse1); + + // 示例2、参数加密,填充敏感字段名,json方式时,直接加密整个请求体即可 + Set encryptParams = new HashSet<>(); + encryptParams.add("$");// $代表整体加密(所有参数均加密) + ApiResponse apiResponse2 = post(SERVER_ROOT, apiUri, contentType, + params, encryptParams, YOP_PUBLIC_KEY, ISV_PRIVATE_KEY); + LOGGER.info("apiResponse:{}", apiResponse2); + } - List parameterStrings = Lists.newArrayList(); - for (Map.Entry> entry : parameters.entrySet()) { - if (forSignature && AUTHORIZATION.equalsIgnoreCase(entry.getKey())) { - continue; - } - String key = entry.getKey(); - checkNotNull(key, "parameter key should not be null"); - Collection value = entry.getValue(); - if (value == null) { - if (forSignature) { - parameterStrings.add(normalize(key) + '='); + /** + * 请求响应处理 + * + * @param serverRoot 请求端点地址 + * @param requestType 请求类型 + * @param httpMethod 请求方法 + * @param apiUri 请求接口地址 + * @param contentType 请求内容类型 + * @param queryParams http-query参数 + * @param bodyParams http-body参数 + * @param encryptParams 待加密参数名 + * @param yopPublicKey 平台公钥 + * @param isvPrivateKey 商户私钥 + * @return + * @throws Exception + */ + public static ApiResponse request(String serverRoot, + YopRequestType requestType, YopRequestMethod httpMethod, String apiUri, + YopRequestContentType contentType, Map queryParams, + Map bodyParams, Set encryptParams, + String yopPublicKey, String isvPrivateKey) throws Exception { + // header 不加密 + Set encryptHeaders = Collections.EMPTY_SET; + + // 非法参数过滤 + queryParams = autoFixParams(queryParams); + bodyParams = autoFixParams(bodyParams); + + + // 敏感业务参数加密 + String encryptKey = null; + Map handledQueryParams = queryParams; + Object handledBodyParams = contentType == YopRequestContentType.JSON ? toJsonString(bodyParams) : bodyParams; + Set handledEncryptParams = new HashSet<>(); + if (encryptParams != null) { + encryptKey = encodeBase64(generateRandomKey()); + handledQueryParams = new HashMap<>(); + for (String key : queryParams.keySet()) { + if (encryptParams.contains(key)) { + handledQueryParams.put(key, encryptParam(encryptKey, queryParams.get(key).toString())); + handledEncryptParams.add(key); } else { - parameterStrings.add(normalize(key)); + handledQueryParams.put(key, queryParams.get(key)); } + } + // json请求,整体加密 + if (contentType == YopRequestContentType.JSON) { + handledBodyParams = toJsonString(encryptParam(encryptKey, (String) handledBodyParams)); + handledEncryptParams.clear(); + handledEncryptParams.add("$"); } else { - for (String item : value) { - parameterStrings.add(normalize(key) + '=' + normalize(item)); + Map tmpBodyParams = new HashMap<>(); + for (String key : bodyParams.keySet()) { + Object value = bodyParams.get(key); + if (encryptParams.contains(key)) { + // 仅加密非文件参数 + if (!isFileParam(value)) { + tmpBodyParams.put(key, encryptParam(encryptKey, value.toString())); + handledEncryptParams.add(key); + } else { + tmpBodyParams.put(key, value); + } + } else { + tmpBodyParams.put(key, value); + } } + handledBodyParams = tmpBodyParams; } } - Collections.sort(parameterStrings); - - return QUERY_STRING_JOINER.join(parameterStrings); - } - private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet(); - private static final String[] PERCENT_ENCODED_STRINGS = new String[256]; + // 构建认证信息 + Map headers = buildAuthHeaders(httpMethod, apiUri, contentType, handledQueryParams, handledBodyParams, + encryptKey, encryptHeaders, handledEncryptParams, yopPublicKey, isvPrivateKey); - static { - /* - * StringBuilder pattern = new StringBuilder(); - * - * pattern .append(Pattern.quote("+")) .append("|") .append(Pattern.quote("*")) .append("|") - * .append(Pattern.quote("%7E")) .append("|") .append(Pattern.quote("%2F")); - * - * ENCODED_CHARACTERS_PATTERN = Pattern.compile(pattern.toString()); - */ - for (int i = 'a'; i <= 'z'; i++) { - URI_UNRESERVED_CHARACTERS.set(i); - } - for (int i = 'A'; i <= 'Z'; i++) { - URI_UNRESERVED_CHARACTERS.set(i); - } - for (int i = '0'; i <= '9'; i++) { - URI_UNRESERVED_CHARACTERS.set(i); - } - URI_UNRESERVED_CHARACTERS.set('-'); - URI_UNRESERVED_CHARACTERS.set('.'); - URI_UNRESERVED_CHARACTERS.set('_'); - URI_UNRESERVED_CHARACTERS.set('~'); + // 构建http请求 + HttpUriRequest httpUriRequest = buildHttpRequest(serverRoot + apiUri, httpMethod, contentType, + headers, handledQueryParams, handledBodyParams); - for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) { - PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i); - } - } + // 发送http请求 + HttpResponse response = httpClient.execute(httpUriRequest); - public static String normalize(String value) { - try { - StringBuilder builder = new StringBuilder(); - for (byte b : value.getBytes(DEFAULT_ENCODING)) { - if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) { - builder.append((char) b); - } else { - builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]); - } - } - return builder.toString(); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + // 处理API响应 + ApiResponse apiResponse = handleResponse(requestType, response, yopPublicKey, encryptKey); + return apiResponse; } - private static byte[] digest(InputStream input) { - try { - MessageDigest md = MessageDigest.getInstance(DEFAULT_DIGEST_ALG); - DigestInputStream digestInputStream = new DigestInputStream(input, md); - byte[] buffer = new byte[1024]; - while (digestInputStream.read(buffer) > -1) {} - return digestInputStream.getMessageDigest().digest(); - } catch (Exception e) { - throw new RuntimeException("SystemError, Digest Fail, alg:" + DEFAULT_DIGEST_ALG + ", ex:", e); + private static Map autoFixParams(Map params) { + if (null == params || params.isEmpty()) { + return Collections.emptyMap(); } + Map result = new HashMap<>(); + params.forEach((key, value) -> { + if (null != key && null!= value) { + result.put(key, value); + } + }); + return result; } /** - * 加密协议头(请求): + * 处理API响应 * - * yop-encrypt-v1/{服务端证书序列号}/{密钥类型(必填)}_{分组模式(必填)}_{填充算法(必填)}/{加密密钥值(必填)}/{IV}{;}{附加信息}/{客户端支持的大参数加密模式(必填)}/{encryptHeaders}/{encryptParams} + * @param requestType 请求类型 + * @param response HTTP响应 + * @param publicKey 平台公钥 + * @param encryptKey 加密会话密钥 + * @return API响应对象 */ - public static String buildEncryptHeader(Set encryptHeaders, Set encryptParams, - String aesKey) throws Exception { - - return "yop-encrypt-v1" + SLASH + - EMPTY + SLASH + //rsa 为空 - StringUtils.replace(AES_ENCRYPT_ALG, SLASH, UNDER_LINE) + SLASH + - encodeUrlSafeBase64(encryptKey(decodeBase64(aesKey), string2PublicKey(YOP_PUBLIC_KEY))) + SLASH + - EMPTY + SLASH + - STREAM + SLASH + - StringUtils.join(encryptHeaders, SEMICOLON) + SLASH + - encodeUrlSafeBase64(StringUtils.join(encryptParams, SEMICOLON).getBytes(DEFAULT_ENCODING)); - } - - public static PublicKey string2PublicKey(String pubKey) { - try { - return KeyFactory.getInstance(RSA).generatePublic( - new X509EncodedKeySpec(decodeBase64(pubKey))); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new RuntimeException("ConfigProblem, YopPublicKey ParseFail, value:" + pubKey + ", ex:", e); - } - } - - private static byte[] encryptKey(byte[] data, Key key) { - Cipher cipher; + public static ApiResponse handleResponse(YopRequestType requestType, HttpResponse response, String publicKey, String encryptKey) { try { - cipher = Cipher.getInstance(RSA_ENCRYPT_ALG); - cipher.init(Cipher.ENCRYPT_MODE, key); - return cipher.doFinal(data); - } catch (Exception e) { - throw new RuntimeException("SystemError, Encrypt Fail, key:" + key + "ex:", e); - } - } + // 1. 获取状态码 + int statusCode = response.getStatusLine().getStatusCode(); - private static String encryptParam(String aesKey, String plain) throws Exception { - final Cipher cipher = getInitializedCipher(Cipher.ENCRYPT_MODE, aesKey); - return encodeUrlSafeBase64(cipher.doFinal(plain.getBytes("UTF-8"))); - } + // 2. 获取响应头 + Map headers = new HashMap<>(); + for (Header header : response.getAllHeaders()) { + headers.put(header.getName(), header.getValue()); + } + String signature = headers.get(YOP_SIGN); + String requestId = headers.get(YOP_REQUEST_ID); + String encrypt = headers.get(YOP_ENCRYPT); + + // 3. 获取响应内容 + HttpEntity entity = response.getEntity(); + if (requestType == YopRequestType.FILE_DOWNLOAD) { + // 文件下载响应 + return handleFileDownloadResponse(headers, response, requestId, encrypt, encryptKey); + } + String responseContent = EntityUtils.toString(entity, DEFAULT_ENCODING); - private static String decryptParam(String aesKey, String encryptContent) throws Exception { - final Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, aesKey); - return new String(cipher.doFinal(decodeBase64(encryptContent)), "UTF-8"); - } + // 4. 验证响应签名(如果有) + if (signature != null && !signature.isEmpty()) { + boolean validSignature = verifyResponseSignature(responseContent, signature, publicKey); + if (!validSignature) { + throw new SecurityException("响应签名验证失败"); + } + } - private static InputStream decryptStream(String aesKey, InputStream encryptStream) throws Exception { - final Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, aesKey); - return new CipherInputStream(encryptStream, cipher); - } + // 5. 解析响应内容 + ApiResponse apiResponse = new ApiResponse(); + apiResponse.setStatusCode(statusCode); + apiResponse.setRequestId(requestId); + apiResponse.setRawContent(responseContent); + + // 6. 根据状态码处理 + if (statusCode == 200) { + // 成功响应 + apiResponse.setSuccess(true); + apiResponse.setResult(parseSuccessResponse(responseContent, encrypt, encryptKey)); + } else if (statusCode == 500) { + // 业务错误 + apiResponse.setSuccess(false); + apiResponse.setError(parseErrorResponse(responseContent)); + } else { + // 其他错误 + apiResponse.setSuccess(false); + ApiError error = new ApiError(); + error.setCode("SYSTEM_ERROR"); + error.setMessage("Unexpected status code: " + statusCode); + apiResponse.setError(error); + } - private static Cipher getInitializedCipher(int mode, String aesKey) { - try { - byte[] key = decodeBase64(aesKey); - Cipher cipher = Cipher.getInstance(AES_ENCRYPT_ALG); - Key secretKey = new SecretKeySpec(key, "AES"); - cipher.init(mode, secretKey); - return cipher; - } catch (Throwable throwable) { - throw new RuntimeException("error happened when initialize cipher", throwable); + return apiResponse; + } catch (Exception e) { + throw new RuntimeException("处理响应失败", e); } } - private static String encodeUrlSafeBase64(byte[] input) { - return Base64.encodeBase64URLSafeString(input); - } - - private static byte[] generateRandomKey() throws NoSuchAlgorithmException { - //实例化 - KeyGenerator generator = KeyGenerator.getInstance("AES"); - //设置密钥长度 - generator.init(128); - //生成密钥 - return generator.generateKey().getEncoded(); - } - - protected static HttpUriRequest buildHttpRequest(String requestUrl, YopRequestMethod requestMethod, - Map headers, Multimap params, - String content) throws UnsupportedEncodingException { - RequestBuilder requestBuilder; - if (YopRequestMethod.POST == requestMethod) { - requestBuilder = RequestBuilder.post(); - } else if (YopRequestMethod.GET == requestMethod) { - requestBuilder = RequestBuilder.get(); - } else { - throw new RuntimeException("unsupported http method"); - } - requestBuilder.setUri(requestUrl); - - // header - for (Map.Entry entry : headers.entrySet()) { - requestBuilder.addHeader(entry.getKey(), entry.getValue()); - } - - // body - try { - if (null != params) { - for (Map.Entry> entry : params.asMap().entrySet()) { - String paramKey = entry.getKey(); - for (String value : entry.getValue()) { - requestBuilder.addParameter(paramKey, URLEncoder.encode(value, DEFAULT_ENCODING)); - } - } - } - } catch (IOException ex) { - throw new RuntimeException("unable to create http request.", ex); - } - if (YopRequestMethod.GET.equals(requestMethod)) { - requestBuilder.addHeader(CONTENT_TYPE, "application/x-www-form-urlencoded;charset=UTF-8"); - return requestBuilder.build(); - } - // json 请求 - if (StringUtils.isNotBlank(content)) { - final byte[] contentBytes = content.getBytes(DEFAULT_ENCODING); - requestBuilder.setEntity(new InputStreamEntity(new ByteArrayInputStream(contentBytes), contentBytes.length)); - requestBuilder.addHeader(CONTENT_TYPE, "application/json;charset=UTF-8"); - } - return requestBuilder.build(); - } - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - private static void handleResponse(YopRequestType requestType, - CloseableHttpResponse httpResponse, - String encryptKey) throws Exception { - // header - Map headers = Maps.newHashMap(); - for (Header header : httpResponse.getAllHeaders()) { - headers.put(header.getName(), header.getValue()); - } - String encryptHeader = headers.get("x-yop-encrypt"),// 加密头 - signHeader = headers.get("x-yop-sign"); //签名头 - - boolean isEncryptResponse = StringUtils.isNotBlank(encryptHeader); - - // body - HttpEntity entity = httpResponse.getEntity(); - int statusCode = httpResponse.getStatusLine().getStatusCode(); - if (statusCode / 100 == HttpStatus.SC_OK / 100 && statusCode != HttpStatus.SC_NO_CONTENT) { - //not a error - if (null != entity && entity.getContent() != null) { - if (isJsonResponse(httpResponse)) { - String content = IOUtils.toString(entity.getContent(), DEFAULT_ENCODING); - System.out.println("Request success, response:" + content); - final JsonNode bizData = OBJECT_MAPPER.readTree(content).get("result"); - if (isEncryptResponse) { - System.out.println("Response decrypt success, bizData:" + decryptParam(encryptKey, bizData.asText())); - } - return; - } else if (YopRequestType.FILE_DOWNLOAD.equals(requestType) || isDownloadResponse(httpResponse)) { - InputStream fileContent = entity.getContent(); - if (isEncryptResponse) { - fileContent = decryptStream(encryptKey, fileContent); - System.out.println("Response file decrypt success"); - } - final File file = saveFile(fileContent, headers); - System.out.println("Request success, file downloaded:" + file); - return; - } else { - throw new RuntimeException("Response Error, contentType:" + httpResponse.getEntity().getContentType()); - } + private static ApiResponse handleFileDownloadResponse(Map rspHeaders, HttpResponse response, String requestId, String encrypt, String encryptKey) throws Exception { + int statusCode = response.getStatusLine().getStatusCode(); + ApiResponse apiResponse = new ApiResponse(); + apiResponse.setStatusCode(statusCode); + apiResponse.setRequestId(requestId); + + if (statusCode == 200 && isDownloadResponse(response)) { + // 成功响应 + apiResponse.setSuccess(true); + InputStream rawContent; + if (null != encrypt && null != encryptKey) { + rawContent = decryptStream(encryptKey, response.getEntity().getContent()); } else { - throw new RuntimeException("Response Error, contentType:" + httpResponse.getEntity().getContentType()); + rawContent = response.getEntity().getContent(); } - } else if (statusCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR && statusCode != HttpStatus.SC_BAD_GATEWAY) { - if (entity.getContent() != null) { - String content = IOUtils.toString(entity.getContent(), DEFAULT_ENCODING); - System.out.println("Request Fail, response:" + content); - return; - } else { - throw new RuntimeException("ResponseError, Empty Content, httpStatusCode:" + httpResponse.getStatusLine().getStatusCode()); - } - } else if (statusCode == HttpStatus.SC_BAD_GATEWAY || statusCode == HttpStatus.SC_NOT_FOUND) { - throw new RuntimeException("Response Error, statusCode:" + statusCode); + apiResponse.setRawContent(rawContent); + File tmpFile = saveFile(rawContent, rspHeaders); + apiResponse.setResult(tmpFile); + } else if (statusCode == 500) { + // 业务错误 + apiResponse.setSuccess(false); + apiResponse.setError(parseErrorResponse(EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING))); + } else { + apiResponse.setSuccess(false); + apiResponse.setRawContent(EntityUtils.toString(response.getEntity(), DEFAULT_ENCODING)); + ApiError error = new ApiError(); + error.setCode("SYSTEM_ERROR"); + error.setMessage("Unexpected status code: " + statusCode); + apiResponse.setError(error); } - throw new RuntimeException("ReqParam Illegal, Bad Request, statusCode:" + statusCode); + return apiResponse; } /** @@ -761,22 +639,23 @@ public static File saveFile(InputStream content, Map headers) { final String contentDisposition = headers.get(CONTENT_DISPOSITION); try { String fileName = getFileNameFromHeader(contentDisposition); - if (StringUtils.isNotBlank(fileName)) { + if (isNotBlank(fileName)) { final String[] split = fileName.split("\\."); if (split.length == 2) { - if (StringUtils.length(split[0]) > 3) { + if (strLength(split[0]) > 3) { filePrefix = split[0]; } - if (StringUtils.isNotBlank(split[1])) { + if (isNotBlank(split[1])) { fileSuffix = "." + split[1]; } } } } catch (Exception e) { - System.out.println(("parse Content-Disposition fail, value:" + contentDisposition + ", ex:" + e)); + LOGGER.error("parse Content-Disposition fail, value:{}, ex:", contentDisposition, e); } File tmpFile = File.createTempFile(filePrefix, fileSuffix); - IOUtils.copy(fileContent, Files.newOutputStream(tmpFile.toPath())); + long fileSize = copyStream(fileContent, Files.newOutputStream(tmpFile.toPath())); + LOGGER.info("downloaded file, name:{}, size:{}", tmpFile.getName(), fileSize); return tmpFile; } catch (Throwable ex) { throw new RuntimeException("fail to save file", ex); @@ -785,6 +664,32 @@ public static File saveFile(InputStream content, Map headers) { } } + private static long copyStream(final InputStream input, final OutputStream output) + throws IOException { + long count = 0; + if (input != null) { + byte[] buffer = new byte[8192]; + int n; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + } + return count; + } + + private static boolean isNotBlank(String str) { + return null != str && !str.trim().isEmpty(); + } + + private static int strLength(final CharSequence cs) { + return cs == null ? 0 : cs.length(); + } + + private static String strTrim(final String str) { + return str == null ? null : str.trim(); + } + public static void closeQuietly(Closeable closeable) { try { if (null != closeable) { @@ -796,64 +701,867 @@ public static void closeQuietly(Closeable closeable) { } private static String getFileNameFromHeader(String contentDisposition) { - if (StringUtils.isBlank(contentDisposition)) { + if (!isNotBlank(contentDisposition)) { return null; } final String[] parts = contentDisposition.split( "filename="); if (parts.length == 2) { - return StringUtils.trim(parts[1]); + return strTrim(parts[1]); } return null; } - private static final String CONTENT_TYPE_JSON = "application/json"; - private static final String CONTENT_TYPE_STREAM = "application/octet-stream"; - - private static boolean isJsonResponse(CloseableHttpResponse response) { - return null != response.getEntity() && StringUtils.startsWith(response.getEntity().getContentType().getValue(), CONTENT_TYPE_JSON); + private static boolean isDownloadResponse(HttpResponse response) { + return null != response.getEntity() && null != response.getEntity().getContentType() && + response.getEntity().getContentType().getValue().startsWith(YOP_HTTP_CONTENT_TYPE_STREAM); } - private static boolean isDownloadResponse(CloseableHttpResponse response) { - return null != response.getEntity() && StringUtils.startsWith(response.getEntity().getContentType().getValue(), CONTENT_TYPE_STREAM); - } + private static String decryptIfNecessary(String responseContent, String encryptHeader) { + if (null == encryptHeader) { + return responseContent; + } - public enum YopRequestMethod { - GET, - POST + return null; } - public enum YopRequestType { - WEB, - FILE_DOWNLOAD, - MULTI_FILE_UPLOAD, - } + /** + * 解析错误响应 + * + * 须引入第三方包 com.fasterxml.jackson.core:jackson-databind + * @param content 响应内容 + * @return 错误对象 + */ + private static ApiError parseErrorResponse(String content) throws Exception { + JsonNode rootNode = OBJECT_MAPPER.readTree(content); - private static final String YOP_HTTP_CONTENT_TYPE_JSON = "application/json"; - private static final String YOP_HTTP_CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"; - private static final String YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM = "multipart/form-data"; - private static final String YOP_HTTP_CONTENT_TYPE_STREAM = "application/octet-stream"; - private static final String YOP_HTTP_CONTENT_TYPE_TEXT = "text/plain;charset=UTF-8"; + ApiError error = new ApiError(); - public enum YopRequestContentType { - FORM_URL_ENCODE(YOP_HTTP_CONTENT_TYPE_FORM), - MULTIPART_FORM(YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM), - JSON(YOP_HTTP_CONTENT_TYPE_JSON), - OCTET_STREAM(YOP_HTTP_CONTENT_TYPE_STREAM), - TEXT_PLAIN(YOP_HTTP_CONTENT_TYPE_TEXT); + // 提取错误信息 + if (rootNode.has("code")) { + error.setCode(rootNode.get("code").asText()); + } - private String value; + if (rootNode.has("message")) { + error.setMessage(rootNode.get("message").asText()); + } - YopRequestContentType(String value) { - this.value = value; + if (rootNode.has("subCode")) { + error.setSubCode(rootNode.get("subCode").asText()); } - public String getValue() { - return value; + if (rootNode.has("subMessage")) { + error.setSubMessage(rootNode.get("subMessage").asText()); } - public void setValue(String value) { - this.value = value; + return error; + } + + /** + * 解析成功响应 + * + * 须引入第三方包 com.fasterxml.jackson.core:jackson-databind + * @param content 响应内容 + * @param encryptHeader 响应加密头 + * @param encryptKey 加密会话密钥 + * @return 解析后的结果对象 + */ + private static Object parseSuccessResponse(String content, String encryptHeader, String encryptKey) throws Exception { + JsonNode rootNode = OBJECT_MAPPER.readTree(content); + + // 获取result节点 + JsonNode resultNode = rootNode.get("result"); + if (resultNode != null) { + if (null != encryptHeader && null != encryptKey) { + String decryptBizData = decryptParam(encryptKey, resultNode.asText()); + return OBJECT_MAPPER.readValue(decryptBizData, Object.class); + } + return OBJECT_MAPPER.convertValue(resultNode, Object.class); + } + + // 没有result节点 + throw new RuntimeException("Unexpected响应字符串:" + content); + } + + private static String decryptParam(String aesKey, String encryptContent) throws Exception { + final Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, aesKey); + // 返回的json有格式化,需要去掉后再解码 + return new String(cipher.doFinal(decodeBase64(urlSafeDecode(encryptContent.replaceAll("[\r\n]", EMPTY)))), DEFAULT_ENCODING); + } + + private static InputStream decryptStream(String aesKey, InputStream encryptStream) throws Exception { + final Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, aesKey); + return new CipherInputStream(encryptStream, cipher); + } + + /** + * 验证响应签名 + * + * @param content 响应内容 + * @param signature 响应签名(来自x-yop-sign头) + * @param publicKeyBase64Str 平台公钥(base64格式) + * @return 验证结果 + */ + public static boolean verifyResponseSignature( + String content, + String signature, + String publicKeyBase64Str) { + + try { + // 1. 准备验证数据(移除空白字符) + String normalizedContent = content.replaceAll("[ \t\n]", EMPTY); + byte[] contentBytes = normalizedContent.getBytes(DEFAULT_ENCODING); + + // 2. 解析签名 + String signatureValue = signature; + String algorithm = "SHA256"; + + // 如果签名包含算法标识,分离出来 + if (signature.contains("$")) { + String[] parts = signature.split("\\$"); + signatureValue = parts[0]; + algorithm = parts[1]; + } + + // 3. 解码签名(URL安全Base64 -> 标准Base64 -> 二进制) + String standardBase64 = urlSafeDecode(signatureValue); + byte[] signatureBytes = decodeBase64(standardBase64); + + // 4. 加载公钥 + PublicKey publicKey = string2PublicKey(publicKeyBase64Str); + + // 5. 验证签名 + Signature verifier = Signature.getInstance(DEFAULT_SIGN_ALG); + verifier.initVerify(publicKey); + verifier.update(contentBytes); + + return verifier.verify(signatureBytes); + } catch (Exception e) { + throw new RuntimeException("验证响应签名失败", e); + } + } + + /** + * 构造HttpRequest + * + * @param requestUrl 请求URL + * @param requestMethod 请求方法 + * @param contentType 请求体格式 + * @param headers 请求头 + * @param queryParams 请求URL参数 + * @param bodyParams 请求体参数 + * @return + * @throws UnsupportedEncodingException + */ + public static HttpUriRequest buildHttpRequest(String requestUrl, + YopRequestMethod requestMethod, + YopRequestContentType contentType, + Map headers, + Map queryParams, + Object bodyParams) + throws UnsupportedEncodingException, JsonProcessingException, FileNotFoundException { + // 3. 创建请求 + HttpRequestBase request; + + if (requestMethod == YopRequestMethod.POST) { + // POST请求 + HttpPost post = new HttpPost(requestUrl); + if (null != bodyParams) { + if (contentType == YopRequestContentType.JSON) { + // application/json格式请求体 + StringEntity entity = new StringEntity((String) bodyParams, DEFAULT_ENCODING); + entity.setContentType(contentType.getValue()); + post.setEntity(entity); + post.addHeader(CONTENT_TYPE, contentType.getValue()); + } else if (contentType == YopRequestContentType.MULTIPART_FORM) { + // multipart/form-data格式请求体 + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + for (Map.Entry entry : ((Map) bodyParams).entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof FileParam) { + // 文件包装类参数 + FileParam file = (FileParam) value; + builder.addBinaryBody(name, file.getInputStream(), + ContentType.DEFAULT_BINARY, file.getFilaName()); + } else if (value instanceof File) { + // 文件参数 + File file = (File) value; + builder.addBinaryBody(name, new FileInputStream(file), ContentType.DEFAULT_BINARY, file.getName()); + } else if (value instanceof InputStream) { + // 流文件参数 + builder.addBinaryBody(name, (InputStream) value, ContentType.DEFAULT_BINARY, randomFileName()); + } else { + // 普通字符串参数,注意,此处urlEncode一次 + builder.addTextBody(name, URLEncoder.encode(value.toString(), DEFAULT_ENCODING), ContentType.TEXT_PLAIN); + } + post.setEntity(builder.build()); + } + } else { + // application/x-www-form-urlencoded格式请求体,注意,此处需要urlEncode两次(手动一次+httpclient处理一次) + List formParams = new ArrayList<>(); + for (Map.Entry entry : ((Map) bodyParams).entrySet()) { + formParams.add(new BasicNameValuePair(entry.getKey(), URLEncoder.encode(entry.getValue().toString(), DEFAULT_ENCODING))); + } + post.setEntity(new UrlEncodedFormEntity(formParams, DEFAULT_ENCODING)); + post.addHeader(CONTENT_TYPE, YopRequestContentType.FORM_URL_ENCODE.getValue()); + } + } + request = post; + } else { + // GET请求 + StringBuilder urlWithParams = new StringBuilder(requestUrl); + + // 添加查询参数 + if (queryParams != null && !queryParams.isEmpty()) { + urlWithParams.append("?"); + List paramPairs = new ArrayList<>(); + + for (Map.Entry entry : queryParams.entrySet()) { + String encodedName = URLEncoder.encode(entry.getKey(), DEFAULT_ENCODING); + // 注意,此处需要urlEncode两次 + String encodedValue = URLEncoder.encode(entry.getValue().toString(), DEFAULT_ENCODING); + paramPairs.add(encodedName + "=" + URLEncoder.encode(encodedValue, DEFAULT_ENCODING)); + } + + urlWithParams.append(String.join("&", paramPairs)); + } + + request = new HttpGet(urlWithParams.toString()); + request.addHeader(CONTENT_TYPE, YopRequestContentType.FORM_URL_ENCODE.getValue()); + } + + // 4. 添加请求头 + for (Map.Entry entry : headers.entrySet()) { + request.addHeader(entry.getKey(), entry.getValue()); + } + return request; + } + + private static byte[] generateRandomKey() throws NoSuchAlgorithmException { + //实例化 + KeyGenerator generator = KeyGenerator.getInstance("AES"); + //设置密钥长度 + generator.init(128); + //生成密钥 + return generator.generateKey().getEncoded(); + } + + + private static Map buildAuthHeaders(YopRequestMethod httpMethod, + String apiUri, + YopRequestContentType contentType, + Map queryParams, + Object bodyParams, + String encryptKey, Set encryptHeaders, Set encryptParams, + String yopPublicKey, String isvPrivateKey) throws Exception { + Map headers = new HashMap<>(); + headers.put(YOP_SDK_LANGS, "java"); + headers.put(YOP_SDK_VERSION, "4.0.0"); + headers.put(YOP_APPKEY, APP_KEY); + headers.put(YOP_REQUEST_ID, UUID.randomUUID().toString()); + + // 加密头 + if (null != encryptKey && null != encryptParams) { + headers.put(YOP_ENCRYPT, buildEncryptHeader(encryptHeaders, encryptParams, encryptKey, yopPublicKey)); + } + + // 摘要头 + headers.put(YOP_CONTENT_SHA256, calculateContentSha256(httpMethod, contentType == YopRequestContentType.JSON, bodyParams)); + + // 签名头 + headers.put(AUTHORIZATION, signRequest(httpMethod, apiUri, contentType, headers, queryParams, isvPrivateKey)); + return headers; + } + + private static String signRequest(YopRequestMethod requestMethod, String requestUri, + YopRequestContentType contentType, + Map headers, + Map queryParams, + String isvPrivateKey) throws Exception { + // 1.构造认证字符串 + String authString = buildAuthString(APP_KEY, 1800); + + // 2.获取规范请求方法 + String httpRequestMethod = requestMethod.name(); + + // 3.获取规范请求URI + String canonicalURI = requestUri; + + // 4.获取规范化查询字符串 + String canonicalQueryString = buildCanonicalQueryString(requestMethod, contentType == YopRequestContentType.JSON, + queryParams); + + // 5.获取规范请求头 + String canonicalHeaders = buildCanonicalHeaders(headers); + + // 6.获取规范请求串 + StringBuilder canonicalRequest = new StringBuilder() + .append(authString) + .append("\n") + .append(httpRequestMethod) + .append("\n") + .append(canonicalURI) + .append("\n") + .append(canonicalQueryString) + .append("\n") + .append(canonicalHeaders); + + + // 7.计算签名 + String signature = urlSafeEncode(encodeBase64(sign(canonicalRequest.toString().getBytes(DEFAULT_ENCODING), isvPrivateKey))) + "$" + "SHA256"; + + // 8.构建认证头 + return buildAuthorizationHeader(authString, String.join(SEMICOLON, HEADERS_TO_SIGN), signature); + } + + /** + * 构建认证头 + * + * @param authString 认证字符串 + * @param signedHeaders 已签名的请求头 + * @param signature 签名结果 + * @return 认证头值 + */ + public static String buildAuthorizationHeader(String authString, String signedHeaders, String signature) { + return DEFAULT_AUTH_PREFIX_RSA2048 + " " + + authString + SLASH + + signedHeaders + SLASH + + signature; + } + + // 签名计算时至少包含这三个头 + private static final List HEADERS_TO_SIGN = Arrays.asList( + "x-yop-appkey", + "x-yop-content-sha256", + "x-yop-request-id" + ); + + /** + * 构建规范请求头 + */ + private static String buildCanonicalHeaders(Map headers) { + if (headers == null || headers.isEmpty()) { + return EMPTY; + } + + // 1. 转换头名称为小写并排序 + List canonicalHeaders = new ArrayList<>(); + for (Map.Entry entry : headers.entrySet()) { + String headerName = entry.getKey().toLowerCase().trim(); + String headerValue = entry.getValue().trim(); + if (HEADERS_TO_SIGN.contains(headerName)) { + canonicalHeaders.add(headerName + ":" + headerValue); + } + } + // 2. 排序 + Collections.sort(canonicalHeaders); + + // 3. 用\n连接所有签名头 + return String.join("\n", canonicalHeaders); + } + + /** + * 构建规范查询字符串 + * + * @param queryParams 请求query参数 + * @return 规范查询字符串 + */ + private static String buildCanonicalQueryString(YopRequestMethod httpMethod, + boolean isJsonRequest, + Map queryParams) { + // 针对POST请求,且内容类型为form,即application/x-www-form-urlencoded或者multipart/form-data的请求,返回空字符串 + if (httpMethod == YopRequestMethod.POST && !isJsonRequest) { + return EMPTY; + } + // 参数编码&排序 + return sortAndEncodeParams(queryParams); + } + + public static byte[] sign(byte[] data, String isvPrivateKey) { + try { + Signature signature = Signature.getInstance(DEFAULT_SIGN_ALG); + signature.initSign(string2PrivateKey(isvPrivateKey)); + signature.update(data); + return signature.sign(); + } catch (Exception e) { + throw new RuntimeException("SystemError, Sign Fail, key:" + ISV_PRIVATE_KEY + ", ex:", e); + } + } + + private static PrivateKey string2PrivateKey(String priKey) { + try { + return KeyFactory.getInstance(RSA).generatePrivate( + new PKCS8EncodedKeySpec(decodeBase64(priKey))); + } catch (Exception e) { + throw new RuntimeException("ConfigProblem, IsvPrivateKey ParseFail, value:" + priKey + ", ex:", e); + } + } + + /** + * 构建认证字符串 + * + * @param appKey 应用标识 + * @param expiredSeconds 过期时间(秒) + * @return 认证字符串 + */ + public static String buildAuthString(String appKey, int expiredSeconds) { + // 1. 协议版本 + String protocolVersion = DEFAULT_YOP_PROTOCOL_VERSION + SLASH; + + // 2. 应用标识 + String appKeyPart = appKey + SLASH; + + // 3. ISO8601格式时间戳 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + String timestamp = sdf.format(new Date()) + SLASH; + + // 4. 过期时间(秒) + String expired = String.valueOf(expiredSeconds); + + // 5. 组合认证字符串 + return protocolVersion + appKeyPart + timestamp + expired; + } + + /** + * 计算请求摘要头 + */ + private static String calculateContentSha256(YopRequestMethod httpMethod, + boolean isJsonRequest, + Object bodyParams) { + // 针对GET请求,计算为空字符串 + if (httpMethod == YopRequestMethod.GET) { + return sha256ToHex(EMPTY); + } + + // 针对POST-JSON格式请求,计算整个json请求体 + if (isJsonRequest) { + return sha256ToHex((String) bodyParams); + } + + // 其他格式请求,仅计算非文件类参数 + Map nonFileParams = new HashMap<>(); + for (Map.Entry entry : ((Map) bodyParams).entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value != null && !isFileParam(value)) { + nonFileParams.put(key, value.toString()); + } + } + return sha256ToHex(sortAndEncodeParams(nonFileParams)); + } + + /** + * 参数编码&排序 + */ + private static String sortAndEncodeParams(Map params) { + if (params == null || params.isEmpty()) { + return EMPTY; + } + + // 1. 参数排序(按键名ASCII升序) + List paramNames = new ArrayList<>(params.keySet()); + Collections.sort(paramNames); + + // 2. 构建规范查询字符串 + List queryParts = new ArrayList<>(); + for (String name : paramNames) { + String value = params.get(name).toString(); + // URL编码参数名和值 + String encodedName = urlEncodeForSign(name); + String encodedValue = urlEncodeForSign(value); + queryParts.add(encodedName + "=" + encodedValue); + } + + // 3. 用&连接所有参数对 + return String.join("&", queryParts); + } + + /** + * URL编码(签名版) + */ + private static String urlEncodeForSign(String value) { + try { + return URLEncoder.encode(value, DEFAULT_ENCODING) + .replace("+", "%20") + .replace("*", "%2A") + .replace("%7E", "~"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("URL编码失败", e); + } + } + + private static boolean isFileParam(Object value) { + return null != value && (value instanceof File || value instanceof InputStream || value instanceof FileParam); + } + + /** + * 转json字符串 + */ + private static String toJsonString(Object bodyParams) { + try { + return OBJECT_MAPPER.writeValueAsString(bodyParams); + } catch (Exception e) { + throw new RuntimeException("toJsonString fail", e); + } + } + + /** + * 计算sha256并转16进制字符串 + */ + private static String sha256ToHex(String content) { + try { + MessageDigest md = MessageDigest.getInstance(DEFAULT_DIGEST_ALG); + ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes(DEFAULT_ENCODING)); + DigestInputStream digestInputStream = new DigestInputStream(inputStream, md); + byte[] buffer = new byte[1024]; + while (digestInputStream.read(buffer) > -1) { + } + byte[] digestBytes = digestInputStream.getMessageDigest().digest(); + return encodeHex(digestBytes); + } catch (Exception e) { + throw new RuntimeException("sha256 fail", e); + } + } + + private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static String encodeHex(byte[] data) { + int l = data.length; + char[] out = new char[l << 1]; + int i = 0; + + for (int j = 0; i < l; ++i) { + out[j++] = DIGITS[(240 & data[i]) >>> 4]; + out[j++] = DIGITS[15 & data[i]]; + } + + return new String(out); + } + + /** + * 加密协议头(请求): + *

+ * yop-encrypt-v1/{服务端证书序列号}/{密钥类型(必填)}_{分组模式(必填)}_{填充算法(必填)}/{加密密钥值(必填)}/{IV}{;}{附加信息}/{客户端支持的大参数加密模式(必填)}/{encryptHeaders}/{encryptParams} + */ + public static String buildEncryptHeader(Set encryptHeaders, Set encryptParams, + String encryptKey, String yopPublicKey) throws Exception { + if (null == encryptHeaders) { + encryptHeaders = Collections.emptySet(); + } + if (null == encryptParams) { + encryptParams = Collections.emptySet(); + } + + return "yop-encrypt-v1" + SLASH + + EMPTY + SLASH + //rsa 为空 + AES_ENCRYPT_ALG.replace(SLASH, UNDER_LINE) + SLASH + + urlSafeEncode(encodeBase64(encryptKey(decodeBase64(encryptKey), string2PublicKey(yopPublicKey)))) + SLASH + + EMPTY + SLASH + + STREAM + SLASH + + String.join(SEMICOLON, encryptHeaders) + SLASH + + urlSafeEncode(encodeBase64(String.join(SEMICOLON, encryptParams).getBytes(DEFAULT_ENCODING))); + } + + private static byte[] encryptKey(byte[] data, Key key) { + Cipher cipher; + try { + cipher = Cipher.getInstance(RSA_ENCRYPT_ALG); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(data); + } catch (Exception e) { + throw new RuntimeException("SystemError, Encrypt Fail, key:" + + key + ", cause:" + e.getMessage(), e); + } + } + + private static String encryptParam(String aesKey, String plain) throws Exception { + final Cipher cipher = getInitializedCipher(Cipher.ENCRYPT_MODE, aesKey); + return urlSafeEncode(encodeBase64(cipher.doFinal(plain.getBytes(DEFAULT_ENCODING)))); + } + + private static Cipher getInitializedCipher(int mode, String aesKey) { + try { + byte[] key = decodeBase64(aesKey); + Cipher cipher = Cipher.getInstance(AES_ENCRYPT_ALG); + Key secretKey = new SecretKeySpec(key, "AES"); + cipher.init(mode, secretKey); + return cipher; + } catch (Throwable throwable) { + throw new RuntimeException("error happened when initialize cipher", throwable); + } + } + + public static PublicKey string2PublicKey(String pubKey) { + try { + return KeyFactory.getInstance(RSA).generatePublic( + new X509EncodedKeySpec(decodeBase64(pubKey))); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException("ConfigProblem, YopPublicKey ParseFail, value:" + pubKey + ", ex:", e); + } + } + + /** + * Base64 URL安全替换 + * 将标准Base64中的'+'替换为'-','/'替换为'_' + * + * @param unSafeBase64Str 标准Base64编码字符串 + * @return URL安全的Base64编码字符串 + */ + public static String urlSafeEncode(String unSafeBase64Str) { + if (unSafeBase64Str == null) { + return null; + } + return unSafeBase64Str.replace('+', '-') + .replace('/', '_'); + } + + /** + * Base64 URL解码转换 + * 将URL安全Base64中的'-'替换回'+','_'替换回'/' + * + * @param safeBase64Str URL安全的Base64编码字符串 + * @return 标准Base64编码字符串 + */ + public static String urlSafeDecode(String safeBase64Str) { + if (safeBase64Str == null) { + return null; + } + return safeBase64Str.replace('-', '+') + .replace('_', '/'); + } + + /** + * Base64 解码(非url安全) + * + * @param input + * @return + */ + public static byte[] decodeBase64(String input) { + return Base64.getDecoder().decode(input); + } + + /** + * Base64 编码(非url安全) + * + * @param input + * @return + */ + public static String encodeBase64(byte[] input) { + return Base64.getEncoder().encodeToString(input); + } + + public enum YopRequestMethod { + GET, + POST + } + + public enum YopRequestType { + /** + * 普通请求,json返回 + */ + WEB, + /** + * 文件下载请求,返回文件流 + */ + FILE_DOWNLOAD, + + /** + * 文件上传请求,返回json + */ + MULTI_FILE_UPLOAD, + } + + private static final String YOP_HTTP_CONTENT_TYPE_JSON = "application/json;charset=UTF-8"; + private static final String YOP_HTTP_CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=UTF-8"; + private static final String YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM = "multipart/form-data"; + private static final String YOP_HTTP_CONTENT_TYPE_STREAM = "application/octet-stream"; + private static final String YOP_HTTP_CONTENT_TYPE_TEXT = "text/plain;charset=UTF-8"; + + public enum YopRequestContentType { + FORM_URL_ENCODE(YOP_HTTP_CONTENT_TYPE_FORM), + MULTIPART_FORM(YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM), + JSON(YOP_HTTP_CONTENT_TYPE_JSON), + OCTET_STREAM(YOP_HTTP_CONTENT_TYPE_STREAM), + TEXT_PLAIN(YOP_HTTP_CONTENT_TYPE_TEXT); + + private String value; + + YopRequestContentType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + private static String randomFileName() { + return UUID.randomUUID() + ".bin"; + } + + public static class FileParam implements Serializable { + private static final long serialVersionUID = -1L; + + // 文件流 + private InputStream inputStream; + + // 文件名 + private String filaName = randomFileName(); + + public FileParam(InputStream inputStream) { + this.inputStream = inputStream; + } + + // 本地文件 + public FileParam(File file) throws FileNotFoundException { + this.inputStream = new FileInputStream(file); + this.filaName = file.getName(); + } + + // 文件流 + public FileParam(InputStream inputStream, String filaName) { + this.inputStream = inputStream; + this.filaName = filaName; + } + + // 远端文件 + public FileParam(URL url, String filaName) throws IOException { + this.inputStream = url.openStream(); + this.filaName = filaName; + } + + public InputStream getInputStream() { + return inputStream; + } + + public String getFilaName() { + return filaName; + } + } + + public static class ApiResponse implements Serializable { + private static final long serialVersionUID = -1L; + + private int statusCode; + private String requestId; + private Object rawContent; + private boolean success; + private Object result; + private ApiError error; + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + public Object getRawContent() { + return rawContent; + } + + public void setRawContent(Object rawContent) { + this.rawContent = rawContent; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + + public ApiError getError() { + return error; + } + + public void setError(ApiError error) { + this.error = error; + } + + @Override + public String toString() { + return "ApiResponse{" + + "statusCode=" + statusCode + + ", requestId='" + requestId + '\'' + + ", rawContent='" + rawContent + '\'' + + ", success=" + success + + ", result=" + result + + ", error=" + error + + '}'; + } + } + + public static class ApiError implements Serializable { + private static final long serialVersionUID = -1L; + private String code; + private String message; + private String subCode; + private String subMessage; + + public ApiError() { + } + + public ApiError(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getSubCode() { + return subCode; + } + + public void setSubCode(String subCode) { + this.subCode = subCode; + } + + public String getSubMessage() { + return subMessage; + } + + public void setSubMessage(String subMessage) { + this.subMessage = subMessage; } } } diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSM2EncryptExample.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSM2EncryptExample.java new file mode 100644 index 00000000..8f625740 --- /dev/null +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSM2EncryptExample.java @@ -0,0 +1,1233 @@ +/* + * Copyright: Copyright (c)2014 + * Company: 易宝支付(YeePay) + */ +package com.yeepay.yop.sdk.example; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Joiner; +import com.google.common.collect.*; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.client.utils.HttpClientUtils; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.bouncycastle.asn1.*; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.signers.SM2Signer; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.ISODateTimeFormat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.*; +import java.math.BigInteger; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * title: SM2 加密示例
+ * description: 描述
+ * Copyright: Copyright (c)2014
+ * Company: 易宝支付(YeePay)
+ * + * @author wdc + * @version 1.0.0 + * @since 2023/9/27 + */ +public class YopSM2EncryptExample { + + private static final Logger LOGGER = LoggerFactory.getLogger(YopSM2EncryptExample.class); + static { + try { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + LOGGER.debug("BouncyCastleProvider added"); + } catch (Exception e) { + LOGGER.warn("error when add BouncyCastleProvider", e); + } + } + + private static final CloseableHttpClient httpClient; + static { + // 自定义连接池大小、默认超时时间 + httpClient = HttpClients.custom() + .setMaxConnPerRoute(100) + .setMaxConnTotal(200) + .setDefaultRequestConfig( + RequestConfig.custom() + .setConnectionRequestTimeout(3000) + .setConnectTimeout(3000) + .setSocketTimeout(30000) + .build()) + .build(); + } + + private static final String APP_KEY = "sandbox_sm_10080041523"; + private static final BCECPrivateKey ISV_PRIVATE_KEY = string2PrivateKey("MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgr0mQ3/jjQOczWI6bnJFdqF4D/DFYHaqXftqXU/jGKpCgCgYIKoEcz1UBgi2hRANCAAQPpkZNnOnXTCXOIHJbfR+i6ea1QkM8HxkdO8KSWK8IgltHZxr5xlxiqR8inOREmmrxUQQagOH5i3oELWgXZz8G"); + private static final String YOP_PUBLIC_KEY_SERIAL_NO = "4059376239"; + // 注意:此处为测试环境国密证书,生产环境请联系技术支持获取 + private static final BCECPublicKey YOP_PUBLIC_KEY = string2PublicKey("MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEStsxeNDxhJzM61Uy0rCmnW9Zs4Ze7oIKX27dgFTB7FsTsiCEzvxD7OTCKd7F17Xa1vpJ07C+2+H2OOFBSyadZA=="); + private static final Map YOP_PUBLIC_KEY_MAP = Collections.singletonMap(YOP_PUBLIC_KEY_SERIAL_NO, YOP_PUBLIC_KEY); + private static final String SERVER_ROOT = "https://sandbox.yeepay.com/yop-center"; + private static final String SLASH = "/"; + private static final String UNDER_LINE = "_"; + private static final String SEMICOLON = ";"; + private static final String EMPTY = ""; + + + + private static final String SM4_CBC_PKCS5PADDING = "SM4/CBC/PKCS5Padding"; + private static final byte[] SM4_IV = new SecureRandom().generateSeed(16); + + private static final String STREAM = "stream"; + private static final String DEFAULT_ENCODING = "UTF-8"; + private static final String DEFAULT_YOP_PROTOCOL_VERSION = "yop-auth-v3"; + public static final String DEFAULT_AUTH_PREFIX_SM2 = "YOP-SM2-SM3"; + private static final String DEFAULT_DIGEST_ALG = "SM3"; + private static final Joiner QUERY_STRING_JOINER = Joiner.on('&'); + + // mode 指定密文结构,旧标准的为C1C2C3,新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2 + // 注意:我们采用C1C3C2,如果bcprov-jdk15on包版本太低(小于1.62),则【不支持报文参数加密】 + private static final boolean BIZ_PARAM_ENCRYPT_SUPPORTED = false;// 当前环境是否支持参数加密 + // 根据mode不同,输出的密文C1C2C3排列顺序不同。C1为65字节第1字节为压缩标识,这里固定为0x04,后面64字节为xy分量各32字节。C3为32字节。C2长度与原文一致。 + private static final ThreadLocal engineThreadLocal = new ThreadLocal() { + @Override + protected SM2Engine initialValue() { + // bcprov-jdk15on包版本太低(小于1.62时),不支持报文参数加密 + if (!BIZ_PARAM_ENCRYPT_SUPPORTED) { + return new SM2Engine(); + } else { + //return new SM2Engine(SM2Engine.Mode.C1C3C2); + } + return null; + } + }; + + + + + public static void main(String[] args) throws Exception { + // 加密会话密钥,可每笔调用都生成,也可以多笔公用一个,建议定时更换 + String encryptKey = encodeUrlSafeBase64(generateRandomKey()); + + // 注意:以下分别展示了不同请求方式的参数构造、加密、解密、验签的完整流程,请根据接口实际情况进行选择 + + // get请求,form参数 + getFormExample(YopRequestMethod.GET, "/rest/v1.0/test/errorcode2", + YopRequestContentType.FORM_URL_ENCODE, encryptKey); + + //post请求,form参数 + postFormExample(YopRequestMethod.POST, "/rest/v1.0/frontcashier/agreement/sign/confirm", + YopRequestContentType.FORM_URL_ENCODE, encryptKey); + + // post请求,文件参数 + postMultipartFormExample(YopRequestMethod.POST, "/yos/v1.0/sys/merchant/qual/upload", + YopRequestContentType.MULTIPART_FORM, encryptKey); + + // post请求,json参数 + postJsonExample(YopRequestMethod.POST, "/rest/v1.0/test-wdc/test/http-json/test", + YopRequestContentType.JSON, encryptKey); + + // get请求,form参数,下载文件 + downloadExample(YopRequestMethod.GET, "/yos/v1.0/test/test/ceph-download", + YopRequestContentType.FORM_URL_ENCODE, encryptKey); + } + + private static void getFormExample(YopRequestMethod requestMethod, String requestUri, + YopRequestContentType requestContentType, + String encryptKey) throws Exception { + // 请求参数 + Multimap formParams = ArrayListMultimap.create(); + Set encryptHeaders = Collections.emptySet(); + Set encryptParams = Sets.newHashSet(); + + // 注意:以下两个代码块分别展示了加密、不加密两种示例,请根据情况选择其一 + + // 参数不加密-------------->开始 +// formParams.put("errorCode", "000027"); + // 参数不加密-------------->结束 + + // 参数加密-------------->开始 + formParams.put("errorCode", encryptParam(encryptKey, "000027")); + encryptParams.add("errorCode"); + // 参数加密-------------->结束 + + // 请求头 + Map headers = buildHeaders(requestMethod, requestUri, formParams, + requestContentType, "", encryptKey, encryptHeaders, encryptParams); + + // 构造http请求 + HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, + requestMethod, headers, formParams, null); + + // 发起http调用 + CloseableHttpResponse response = null; + try { + response = httpClient.execute(request); + handleResponse(YopRequestType.WEB, response, encryptKey); + } finally { + HttpClientUtils.closeQuietly(response); + } + } + + + private static Map buildHeaders(YopRequestMethod httpMethod, String apiUri, + Multimap params, + YopRequestContentType contentType, String content, + String encryptKey, Set encryptHeaders, Set encryptParams) throws Exception { + Map headers = new HashMap<>(); + headers.put(YOP_SDK_LANGS, "java"); + headers.put(YOP_SDK_VERSION, "4.3.0"); + headers.put(YOP_APPKEY, APP_KEY); + headers.put(YOP_REQUEST_ID, UUID.randomUUID().toString()); + + // 加密头:请求参数不加密的情况, 也需设置加密头,用于响应结果加密 + if (BIZ_PARAM_ENCRYPT_SUPPORTED) { + headers.put(YOP_ENCRYPT, buildEncryptHeader(encryptHeaders, encryptParams, encryptKey)); + } + + // 摘要头 + headers.put(YOP_CONTENT_SM3, calculateContentHash(httpMethod, contentType, params, content)); + + // 签名头 + headers.put(AUTHORIZATION, signRequest(httpMethod, apiUri, headers, params)); + return headers; + } + + private static void postFormExample(YopRequestMethod requestMethod, String requestUri, + YopRequestContentType requestContentType, + String encryptKey) throws Exception { + + // 请求参数 + Multimap params = ArrayListMultimap.create(); + Set encryptHeaders = Collections.emptySet(); + Set encryptParams = Sets.newHashSet(); + + // 注意:以下两个代码块分别展示了加密、不加密两种示例,请根据情况选择其一 + + // 参数不加密-------------->开始 + params.put("parentMerchant", "10080041523"); + params.put("merchantNo", "10080041523"); + params.put("smsCode", "11111131"); + params.put("merchantFlowId", "11111131"); + // 参数不加密-------------->结束 + + + // 参数加密-------------->开始 +// params.put("parentMerchant", encryptParam(encryptKey , "10080041523" )); +// params.put("merchantNo", encryptParam(encryptKey , "10080041523" )); +// params.put("smsCode", encryptParam(encryptKey , "11111131" )); +// params.put("merchantFlowId", encryptParam(encryptKey , "111111" )); +// encryptParams.add("parentMerchant"); +// encryptParams.add("merchantNo"); +// encryptParams.add("smsCode"); +// encryptParams.add("merchantFlowId"); + // 参数加密-------------->结束 + + // 请求头 + Map headers = buildHeaders(requestMethod, requestUri, params, + requestContentType, "", encryptKey, encryptHeaders, encryptParams); + + // 构造http请求 + HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, + requestMethod, headers, params, null); + + // 发起http调用 + CloseableHttpResponse response = null; + try { + response = httpClient.execute(request); + handleResponse(YopRequestType.WEB, response, encryptKey); + } finally { + HttpClientUtils.closeQuietly(response); + } + } + + private static void postMultipartFormExample(YopRequestMethod requestMethod, String requestUri, + YopRequestContentType requestContentType, String encryptKey) throws Exception { + + + // 普通参数参与签名,clientId + Multimap params = ArrayListMultimap.create(); + Set encryptHeaders = Collections.emptySet(); + Set encryptParams = Sets.newHashSet(); + + // 注意:以下两个代码块分别展示了加密、不加密两种示例,请根据情况选择其一 + + // 参数不加密--------------->开始 + params.put("clientId", APP_KEY); + // 参数不加密--------------->结束 + + // 参数加密--------------->开始 +// params.put("clientId", encryptParam(encryptKey, APP_KEY)); +// encryptParams.add("clientId"); + // 参数加密--------------->结束 + + // 请求头 + Map headers = buildHeaders(requestMethod, requestUri, params, + requestContentType, "", encryptKey, encryptHeaders, encryptParams); + + // 构造http请求 + HttpPost postMethod = new HttpPost(SERVER_ROOT + requestUri); + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + + // 添加普通参数 + for (Map.Entry> entry : params.asMap().entrySet()) { + String paramKey = entry.getKey(); + for (String value : entry.getValue()) { + builder.addTextBody(normalize(paramKey), normalize(value)); + } + } + + // 注意:以下两个代码块分别展示了本地文件、远程文件两种示例,请根据情况选择其一 + // 添加文件参数,merQual + // 方式一:本地文件流 +// builder.addBinaryBody("merQual", new FileInputStream("abc.txt"), +// ContentType.DEFAULT_BINARY, "abc.txt"); + // 方式二:远程文件流 + builder.addBinaryBody("merQual", new URL("https://open.yeepay.com/apis/docs/apis/common/ALL.json").openStream(), + ContentType.DEFAULT_BINARY, "abc.txt"); + + postMethod.setEntity(builder.build()); + + // 添加请求头 + for (Map.Entry entry : headers.entrySet()) { + postMethod.addHeader(entry.getKey(), entry.getValue()); + } + + // 发起http调用 + CloseableHttpResponse response = null; + try { + response = httpClient.execute(postMethod); + handleResponse(YopRequestType.WEB, response, encryptKey); + } finally { + HttpClientUtils.closeQuietly(response); + } + + } + + private static void postJsonExample(YopRequestMethod requestMethod, String requestUri, + YopRequestContentType requestContentType, + String encryptKey) throws Exception { + Set encryptHeaders = Collections.emptySet(); + Set encryptParams = Sets.newHashSet(); + + String plainJsonContent = "{\n" + + " \"arg1\" : {\n" + + " \"appId\" : \"app_1111111111\",\n" + + " \"customerNo\" : \"333333333\"\n" + + " },\n" + + " \"arg0\" : {\n" + + " \"string\" : \"hello\",\n" + + " \"array\" : [ \"test\" ]\n" + + " }\n" + + "}", + encryptJsonContent = encryptParam(encryptKey, plainJsonContent); + + // 注意:以下两个代码块分别展示了加密、不加密两种示例,请根据情况选择其一 + String finalJsonContent + // 不加密------------------->开始 + = plainJsonContent; + // 不加密------------------->结束 + + + // 加密-------------------->开始 +// = encryptJsonContent; +// encryptParams.add("$"); // 目前json推荐整体加密,$代表整体加密 + // 加密-------------------->结束 + + // 请求头 + // 目前不允许json接口带有form参数,置空即可 + final ArrayListMultimap params = ArrayListMultimap.create(); + Map headers = buildHeaders(requestMethod, requestUri, params, + requestContentType, finalJsonContent, encryptKey, encryptHeaders, encryptParams); + + // 构造http请求 + HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, + requestMethod, headers, params, finalJsonContent); + + // 发起http调用 + CloseableHttpResponse response = null; + try { + response = httpClient.execute(request); + handleResponse(YopRequestType.WEB, response, encryptKey); + } finally { + HttpClientUtils.closeQuietly(response); + } + } + + private static void downloadExample(YopRequestMethod requestMethod, String requestUri, + YopRequestContentType requestContentType, + String encryptKey) throws Exception { + + // 根据实际情况来,【文件下载接口】可能是post方法,请求报文可能是form,也可能是json,可参考其他方式的入参处理 + // 此处仅演示文件响应流的处理 + + // 请求参数 + Multimap params = ArrayListMultimap.create(); + Set encryptHeaders = Collections.emptySet(); + Set encryptParams = Sets.newHashSet(); + + // 注意:以下两个代码块分别展示了加密、不加密两种示例,请根据情况选择其一 + // 不加密------------------->开始 + params.put("fileName", "wym-test.txt"); + // 不加密------------------->结束 + + // 加密--------------------->开始 +// params.put("fileName", encryptParam(encryptKey, "wym-test.txt")); +// encryptParams.add("fileName"); + // 加密--------------------->结束 + + // 请求头 + Map headers = buildHeaders(requestMethod, requestUri, params, + requestContentType, "", encryptKey, encryptHeaders, encryptParams); + + // 构造http请求 + HttpUriRequest request = buildHttpRequest(SERVER_ROOT + requestUri, + requestMethod, headers, params, ""); + + // 发起请求 + CloseableHttpResponse response = null; + try { + response = httpClient.execute(request); + handleResponse(YopRequestType.FILE_DOWNLOAD, response, encryptKey); + } finally { + HttpClientUtils.closeQuietly(response); + } + } + + private static final Set DEFAULT_HEADERS_TO_SIGN = Sets.newHashSet(); + private static final Joiner HEADER_JOINER = Joiner.on('\n'); + private static final Joiner SIGNED_HEADER_STRING_JOINER = Joiner.on(';'); + + private static final String YOP_SDK_VERSION = "x-yop-sdk-version"; + private static final String YOP_SDK_LANGS = "x-yop-sdk-langs"; + private static final String YOP_REQUEST_ID = "x-yop-request-id"; + private static final String YOP_APPKEY = "x-yop-appkey"; + private static final String YOP_CONTENT_SM3 = "x-yop-content-sm3"; + private static final String YOP_ENCRYPT = "x-yop-encrypt"; + private static final String AUTHORIZATION = "Authorization"; + private static final String CONTENT_DISPOSITION = "Content-Disposition"; + private static final String CONTENT_TYPE = "Content-Type"; + + static { + DEFAULT_HEADERS_TO_SIGN.add(YOP_REQUEST_ID); + DEFAULT_HEADERS_TO_SIGN.add(YOP_APPKEY); + DEFAULT_HEADERS_TO_SIGN.add(YOP_CONTENT_SM3); + DEFAULT_HEADERS_TO_SIGN.add(YOP_ENCRYPT); + } + + private static String signRequest(YopRequestMethod requestMethod, String requestUri, + Map headers, Multimap params) throws Exception { + // A.构造认证字符串 + String authString = buildAuthString(); + + // B.获取规范请求串 + SortedMap headersToSign = getHeadersToSign(headers, DEFAULT_HEADERS_TO_SIGN); + String canonicalRequest = buildCanonicalRequest(requestMethod, requestUri, params, authString, headersToSign); + + // C.计算签名 + String signature = encodeUrlSafeBase64(sign(canonicalRequest.getBytes(DEFAULT_ENCODING))) + "$" + DEFAULT_DIGEST_ALG; + + // D.添加认证头 + return buildAuthHeader(authString, headersToSign, signature); + } + + private static String buildAuthHeader(String authString, + SortedMap headersToSign, + String signature) { + String signedHeaders = SIGNED_HEADER_STRING_JOINER.join(headersToSign.keySet()); + signedHeaders = signedHeaders.trim().toLowerCase(); + return DEFAULT_AUTH_PREFIX_SM2 + " " + authString + "/" + signedHeaders + "/" + signature; + } + + // SM2 + public static final SM2P256V1Curve CURVE = new SM2P256V1Curve(); + public final static BigInteger SM2_ECC_N = CURVE.getOrder(); + public final static BigInteger SM2_ECC_H = CURVE.getCofactor(); + public final static BigInteger SM2_ECC_GX = new BigInteger( + "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); + public final static BigInteger SM2_ECC_GY = new BigInteger( + "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); + public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY); + public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, + SM2_ECC_N, SM2_ECC_H); + public static final int CURVE_LEN = getCurveLength(DOMAIN_PARAMS); + public static int getCurveLength(ECDomainParameters domainParams) { + return (domainParams.getCurve().getFieldSize() + 7) / 8; + } + + private static byte[] sign(byte[] data) { + try { + ECParameterSpec parameterSpec = ISV_PRIVATE_KEY.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + ECPrivateKeyParameters priKeyParameters = new ECPrivateKeyParameters(ISV_PRIVATE_KEY.getD(), domainParameters); + //der编码后的签名值 + byte[] derSign = sign(priKeyParameters, null, data); + + //der解码过程 + ASN1Sequence as = DERSequence.getInstance(derSign); + byte[] rBytes = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray(); + byte[] sBytes = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray(); + //由于大数的补0规则,所以可能会出现33个字节的情况,要修正回32个字节 + rBytes = fixToCurveLengthBytes(rBytes); + sBytes = fixToCurveLengthBytes(sBytes); + byte[] rawSign = new byte[rBytes.length + sBytes.length]; + System.arraycopy(rBytes, 0, rawSign, 0, rBytes.length); + System.arraycopy(sBytes, 0, rawSign, rBytes.length, sBytes.length); + return rawSign; + } catch (Exception e) { + throw new RuntimeException("SystemError, Sign Fail, key:" + ISV_PRIVATE_KEY + ", ex:", e); + } + } + + private static boolean verifySign(String content, String signature, BCECPublicKey publicKey) { + try { + byte[] srcData = content.getBytes(DEFAULT_ENCODING); + byte[] signData = decodeBase64(signature); + ECParameterSpec parameterSpec = publicKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + ECPublicKeyParameters pubKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), domainParameters); + return verify(pubKeyParameters, null, srcData, encodeSM2SignToDER(signData)); + } catch (IOException e) { + throw new RuntimeException("UnexpectedError, VerifySign Fail, data:" + + content + ", sign:" + signature + ", key:" + publicKey + ", ex:", e); + } + } + + /** + * 把64字节的纯R+S字节数组编码成DER编码 + * + * @param rawSign 64字节数组形式的SM2签名值,前32字节为R,后32字节为S + * @return DER编码后的SM2签名值 + * @throws IOException + */ + private static byte[] encodeSM2SignToDER(byte[] rawSign) throws IOException { + //要保证大数是正数 + BigInteger r = new BigInteger(1, extractBytes(rawSign, 0, 32)); + BigInteger s = new BigInteger(1, extractBytes(rawSign, 32, 32)); + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new ASN1Integer(r)); + v.add(new ASN1Integer(s)); + return new DERSequence(v).getEncoded(ASN1Encoding.DER); + } + + private static byte[] extractBytes(byte[] src, int offset, int length) { + byte[] result = new byte[length]; + System.arraycopy(src, offset, result, 0, result.length); + return result; + } + + /** + * 验签 + * + * @param pubKeyParameters 公钥 + * @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes() + * @param srcData 原文 + * @param sign DER编码的签名值 + * @return 验签成功返回true,失败返回false + */ + public static boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] withId, byte[] srcData, byte[] sign) { + SM2Signer signer = new SM2Signer(); + CipherParameters param; + if (withId != null) { + param = new ParametersWithID(pubKeyParameters, withId); + } else { + param = pubKeyParameters; + } + signer.init(false, param); + signer.update(srcData, 0, srcData.length); + return signer.verifySignature(sign); + } + + private static byte[] fixToCurveLengthBytes(byte[] src) { + if (src.length == CURVE_LEN) { + return src; + } + + byte[] result = new byte[CURVE_LEN]; + if (src.length > CURVE_LEN) { + System.arraycopy(src, src.length - result.length, result, 0, result.length); + } else { + System.arraycopy(src, 0, result, result.length - src.length, src.length); + } + return result; + } + + /** + * 签名 + * + * @param priKeyParameters 私钥 + * @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes() + * @param srcData 源数据 + * @return DER编码后的签名值 + * @throws CryptoException + */ + public static byte[] sign(ECPrivateKeyParameters priKeyParameters, byte[] withId, byte[] srcData) + throws CryptoException { + SM2Signer signer = new SM2Signer(); + CipherParameters param; + ParametersWithRandom pwr = new ParametersWithRandom(priKeyParameters, new SecureRandom()); + if (withId != null) { + param = new ParametersWithID(pwr, withId); + } else { + param = pwr; + } + signer.init(true, param); + signer.update(srcData, 0, srcData.length); + return signer.generateSignature(); + } + + private static BCECPrivateKey string2PrivateKey(String priKey) { + try { + return (BCECPrivateKey)KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME).generatePrivate( + new PKCS8EncodedKeySpec(decodeBase64(priKey))); + } catch (Exception e) { + throw new RuntimeException("ConfigProblem, IsvPrivateKey ParseFail, value:" + priKey + ", ex:", e); + } + } + + private static byte[] decodeBase64(String input) { + return Base64.decodeBase64(input); + } + + private static String buildCanonicalRequest(YopRequestMethod httpMethod, String apiUri, Multimap params, + String authString, + SortedMap headersToSign) { + String canonicalQueryString; + if (YopRequestMethod.GET.equals(httpMethod) && null != params) { + canonicalQueryString = getCanonicalQueryString(params, true); + } else { + canonicalQueryString = "";// post from 与json时,均为空,此处先简单处理 + } + String canonicalHeaders = getCanonicalHeaders(headersToSign); + + String canonicalURI = getCanonicalURIPath(apiUri); + return authString + "\n" + + httpMethod.name() + "\n" + + canonicalURI + "\n" + + canonicalQueryString + "\n" + + canonicalHeaders; + } + + private static String getCanonicalURIPath(String path) { + if (path == null) { + return "/"; + } else if (path.startsWith("/")) { + return normalizePath(path); + } else { + return "/" + normalizePath(path); + } + } + + private static String normalizePath(String path) { + return normalize(path).replace("%2F", "/"); + } + + private static SortedMap getHeadersToSign(Map headers, Set headersToSign) { + SortedMap ret = Maps.newTreeMap(); + if (headersToSign != null) { + Set tempSet = Sets.newHashSet(); + for (String header : headersToSign) { + tempSet.add(header.trim().toLowerCase()); + } + headersToSign = tempSet; + } + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + if (entry.getValue() != null && !entry.getValue().isEmpty()) { + if ((headersToSign != null && headersToSign.contains(key.toLowerCase()) + && !AUTHORIZATION.equalsIgnoreCase(key))) { + ret.put(key, entry.getValue()); + } + } + } + return ret; + } + + private static String getCanonicalHeaders(SortedMap headers) { + if (headers.isEmpty()) { + return ""; + } + + List headerStrings = Lists.newArrayList(); + for (Map.Entry entry : headers.entrySet()) { + String key = entry.getKey(); + if (key == null) { + continue; + } + String value = entry.getValue(); + if (value == null) { + value = ""; + } + headerStrings.add(normalize(key.trim().toLowerCase()) + ':' + normalize(value.trim())); + } + Collections.sort(headerStrings); + + return HEADER_JOINER.join(headerStrings); + } + + private static final DateTimeFormatter alternateIso8601DateFormat = + ISODateTimeFormat.dateTimeNoMillis().withZone(DateTimeZone.UTC); + + private static String buildAuthString() { + Date timestamp = new Date(); + return DEFAULT_YOP_PROTOCOL_VERSION + "/" + + APP_KEY + "/" + + alternateIso8601DateFormat.print(new DateTime(timestamp)) + "/" + + "1800"; + } + + private static String calculateContentHash(String jsonContent) throws Exception { + InputStream contentStream = getContentStream(jsonContent); + return Hex.encodeHexString((digest(contentStream))); + } + + private static String calculateContentHash(YopRequestMethod requestMethod, YopRequestContentType requestContentType, + Multimap params, String content) throws Exception { + String digestSource; + + if (requestMethod.equals(YopRequestMethod.GET)) { + digestSource = ""; + } else if (requestMethod.equals(YopRequestMethod.POST) + && requestContentType.equals(YopRequestContentType.JSON)) { + digestSource = content; + } else { + digestSource = getCanonicalQueryString(params, true); + } + InputStream contentStream = getContentStream(digestSource); + return Hex.encodeHexString((digest(contentStream))); + } + + private static ByteArrayInputStream getContentStream(Multimap params) throws Exception { + return getContentStream(getCanonicalQueryString(params, true)); + } + + private static ByteArrayInputStream getContentStream(String paramStr) throws Exception { + byte[] bytes; + if (StringUtils.isEmpty(paramStr)) { + bytes = new byte[0]; + } else { + bytes = paramStr.getBytes(DEFAULT_ENCODING); + } + return new ByteArrayInputStream(bytes); + } + + public static String getCanonicalQueryString(Multimap params, boolean forSignature) { + Map> parameters; + if (null == params || (parameters = params.asMap()).isEmpty()) { + return ""; + } + + List parameterStrings = Lists.newArrayList(); + for (Map.Entry> entry : parameters.entrySet()) { + if (forSignature && AUTHORIZATION.equalsIgnoreCase(entry.getKey())) { + continue; + } + String key = entry.getKey(); + checkNotNull(key, "parameter key should not be null"); + Collection value = entry.getValue(); + if (value == null) { + if (forSignature) { + parameterStrings.add(normalize(key) + '='); + } else { + parameterStrings.add(normalize(key)); + } + } else { + for (String item : value) { + parameterStrings.add(normalize(key) + '=' + normalize(item)); + } + } + } + Collections.sort(parameterStrings); + + return QUERY_STRING_JOINER.join(parameterStrings); + } + + private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet(); + private static final String[] PERCENT_ENCODED_STRINGS = new String[256]; + + static { + /* + * StringBuilder pattern = new StringBuilder(); + * + * pattern .append(Pattern.quote("+")) .append("|") .append(Pattern.quote("*")) .append("|") + * .append(Pattern.quote("%7E")) .append("|") .append(Pattern.quote("%2F")); + * + * ENCODED_CHARACTERS_PATTERN = Pattern.compile(pattern.toString()); + */ + for (int i = 'a'; i <= 'z'; i++) { + URI_UNRESERVED_CHARACTERS.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + URI_UNRESERVED_CHARACTERS.set(i); + } + for (int i = '0'; i <= '9'; i++) { + URI_UNRESERVED_CHARACTERS.set(i); + } + URI_UNRESERVED_CHARACTERS.set('-'); + URI_UNRESERVED_CHARACTERS.set('.'); + URI_UNRESERVED_CHARACTERS.set('_'); + URI_UNRESERVED_CHARACTERS.set('~'); + + for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) { + PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i); + } + } + + public static String normalize(String value) { + try { + StringBuilder builder = new StringBuilder(); + for (byte b : value.getBytes(DEFAULT_ENCODING)) { + if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) { + builder.append((char) b); + } else { + builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]); + } + } + return builder.toString(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + private static byte[] digest(InputStream input) { + try { + MessageDigest md = MessageDigest.getInstance(DEFAULT_DIGEST_ALG, BouncyCastleProvider.PROVIDER_NAME); + DigestInputStream digestInputStream = new DigestInputStream(input, md); + byte[] buffer = new byte[1024]; + while (digestInputStream.read(buffer) > -1) {} + return digestInputStream.getMessageDigest().digest(); + } catch (Exception e) { + throw new RuntimeException("SystemError, Digest Fail, alg:" + DEFAULT_DIGEST_ALG + ", ex:", e); + } + } + + /** + * 加密协议头(请求): + * + * yop-encrypt-v1/{服务端证书序列号}/{密钥类型(必填)}_{分组模式(必填)}_{填充算法(必填)}/{加密密钥值(必填)}/{IV}{;}{附加信息}/{客户端支持的大参数加密模式(必填)}/{encryptHeaders}/{encryptParams} + */ + public static String buildEncryptHeader(Set encryptHeaders, Set encryptParams, + String sm4Key) throws Exception { + + return "yop-encrypt-v1" + SLASH + + YOP_PUBLIC_KEY_SERIAL_NO + SLASH + //平台SM2证书序列号 + StringUtils.replace(SM4_CBC_PKCS5PADDING, SLASH, UNDER_LINE) + SLASH + + encodeUrlSafeBase64(encryptKey(decodeBase64(sm4Key))) + SLASH + + encodeUrlSafeBase64(SM4_IV) + SEMICOLON + EMPTY + SLASH + + STREAM + SLASH + + StringUtils.join(encryptHeaders, SEMICOLON) + SLASH + + encodeUrlSafeBase64(StringUtils.join(encryptParams, SEMICOLON).getBytes(DEFAULT_ENCODING)); + } + + public static BCECPublicKey string2PublicKey(String pubKey) { + try { + return (BCECPublicKey) KeyFactory.getInstance("EC").generatePublic( + new X509EncodedKeySpec(decodeBase64(pubKey))); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException("ConfigProblem, YopPublicKey ParseFail, value:" + pubKey + ", ex:", e); + } + } + + private static byte[] encryptKey(byte[] sm4Key) { + try { + SM2Engine engine = engineThreadLocal.get(); + ECPublicKeyParameters pubKeyParameters = convertPublicKeyToParameters(); + ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParameters, new SecureRandom()); + engine.init(true, pwr); + return engine.processBlock(sm4Key, 0, sm4Key.length); + } catch (Throwable e) { + throw new RuntimeException("SystemError, Encrypt Fail, publicKey:" + + YOP_PUBLIC_KEY + ", cause:" + e.getMessage(), e); + } + } + + private static ECPublicKeyParameters convertPublicKeyToParameters() { + ECParameterSpec parameterSpec = YOP_PUBLIC_KEY.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + return new ECPublicKeyParameters(YOP_PUBLIC_KEY.getQ(), domainParameters); + } + + private static String encryptParam(String sm4Key, String plain) throws Exception { + if (BIZ_PARAM_ENCRYPT_SUPPORTED) { + final Cipher cipher = getInitializedCipher(Cipher.ENCRYPT_MODE, sm4Key); + return encodeUrlSafeBase64(cipher.doFinal(plain.getBytes("UTF-8"))); + } else { + return plain; + } + } + + private static String decryptParam(String sm4Key, String encryptContent) throws Exception { + final Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, sm4Key); + return new String(cipher.doFinal(decodeBase64(encryptContent)), "UTF-8"); + } + + private static InputStream decryptStream(String sm4Key, InputStream encryptStream) throws Exception { + final Cipher cipher = getInitializedCipher(Cipher.DECRYPT_MODE, sm4Key); + return new CipherInputStream(encryptStream, cipher); + } + + private static Cipher getInitializedCipher(int mode, String sm4Key) { + try { + byte[] key = decodeBase64(sm4Key); + Cipher cipher = Cipher.getInstance(SM4_CBC_PKCS5PADDING, BouncyCastleProvider.PROVIDER_NAME); + IvParameterSpec spec = new IvParameterSpec(SM4_IV); + Key secretKey = new SecretKeySpec(key, "SM4"); + cipher.init(mode, secretKey, spec); + return cipher; + } catch (Throwable throwable) { + throw new RuntimeException("error happened when initialize cipher", throwable); + } + } + + private static String encodeUrlSafeBase64(byte[] input) { + return Base64.encodeBase64URLSafeString(input); + } + + private static byte[] generateRandomKey() throws NoSuchAlgorithmException, NoSuchProviderException { + //实例化 + KeyGenerator generator = KeyGenerator.getInstance("SM4", BouncyCastleProvider.PROVIDER_NAME); + //设置密钥长度,SM4算法目前只支持128位(即密钥16字节) + generator.init(128); + //生成密钥 + return generator.generateKey().getEncoded(); + } + + protected static HttpUriRequest buildHttpRequest(String requestUrl, YopRequestMethod requestMethod, + Map headers, Multimap params, + String content) throws UnsupportedEncodingException { + RequestBuilder requestBuilder; + if (YopRequestMethod.POST == requestMethod) { + requestBuilder = RequestBuilder.post(); + } else if (YopRequestMethod.GET == requestMethod) { + requestBuilder = RequestBuilder.get(); + } else { + throw new RuntimeException("unsupported http method"); + } + requestBuilder.setUri(requestUrl); + + // header + for (Map.Entry entry : headers.entrySet()) { + requestBuilder.addHeader(entry.getKey(), entry.getValue()); + } + + // body + try { + if (null != params) { + for (Map.Entry> entry : params.asMap().entrySet()) { + String paramKey = entry.getKey(); + for (String value : entry.getValue()) { + requestBuilder.addParameter(paramKey, URLEncoder.encode(value, DEFAULT_ENCODING)); + } + } + } + } catch (IOException ex) { + throw new RuntimeException("unable to create http request.", ex); + } + if (YopRequestMethod.GET.equals(requestMethod)) { + requestBuilder.addHeader(CONTENT_TYPE, "application/x-www-form-urlencoded;charset=UTF-8"); + return requestBuilder.build(); + } + // json 请求 + if (StringUtils.isNotBlank(content)) { + final byte[] contentBytes = content.getBytes(DEFAULT_ENCODING); + requestBuilder.setEntity(new InputStreamEntity(new ByteArrayInputStream(contentBytes), contentBytes.length)); + requestBuilder.addHeader(CONTENT_TYPE, "application/json;charset=UTF-8"); + } + return requestBuilder.build(); + } + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private static void handleResponse(YopRequestType requestType, + CloseableHttpResponse httpResponse, + String encryptKey) throws Exception { + // header + Map headers = Maps.newHashMap(); + for (Header header : httpResponse.getAllHeaders()) { + headers.put(header.getName(), header.getValue()); + } + String encryptHeader = headers.get("x-yop-encrypt"),// 加密头 + signHeader = headers.get("x-yop-sign"), //签名头 + signSerialNo = headers.get("x-yop-sign-serial-no"); //签名序列号,可能会变 + + boolean isEncryptResponse = StringUtils.isNotBlank(encryptHeader); + + // body + HttpEntity entity = httpResponse.getEntity(); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode / 100 == HttpStatus.SC_OK / 100 && statusCode != HttpStatus.SC_NO_CONTENT) { + //not a error + if (null != entity && entity.getContent() != null) { + if (isJsonResponse(httpResponse)) { + String content = IOUtils.toString(entity.getContent(), DEFAULT_ENCODING); + System.out.println("Request success, response:" + content); + verifyResponseSign(signHeader, content, signSerialNo); + final JsonNode bizData = OBJECT_MAPPER.readTree(content).get("result"); + if (isEncryptResponse) { + System.out.println("Response decrypt success, bizData:" + decryptParam(encryptKey, bizData.asText())); + } + return; + } else if (YopRequestType.FILE_DOWNLOAD.equals(requestType) || isDownloadResponse(httpResponse)) { + InputStream fileContent = entity.getContent(); + if (isEncryptResponse) { + fileContent = decryptStream(encryptKey, fileContent); + System.out.println("Response file decrypt success"); + } + final File file = saveFile(fileContent, headers); + System.out.println("Request success, file downloaded:" + file); + return; + } else { + throw new RuntimeException("Response Error, contentType:" + httpResponse.getEntity().getContentType()); + } + } else { + throw new RuntimeException("Response Error, contentType:" + httpResponse.getEntity().getContentType()); + } + } else if (statusCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR && statusCode != HttpStatus.SC_BAD_GATEWAY) { + if (entity.getContent() != null) { + String content = IOUtils.toString(entity.getContent(), DEFAULT_ENCODING); + System.out.println("Request Fail, response:" + content); + verifyResponseSign(signHeader, content, signSerialNo); + return; + } else { + throw new RuntimeException("ResponseError, Empty Content, httpStatusCode:" + httpResponse.getStatusLine().getStatusCode()); + } + } else if (statusCode == HttpStatus.SC_BAD_GATEWAY || statusCode == HttpStatus.SC_NOT_FOUND) { + throw new RuntimeException("Response Error, statusCode:" + statusCode); + } + throw new RuntimeException("ReqParam Illegal, Bad Request, statusCode:" + statusCode); + } + + private static void verifyResponseSign(String signHeader, String content, String signSerialNo) { + if (StringUtils.isBlank(signHeader) || StringUtils.isBlank(content)|| StringUtils.isBlank(signSerialNo)) { + System.out.println("Response Sign Skip, signature:" + signHeader + ", content:" + content + ", signSerialNo:" + signSerialNo); + return; + } + String signContent = content.replaceAll("[ \t\n]", ""); + System.out.println("Response Sign Begin, signature:" + signHeader + ", content:" + signContent + ", signSerialNo:" + signSerialNo); + if (!YOP_PUBLIC_KEY_MAP.containsKey(signSerialNo)) { + System.out.println("Response Sign Skip verify, No YopPublicKey Found"); + } + if (!verifySign(signContent, signHeader, YOP_PUBLIC_KEY_MAP.get(signSerialNo))) { + System.out.println("Response Sign Verify Fail"); + } else { + System.out.println("Response Sign Verify Success"); + } + } + + /** + * 保存文件到本地 + * + * @param content + * @param headers + * @return File + */ + public static File saveFile(InputStream content, Map headers) { + InputStream fileContent = content; + try { + String filePrefix = "yos-", fileSuffix = ".tmp"; + final String contentDisposition = headers.get(CONTENT_DISPOSITION); + try { + String fileName = getFileNameFromHeader(contentDisposition); + if (StringUtils.isNotBlank(fileName)) { + final String[] split = fileName.split("\\."); + if (split.length == 2) { + if (StringUtils.length(split[0]) > 3) { + filePrefix = split[0]; + } + if (StringUtils.isNotBlank(split[1])) { + fileSuffix = "." + split[1]; + } + } + } + } catch (Exception e) { + System.out.println(("parse Content-Disposition fail, value:" + contentDisposition + ", ex:" + e)); + } + File tmpFile = File.createTempFile(filePrefix, fileSuffix); + IOUtils.copy(fileContent, Files.newOutputStream(tmpFile.toPath())); + return tmpFile; + } catch (Throwable ex) { + throw new RuntimeException("fail to save file"); + } finally { + closeQuietly(fileContent); + } + } + + public static void closeQuietly(Closeable closeable) { + try { + if (null != closeable) { + closeable.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static String getFileNameFromHeader(String contentDisposition) { + if (StringUtils.isBlank(contentDisposition)) { + return null; + } + + final String[] parts = contentDisposition.split( "filename="); + if (parts.length == 2) { + return StringUtils.trim(parts[1]); + } + return null; + } + + private static final String CONTENT_TYPE_JSON = "application/json"; + private static final String CONTENT_TYPE_STREAM = "application/octet-stream"; + + private static boolean isJsonResponse(CloseableHttpResponse response) { + return null != response.getEntity() && StringUtils.startsWith(response.getEntity().getContentType().getValue(), CONTENT_TYPE_JSON); + } + + private static boolean isDownloadResponse(CloseableHttpResponse response) { + return null != response.getEntity() && StringUtils.startsWith(response.getEntity().getContentType().getValue(), CONTENT_TYPE_STREAM); + } + + public enum YopRequestMethod { + GET, + POST + } + + public enum YopRequestType { + WEB, + FILE_DOWNLOAD, + MULTI_FILE_UPLOAD, + } + + private static final String YOP_HTTP_CONTENT_TYPE_JSON = "application/json"; + private static final String YOP_HTTP_CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"; + private static final String YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM = "multipart/form-data"; + private static final String YOP_HTTP_CONTENT_TYPE_STREAM = "application/octet-stream"; + private static final String YOP_HTTP_CONTENT_TYPE_TEXT = "text/plain;charset=UTF-8"; + + public enum YopRequestContentType { + FORM_URL_ENCODE(YOP_HTTP_CONTENT_TYPE_FORM), + MULTIPART_FORM(YOP_HTTP_CONTENT_TYPE_MULTIPART_FORM), + JSON(YOP_HTTP_CONTENT_TYPE_JSON), + OCTET_STREAM(YOP_HTTP_CONTENT_TYPE_STREAM), + TEXT_PLAIN(YOP_HTTP_CONTENT_TYPE_TEXT); + + private String value; + + YopRequestContentType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + + // 附录:jar包依赖情况 + /** + * + * org.apache.httpcomponents + * httpcore + * 4.4.13 + * + * + * org.apache.httpcomponents + * httpclient + * 4.5.13 + * + * + * org.apache.httpcomponents + * httpmime + * 4.5.13 + * + * + * + * commons-codec + * commons-codec + * 1.10 + * + * + * commons-io + * commons-io + * 2.8.0 + * + * + * com.google.guava + * guava + * 30.0-jre + * + * + * joda-time + * joda-time + * 2.9.4 + * + * + * org.apache.commons + * commons-lang3 + * 3.6 + * + * + * com.fasterxml.jackson.core + * jackson-databind + * 2.13.2 + * + * + * org.bouncycastle + * bcprov-jdk15on + * 1.67 + * + * + * org.bouncycastle + * bcpkix-jdk15on + * 1.67 + * + */ +} diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSm2CallbackExample.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSm2CallbackExample.java index 4468c7ca..6247ca05 100644 --- a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSm2CallbackExample.java +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/example/YopSm2CallbackExample.java @@ -5,21 +5,18 @@ import com.google.common.collect.Sets; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1Encoding; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.*; import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.engines.SM2Engine; -import org.bouncycastle.crypto.params.ECDomainParameters; -import org.bouncycastle.crypto.params.ECPrivateKeyParameters; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import org.bouncycastle.crypto.params.ParametersWithID; +import org.bouncycastle.crypto.params.*; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,10 +26,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; -import java.security.Key; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.Security; +import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @@ -176,6 +170,98 @@ public static void main(String[] args) throws Exception { // 解密业务参数 final String bizContent = decryptBizContent(jsonReqBody, decryptedSecretKey, iv); LOGGER.info("解密结果:{}", bizContent); + + // 构造应答报文 +// String respBody = "{\"result\":\"SUCCESS\"}"; + //HttpServletResponse resp; +// resp.addHeader("x-yop-sign", encodeUrlSafeBase64(sign(respBody.getBytes(UTF_8)))); +// // 注意:此处为测试环境国密证书,生产环境请联系技术支持获取 +// resp.addHeader("x-yop-sign-serial-no", "4059376239"); +// resp.setContentType("application/json"); +// resp.getOutputStream().write(respBody.getBytes(UTF_8)); + } + + private static byte[] sign(byte[] data, String appKey) { + BCECPrivateKey isvPrivateKey = isvPrivateKeyMap.get(appKey); + try { + ECParameterSpec parameterSpec = isvPrivateKey.getParameters(); + ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), + parameterSpec.getN(), parameterSpec.getH()); + ECPrivateKeyParameters priKeyParameters = new ECPrivateKeyParameters(isvPrivateKey.getD(), domainParameters); + //der编码后的签名值 + byte[] derSign = sign(priKeyParameters, null, data); + + //der解码过程 + ASN1Sequence as = DERSequence.getInstance(derSign); + byte[] rBytes = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray(); + byte[] sBytes = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray(); + //由于大数的补0规则,所以可能会出现33个字节的情况,要修正回32个字节 + rBytes = fixToCurveLengthBytes(rBytes); + sBytes = fixToCurveLengthBytes(sBytes); + byte[] rawSign = new byte[rBytes.length + sBytes.length]; + System.arraycopy(rBytes, 0, rawSign, 0, rBytes.length); + System.arraycopy(sBytes, 0, rawSign, rBytes.length, sBytes.length); + return rawSign; + } catch (Exception e) { + throw new RuntimeException("SystemError, Sign Fail, key:" + isvPrivateKey + ", ex:", e); + } + } + + /** + * 签名 + * + * @param priKeyParameters 私钥 + * @param withId 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes() + * @param srcData 源数据 + * @return DER编码后的签名值 + * @throws CryptoException + */ + public static byte[] sign(ECPrivateKeyParameters priKeyParameters, byte[] withId, byte[] srcData) + throws CryptoException { + SM2Signer signer = new SM2Signer(); + CipherParameters param; + ParametersWithRandom pwr = new ParametersWithRandom(priKeyParameters, new SecureRandom()); + if (withId != null) { + param = new ParametersWithID(pwr, withId); + } else { + param = pwr; + } + signer.init(true, param); + signer.update(srcData, 0, srcData.length); + return signer.generateSignature(); + } + + public static final SM2P256V1Curve CURVE = new SM2P256V1Curve(); + public final static BigInteger SM2_ECC_N = CURVE.getOrder(); + public final static BigInteger SM2_ECC_H = CURVE.getCofactor(); + public final static BigInteger SM2_ECC_GX = new BigInteger( + "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); + public final static BigInteger SM2_ECC_GY = new BigInteger( + "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); + public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY); + public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT, + SM2_ECC_N, SM2_ECC_H); + public static final int CURVE_LEN = getCurveLength(DOMAIN_PARAMS); + public static int getCurveLength(ECDomainParameters domainParams) { + return (domainParams.getCurve().getFieldSize() + 7) / 8; + } + + private static byte[] fixToCurveLengthBytes(byte[] src) { + if (src.length == CURVE_LEN) { + return src; + } + + byte[] result = new byte[CURVE_LEN]; + if (src.length > CURVE_LEN) { + System.arraycopy(src, src.length - result.length, result, 0, result.length); + } else { + System.arraycopy(src, 0, result, result.length - src.length, src.length); + } + return result; + } + + private static String encodeUrlSafeBase64(byte[] input) { + return Base64.encodeBase64URLSafeString(input); } private static byte[] decryptKey(byte[] encryptedCredentialBytes, BCECPrivateKey isvPrivateKey) { diff --git a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/service/CustomFixedSdkConfigProvider.java b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/service/CustomFixedSdkConfigProvider.java index f26f7a77..e77b6a70 100644 --- a/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/service/CustomFixedSdkConfigProvider.java +++ b/yop-java-sdk-test/src/test/java/com/yeepay/yop/sdk/service/CustomFixedSdkConfigProvider.java @@ -4,9 +4,12 @@ */ package com.yeepay.yop.sdk.service; -import com.yeepay.yop.sdk.YopConstants; import com.yeepay.yop.sdk.base.config.provider.YopFixedSdkConfigProvider; import com.yeepay.yop.sdk.config.YopSdkConfig; +import com.yeepay.yop.sdk.config.provider.file.YopCertStore; +import com.yeepay.yop.sdk.config.provider.file.YopProxyConfig; + +import java.util.Arrays; /** * title: 自定义SDK配置
@@ -23,10 +26,26 @@ public class CustomFixedSdkConfigProvider extends YopFixedSdkConfigProvider { @Override protected YopSdkConfig loadSdkConfig() { YopSdkConfig yopSdkConfig = new YopSdkConfig(); - // 可以根据当前环境(测试/灰度/生产等等),自定义其他请求根路径 - yopSdkConfig.setServerRoot(YopConstants.DEFAULT_SERVER_ROOT); - yopSdkConfig.setYosServerRoot(YopConstants.DEFAULT_YOS_SERVER_ROOT); - yopSdkConfig.setSandboxServerRoot(YopConstants.DEFAULT_SANDBOX_SERVER_ROOT); + + // 示例:普通接口请求端点地址,配置多个时,遇到故障可以自动切换 + yopSdkConfig.setPreferredServerRoots(Arrays.asList("https://域名A","https://域名B")); + // 示例:文件上传、下载类接口请求端点地址 + yopSdkConfig.setYosServerRoot("https://域名C"); + // 示例:沙箱环境请求地址 + yopSdkConfig.setSandboxServerRoot("https://域名D"); + + // 示例:请求代理配置 + final YopProxyConfig proxyConfig = new YopProxyConfig(); + proxyConfig.setHost("xxx"); + proxyConfig.setPort(1234); + yopSdkConfig.setProxy(proxyConfig); + + // 示例:国密证书分发配置 + YopCertStore yopCertStore = new YopCertStore(); + yopCertStore.setEnable(true);// 默认会开启本地存储,将远程拉取的证书存放本地文件 + yopCertStore.setPath("/tmp/yop/certs");//默认存放位置 + yopSdkConfig.setYopCertStore(yopCertStore); + // 连接超时时间、读取超时时间等其他配置,可根据需要setXXX即可 return yopSdkConfig; }