diff --git a/scfs-api-core/pom.xml b/scfs-api-core/pom.xml index b20c346..70c8f30 100644 --- a/scfs-api-core/pom.xml +++ b/scfs-api-core/pom.xml @@ -32,5 +32,18 @@ httpclient5 5.2.3 + + + ch.qos.logback + logback-classic + 1.2.12 + test + + + ch.qos.logback + logback-core + 1.2.12 + test + diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateProvider.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateProvider.java index bf461de..1b97340 100644 --- a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateProvider.java +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/cipher/CertificateProvider.java @@ -22,4 +22,9 @@ public interface CertificateProvider { * @return X.509证书实例 */ X509Certificate getAvailableCertificate(); + + /** + * 校验证书是否有效 + */ + boolean isAvailableCertificate(String serialNumber); } 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 436e2d5..28ac73d 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 @@ -46,6 +46,16 @@ public final class DefaultValidator implements Validator { validateResponseSignature(response, channel); } + @Override + public void validate(HttpRequest newRequest) { + // 校验证书 + CertificateProvider provider = getProfile().getSignature().getCertificateProvider(); + Signer signer = getProfile().getSignature().getSigner(); + if (!provider.isAvailableCertificate(signer.getCertificateSerial())) { + throw new ValidationException(String.format("证书已失效, 序列号:%s", signer.getCertificateSerial())); + } + } + public void isInvalidHttpCode(OriginalResponse response) { if (response.getStatusCode() < HTTP_OK || response.getStatusCode() >= HTTP_MULT_CHOICE) { throw new ValidationException(String.format("校验失败, HttpStatusCode=%s, Request-Id=%s, HttpResponseBody=%s", 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 9fb0139..b8454e0 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 @@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory; import java.security.cert.X509Certificate; import java.util.List; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import static com.czcb.scfs.api.core.util.Strings.timeRange; @@ -24,11 +25,9 @@ public final class LocalCertificateProvider implements CertificateProvider { private X509Certificate availableCertificate; public LocalCertificateProvider(List certificates) { - if (certificates.isEmpty()) { - throw new IllegalArgumentException("The parameter list of constructor is empty."); + if (certificates != null && !certificates.isEmpty()) { + certificates.forEach(this::addX509Certificate); } - - certificates.forEach(this::addX509Certificate); } /** @@ -44,8 +43,9 @@ public final class LocalCertificateProvider implements CertificateProvider { public void addX509Certificate(X509Certificate certificate) { if (!validity.withinValidity(certificate)) { - logger.error("证书已失效, 序列号:{}, 有效范围:[{}]", toHexUpper(certificate.getSerialNumber()), timeRange(certificate)); - return; + String serialNumberHexUpper = toHexUpper(certificate.getSerialNumber()); + String timeRange = timeRange(certificate); + logger.error("证书已失效, 序列号:{}, 有效期:[{}]", serialNumberHexUpper, timeRange); } certificates.put(toHexUpper(certificate.getSerialNumber()), certificate); @@ -62,10 +62,12 @@ public final class LocalCertificateProvider implements CertificateProvider { availableCertificate = validity.getLongestCertificate(certificates); } - if (availableCertificate == null) { - throw new IllegalArgumentException("没有有效的证书"); - } - return availableCertificate; } + + @Override + public boolean isAvailableCertificate(String serialNumber) { + X509Certificate certificate = getCertificate(serialNumber); + return Objects.nonNull(certificate) && validity.withinValidity(certificate); + } } 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 8da22ca..aa8b32f 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 @@ -53,6 +53,9 @@ public abstract class AbstractApiClient implements ApiClient { public HttpResponse exchange(HttpRequest request, Class responseClass) { // 组装请求对象 HttpRequest newRequest = assembleRequest(request); + // 请求校验 + validateRequest(newRequest); + // 打印请求数据 httpLogger.logRequest(newRequest, getHttpVersion()); @@ -155,6 +158,10 @@ public abstract class AbstractApiClient implements ApiClient { return credential.buildRequestAuthorization(request, channel); } + private void validateRequest(HttpRequest newRequest) { + validator.validate(newRequest); + } + /** * 校验响应 * diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java index 72b0a02..b5d4b66 100644 --- a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/http/ApiClientBuilderTest.java @@ -29,7 +29,7 @@ class ApiClientBuilderTest { list.add(certificate); CertificateProvider certificateProvider = new LocalCertificateProvider(list); Privacy privacy = new TestPrivacy(privateKey, certificateProvider); - Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey,""), new TestVerifier(certificateProvider)); + Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey, ""), new TestVerifier(certificateProvider)); return new TestProfile( privacy, @@ -106,7 +106,7 @@ class ApiClientBuilderTest { list.add(certificate); CertificateProvider certificateProvider = new LocalCertificateProvider(list); Privacy privacy = new TestPrivacy(privateKey, certificateProvider); - Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey,""), new TestVerifier(certificateProvider)); + Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey, ""), new TestVerifier(certificateProvider)); return new TestProfile( privacy, @@ -126,7 +126,7 @@ class ApiClientBuilderTest { list.add(certificate); CertificateProvider certificateProvider = new LocalCertificateProvider(list); Privacy privacy = new TestPrivacy(privateKey, certificateProvider); - Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey,""), new TestVerifier(certificateProvider)); + Signature signature = new DefaultSignature(certificateProvider, new TestSigner(privateKey, ""), new TestVerifier(certificateProvider)); return new TestProfile( privacy, 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 ac15e08..2e5251e 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 @@ -45,7 +45,7 @@ class ApacheHttpclientV3Test { CertificateProvider certificateProvider = new LocalCertificateProvider(list); Privacy privacy = new TestPrivacy(privateKey, certificateProvider); Signature signature = new DefaultSignature(certificateProvider, - new TestSigner(privateKey,"6CDDAA92CAD75998325027647847330C1756291"), + new TestSigner(privateKey, "6CDDAA92CAD75998325027647847330C1756291"), new TestVerifier(certificateProvider)); return new TestProfile( diff --git a/scfs-api-test/src/main/resources/invalid_rsa_certificate.pem b/scfs-api-test/src/main/resources/invalid_rsa_certificate.pem new file mode 100644 index 0000000..378e3b8 --- /dev/null +++ b/scfs-api-test/src/main/resources/invalid_rsa_certificate.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIDeTCCAmGgAwIBAgIhAII88+MQ8uLtGvhVBudKldxDAQBv3vL9AZlT+vTeEqi/MA0GCSqGSIb3 +DQEBCwUAMIGNMQswCQYDVQQGEwJDTjESMBAGA1UECAwJ5rWZ5rGf55yBMRIwEAYDVQQHDAnmna3l +t57luIIxITAfBgNVBAoMGOa1meaxn+eooOW3nuWVhuS4mumTtuihjDEYMBYGA1UECwwP5pWw5a2X +6YeR6J6N6YOoMRkwFwYDVQQDDBBzY2ZzLmN6Y2IuY29tLmNuMB4XDTIzMDYxODA5MDczMVoXDTI0 +MDYxODA5MDczMVowUjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCea1meaxn+ecgTESMBAGA1UEBwwJ +5p2t5bee5biCMRswGQYDVQQKDBLnqKDlt57llYbkuJrpk7booYwwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCfHOgnUYbFi780EX9xQTdWPvCyBhaEnU5Y2p1bW4dHoumgEtjQOkLlRe3U +g1lu6TfhuE9YOQ9+V+Dsnzt7MXIRI7KlOuwpfwXn3e/MYP5ZtDBUiuSGNNVSP39wgb6aYXhvFY/L +m9gaO8Q4rauzK94Clw4sH3a7J6ST50xHss8VjSVFUkcPhpH+OJBTUrXWiccZCn01XDz0vmq6J3Au +jM55WBEmoz2r9iiVdCjZsgB4veQIpCKuMvJsEXVgRzULUnaqdX+7BTDBs30kCGyyBarR+wXLAKNQ +1nENFs1IGM99I+O8UsD6CvUnt2t7l3B8/qIlOSfds8x+BoUxQwhmUaMjAgMBAAEwDQYJKoZIhvcN +AQELBQADggEBACRCHOYH8ncOiYjMm3As7OFdnVDuGByMoZsDucqwrs0mJZVdp3OMgvGhC9zkzdZX +sJFKQeIRp/13cD1SKxtwfU7w4J+/FWpWPEG9Jf2bLqurYivu0tTa1xe5SDL4unNaj/o7BA0vaKJe +gagyULAilNCGBCfy59BSR/GQbgAC6pdl3soMx/s1c9BcZVplbq12/rmStGce6h3QqNjwpRMowbVW +XswXhr08AUevF7UriDjHkCsa6MqQ5x+ShV9qO1f2LDYBQRnM2Ty44EV5eUbHyKOJAYF+WqT6IRiA +2sMZrKRTHaNZB4j0Vc87HuxDtTNh/EEXU2sO31WZHs3ymAChbC4= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/scfs-api-test/src/main/resources/invalid_rsa_private_key.pem b/scfs-api-test/src/main/resources/invalid_rsa_private_key.pem new file mode 100644 index 0000000..18b2861 --- /dev/null +++ b/scfs-api-test/src/main/resources/invalid_rsa_private_key.pem @@ -0,0 +1,24 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCfHOgnUYbFi780EX9xQTdWPvCy +BhaEnU5Y2p1bW4dHoumgEtjQOkLlRe3Ug1lu6TfhuE9YOQ9+V+Dsnzt7MXIRI7KlOuwpfwXn3e/M +YP5ZtDBUiuSGNNVSP39wgb6aYXhvFY/Lm9gaO8Q4rauzK94Clw4sH3a7J6ST50xHss8VjSVFUkcP +hpH+OJBTUrXWiccZCn01XDz0vmq6J3AujM55WBEmoz2r9iiVdCjZsgB4veQIpCKuMvJsEXVgRzUL +UnaqdX+7BTDBs30kCGyyBarR+wXLAKNQ1nENFs1IGM99I+O8UsD6CvUnt2t7l3B8/qIlOSfds8x+ +BoUxQwhmUaMjAgMBAAECggEAQ+meKz4QdJvnse0wBKKN4Hl/2bRggxzzVliFJnvEG27tIb45nXLo +n5x/3R9tGjpf+C9namP8eXQ/1C9Iv5XEto0SkJS8PR/y4NspIYZaueX/ZO5diOzfCjqBBf/S32jv +8xX0aLbtf5D3+SsjaJe2LEvWKD4Luuk6RUjJlaa73dnSuGFSuvYV8MvFdHtfU8L8ZRoqZwmM9QTg ++Gpix4z6Hy/Mmi1xRl0EhIITq+mV9wR9Ock/0o12nvsNDyDSyrrt3niXTTkVCbct+t4UFwtnrZyH +dwl1OQ+WleTkUQY+wNgpq4jLjwGowXnqXlKff3tvXEt+3tpdOS8i+kXYwIrvIQKBgQDVnldDo2iq +TwLcZjXbreHskn/4hvWYUPqucEZ93jmyYNKUKPlXkVnc+kXnS0uuM5JZpi/7+FDkTqwHK83ET4/n +kTC9zM+K7KyIBbljclPjzXYJAW7nwD8A/vKx6CWi++f4buYc+lttsTprdAZ4/kWPTnvNJSjhSTAR +SQ32HxkiIQKBgQC+rjujW31WN4d2j48+K0B+7bIQON+VtmBZ48u2ZIQOi4PdoBvHv8HqQyhJlR+6 +z49k5WczSqAXdG2+nIgs8fpjj0lc7YiMIYs0VsLodOToH9J2MfXjWi+A4Y2vbfcjfUuCWhKSZs6B +eMLNe1LPIBDmlT3A1X83qkCpvAYYQWAkwwKBgFVtslZRZk0dtfYwRf+phT1XxSe9yT/1uprCOd6i +XY6RnAU2cajsbvSpfgUmnoh3BWMmy+/HeYokUDW59ds5OkKQVN7CpolXZxQqvd4gXZ4vj7HASfsS +bd/XFXXCcjLA7R70MsCJ+sBebQ+F4gTHI0hRSb9bygJ2g2uWPKgd/a4hAoGABCtZIHxKpEz4iE4h +SrG1alEWOKaVtPdU6gJCHQ3bmVnRm1H56Yc23UF0qw84r2QEdadSd1ulXn3sPGO90oXD/NNQPljv +SGkfWxiekGil7LFtb6ot/zeknEPSTkiwQ7VkpkgD6fGXiFs0nzuYFvFTjUcsH4BLlNMDMPLsizE6 +wfMCgYANvw4Lq1cVfHAl3f6IZlpWHPFEEJbPcBLu9+qtUlZjleCaWA8WXuiBxkqaIkeVi3JMst34 +adfIfBsAk4FeyLpkiTYNjOckZvXFXYKA3a05l/RJ5rsnnI9GRh+3Gk3V+87OU7HwMU6jNZmQiPIO +/jerEvZ9A5tbuzKkfJj2F0ZXfw== +-----END PRIVATE KEY----- \ No newline at end of file