From 6063c84f3228a175251e7b5e4412f79832151e03 Mon Sep 17 00:00:00 2001 From: 13009 Date: Mon, 1 Apr 2024 15:32:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=90=BA=E5=B8=A6=20?= =?UTF-8?q?=E8=AF=81=E4=B9=A6=E5=BA=8F=E5=88=97=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/czcb/scfs/api/core/Constants.java | 5 ++++ .../core/cipher/AbstractPrivacyEncryptor.java | 9 +++++++- .../scfs/api/core/cipher/AbstractSigner.java | 11 +++++++-- .../api/core/cipher/AbstractVerifier.java | 4 ++-- .../api/core/cipher/CertificateValidity.java | 5 ++-- .../api/core/cipher/DefaultCredential.java | 23 +++++++++++-------- .../api/core/cipher/DefaultValidator.java | 9 +++++--- .../core/cipher/LocalCertificateProvider.java | 19 +++++---------- .../api/core/cipher/PrivacyEncryptor.java | 2 ++ .../scfs/api/core/cipher/SignatureResult.java | 13 +++++++++-- .../com/czcb/scfs/api/core/cipher/Signer.java | 2 ++ .../czcb/scfs/api/core/cipher/Verifier.java | 2 +- .../scfs/api/core/http/AbstractApiClient.java | 5 +++- .../com/czcb/scfs/api/core/util/Strings.java | 21 +++++++++++++++++ .../cipher/AbstractPrivacyEncryptorTest.java | 2 +- .../api/core/cipher/AbstractSignerTest.java | 6 ++--- .../core/cipher/CertificateValidityTest.java | 5 ++-- .../api/core/cipher/SignatureResultTest.java | 6 ++--- .../client/ApacheHttpclientProxyTest.java | 2 +- .../http/client/ApacheHttpclientTest.java | 2 +- .../client/ApacheHttpclientTestProxyTest.java | 2 +- .../http/client/ApacheHttpclientV2Test.java | 4 ++-- .../http/client/ApacheHttpclientV3Test.java | 4 ++-- .../http/client/TestPrivacyEncryptor.java | 2 +- .../scfs/api/core/http/client/TestSigner.java | 2 +- .../scfs/api/rsa/RsaPrivacyEncryptor.java | 2 +- .../com/czcb/scfs/api/rsa/RsaProfile.java | 11 ++++++++- .../java/com/czcb/scfs/api/rsa/RsaSigner.java | 4 ++-- .../com/czcb/scfs/api/rsa/RsaProfileTest.java | 2 +- .../czcb/scfs/api/service/MockResponse.java | 2 +- .../service/cipher/TestPrivacyEncryptor.java | 2 +- .../scfs/api/service/cipher/TestSigner.java | 2 +- .../czcb/scfs/api/sm/Sm2PrivacyEncryptor.java | 4 ++-- .../java/com/czcb/scfs/api/sm/Sm2Signer.java | 4 ++-- .../java/com/czcb/scfs/api/sm/SmPrivacy.java | 10 +++++--- .../java/com/czcb/scfs/api/sm/SmProfile.java | 19 ++++++++++++++- .../com/czcb/scfs/api/sm/SmProfileTest.java | 2 +- .../spring/boot/starter/RsaConfiguration.java | 15 ++++++++---- .../starter/ScfsApiGatewayProperties.java | 19 +++++++-------- .../spring/boot/starter/SmConfiguration.java | 16 +++++++++---- .../starter/ScfsApiGatewayPropertiesTest.java | 5 ++-- .../boot/starter/SmConfigurationTest.java | 5 ++-- .../src/main/resources/application.properties | 9 ++++---- .../main/resources/merchant_certificate.pem | 12 ---------- .../main/resources/merchant_private_key.pem | 5 ---- .../main/resources/scfs_sm2_certificate.pem | 12 ++++++++++ .../src/main/resources/sm2_private_key.pem | 5 ++++ .../czcb/scfs/api/test/ApiGatewayTest.java | 4 ++-- .../com/czcb/scfs/api/test/MockResponse.java | 2 +- 49 files changed, 219 insertions(+), 121 deletions(-) delete mode 100644 scfs-api-test/src/main/resources/merchant_certificate.pem delete mode 100644 scfs-api-test/src/main/resources/merchant_private_key.pem create mode 100644 scfs-api-test/src/main/resources/scfs_sm2_certificate.pem create mode 100644 scfs-api-test/src/main/resources/sm2_private_key.pem diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/Constants.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/Constants.java index c1a8dc0..9c8581d 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/Constants.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/Constants.java @@ -17,6 +17,11 @@ public final class Constants { public static final String APP_NO = "X-SCFS-App-No"; public static final String SIGNATURE = "X-SCFS-Signature"; public static final String API_VERSION = "X-SCFS-Api-Version"; + // 银行侧证书编号 + public static final String BANK_CERTIFICATE_SERIAL = "X-SCFS-Serial"; + + // 渠道侧证书编号 + public static final String CHANNEL_CERTIFICATE_SERIAL = "X-SCFS-Channel-Serial"; // http 自定义字段 public static final String TIMESTAMP = "X-SCFS-Timestamp"; public static final String NONCE = "X-SCFS-Nonce"; diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptor.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptor.java index a95df67..b365753 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptor.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptor.java @@ -23,6 +23,7 @@ public abstract class AbstractPrivacyEncryptor implements PrivacyEncryptor { protected final String transformation; protected final PublicKey publicKey; protected final Provider provider; + private final String certificateSerial; /** * 构造敏感信息加密的抽象类 @@ -31,10 +32,11 @@ public abstract class AbstractPrivacyEncryptor implements PrivacyEncryptor { * @param publicKey 加密使用的公钥 * @param provider 安全库提供商 */ - protected AbstractPrivacyEncryptor(String transformation, PublicKey publicKey, Provider provider) { + protected AbstractPrivacyEncryptor(String transformation, PublicKey publicKey, Provider provider, String certificateSerial) { this.transformation = requireNonNull(transformation); this.publicKey = requireNonNull(publicKey); this.provider = provider; + this.certificateSerial = certificateSerial; } /** @@ -76,4 +78,9 @@ public abstract class AbstractPrivacyEncryptor implements PrivacyEncryptor { public PublicKey getPublicKey() { return publicKey; } + + @Override + public String getCertificateSerial() { + return certificateSerial; + } } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractSigner.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractSigner.java index 9b51d7b..cc0c600 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractSigner.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractSigner.java @@ -19,6 +19,7 @@ public abstract class AbstractSigner implements Signer { protected final String algorithmName; protected final PrivateKey privateKey; protected final Provider provider; + private final String certificateSerial; /** * AbstractSigner 构造函数 @@ -27,11 +28,12 @@ public abstract class AbstractSigner implements Signer { * @param algorithmName 获取Signature对象时指定的算法,例如SHA256withRSA * @param privateKey API私钥 */ - protected AbstractSigner(String algorithm, String algorithmName, PrivateKey privateKey, Provider provider) { + protected AbstractSigner(String algorithm, String algorithmName, PrivateKey privateKey, Provider provider, String certificateSerial) { this.algorithm = requireNonNull(algorithm); this.algorithmName = requireNonNull(algorithmName); this.privateKey = requireNonNull(privateKey); this.provider = provider; + this.certificateSerial = requireNonNull(certificateSerial); } private java.security.Signature getSignature() { @@ -51,7 +53,7 @@ public abstract class AbstractSigner implements Signer { java.security.Signature signature = getSignature(); signature.initSign(privateKey); signature.update(Strings.toBytes(message)); - return new SignatureResult(Base64.encodeStr(signature.sign())); + return new SignatureResult(getCertificateSerial(), Base64.encodeStr(signature.sign())); } catch (InvalidKeyException e) { throw new IllegalArgumentException(algorithm + " signature uses an illegal privateKey.", e); } catch (SignatureException e) { @@ -59,6 +61,11 @@ public abstract class AbstractSigner implements Signer { } } + @Override + public String getCertificateSerial() { + return certificateSerial; + } + @Override public String getAlgorithm() { return algorithm; diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractVerifier.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractVerifier.java index d1859f1..e4f0cf4 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractVerifier.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/AbstractVerifier.java @@ -58,9 +58,9 @@ public abstract class AbstractVerifier implements Verifier { @Override public boolean verify(String serialNumber, String message, String signature) { - X509Certificate certificate = certificateProvider.getAvailableCertificate(); + X509Certificate certificate = certificateProvider.getCertificate(serialNumber); if (certificate == null) { - logger.error("校验证书不存在"); + logger.error("校验证书不存在, 证书序列号: {}", serialNumber); return false; } return verify(certificate, message, signature); diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateValidity.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateValidity.java index d4ab14a..dfbc93e 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateValidity.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateValidity.java @@ -1,6 +1,5 @@ package com.czcb.scfs.api.core.cipher; -import java.math.BigInteger; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; @@ -14,13 +13,13 @@ import java.util.Map; */ public class CertificateValidity { - public X509Certificate getLongestCertificate(Map certificates) { + public X509Certificate getLongestCertificate(Map certificates) { if (certificates == null || certificates.isEmpty()) { return null; } X509Certificate longest = null; - for (Map.Entry item : certificates.entrySet()) { + for (Map.Entry item : certificates.entrySet()) { if (longest == null || item.getValue().getNotAfter().after(longest.getNotAfter())) { longest = item.getValue(); } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java index a9eaef5..ec83de1 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultCredential.java @@ -26,17 +26,18 @@ public class DefaultCredential implements Credential { @Override public String buildRequestAuthorization(HttpRequest request, Channel channel) { // 添加请求必要头部 - addNecessityCustomizeHeaders(request); + addNecessityCustomizeHeaders(signer, request); // 认证串 - return String.format("SCFS-%s %s", signer.getAlgorithm(), authText(channel, buildRequestSignature(request))); + return String.format("SCFS-%s %s", signer.getAlgorithm(), authorizationMessage(channel, buildRequestSignature(request))); } - private void addNecessityCustomizeHeaders(HttpRequest request) { + private void addNecessityCustomizeHeaders(Signer signer, HttpRequest request) { + request.getHttpHeaders().addHeader(CHANNEL_CERTIFICATE_SERIAL, signer.getCertificateSerial()); request.getHttpHeaders().addHeader(NONCE, request.getId()); request.getHttpHeaders().addHeader(TIMESTAMP, DateTimes.ofTimestamp()); } - private String buildRequestSignature(HttpRequest request) { + private SignatureResult buildRequestSignature(HttpRequest request) { // 签名头 X-SCFS-Nonce=xx,X-SCFS-Timestamp=xxx,X-SCFS-Secret-Key=xx String signatureHeader = getSignatureHeader(request.getHttpHeaders()); // 请求方法GET/POST,需要大写 @@ -48,14 +49,16 @@ public class DefaultCredential implements Credential { // 构建待签名串 String message = buildRequestMessage(signatureHeader, method, url, body); // 私钥加签 - return signer.sign(message).getResult(); + return signer.sign(message); } private String getSignatureHeader(HttpHeaders httpHeaders) { Map headers = httpHeaders.getHeaders(); - return NONCE + "=" + headers.get(NONCE) + "," + - TIMESTAMP + "=" + headers.get(TIMESTAMP) + "," + - SECRET_KEY + "=" + headers.get(SECRET_KEY); + return NONCE + "=" + Strings.fmtEmpty(headers.get(NONCE)) + "," + + TIMESTAMP + "=" + Strings.fmtEmpty(headers.get(TIMESTAMP)) + "," + + BANK_CERTIFICATE_SERIAL + "=" + Strings.fmtEmpty(httpHeaders.getHeader(BANK_CERTIFICATE_SERIAL)) + "," + + CHANNEL_CERTIFICATE_SERIAL + "=" + Strings.fmtEmpty(httpHeaders.getHeader(CHANNEL_CERTIFICATE_SERIAL)) + "," + + SECRET_KEY + "=" + Strings.fmtEmpty(headers.get(SECRET_KEY)); } private String getSignatureBody(HttpRequest request) { @@ -72,11 +75,11 @@ public class DefaultCredential implements Credential { requestBody + "\n"; } - private String authText(Channel channel, String signature) { + private String authorizationMessage(Channel channel, SignatureResult result) { // 认证串 return CHANNEL_NO + "=" + channel.getChannelNo() + "," + APP_NO + "=" + channel.getAppNo() + "," + - SIGNATURE + "=" + signature; + SIGNATURE + "=" + result.getSignature(); } @Override diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java index 702b6b0..f39251c 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/DefaultValidator.java @@ -66,7 +66,6 @@ public final class DefaultValidator implements Validator { // 校验时间戳 validateTimestamp(request, response); - // 请求头包含 SecretKey 进行验签 if (!headerContainsSecretKey(response)) { return; } @@ -80,7 +79,9 @@ public final class DefaultValidator implements Validator { response.getHttpHeaders().getHeader(REQUEST_ID))); } - if (!verifier.verify(null, message, signature)) { + // 签名证书编号 + String serialNumber = response.getHttpHeaders().getHeader(BANK_CERTIFICATE_SERIAL); + if (!verifier.verify(serialNumber, message, signature)) { httpLogger.logResponseError(request, response); throw new ValidationException(String.format("响应校验失败, 签名校验未通过, Request-Id=%s", response.getHttpHeaders().getHeader(REQUEST_ID))); @@ -91,6 +92,8 @@ public final class DefaultValidator implements Validator { } private boolean headerContainsSecretKey(OriginalResponse response) { - return Strings.isNotEmpty(response.getHttpHeaders().getHeader(SECRET_KEY)); + return Strings.isNotEmpty(response.getHttpHeaders().getHeader(SECRET_KEY)) + && Strings.isNotEmpty(response.getHttpHeaders().getHeader(CHANNEL_CERTIFICATE_SERIAL)) + && Strings.isNotEmpty(response.getHttpHeaders().getHeader(BANK_CERTIFICATE_SERIAL)); } } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/LocalCertificateProvider.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/LocalCertificateProvider.java index 916be59..9fb0139 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/LocalCertificateProvider.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/LocalCertificateProvider.java @@ -1,15 +1,14 @@ package com.czcb.scfs.api.core.cipher; -import com.czcb.scfs.api.core.util.DateTimes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.math.BigInteger; import java.security.cert.X509Certificate; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import static com.czcb.scfs.api.core.Constants.HEX; +import static com.czcb.scfs.api.core.util.Strings.timeRange; +import static com.czcb.scfs.api.core.util.Strings.toHexUpper; /** * 证书提供器的简单实现,证书存储在内存ConcurrentHashMap中 @@ -19,7 +18,7 @@ import static com.czcb.scfs.api.core.Constants.HEX; public final class LocalCertificateProvider implements CertificateProvider { private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ConcurrentHashMap certificates = new ConcurrentHashMap<>(); + private final ConcurrentHashMap certificates = new ConcurrentHashMap<>(); // 证书有效期校验 private final CertificateValidity validity = new CertificateValidity(); private X509Certificate availableCertificate; @@ -40,22 +39,16 @@ public final class LocalCertificateProvider implements CertificateProvider { */ @Override public X509Certificate getCertificate(String serialNumber) { - BigInteger key = new BigInteger(serialNumber, HEX); - return certificates.get(key); + return certificates.get(serialNumber); } public void addX509Certificate(X509Certificate certificate) { if (!validity.withinValidity(certificate)) { - logger.error("证书已失效, 序列号:{}, 有效范围:[{}]", certificate.getSerialNumber(), timeRange(certificate)); + logger.error("证书已失效, 序列号:{}, 有效范围:[{}]", toHexUpper(certificate.getSerialNumber()), timeRange(certificate)); return; } - certificates.put(certificate.getSerialNumber(), certificate); - } - - private String timeRange(X509Certificate certificate) { - return String.format("%s,%s", DateTimes.ofPatternDate(certificate.getNotBefore()), - DateTimes.ofPatternDate(certificate.getNotAfter())); + certificates.put(toHexUpper(certificate.getSerialNumber()), certificate); } /** diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/PrivacyEncryptor.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/PrivacyEncryptor.java index e1ed9a6..1a7cf28 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/PrivacyEncryptor.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/PrivacyEncryptor.java @@ -23,4 +23,6 @@ public interface PrivacyEncryptor { * @return PublicKey */ PublicKey getPublicKey(); + + String getCertificateSerial(); } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/SignatureResult.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/SignatureResult.java index 8bc7feb..1c260dc 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/SignatureResult.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/SignatureResult.java @@ -10,18 +10,27 @@ import static java.util.Objects.requireNonNull; * @since 2.0.0 */ public class SignatureResult { + private final String certificateSerial; private final String signature; - public SignatureResult(String signature) { + public SignatureResult(String certificateSerial, String signature) { + this.certificateSerial = certificateSerial; this.signature = requireNonNull(signature); } + /** + * @return 渠道对应的证书序列号 + */ + public String getCertificateSerial() { + return certificateSerial; + } + /** * 获取签名 * * @return 签名 */ - public String getResult() { + public String getSignature() { return signature; } diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Signer.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Signer.java index b91ed57..ffb42c0 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Signer.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Signer.java @@ -14,6 +14,8 @@ public interface Signer { */ SignatureResult sign(String message); + String getCertificateSerial(); + /** * 获取签名算法 * diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Verifier.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Verifier.java index 4be24fb..f450757 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Verifier.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/Verifier.java @@ -15,4 +15,4 @@ public interface Verifier { * @return 是否验证通过 */ boolean verify(String serialNumber, String message, String signature); -} +} \ No newline at end of file diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java index de89fd6..ac7dacf 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/http/AbstractApiClient.java @@ -139,9 +139,12 @@ public abstract class AbstractApiClient implements ApiClient { // 生成密钥 byte[] secretKey = cipher.getSecretKey(); // 密钥进行非对称加密 - String encryptSecretKey = privacy.getEncryptor().encrypt(Strings.toStr(secretKey)); + PrivacyEncryptor encryptor = privacy.getEncryptor(); + String encryptSecretKey = encryptor.encrypt(Strings.toStr(secretKey)); // 加密后的密钥放入到请求头 httpRequest.getHttpHeaders().addHeader(SECRET_KEY, encryptSecretKey); + // 加密证书序列号 + httpRequest.getHttpHeaders().addHeader(BANK_CERTIFICATE_SERIAL, encryptor.getCertificateSerial()); // 原始body byte[] originalBody = httpRequest.getRequestBody().getBody(); diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Strings.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Strings.java index 6ddb081..017d9b4 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Strings.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Strings.java @@ -2,12 +2,16 @@ package com.czcb.scfs.api.core.util; import com.czcb.scfs.api.core.exception.EncodingException; +import java.math.BigInteger; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.SecureRandom; +import java.security.cert.X509Certificate; import java.util.Objects; import java.util.Random; +import static com.czcb.scfs.api.core.Constants.HEX; + /** * @author wangwei * @since 2.0.0 @@ -93,4 +97,21 @@ public class Strings { throw new EncodingException(e); } } + + public static String fmtEmpty(String text) { + return Strings.isEmpty(text) ? "" : text; + } + + public static String toHexUpper(BigInteger data) { + return data.toString(HEX).toUpperCase(); + } + + public static String toUpper(String data) { + return data.toUpperCase(); + } + + public static String timeRange(X509Certificate certificate) { + return String.format("%s,%s", DateTimes.ofPatternDate(certificate.getNotBefore()), + DateTimes.ofPatternDate(certificate.getNotAfter())); + } } diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptorTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptorTest.java index ca966d2..02ce59c 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptorTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractPrivacyEncryptorTest.java @@ -43,7 +43,7 @@ class AbstractPrivacyEncryptorTest { private static class TestPrivacyEncryptorv2 extends AbstractPrivacyEncryptor { public TestPrivacyEncryptorv2(PublicKey publicKey, Provider provider) { - super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, provider); + super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, provider, ""); } } } \ No newline at end of file diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractSignerTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractSignerTest.java index 10c8436..0dde518 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractSignerTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/AbstractSignerTest.java @@ -17,7 +17,7 @@ class AbstractSignerTest { void sign() { TestSigner signer = new TestSigner(KeyText.loadTestPrivateKeyRSA(), null); String message = "1234567890"; - String sg = signer.sign(message).getResult(); + String sg = signer.sign(message).getSignature(); Assertions.assertNotNull(sg); Assertions.assertEquals("SHA256withRSA", signer.getAlgorithm()); @@ -41,12 +41,12 @@ class AbstractSignerTest { * @param privateKey API私钥 */ public TestSigner(PrivateKey privateKey, Provider provider) { - super("SHA256withRSA", "SHA256withRSA", privateKey, provider); + super("SHA256withRSA", "SHA256withRSA", privateKey, provider, ""); } public TestSigner(PrivateKey privateKey) { // 错误的签名算法 - super("SHA256withRSA1", "SHA256withRSA1", privateKey, null); + super("SHA256withRSA1", "SHA256withRSA1", privateKey, null, ""); } } } \ No newline at end of file diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/CertificateValidityTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/CertificateValidityTest.java index 79acbf6..c3f42ab 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/CertificateValidityTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/CertificateValidityTest.java @@ -5,7 +5,6 @@ import com.czcb.scfs.api.core.util.PemFile; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.math.BigInteger; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; @@ -64,9 +63,9 @@ class CertificateValidityTest { Assertions.assertNull(validity.getLongestCertificate(null)); Assertions.assertNull(validity.getLongestCertificate(new HashMap<>())); - ConcurrentHashMap certificates = new ConcurrentHashMap<>(); + ConcurrentHashMap certificates = new ConcurrentHashMap<>(); X509Certificate certificate = KeyText.loadTestRSA(); - certificates.put(certificate.getSerialNumber(), certificate); + certificates.put(certificate.getSerialNumber().toString(), certificate); Assertions.assertNotNull(validity.getLongestCertificate(certificates)); } diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/SignatureResultTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/SignatureResultTest.java index 336a757..2ecb0aa 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/SignatureResultTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/cipher/SignatureResultTest.java @@ -7,13 +7,13 @@ class SignatureResultTest { @Test void getResult() { - SignatureResult result = new SignatureResult("123456"); - Assertions.assertEquals("123456", result.getResult()); + SignatureResult result = new SignatureResult("", "123456"); + Assertions.assertEquals("123456", result.getSignature()); } @Test void testToString() { - SignatureResult result = new SignatureResult("123456"); + SignatureResult result = new SignatureResult("", "123456"); Assertions.assertEquals("{\"signature\":\"123456\"}", result.toString()); } } \ No newline at end of file diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java index 6a58918..ce6206b 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientProxyTest.java @@ -84,7 +84,7 @@ class ApacheHttpclientProxyTest { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.POST.getUpperName()) diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java index 0827ed2..195cb4f 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTest.java @@ -123,7 +123,7 @@ class ApacheHttpclientTest { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.POST.getUpperName()) diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java index 192dbc3..270526c 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientTestProxyTest.java @@ -84,7 +84,7 @@ class ApacheHttpclientTestProxyTest { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.POST.getUpperName()) diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java index d157c33..994a878 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV2Test.java @@ -85,7 +85,7 @@ class ApacheHttpclientV2Test { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.POST.getUpperName()) @@ -129,7 +129,7 @@ class ApacheHttpclientV2Test { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.GET.getUpperName()) diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java index 72bddf7..dcf8202 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/ApacheHttpclientV3Test.java @@ -85,7 +85,7 @@ class ApacheHttpclientV3Test { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.POST.getUpperName()) @@ -127,7 +127,7 @@ class ApacheHttpclientV3Test { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); client.when(request() .withMethod(HttpMethod.POST.getUpperName()) diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java index 45b0710..c777722 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestPrivacyEncryptor.java @@ -11,6 +11,6 @@ public class TestPrivacyEncryptor extends AbstractPrivacyEncryptor { * @param publicKey 加密使用的公钥 */ public TestPrivacyEncryptor(PublicKey publicKey) { - super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null); + super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null, ""); } } diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestSigner.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestSigner.java index 995e787..5a5af34 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestSigner.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/client/TestSigner.java @@ -14,6 +14,6 @@ public class TestSigner extends AbstractSigner { * @param privateKey API私钥 */ public TestSigner(PrivateKey privateKey) { - super("SHA256withRSA", "SHA256withRSA", privateKey, null); + super("SHA256withRSA", "SHA256withRSA", privateKey, null, ""); } } diff --git a/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaPrivacyEncryptor.java b/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaPrivacyEncryptor.java index a1388e4..0bfab21 100644 --- a/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaPrivacyEncryptor.java +++ b/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaPrivacyEncryptor.java @@ -16,6 +16,6 @@ public final class RsaPrivacyEncryptor extends AbstractPrivacyEncryptor { * @param publicKey 加密使用的公钥 */ public RsaPrivacyEncryptor(PublicKey publicKey) { - super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null); + super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null, ""); } } diff --git a/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaProfile.java b/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaProfile.java index 2589732..46e76dd 100644 --- a/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaProfile.java +++ b/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaProfile.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static com.czcb.scfs.api.core.Constants.HEX; import static java.util.Objects.requireNonNull; /** @@ -40,6 +41,11 @@ public class RsaProfile extends AbstractProfile { return privateKey(PemFile.loadPrivateKeyFromAbsolutePath(privateKeyPath)); } + public Builder addCertificates(List certificate) { + certificates.addAll(certificate); + return this; + } + public Builder addCertificate(X509Certificate certificate) { certificates.add(certificate); return this; @@ -64,7 +70,10 @@ public class RsaProfile extends AbstractProfile { CertificateProvider certificateProvider = new LocalCertificateProvider(certificates); this.privacy = new RsaPrivacy(privateKey, certificateProvider); - this.signature = new DefaultSignature(certificateProvider, new RsaSigner(privateKey), new RsaVerifier(certificateProvider)); + X509Certificate certificate = certificateProvider.getAvailableCertificate(); + this.signature = new DefaultSignature(certificateProvider, + new RsaSigner(privateKey, certificate.getSerialNumber().toString(HEX)), + new RsaVerifier(certificateProvider)); httpProfile(httpProfile); if (Objects.isNull(httpProfile)) { diff --git a/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaSigner.java b/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaSigner.java index ea2ddb4..8b79d93 100644 --- a/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaSigner.java +++ b/scfs-api-rsa/src/main/java/com/czcb/scfs/api/rsa/RsaSigner.java @@ -13,7 +13,7 @@ public class RsaSigner extends AbstractSigner { /** * @param privateKey API私钥 */ - protected RsaSigner(PrivateKey privateKey) { - super("SHA256withRSA", "SHA256withRSA", privateKey, null); + protected RsaSigner(PrivateKey privateKey, String certificateSerialNumber) { + super("SHA256withRSA", "SHA256withRSA", privateKey, null, certificateSerialNumber); } } diff --git a/scfs-api-rsa/src/test/java/com/czcb/scfs/api/rsa/RsaProfileTest.java b/scfs-api-rsa/src/test/java/com/czcb/scfs/api/rsa/RsaProfileTest.java index 6a05862..573e4cf 100644 --- a/scfs-api-rsa/src/test/java/com/czcb/scfs/api/rsa/RsaProfileTest.java +++ b/scfs-api-rsa/src/test/java/com/czcb/scfs/api/rsa/RsaProfileTest.java @@ -27,7 +27,7 @@ class RsaProfileTest { Assertions.assertNotNull(profile); String message = "1234567"; - String signResult = profile.getSignature().getSigner().sign(message).getResult(); + String signResult = profile.getSignature().getSigner().sign(message).getSignature(); Assertions.assertTrue(profile.getSignature().getVerifier().verify(null, message, signResult)); Assertions.assertEquals("0000", profile.getChannel().getChannelNo()); diff --git a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java index 6fe4da2..7e88d59 100644 --- a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java +++ b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/MockResponse.java @@ -80,7 +80,7 @@ public class MockResponse { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); return mock; } } diff --git a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestPrivacyEncryptor.java b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestPrivacyEncryptor.java index b19df12..c2de389 100644 --- a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestPrivacyEncryptor.java +++ b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestPrivacyEncryptor.java @@ -11,6 +11,6 @@ public class TestPrivacyEncryptor extends AbstractPrivacyEncryptor { * @param publicKey 加密使用的公钥 */ protected TestPrivacyEncryptor(PublicKey publicKey) { - super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null); + super("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", publicKey, null, ""); } } diff --git a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestSigner.java b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestSigner.java index e58d1a1..3903236 100644 --- a/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestSigner.java +++ b/scfs-api-service/src/test/java/com/czcb/scfs/api/service/cipher/TestSigner.java @@ -14,6 +14,6 @@ public class TestSigner extends AbstractSigner { * @param privateKey API私钥 */ public TestSigner(PrivateKey privateKey) { - super("SHA256withRSA", "SHA256withRSA", privateKey, null); + super("SHA256withRSA", "SHA256withRSA", privateKey, null, ""); } } diff --git a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2PrivacyEncryptor.java b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2PrivacyEncryptor.java index 949e5a5..1c956c0 100644 --- a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2PrivacyEncryptor.java +++ b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2PrivacyEncryptor.java @@ -13,7 +13,7 @@ public final class Sm2PrivacyEncryptor extends AbstractPrivacyEncryptor { /** * @param publicKey 请求的敏感信息加密时使用的国密公钥 */ - public Sm2PrivacyEncryptor(PublicKey publicKey) { - super("SM2", publicKey, SmProvider.getProvider()); + public Sm2PrivacyEncryptor(PublicKey publicKey, String certificateSerial) { + super("SM2", publicKey, SmProvider.getProvider(), certificateSerial); } } diff --git a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2Signer.java b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2Signer.java index 5a32e16..a1d0d8d 100644 --- a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2Signer.java +++ b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/Sm2Signer.java @@ -14,7 +14,7 @@ public class Sm2Signer extends AbstractSigner { * * @param privateKey API私钥 */ - public Sm2Signer(PrivateKey privateKey) { - super("SM3withSM2", "SM2", privateKey, SmProvider.getProvider()); + public Sm2Signer(PrivateKey privateKey, String certificateSerialNumber) { + super("SM3withSM2", "SM2", privateKey, SmProvider.getProvider(), certificateSerialNumber); } } diff --git a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmPrivacy.java b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmPrivacy.java index d42b168..07099fc 100644 --- a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmPrivacy.java +++ b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmPrivacy.java @@ -1,6 +1,7 @@ package com.czcb.scfs.api.sm; import com.czcb.scfs.api.core.cipher.*; +import com.czcb.scfs.api.core.util.Strings; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -20,10 +21,13 @@ public class SmPrivacy implements Privacy { public SmPrivacy(PrivateKey privateKey, CertificateProvider certificateProvider) { this.privacyDecryptor = new Sm2PrivacyDecryptor(privateKey); - - X509Certificate certificate = certificateProvider.getAvailableCertificate(); - this.privacyEncryptor = new Sm2PrivacyEncryptor(certificate.getPublicKey()); this.secretCipher = new Sm4SecretCipher(); + + // 获取有效的证书,多个取最长过期时间 + X509Certificate certificate = certificateProvider.getAvailableCertificate(); + + String serialNumber = Strings.toHexUpper(certificate.getSerialNumber()); + this.privacyEncryptor = new Sm2PrivacyEncryptor(certificate.getPublicKey(), serialNumber); } @Override diff --git a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmProfile.java b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmProfile.java index 0e54960..24c35b2 100644 --- a/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmProfile.java +++ b/scfs-api-sm/src/main/java/com/czcb/scfs/api/sm/SmProfile.java @@ -32,6 +32,10 @@ public final class SmProfile extends AbstractProfile { } private PrivateKey privateKey; + /** + * 证书序列号 + */ + private String certificateSerial; private final List certificates = new ArrayList<>(); private Privacy privacy; private Signature signature; @@ -47,6 +51,17 @@ public final class SmProfile extends AbstractProfile { return privateKey(PemFile.loadPrivateKeyFromAbsolutePath(privateKeyPath, "EC", KonaProvider.NAME)); } + public Builder certificateSerial(String certificateSerial) { + Objects.requireNonNull(certificateSerial); + this.certificateSerial = certificateSerial.toUpperCase(); + return this; + } + + public Builder addCertificates(List certificate) { + certificates.addAll(certificate); + return this; + } + public Builder addCertificate(X509Certificate certificate) { certificates.add(certificate); return this; @@ -74,7 +89,9 @@ public final class SmProfile extends AbstractProfile { // 加密器 this.privacy = new SmPrivacy(privateKey, certificateProvider); // 签名器 - this.signature = new DefaultSignature(certificateProvider, new Sm2Signer(privateKey), new Sm2Verifier(certificateProvider)); + this.signature = new DefaultSignature(certificateProvider, + new Sm2Signer(privateKey, certificateSerial), + new Sm2Verifier(certificateProvider)); httpProfile(httpProfile); if (Objects.isNull(httpProfile)) { diff --git a/scfs-api-sm/src/test/java/com/czcb/scfs/api/sm/SmProfileTest.java b/scfs-api-sm/src/test/java/com/czcb/scfs/api/sm/SmProfileTest.java index 237a1b8..faf0628 100644 --- a/scfs-api-sm/src/test/java/com/czcb/scfs/api/sm/SmProfileTest.java +++ b/scfs-api-sm/src/test/java/com/czcb/scfs/api/sm/SmProfileTest.java @@ -28,7 +28,7 @@ class SmProfileTest { Assertions.assertNotNull(profile); String message = "1234567"; - String signResult = profile.getSignature().getSigner().sign(message).getResult(); + String signResult = profile.getSignature().getSigner().sign(message).getSignature(); Assertions.assertTrue(profile.getSignature().getVerifier().verify(null, message, signResult)); Assertions.assertEquals("0000", profile.getChannel().getChannelNo()); diff --git a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/RsaConfiguration.java b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/RsaConfiguration.java index bb419c7..d7c19c9 100644 --- a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/RsaConfiguration.java +++ b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/RsaConfiguration.java @@ -10,7 +10,9 @@ import javax.annotation.Resource; import java.io.InputStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * @since 2.0.0 @@ -27,13 +29,16 @@ public class RsaConfiguration extends AbstractAutoConfiguration { return properties; } - private X509Certificate getCertificate() { + private List getCertificates() { if (Objects.equals(getProperties().getCipher().getStoreType(), StoreType.FILE)) { - return PemFile.loadX509FromAbsolutePath(getProperties().getCipher().getCertificate()); + return getProperties().getCipher().getCertificate().stream() + .map(PemFile::loadX509FromAbsolutePath) + .collect(Collectors.toList()); } - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(getProperties().getCipher().getCertificate()); - return PemFile.loadX509FromStream(inputStream); + return getProperties().getCipher().getCertificate().stream() + .map(cert -> PemFile.loadX509FromStream(getClass().getClassLoader().getResourceAsStream(cert))) + .collect(Collectors.toList()); } private PrivateKey getPrivateKey() { @@ -50,7 +55,7 @@ public class RsaConfiguration extends AbstractAutoConfiguration { .channel(getChannel()) .httpProfile(getHttpProfile()) .privateKey(getPrivateKey()) - .addCertificate(getCertificate()) + .addCertificates(getCertificates()) .build(); } } diff --git a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java index dcedb76..db45991 100644 --- a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java +++ b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayProperties.java @@ -4,6 +4,7 @@ import com.czcb.scfs.api.core.cipher.StoreType; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -116,13 +117,13 @@ public class ScfsApiGatewayProperties { private String channelPrivateKey; /** - * 渠道端证书文件地址 + * 渠道端证书序列号 */ - private String channelCertificate; + private String channelCertificateSerial; /** * 银行侧证书地址 */ - private String certificate; + private List certificate; public StoreType getStoreType() { return storeType; @@ -140,19 +141,19 @@ public class ScfsApiGatewayProperties { this.channelPrivateKey = channelPrivateKey; } - public String getChannelCertificate() { - return channelCertificate; + public String getChannelCertificateSerial() { + return channelCertificateSerial; } - public void setChannelCertificate(String channelCertificate) { - this.channelCertificate = channelCertificate; + public void setChannelCertificateSerial(String channelCertificateSerial) { + this.channelCertificateSerial = channelCertificateSerial; } - public String getCertificate() { + public List getCertificate() { return certificate; } - public void setCertificate(String certificate) { + public void setCertificate(List certificate) { this.certificate = certificate; } } diff --git a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/SmConfiguration.java b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/SmConfiguration.java index ecde487..0e3ecd3 100644 --- a/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/SmConfiguration.java +++ b/scfs-api-spring-boot-starter/src/main/java/com/czcb/scfs/spring/boot/starter/SmConfiguration.java @@ -13,7 +13,9 @@ import javax.annotation.Resource; import java.io.InputStream; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * @since 2.0.0 @@ -29,13 +31,16 @@ public class SmConfiguration extends AbstractAutoConfiguration { return properties; } - private X509Certificate getCertificate() { + private List getCertificates() { if (Objects.equals(getProperties().getCipher().getStoreType(), StoreType.FILE)) { - return PemFile.loadX509FromAbsolutePath(getProperties().getCipher().getCertificate(), KonaProvider.NAME); + return getProperties().getCipher().getCertificate().stream() + .map(cert -> PemFile.loadX509FromAbsolutePath(cert, KonaProvider.NAME)) + .collect(Collectors.toList()); } - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(getProperties().getCipher().getCertificate()); - return PemFile.loadX509FromStream(inputStream, KonaProvider.NAME); + return getProperties().getCipher().getCertificate().stream() + .map(cert -> PemFile.loadX509FromStream(getClass().getClassLoader().getResourceAsStream(cert), KonaProvider.NAME)) + .collect(Collectors.toList()); } private PrivateKey getPrivateKey() { @@ -54,7 +59,8 @@ public class SmConfiguration extends AbstractAutoConfiguration { .channel(getChannel()) .httpProfile(getHttpProfile()) .privateKey(getPrivateKey()) - .addCertificate(getCertificate()) + .certificateSerial(properties.getCipher().getChannelCertificateSerial()) + .addCertificates(getCertificates()) .build(); } } diff --git a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java index 21fbd65..635da56 100644 --- a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java +++ b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/ScfsApiGatewayPropertiesTest.java @@ -1,6 +1,7 @@ package com.czcb.scfs.spring.boot.starter; import com.czcb.scfs.api.core.cipher.StoreType; +import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -84,8 +85,8 @@ class ScfsApiGatewayPropertiesTest { ScfsApiGatewayProperties.Cipher cipher = new ScfsApiGatewayProperties.Cipher(); cipher.setStoreType(StoreType.RESOURCES); cipher.setChannelPrivateKey("/home/key.pem"); - cipher.setChannelCertificate("/home/c1.pem"); - cipher.setCertificate("/home/c2.pem"); + cipher.setChannelCertificateSerial("/home/c1.pem"); + cipher.setCertificate(Lists.newArrayList("/home/c2.pem")); ScfsApiGatewayProperties properties = new ScfsApiGatewayProperties(); properties.setCipher(cipher); diff --git a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java index a6f32aa..4b61c7e 100644 --- a/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java +++ b/scfs-api-spring-boot-starter/src/test/java/com/czcb/scfs/spring/boot/starter/SmConfigurationTest.java @@ -1,6 +1,7 @@ package com.czcb.scfs.spring.boot.starter; import com.czcb.scfs.api.core.cipher.StoreType; +import org.assertj.core.util.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -52,8 +53,8 @@ class SmConfigurationTest { ScfsApiGatewayProperties.Cipher cipher = new ScfsApiGatewayProperties.Cipher(); cipher.setStoreType(StoreType.RESOURCES); cipher.setChannelPrivateKey("/home/key.pem"); - cipher.setChannelCertificate("/home/c1.pem"); - cipher.setCertificate("/home/c2.pem"); + cipher.setChannelCertificateSerial("/home/c1.pem"); + cipher.setCertificate(Lists.list("/home/c2.pem")); properties.setCipher(cipher); properties.setOnline(true); diff --git a/scfs-api-test/src/main/resources/application.properties b/scfs-api-test/src/main/resources/application.properties index 0f7fa70..c319107 100644 --- a/scfs-api-test/src/main/resources/application.properties +++ b/scfs-api-test/src/main/resources/application.properties @@ -1,9 +1,10 @@ scfs.api-gateway.online=false scfs.api-gateway.host=http://127.0.0.1:8088/api-gateway -scfs.api-gateway.channel.app-no=10001 -scfs.api-gateway.channel.channel-no=1000 +scfs.api-gateway.channel.channel-no=0000 +scfs.api-gateway.channel.app-no=000000 scfs.api-gateway.cipher.store-type=resources #scfs.api-gateway.cipher.channel-private-key=rsa_channel_private_key.pem #scfs.api-gateway.cipher.certificate=rsa_channel_certificate.pem -scfs.api-gateway.cipher.channel-private-key=merchant_private_key.pem -scfs.api-gateway.cipher.certificate=merchant_certificate.pem \ No newline at end of file +scfs.api-gateway.cipher.channel-private-key=sm2_private_key.pem +scfs.api-gateway.cipher.channel-certificate-serial=12312312879h89u8hhh89h989 +scfs.api-gateway.cipher.certificate=scfs_sm2_certificate.pem \ No newline at end of file diff --git a/scfs-api-test/src/main/resources/merchant_certificate.pem b/scfs-api-test/src/main/resources/merchant_certificate.pem deleted file mode 100644 index 5a424ab..0000000 --- a/scfs-api-test/src/main/resources/merchant_certificate.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICKDCCAc+gAwIBAgIhAMPbc74uYS3FysLkYMHB5P+q7DCzriTkomU9a3S9QAL9MAoGCCqBHM9V -AYN1MIGNMQswCQYDVQQGEwJDTjESMBAGA1UECAwJ5rWZ5rGf55yBMRIwEAYDVQQHDAnmna3lt57l -uIIxITAfBgNVBAoMGOa1meaxn+eooOW3nuWVhuS4mumTtuihjDEYMBYGA1UECwwP5pWw5a2X6YeR -6J6N6YOoMRkwFwYDVQQDDBBzY2ZzLmN6Y2IuY29tLmNuMB4XDTI0MDIyMTA1NDU0MFoXDTI5MDIy -MTA1NDU0MFowgY0xCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAnmtZnmsZ/nnIExEjAQBgNVBAcMCead -reW3nuW4gjEhMB8GA1UECgwY5rWZ5rGf56ig5bee5ZWG5Lia6ZO26KGMMRgwFgYDVQQLDA/mlbDl -rZfph5Hono3pg6gxGTAXBgNVBAMMEHNjZnMuY3pjYi5jb20uY24wWTATBgcqhkjOPQIBBggqgRzP -VQGCLQNCAAS3xG2kVL1N2p71VUsFO5WyD2QCzq5SFarPpKlYpEXCx57QeEUypZFmvzTDNpWv5y11 -e798SvOBSmZTFrF6CStIMAoGCCqBHM9VAYN1A0cAMEQCIHVbD5xUf/6OORWvelFm9wSHuTQ5NRjC -iKP4iOpCTS+oAiB4VVZ0XOdVr3ENWvzI2c1ZVvkLXy4T66CQc/Pg57P0/Q== ------END CERTIFICATE----- \ No newline at end of file diff --git a/scfs-api-test/src/main/resources/merchant_private_key.pem b/scfs-api-test/src/main/resources/merchant_private_key.pem deleted file mode 100644 index 6a2f0c9..0000000 --- a/scfs-api-test/src/main/resources/merchant_private_key.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgplcULkGrRiFDra/9MywSc4Ornu/k -jdlSSXCqoHPPM0WgCgYIKoEcz1UBgi2hRANCAAS3xG2kVL1N2p71VUsFO5WyD2QCzq5SFarPpKlY -pEXCx57QeEUypZFmvzTDNpWv5y11e798SvOBSmZTFrF6CStI ------END PRIVATE KEY----- \ No newline at end of file diff --git a/scfs-api-test/src/main/resources/scfs_sm2_certificate.pem b/scfs-api-test/src/main/resources/scfs_sm2_certificate.pem new file mode 100644 index 0000000..fa6fb65 --- /dev/null +++ b/scfs-api-test/src/main/resources/scfs_sm2_certificate.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIICGTCCAcCgAwIBAgIhAPtOPc4Cl0OO05B3H0ZXa5L9fmt/HpvRKn2H6CG1mUjlMAoGCCqBHM9V +AYN1MIGNMQswCQYDVQQGEwJDTjESMBAGA1UECAwJ5rWZ5rGf55yBMRIwEAYDVQQHDAnmna3lt57l +uIIxITAfBgNVBAoMGOa1meaxn+eooOW3nuWVhuS4mumTtuihjDEYMBYGA1UECwwP5pWw5a2X6YeR +6J6N6YOoMRkwFwYDVQQDDBBzY2ZzLmN6Y2IuY29tLmNuMB4XDTI0MDMyNjA4NDU1MFoXDTI5MDMy +NjA4NDU1MFowfzELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCea1meaxn+ecgTESMBAGA1UEBwwJ5p2t +5bee5biCMSEwHwYDVQQKDBjmtZnmsZ/nqKDlt57llYbkuJrpk7booYwxJTAjBgNVBAsMHOaVsOWt +l+mHkeiejemDqC3mtYvor5Xor4HkuaYwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAT0KcBDXLn6 +Zv5vsEtuDzZclr30phN++uOVaQoFcDhhbeZlqRSIqRdg6YstCHUenN7NL2S1b1JlsWeIUxGCndZU +MAoGCCqBHM9VAYN1A0cAMEQCIGp00kvdZqtobJ9X7YHAKBtGZXnXBKeuhdM+ZIhYelwBAiB1Dv0s +0ahzWO+jJ/DVN8zM1b4noePZArFed8qHXsvt+Q== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/scfs-api-test/src/main/resources/sm2_private_key.pem b/scfs-api-test/src/main/resources/sm2_private_key.pem new file mode 100644 index 0000000..1e740a9 --- /dev/null +++ b/scfs-api-test/src/main/resources/sm2_private_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgR6jTgU+vLuT00j3QM7b/fPJ9iUGR +zZeVwr90JeVhChygCgYIKoEcz1UBgi2hRANCAAT0KcBDXLn6Zv5vsEtuDzZclr30phN++uOVaQoF +cDhhbeZlqRSIqRdg6YstCHUenN7NL2S1b1JlsWeIUxGCndZU +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/scfs-api-test/src/test/java/com/czcb/scfs/api/test/ApiGatewayTest.java b/scfs-api-test/src/test/java/com/czcb/scfs/api/test/ApiGatewayTest.java index 063bec8..f1303b2 100644 --- a/scfs-api-test/src/test/java/com/czcb/scfs/api/test/ApiGatewayTest.java +++ b/scfs-api-test/src/test/java/com/czcb/scfs/api/test/ApiGatewayTest.java @@ -31,8 +31,8 @@ class ApiGatewayTest { @Test void testQuery() { QueryBalanceRequest queryBalanceRequest = new QueryBalanceRequest(); - queryBalanceRequest.setChannelNo("1012"); - queryBalanceRequest.setAppNo(""); + queryBalanceRequest.setChannelNo("0000"); + queryBalanceRequest.setAppNo("000000"); queryBalanceRequest.setSerialNo(UUID.randomUUID().toString().replace("-", "")); queryBalanceRequest.setAccountNo("1012230221000010"); queryBalanceRequest.setRelationAcct("1"); diff --git a/scfs-api-test/src/test/java/com/czcb/scfs/api/test/MockResponse.java b/scfs-api-test/src/test/java/com/czcb/scfs/api/test/MockResponse.java index a66bd66..1301cee 100644 --- a/scfs-api-test/src/test/java/com/czcb/scfs/api/test/MockResponse.java +++ b/scfs-api-test/src/test/java/com/czcb/scfs/api/test/MockResponse.java @@ -40,7 +40,7 @@ public class MockResponse { SECRET_KEY + "=" + secretKey; String message = buildAuth + "\n" + responseBody + "\n"; - mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getResult()); + mock.withHeader(SIGNATURE, apiClient.getProfile().getSignature().getSigner().sign(message).getSignature()); return mock; } }