summaryrefslogtreecommitdiffstats
path: root/certService/src/main/java/org/onap/oom/certservice
diff options
context:
space:
mode:
Diffstat (limited to 'certService/src/main/java/org/onap/oom/certservice')
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/CertServiceApplication.java37
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java96
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/api/ReadinessController.java61
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/api/ReloadConfigController.java64
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/api/advice/CertificationExceptionAdvice.java100
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/api/advice/ReloadConfigExceptionAdvice.java43
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/api/configuration/OpenApiConfig.java44
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/CertificateFactoryProvider.java42
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/CertificationData.java128
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java70
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java78
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java113
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/PemObjectFactory.java49
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/Pkcs10CertificationRequestFactory.java45
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/RsaContentSignerBuilder.java46
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateBuilder.java56
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpClientConfig.java50
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfig.java87
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoader.java64
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoadingException.java32
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/Cmpv2ServerProvider.java43
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Authentication.java60
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CaMode.java35
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CmpServers.java37
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Cmpv2Server.java98
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/Cmpv2ServersConfigurationValidator.java68
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2Url.java41
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2UrlValidator.java55
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/PortNumberViolation.java43
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolation.java49
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/UrlServerViolation.java25
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ClientAdapterException.java28
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ServerNotFoundException.java27
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/exception/CsrDecryptionException.java31
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/exception/DecryptionException.java33
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/exception/ErrorResponseModel.java36
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/exception/KeyDecryptionException.java31
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/model/CertificationModel.java44
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java170
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java74
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/CmpClientException.java50
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/PkiErrorException.java47
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java242
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageBuilder.java57
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageHelper.java246
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java335
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseValidationHelper.java241
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpUtil.java153
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/Cmpv2HttpClient.java83
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java128
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/model/Cmpv2CertificationModel.java44
51 files changed, 3959 insertions, 0 deletions
diff --git a/certService/src/main/java/org/onap/oom/certservice/CertServiceApplication.java b/certService/src/main/java/org/onap/oom/certservice/CertServiceApplication.java
new file mode 100644
index 00000000..c320d461
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/CertServiceApplication.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.PropertySource;
+
+@SpringBootApplication
+@PropertySource(value = {"classpath:application.properties"})
+public class CertServiceApplication {
+
+ // We are excluding this line in Sonar due to fact that
+ // Spring is handling arguments
+ public static void main(String[] args) { // NOSONAR
+ SpringApplication.run(CertServiceApplication.class, args);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java b/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java
new file mode 100644
index 00000000..d3a83ed1
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.onap.oom.certservice.certification.CertificationModelFactory;
+import org.onap.oom.certservice.certification.exception.DecryptionException;
+import org.onap.oom.certservice.certification.exception.ErrorResponseModel;
+import org.onap.oom.certservice.certification.model.CertificationModel;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+@Tag(name = "CertificationService")
+public class CertificationController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CertificationController.class);
+
+ private final CertificationModelFactory certificationModelFactory;
+
+ @Autowired
+ CertificationController(CertificationModelFactory certificationModelFactory) {
+ this.certificationModelFactory = certificationModelFactory;
+ }
+
+ /**
+ * Request for signing certificate by given CA.
+ *
+ * @param caName the name of Certification Authority that will sign root certificate
+ * @param encodedCsr Certificate Sign Request encoded in Base64 form
+ * @param encodedPrivateKey Private key for CSR, needed for PoP, encoded in Base64 form
+ * @return JSON containing trusted certificates and certificate chain
+ */
+ @GetMapping(value = "v1/certificate/{caName}", produces = "application/json")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Certificate successfully signed"),
+ @ApiResponse(responseCode = "400", description = "Given CSR or/and PK is incorrect",
+ content = @Content(schema = @Schema(implementation = ErrorResponseModel.class))),
+ @ApiResponse(responseCode = "404", description = "CA not found for given name",
+ content = @Content(schema = @Schema(implementation = ErrorResponseModel.class))),
+ @ApiResponse(responseCode = "500", description = "Something went wrong during connectiion to CMPv2 server",
+ content = @Content(schema = @Schema(implementation = ErrorResponseModel.class)))
+ })
+ @Operation(
+ summary = "sign certificate",
+ description = "Web endpoint for requesting certificate signing. Used by system components to gain certificate signed by CA.",
+ tags = {"CertificationService"})
+ public ResponseEntity<CertificationModel> signCertificate(
+ @Parameter(description = "Name of certification authority that will sign CSR.")
+ @PathVariable String caName,
+ @Parameter(description = "Certificate signing request in form of PEM object encoded in Base64 (with header and footer).")
+ @RequestHeader("CSR") String encodedCsr,
+ @Parameter(description = "Private key in form of PEM object encoded in Base64 (with header and footer).")
+ @RequestHeader("PK") String encodedPrivateKey
+ ) throws DecryptionException, CmpClientException {
+ caName = caName.replaceAll("[\n|\r|\t]", "_");
+ LOGGER.info("Received certificate signing request for CA named: {}", caName);
+ CertificationModel certificationModel = certificationModelFactory
+ .createCertificationModel(encodedCsr, encodedPrivateKey, caName);
+ return new ResponseEntity<>(certificationModel, HttpStatus.OK);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/ReadinessController.java b/certService/src/main/java/org/onap/oom/certservice/api/ReadinessController.java
new file mode 100644
index 00000000..3be54fd2
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/api/ReadinessController.java
@@ -0,0 +1,61 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.onap.oom.certservice.certification.configuration.CmpServersConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@Tag(name = "CertificationService")
+public final class ReadinessController {
+
+ private final CmpServersConfig cmpServersConfig;
+
+ @Autowired
+ public ReadinessController(CmpServersConfig cmpServersConfig) {
+ this.cmpServersConfig = cmpServersConfig;
+ }
+
+ @GetMapping(value = "/ready", produces = "application/json")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Configuration is loaded and service is ready to use"),
+ @ApiResponse(responseCode = "503", description = "Configuration loading failed and service is unavailable")
+ })
+ @Operation(
+ summary = "Check if CertService application is ready",
+ description = "Web endpoint for checking if service is ready to be used.",
+ tags = {"CertificationService"})
+ public ResponseEntity<String> checkReady() {
+ if (cmpServersConfig.isReady()) {
+ return new ResponseEntity<>(HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE);
+ }
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/ReloadConfigController.java b/certService/src/main/java/org/onap/oom/certservice/api/ReloadConfigController.java
new file mode 100644
index 00000000..b510d4f0
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/api/ReloadConfigController.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.onap.oom.certservice.certification.configuration.CmpServersConfig;
+import org.onap.oom.certservice.certification.configuration.CmpServersConfigLoadingException;
+import org.onap.oom.certservice.certification.exception.ErrorResponseModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@Tag(name = "CertificationService")
+public final class ReloadConfigController {
+
+ private final CmpServersConfig cmpServersConfig;
+
+ @Autowired
+ public ReloadConfigController(CmpServersConfig cmpServersConfig) {
+ this.cmpServersConfig = cmpServersConfig;
+ }
+
+ @GetMapping(value = "/reload", produces = "application/json")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Configuration has been successfully reloaded"),
+ @ApiResponse(responseCode = "500", description = "Something went wrong during configuration loading",
+ content = @Content(schema = @Schema(implementation = ErrorResponseModel.class)))
+ })
+ @Operation(
+ summary = "Reload CMPv2 servers configuration from configuration file",
+ description = "Web endpoint for performing configuration reload. Used to reload configuration from file.",
+ tags = {"CertificationService"})
+ public ResponseEntity<String> reloadConfiguration() throws CmpServersConfigLoadingException {
+ cmpServersConfig.reloadConfiguration();
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/advice/CertificationExceptionAdvice.java b/certService/src/main/java/org/onap/oom/certservice/api/advice/CertificationExceptionAdvice.java
new file mode 100644
index 00000000..5fb9d2ad
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/api/advice/CertificationExceptionAdvice.java
@@ -0,0 +1,100 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.api.advice;
+
+import org.onap.oom.certservice.api.CertificationController;
+import org.onap.oom.certservice.certification.exception.Cmpv2ClientAdapterException;
+import org.onap.oom.certservice.certification.exception.Cmpv2ServerNotFoundException;
+import org.onap.oom.certservice.certification.exception.CsrDecryptionException;
+import org.onap.oom.certservice.certification.exception.ErrorResponseModel;
+import org.onap.oom.certservice.certification.exception.KeyDecryptionException;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice(assignableTypes = CertificationController.class)
+public final class CertificationExceptionAdvice {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CertificationExceptionAdvice.class);
+
+ @ExceptionHandler(value = CsrDecryptionException.class)
+ public ResponseEntity<ErrorResponseModel> handle(CsrDecryptionException exception) {
+ LOGGER.error("Exception occurred during decoding certificate sign request:", exception);
+ return getErrorResponseEntity(
+ "Wrong certificate signing request (CSR) format",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ @ExceptionHandler(value = KeyDecryptionException.class)
+ public ResponseEntity<ErrorResponseModel> handle(KeyDecryptionException exception) {
+ LOGGER.error("Exception occurred during decoding key:", exception);
+ return getErrorResponseEntity(
+ "Wrong key (PK) format",
+ HttpStatus.BAD_REQUEST
+ );
+ }
+
+ @ExceptionHandler(value = Cmpv2ServerNotFoundException.class)
+ public ResponseEntity<ErrorResponseModel> handle(Cmpv2ServerNotFoundException exception) {
+ LOGGER.error("Exception occurred selecting CMPv2 server:", exception);
+ return getErrorResponseEntity(
+ "Certification authority not found for given CAName",
+ HttpStatus.NOT_FOUND
+ );
+ }
+
+ @ExceptionHandler(value = RuntimeException.class)
+ public ResponseEntity<ErrorResponseModel> handle(RuntimeException exception) throws CmpClientException {
+ throw new CmpClientException("Runtime exception occurred calling cmp client business logic", exception);
+ }
+
+ @ExceptionHandler(value = CmpClientException.class)
+ public ResponseEntity<ErrorResponseModel> handle(CmpClientException exception) {
+ LOGGER.error("Exception occurred calling cmp client:", exception);
+ return getErrorResponseEntity(
+ "Exception occurred during call to cmp client",
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ @ExceptionHandler(value = Cmpv2ClientAdapterException.class)
+ public ResponseEntity<ErrorResponseModel> handle(Cmpv2ClientAdapterException exception) {
+ LOGGER.error("Exception occurred parsing cmp client response:", exception);
+ return getErrorResponseEntity(
+ "Exception occurred parsing cmp client response",
+ HttpStatus.INTERNAL_SERVER_ERROR
+ );
+ }
+
+ private ResponseEntity<ErrorResponseModel> getErrorResponseEntity(String errorMessage, HttpStatus status) {
+ ErrorResponseModel errorResponse = new ErrorResponseModel(errorMessage);
+ return new ResponseEntity<>(
+ errorResponse,
+ status
+ );
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/advice/ReloadConfigExceptionAdvice.java b/certService/src/main/java/org/onap/oom/certservice/api/advice/ReloadConfigExceptionAdvice.java
new file mode 100644
index 00000000..b52111de
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/api/advice/ReloadConfigExceptionAdvice.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.api.advice;
+
+import org.onap.oom.certservice.api.ReloadConfigController;
+import org.onap.oom.certservice.certification.configuration.CmpServersConfigLoadingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice(assignableTypes = ReloadConfigController.class)
+public final class ReloadConfigExceptionAdvice {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfigExceptionAdvice.class);
+
+ @ExceptionHandler(value = CmpServersConfigLoadingException.class)
+ public ResponseEntity<String> handle(CmpServersConfigLoadingException exception) {
+ LOGGER.error(exception.getMessage(), exception.getCause());
+ return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/api/configuration/OpenApiConfig.java b/certService/src/main/java/org/onap/oom/certservice/api/configuration/OpenApiConfig.java
new file mode 100644
index 00000000..9a5840ab
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/api/configuration/OpenApiConfig.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.api.configuration;
+
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OpenApiConfig {
+
+ @Bean
+ public OpenAPI customOpenApi() {
+ return new OpenAPI()
+ .components(new Components())
+ .info(
+ new Info()
+ .title("CertService Documentation")
+ .description("Certification service API documentation")
+ .version("1.0.1")
+ );
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificateFactoryProvider.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificateFactoryProvider.java
new file mode 100644
index 00000000..93fa4c21
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificateFactoryProvider.java
@@ -0,0 +1,42 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Cert Service
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import java.io.InputStream;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CertificateFactoryProvider {
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ X509Certificate generateCertificate(InputStream inStream) throws CertificateException, NoSuchProviderException {
+ return (X509Certificate) CertificateFactory.getInstance("X.509", "BC").generateCertificate(inStream);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationData.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationData.java
new file mode 100644
index 00000000..11e81807
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationData.java
@@ -0,0 +1,128 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+
+final class CertificationData {
+
+ private CertificationData() {
+ }
+
+ private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----\n";
+ private static final String END_CERTIFICATE = "-----END CERTIFICATE-----";
+
+ static final String EXTRA_CA_CERT = ""
+ + BEGIN_CERTIFICATE
+ + "MIIDvzCCAqcCFF5DejiyfoNfPiiMmBXulniBewBGMA0GCSqGSIb3DQEBCwUAMIGb\n"
+ + "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\n"
+ + "LUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwE\n"
+ + "T05BUDEVMBMGA1UEAwwMbmV3Lm9uYXAub3JnMR4wHAYJKoZIhvcNAQkBFg90ZXN0\n"
+ + "ZXJAb25hcC5vcmcwHhcNMjAwMjEyMDk1OTM3WhcNMjEwMjExMDk1OTM3WjCBmzEL\n"
+ + "MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbi1G\n"
+ + "cmFuY2lzY28xGTAXBgNVBAoMEExpbnV4LUZvdW5kYXRpb24xDTALBgNVBAsMBE9O\n"
+ + "QVAxFTATBgNVBAMMDG5ldy5vbmFwLm9yZzEeMBwGCSqGSIb3DQEJARYPdGVzdGVy\n"
+ + "QG9uYXAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtF4FXeDV\n"
+ + "ng/inC/bTACmZnLC9IiC7PyG/vVbMxxN1bvQLRAwC/Hbl3i9zD68Vs/jPPr/SDr9\n"
+ + "2rgItdDdUY1V30Y3PT06F11XdEaRb+t++1NX0rDf1AqPaBZgnBmB86s1wbqHdJTr\n"
+ + "wEImDZ5xMPfP3fiWy/9Yw/U7iRMIi1/oI0lWuHJV0bn908shuJ6dvInpRCoDnoTX\n"
+ + "YP/FiDSZCFVewQcq4TigB7kRqZrDcPZWbSlqHklDMXRwbCxAiFSziuX6TBwru9Rn\n"
+ + "HhIeXVSgMU1ZSSopVbJGtQ4zSsU1nvTK5Bhc2UHGcAOZy1xTN5D9EEbTqh7l+Wtx\n"
+ + "y8ojkEXvFG8lVwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAE+bUphwHit78LK8sb\n"
+ + "OMjt4DiEu32KeSJOpYgPLeBeAIynaNsa7sQrpuxerGNTmQWIcw6olXI0J+OOwkik\n"
+ + "II7elrYtd5G1uALxXWdamNsaY0Du34moVL1YjexJ7qQ4oBUxg2tuY8NAQGDK+23I\n"
+ + "nCA+ZwzdTJo73TYS6sx64d/YLWkX4nHGUoMlF+xUH34csDyhpuTSzQhC2quB5N8z\n"
+ + "tSFdpe4z2jqx07qo2EBFxi03EQ8Q0ex6l421QM2gbs7cZQ66K0DkpPcF2+iHZnyx\n"
+ + "xq1lnlsWHklElF2bhyXTn3fPp5wtan00P8IolKx7CAWb92QjkW6M0RvTW/xuwIzh\n"
+ + "0rTO\n"
+ + END_CERTIFICATE;
+
+ static final String CA_CERT = ""
+ + BEGIN_CERTIFICATE
+ + "MIIDtzCCAp8CFAwqQddh4/iyGfP8UZ3dpXlxfAN8MA0GCSqGSIb3DQEBCwUAMIGX\n"
+ + "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\n"
+ + "LUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwE\n"
+ + "T05BUDERMA8GA1UEAwwIb25hcC5vcmcxHjAcBgkqhkiG9w0BCQEWD3Rlc3RlckBv\n"
+ + "bmFwLm9yZzAeFw0yMDAyMTIwOTM0MjdaFw0yMTAyMTEwOTM0MjdaMIGXMQswCQYD\n"
+ + "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuLUZyYW5j\n"
+ + "aXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwET05BUDER\n"
+ + "MA8GA1UEAwwIb25hcC5vcmcxHjAcBgkqhkiG9w0BCQEWD3Rlc3RlckBvbmFwLm9y\n"
+ + "ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCFrnO7/eT6V+7XkPPd\n"
+ + "eiL/6xXreuegvit/1/jTVjG+3AOVcmTn2WXwXXRcQLvkWQfJVPoltsY8E3FqFRti\n"
+ + "797XjY6cdQJFVDyzNU0+Fb4vJL9FK5wSvnS6EFjBEn3JvXRlENorDCs/mfjkjJoa\n"
+ + "Dl74gXQEJYcg4nsTeNIj7cm3Q7VK3mZt1t7LSJJ+czxv69UJDuNJpmQ/2WOKyLZA\n"
+ + "gTtBJ+Hyol45/OLsrqwq1dAn9ZRWIFPvRt/XQYH9bI/6MtqSreRVUrdYCiTe/XpP\n"
+ + "B/OM6NEi2+p5QLi3Yi70CEbqP3HqUVbkzF+r7bwIb6M5/HxfqzLmGwLvD+6rYnUn\n"
+ + "Bm8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAhXoO65DXth2X/zFRNsCNpLwmDy7r\n"
+ + "PxT9ZAIZAzSxx3/aCYiuTrKP1JnqjkO+F2IbikrI4n6sKO49SKnRf9SWTFhd+5dX\n"
+ + "vxq5y7MaqxHAY9J7+Qzq33+COVFQnaF7ddel2NbyUVb2b9ZINNsaZkkPXui6DtQ7\n"
+ + "/Fb/1tmAGWd3hMp75G2thBSzs816JMKKa9WD+4VGATEs6OSll4sv2fOZEn+0mAD3\n"
+ + "9q9c+WtLGIudOwcHwzPb2njtNntQSCK/tVOqbY+vzhMY3JW+p9oSrLDSdGC+pAKK\n"
+ + "m/wB+2VPIYcsPMtIhHC4tgoSaiCqjXYptaOh4b8ye8CPBUCpX/AYYkN0Ow==\n"
+ + END_CERTIFICATE;
+
+ static final String INTERMEDIATE_CERT = ""
+ + BEGIN_CERTIFICATE
+ + "MIIDqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT\n"
+ + "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkw\n"
+ + "FwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMREwDwYDVQQD\n"
+ + "DAhvbmFwLm9yZzEeMBwGCSqGSIb3DQEJARYPdGVzdGVyQG9uYXAub3JnMB4XDTIw\n"
+ + "MDIxMjA5NDAxMloXDTIyMTEwODA5NDAxMlowgYQxCzAJBgNVBAYTAlVTMRMwEQYD\n"
+ + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkwFwYDVQQK\n"
+ + "DBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMR4wHAYDVQQDDBVpbnRl\n"
+ + "cm1lZGlhdGUub25hcC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\n"
+ + "AQC1oOYMZ6G+2DGDAizYnzdCNiogivlht1s4oqgem7fM1XFPxD2p31ATIibOdqr/\n"
+ + "gv1qemO9Q4r1xn6w1Ufq7T1K7PjnMzdSeTqZefurE2JM/HHx2QvW4TjMlz2ILgaD\n"
+ + "L1LN60kmMQSOi5VxKJpsrCQxbOsxhvefd212gny5AZMcjJe23kUd9OxUrtvpdLEv\n"
+ + "wI3vFEvT7oRUnEUg/XNz7qeg33vf1C39yMR+6O4s6oevgsEebVKjb+yOoS6zzGtz\n"
+ + "72wZjm07C54ZlO+4Uy+QAlMjRiU3mgWkKbkOy+4CvwehjhpTikdBs2DX39ZLGHhn\n"
+ + "L/0a2NYtGulp9XEqmTvRoI+PAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n"
+ + "hvcNAQELBQADggEBADcitdJ6YswiV8jAD9GK0gf3+zqcGegt4kt+79JXlXYbb1sY\n"
+ + "q3o6prcB7nSUoClgF2xUPCslFGpM0Er9FCSFElQM/ru0l/KVmJS6kSpwEHvsYIH3\n"
+ + "q5anta+Pyk8JSQWAAw+qrind0uBQMnhR8Tn13tgV+Kjvg/xlH/nZIEdN5YtLB1cA\n"
+ + "beVsZRyRfVL9DeZU8s/MZ5wC3kgcEp5A4m5lg7HyBxBdqhzFcDr6xiy6OGqW8Yep\n"
+ + "xrwfc8Fw8a/lOv4U+tBeGNKPQDYaL9hh+oM+qMkNXsHXDqdJsuEGJtU4i3Wcwzoc\n"
+ + "XGN5NWV//4bP+NFmwgcn7AYCdRvz04A8GU/0Cwg=\n"
+ + END_CERTIFICATE;
+
+ static final String ENTITY_CERT = ""
+ + BEGIN_CERTIFICATE
+ + "MIIDjDCCAnSgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgYQxCzAJBgNVBAYTAlVT\n"
+ + "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkw\n"
+ + "FwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMR4wHAYDVQQD\n"
+ + "DBVpbnRlcm1lZGlhdGUub25hcC5vcmcwHhcNMjAwMjEyMDk1MTI2WhcNMjIxMTA4\n"
+ + "MDk1MTI2WjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG\n"
+ + "A1UEBwwNU2FuLUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjEN\n"
+ + "MAsGA1UECwwET05BUDEVMBMGA1UEAwwMdmlkLm9uYXAub3JnMIIBIjANBgkqhkiG\n"
+ + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw+GIRzJzUOh0gtc+wzFJEdTnn+q5F10L0Yhr\n"
+ + "G1xKdjPieHIFGsoiXwcuCU8arNSqlz7ocx62KQRkcA8y6edlOAsYtdOEJvqEI9vc\n"
+ + "eyTB/HYsbzw3URPGch4AmibrQkKU9QvGwouHtHn4R2Ft2Y0tfEqv9hxj9v4njq4A\n"
+ + "EiDLAFLl5FmVyCZu/MtKngSgu1smcaFKTYySPMxytgJZexoa/ALZyyE0gRhsvwHm\n"
+ + "NLGCPt1bmE/PEGZybsCqliyTO0S56ncD55The7+D/UDS4kE1Wg0svlWon/YsE6QW\n"
+ + "B3oeJDX7Kr8ebDTIAErevIAD7Sm4ee5se2zxYrsYlj0MzHZtvwIDAQABoxAwDjAM\n"
+ + "BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvQ1pTvjON6vSlcJRKSY4r\n"
+ + "8q7L4/9ZaVXWJAjzEYJtPIqsgGiPWz0vGfgklowU6tZxp9zRZFXfMil+mPQSe+yo\n"
+ + "ULrZSQ/z48YHPueE/BNO/nT4aaVBEhPLR5aVwC7uQVX8H+m1V1UGT8lk9vdI9rej\n"
+ + "CI9l524sLCpdE4dFXiWK2XHEZ0Vfylk221u3IYEogVVA+UMX7BFPSsOnI2vtYK/i\n"
+ + "lwZtlri8LtTusNe4oiTkYyq+RSyDhtAswg8ANgvfHolhCHoLFj6w1IkG88UCmbwN\n"
+ + "d7BoGMy06y5MJxyXEZG0vR7eNeLey0TIh+rAszAFPsIQvrOHW+HuA+WLQAj1mhnm\n"
+ + END_CERTIFICATE;
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java
new file mode 100644
index 00000000..23440ac5
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java
@@ -0,0 +1,70 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import org.onap.oom.certservice.certification.configuration.Cmpv2ServerProvider;
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.exception.DecryptionException;
+import org.onap.oom.certservice.certification.model.CertificationModel;
+import org.onap.oom.certservice.certification.model.CsrModel;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CertificationModelFactory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CertificationModelFactory.class);
+
+ private final CsrModelFactory csrModelFactory;
+ private final Cmpv2ServerProvider cmpv2ServerProvider;
+ private final CertificationProvider certificationProvider;
+
+ @Autowired
+ CertificationModelFactory(
+ CsrModelFactory csrModelFactory,
+ Cmpv2ServerProvider cmpv2ServerProvider,
+ CertificationProvider certificationProvider
+ ) {
+ this.cmpv2ServerProvider = cmpv2ServerProvider;
+ this.csrModelFactory = csrModelFactory;
+ this.certificationProvider = certificationProvider;
+ }
+
+ public CertificationModel createCertificationModel(String encodedCsr, String encodedPrivateKey, String caName)
+ throws DecryptionException, CmpClientException {
+ CsrModel csrModel = csrModelFactory.createCsrModel(
+ new CsrModelFactory.StringBase64(encodedCsr),
+ new CsrModelFactory.StringBase64(encodedPrivateKey)
+ );
+ LOGGER.debug("Received CSR meta data: \n{}", csrModel);
+
+ Cmpv2Server cmpv2Server = cmpv2ServerProvider.getCmpv2Server(caName);
+ LOGGER.debug("Found server for given CA name: \n{}", cmpv2Server);
+
+ LOGGER.info("Sending sign request for certification model for CA named: {}, and certificate signing request:\n{}",
+ caName, csrModel);
+ return certificationProvider.signCsr(csrModel, cmpv2Server);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java
new file mode 100644
index 00000000..91148a22
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationProvider.java
@@ -0,0 +1,78 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Cert Service
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
+import org.bouncycastle.util.io.pem.PemObjectGenerator;
+import org.bouncycastle.util.io.pem.PemWriter;
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.model.CertificationModel;
+import org.onap.oom.certservice.certification.model.CsrModel;
+import org.onap.oom.certservice.cmpv2client.api.CmpClient;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class CertificationProvider {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CertificationProvider.class);
+
+ private final CmpClient cmpClient;
+
+ @Autowired
+ public CertificationProvider(CmpClient cmpClient) {
+ this.cmpClient = cmpClient;
+ }
+
+ public CertificationModel signCsr(CsrModel csrModel, Cmpv2Server server)
+ throws CmpClientException {
+ Cmpv2CertificationModel certificates = cmpClient.createCertificate(csrModel, server);
+ return new CertificationModel(convertFromX509CertificateListToPemList(certificates.getCertificateChain()),
+ convertFromX509CertificateListToPemList(certificates.getTrustedCertificates()));
+ }
+
+ private static List<String> convertFromX509CertificateListToPemList(List<X509Certificate> certificates) {
+ return certificates.stream().map(CertificationProvider::convertFromX509CertificateToPem).filter(cert -> !cert.isEmpty())
+ .collect(Collectors.toList());
+ }
+
+ private static String convertFromX509CertificateToPem(X509Certificate certificate) {
+ StringWriter sw = new StringWriter();
+ try (PemWriter pw = new PemWriter(sw)) {
+ PemObjectGenerator gen = new JcaMiscPEMGenerator(certificate);
+ pw.writeObject(gen);
+ } catch (IOException e) {
+ LOGGER.error("Exception occurred during convert of X509 certificate", e);
+ }
+ return sw.toString();
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java
new file mode 100644
index 00000000..758427f6
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java
@@ -0,0 +1,113 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import java.util.Base64;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.onap.oom.certservice.certification.exception.CsrDecryptionException;
+import org.onap.oom.certservice.certification.exception.DecryptionException;
+import org.onap.oom.certservice.certification.exception.KeyDecryptionException;
+import org.onap.oom.certservice.certification.model.CsrModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+
+@Service
+public class CsrModelFactory {
+
+ private final PemObjectFactory pemObjectFactory
+ = new PemObjectFactory();
+ private final Pkcs10CertificationRequestFactory certificationRequestFactory
+ = new Pkcs10CertificationRequestFactory();
+
+
+ public CsrModel createCsrModel(StringBase64 csr, StringBase64 privateKey)
+ throws DecryptionException {
+ PKCS10CertificationRequest decodedCsr = decodeCsr(csr);
+ PemObject decodedPrivateKey = decodePrivateKey(privateKey);
+ return new CsrModel.CsrModelBuilder(decodedCsr, decodedPrivateKey).build();
+ }
+
+ private PemObject decodePrivateKey(StringBase64 privateKey)
+ throws KeyDecryptionException {
+
+ return privateKey.asString()
+ .flatMap(pemObjectFactory::createPemObject)
+ .orElseThrow(
+ () -> new KeyDecryptionException("Incorrect Key, decryption failed")
+ );
+ }
+
+ private PKCS10CertificationRequest decodeCsr(StringBase64 csr)
+ throws CsrDecryptionException {
+ return csr.asString()
+ .flatMap(pemObjectFactory::createPemObject)
+ .flatMap(certificationRequestFactory::createPkcs10CertificationRequest)
+ .orElseThrow(
+ () -> new CsrDecryptionException("Incorrect CSR, decryption failed")
+ );
+ }
+
+ public static class StringBase64 {
+ private final String value;
+ private final Base64.Decoder decoder = Base64.getDecoder();
+ private static final Logger LOGGER = LoggerFactory.getLogger(StringBase64.class);
+
+ public StringBase64(String value) {
+ this.value = value;
+ }
+
+ public Optional<String> asString() {
+ try {
+ String decodedString = new String(decoder.decode(value));
+ return Optional.of(decodedString);
+ } catch (RuntimeException e) {
+ LOGGER.error("Exception occurred during decoding:", e);
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public boolean equals(Object otherObject) {
+ if (this == otherObject) {
+ return true;
+ }
+ if (otherObject == null || getClass() != otherObject.getClass()) {
+ return false;
+ }
+ StringBase64 that = (StringBase64) otherObject;
+ return Objects.equals(value, that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+ }
+
+}
+
+
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/PemObjectFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/PemObjectFactory.java
new file mode 100644
index 00000000..24d32bdd
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/PemObjectFactory.java
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Optional;
+
+import org.bouncycastle.util.encoders.DecoderException;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class PemObjectFactory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PemObjectFactory.class);
+
+ public Optional<PemObject> createPemObject(String pem) {
+
+ try (StringReader stringReader = new StringReader(pem);
+ PemReader pemReader = new PemReader(stringReader)) {
+ return Optional.ofNullable(pemReader.readPemObject());
+ } catch (DecoderException | IOException e) {
+ LOGGER.error("Exception occurred during creation of PEM:", e);
+ return Optional.empty();
+ }
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/Pkcs10CertificationRequestFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/Pkcs10CertificationRequestFactory.java
new file mode 100644
index 00000000..c38eb0a1
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/Pkcs10CertificationRequestFactory.java
@@ -0,0 +1,45 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.util.encoders.DecoderException;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class Pkcs10CertificationRequestFactory {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Pkcs10CertificationRequestFactory.class);
+
+ public Optional<PKCS10CertificationRequest> createPkcs10CertificationRequest(PemObject pemObject) {
+ try {
+ LOGGER.debug("Creating certification request from pem object");
+ return Optional.of(new PKCS10CertificationRequest(pemObject.getContent()));
+ } catch (DecoderException | IOException e) {
+ LOGGER.error("Exception occurred during creation of certification request:", e);
+ return Optional.empty();
+ }
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/RsaContentSignerBuilder.java b/certService/src/main/java/org/onap/oom/certservice/certification/RsaContentSignerBuilder.java
new file mode 100644
index 00000000..741b20ab
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/RsaContentSignerBuilder.java
@@ -0,0 +1,46 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Cert Service
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import java.io.IOException;
+import java.security.PrivateKey;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RsaContentSignerBuilder {
+
+ ContentSigner build(PKCS10CertificationRequest csr, PrivateKey privateKey)
+ throws IOException, OperatorCreationException {
+ AlgorithmIdentifier sigAlgId = csr.getSignatureAlgorithm();
+ AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+
+ return new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
+ .build(PrivateKeyFactory.createKey(privateKey.getEncoded()));
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateBuilder.java b/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateBuilder.java
new file mode 100644
index 00000000..67aecb7c
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateBuilder.java
@@ -0,0 +1,56 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Cert Service
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Date;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.springframework.stereotype.Component;
+
+@Component
+public class X509CertificateBuilder {
+
+ private static final int SECURE_NEXT_BYTES = 16;
+ private static final int VALID_PERIOD_IN_DAYS = 365;
+
+ X509v3CertificateBuilder build(PKCS10CertificationRequest csr) throws IOException {
+ return new X509v3CertificateBuilder(csr.getSubject(), createSerial(),
+ Date.from(LocalDateTime.now().toInstant(ZoneOffset.UTC)),
+ Date.from(LocalDateTime.now().plusDays(VALID_PERIOD_IN_DAYS).toInstant(ZoneOffset.UTC)),
+ new PKCS10CertificationRequest(csr.getEncoded()).getSubject(),
+ SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(csr.getSubjectPublicKeyInfo().getEncoded())));
+
+ }
+
+ private BigInteger createSerial() {
+ byte[] serial = new byte[SECURE_NEXT_BYTES];
+ new SecureRandom().nextBytes(serial);
+ return new BigInteger(serial).abs();
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpClientConfig.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpClientConfig.java
new file mode 100644
index 00000000..9cf6fd15
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpClientConfig.java
@@ -0,0 +1,50 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Cert Service
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.onap.oom.certservice.cmpv2client.api.CmpClient;
+import org.onap.oom.certservice.cmpv2client.impl.CmpClientImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Configuration
+public class CmpClientConfig {
+
+ @Bean
+ CmpClient cmpClient(CloseableHttpClient closeableHttpClient) {
+ return new CmpClientImpl(closeableHttpClient);
+ }
+
+ @Bean
+ @RequestScope
+ CloseableHttpClient closeableHttpClient(HttpClientBuilder httpClientBuilder) {
+ return httpClientBuilder.build();
+ }
+
+ @Bean
+ HttpClientBuilder httpClientBuilder() {
+ return HttpClientBuilder.create();
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfig.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfig.java
new file mode 100644
index 00000000..727f685d
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfig.java
@@ -0,0 +1,87 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class CmpServersConfig {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CmpServersConfig.class);
+ private static final String INIT_CONFIGURATION = "Loading initial configuration";
+ private static final String RELOADING_CONFIGURATION = "Reloading configuration";
+ private static final String LOADING_SUCCESS_MESSAGE = "CMP Servers configuration successfully loaded from file {}";
+ private static final String CMP_SERVERS_CONFIG_FILENAME = "cmpServers.json";
+
+ private final String configPath;
+ private final CmpServersConfigLoader cmpServersConfigLoader;
+
+ private List<Cmpv2Server> cmpServers;
+ private volatile boolean isReady;
+
+ @Autowired
+ public CmpServersConfig(@Value("${app.config.path}") String configPath,
+ CmpServersConfigLoader cmpServersConfigLoader) {
+ this.cmpServersConfigLoader = cmpServersConfigLoader;
+ this.configPath = configPath;
+ }
+
+ @PostConstruct
+ void init() {
+ try {
+ LOGGER.info(INIT_CONFIGURATION);
+ loadConfiguration();
+ } catch (CmpServersConfigLoadingException e) {
+ LOGGER.error(e.getMessage(), e.getCause());
+ }
+ }
+
+ public void reloadConfiguration() throws CmpServersConfigLoadingException {
+ LOGGER.info(RELOADING_CONFIGURATION);
+ loadConfiguration();
+ }
+
+
+ synchronized void loadConfiguration() throws CmpServersConfigLoadingException {
+ isReady = false;
+ String configFilePath = configPath + File.separator + CMP_SERVERS_CONFIG_FILENAME;
+ this.cmpServers = Collections.unmodifiableList(cmpServersConfigLoader.load(configFilePath));
+ LOGGER.info(LOADING_SUCCESS_MESSAGE, configFilePath);
+ isReady = true;
+ }
+
+ public List<Cmpv2Server> getCmpServers() {
+ return cmpServers;
+ }
+
+ public boolean isReady() {
+ return isReady;
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoader.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoader.java
new file mode 100644
index 00000000..72795b03
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoader.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import java.security.InvalidParameterException;
+import java.util.List;
+
+import org.onap.oom.certservice.certification.configuration.model.CmpServers;
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.configuration.validation.Cmpv2ServersConfigurationValidator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+class CmpServersConfigLoader {
+
+ private static final String LOADING_EXCEPTION_MESSAGE = "Exception occurred during CMP Servers configuration loading";
+ private static final String VALIDATION_EXCEPTION_MESSAGE = "Validation of CMPv2 servers configuration failed";
+
+ private final Cmpv2ServersConfigurationValidator validator;
+
+ @Autowired
+ CmpServersConfigLoader(Cmpv2ServersConfigurationValidator validator) {
+ this.validator = validator;
+ }
+
+ List<Cmpv2Server> load(String path) throws CmpServersConfigLoadingException {
+ try {
+ List<Cmpv2Server> servers = loadConfigFromFile(path).getCmpv2Servers();
+ validator.validate(servers);
+ return servers;
+ } catch (IOException e) {
+ throw new CmpServersConfigLoadingException(LOADING_EXCEPTION_MESSAGE, e);
+ } catch (InvalidParameterException e) {
+ throw new CmpServersConfigLoadingException(VALIDATION_EXCEPTION_MESSAGE, e);
+ }
+ }
+
+ private CmpServers loadConfigFromFile(String path) throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ return objectMapper.readValue(new File(path), CmpServers.class);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoadingException.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoadingException.java
new file mode 100644
index 00000000..de0a0729
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/CmpServersConfigLoadingException.java
@@ -0,0 +1,32 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration;
+
+public class CmpServersConfigLoadingException extends Exception {
+
+ public CmpServersConfigLoadingException(String message) {
+ super(message);
+ }
+
+ public CmpServersConfigLoadingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/Cmpv2ServerProvider.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/Cmpv2ServerProvider.java
new file mode 100644
index 00000000..a8d43fe8
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/Cmpv2ServerProvider.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration;
+
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.exception.Cmpv2ServerNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class Cmpv2ServerProvider {
+
+ private final CmpServersConfig cmpServersConfig;
+
+ @Autowired
+ Cmpv2ServerProvider(CmpServersConfig cmpServersConfig) {
+ this.cmpServersConfig = cmpServersConfig;
+ }
+
+ public Cmpv2Server getCmpv2Server(String caName) {
+ return cmpServersConfig.getCmpServers().stream().filter(server -> server.getCaName().equals(caName)).findFirst()
+ .orElseThrow(() -> new Cmpv2ServerNotFoundException("No server found for given CA name"));
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Authentication.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Authentication.java
new file mode 100644
index 00000000..e354ca60
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Authentication.java
@@ -0,0 +1,60 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.model;
+
+import javax.validation.constraints.NotNull;
+import org.hibernate.validator.constraints.Length;
+
+public class Authentication {
+
+ private static final int MAX_IAK_RV_LENGTH = 256;
+
+ @NotNull
+ @Length(min = 1, max = MAX_IAK_RV_LENGTH)
+ private String iak;
+ @NotNull
+ @Length(min = 1, max = MAX_IAK_RV_LENGTH)
+ private String rv;
+
+ public String getIak() {
+ return iak;
+ }
+
+ public void setIak(String iak) {
+ this.iak = iak;
+ }
+
+ public String getRv() {
+ return rv;
+ }
+
+ public void setRv(String rv) {
+ this.rv = rv;
+ }
+
+ @Override
+ public String toString() {
+ return "Authentication{"
+ + " iak=*****"
+ + ", rv=*****"
+ + '}';
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CaMode.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CaMode.java
new file mode 100644
index 00000000..9980ef50
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CaMode.java
@@ -0,0 +1,35 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.model;
+
+public enum CaMode {
+ RA("RA"), CLIENT("Client");
+
+ private String profile;
+
+ CaMode(String profile) {
+ this.profile = profile;
+ }
+
+ public String getProfile() {
+ return profile;
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CmpServers.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CmpServers.java
new file mode 100644
index 00000000..9fbe0eaf
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/CmpServers.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.model;
+
+import java.util.List;
+
+public class CmpServers {
+
+ private List<Cmpv2Server> cmpv2Servers;
+
+ public List<Cmpv2Server> getCmpv2Servers() {
+ return cmpv2Servers;
+ }
+
+ public void setCmpv2Servers(List<Cmpv2Server> cmpv2Servers) {
+ this.cmpv2Servers = cmpv2Servers;
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Cmpv2Server.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Cmpv2Server.java
new file mode 100644
index 00000000..b27f2888
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/model/Cmpv2Server.java
@@ -0,0 +1,98 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.model;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.hibernate.validator.constraints.Length;
+import org.onap.oom.certservice.certification.configuration.validation.constraints.Cmpv2Url;
+
+public class Cmpv2Server {
+
+ private static final int MAX_CA_NAME_LENGTH = 128;
+
+ @NotNull
+ @Valid
+ private Authentication authentication;
+ @NotNull
+ private CaMode caMode;
+ @NotNull
+ @Length(min = 1, max = MAX_CA_NAME_LENGTH)
+ private String caName;
+ @NotNull
+ private X500Name issuerDN;
+ @Cmpv2Url
+ private String url;
+
+ public Authentication getAuthentication() {
+ return authentication;
+ }
+
+ public void setAuthentication(Authentication authentication) {
+ this.authentication = authentication;
+ }
+
+ public CaMode getCaMode() {
+ return caMode;
+ }
+
+ public void setCaMode(CaMode caMode) {
+ this.caMode = caMode;
+ }
+
+ public String getCaName() {
+ return caName;
+ }
+
+ public void setCaName(String caName) {
+ this.caName = caName;
+ }
+
+ public X500Name getIssuerDN() {
+ return issuerDN;
+ }
+
+ public void setIssuerDN(X500Name issuerDN) {
+ this.issuerDN = issuerDN;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public String toString() {
+ return "Cmpv2Server{"
+ + "authentication=" + authentication
+ + ", caMode=" + caMode
+ + ", caName='" + caName + '\''
+ + ", issuerDN='" + issuerDN + '\''
+ + ", url='" + url + '\''
+ + '}';
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/Cmpv2ServersConfigurationValidator.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/Cmpv2ServersConfigurationValidator.java
new file mode 100644
index 00000000..59014b45
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/Cmpv2ServersConfigurationValidator.java
@@ -0,0 +1,68 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.validation;
+
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.security.InvalidParameterException;
+import java.util.List;
+import java.util.Set;
+
+@Service
+public class Cmpv2ServersConfigurationValidator {
+
+ private final Validator validator;
+
+ @Autowired
+ public Cmpv2ServersConfigurationValidator(Validator validator) {
+ this.validator = validator;
+ }
+
+ public void validate(List<Cmpv2Server> servers) {
+ servers.forEach(this::validateServer);
+ validateUniqueCaNames(servers);
+ }
+
+ private void validateServer(Cmpv2Server serverDetails) {
+ Set<ConstraintViolation<Cmpv2Server>> violations = validator.validate(serverDetails);
+ if (!violations.isEmpty()) {
+ throw new InvalidParameterException(violations.toString());
+ }
+ }
+
+ private void validateUniqueCaNames(List<Cmpv2Server> servers) {
+ long distinctCAs = getNumberOfUniqueCaNames(servers);
+ if (servers.size() != distinctCAs) {
+ throw new InvalidParameterException("CA names are not unique within given CMPv2 servers");
+ }
+ }
+
+ private long getNumberOfUniqueCaNames(List<Cmpv2Server> servers) {
+ return servers.stream().map(Cmpv2Server::getCaName)
+ .distinct()
+ .count();
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2Url.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2Url.java
new file mode 100644
index 00000000..36e880e3
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2Url.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.validation.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({FIELD, ANNOTATION_TYPE})
+@Retention(RUNTIME)
+@Constraint(validatedBy = Cmpv2UrlValidator.class)
+public @interface Cmpv2Url {
+ String message() default "Server URL is invalid.";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2UrlValidator.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2UrlValidator.java
new file mode 100644
index 00000000..67031682
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/Cmpv2UrlValidator.java
@@ -0,0 +1,55 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.oom.certservice.certification.configuration.validation.constraints;
+
+import org.onap.oom.certservice.certification.configuration.validation.constraints.violations.PortNumberViolation;
+import org.onap.oom.certservice.certification.configuration.validation.constraints.violations.RequestTypeViolation;
+import org.onap.oom.certservice.certification.configuration.validation.constraints.violations.UrlServerViolation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+class Cmpv2UrlValidator implements ConstraintValidator<Cmpv2Url, String> {
+
+ private final List<UrlServerViolation> violations;
+
+ Cmpv2UrlValidator() {
+ this.violations = Arrays.asList(
+ new PortNumberViolation(),
+ new RequestTypeViolation()
+ );
+ }
+
+ @Override
+ public boolean isValid(String url, ConstraintValidatorContext context) {
+ AtomicBoolean isValid = new AtomicBoolean(true);
+ violations.forEach(violation -> {
+ if (!violation.validate(url)) {
+ isValid.set(false);
+ }
+ });
+ return isValid.get();
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/PortNumberViolation.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/PortNumberViolation.java
new file mode 100644
index 00000000..362a2bb8
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/PortNumberViolation.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.validation.constraints.violations;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class PortNumberViolation implements UrlServerViolation {
+
+ private static final int MIN_PORT = 1;
+ private static final int MAX_PORT = 65535;
+ private static final int PORT_UNDEFINED = -1;
+
+ @Override
+ public boolean validate(String serverUrl) {
+ try {
+ URL url = new URL(serverUrl);
+ int port = url.getPort();
+ return port >= MIN_PORT && port <= MAX_PORT || port == PORT_UNDEFINED;
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolation.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolation.java
new file mode 100644
index 00000000..7f7c5169
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/RequestTypeViolation.java
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+
+package org.onap.oom.certservice.certification.configuration.validation.constraints.violations;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class RequestTypeViolation implements UrlServerViolation {
+
+ private static final List<String> VALID_REQUESTS = Collections.singletonList("http");
+
+ @Override
+ public boolean validate(String serverUrl) {
+ try {
+ AtomicBoolean isValid = new AtomicBoolean(false);
+ String protocol = new URL(serverUrl).getProtocol();
+ VALID_REQUESTS.forEach(requestType -> {
+ if (protocol.equals(requestType)) {
+ isValid.set(true);
+ }
+ });
+ return isValid.get();
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/UrlServerViolation.java b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/UrlServerViolation.java
new file mode 100644
index 00000000..96d1688a
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/configuration/validation/constraints/violations/UrlServerViolation.java
@@ -0,0 +1,25 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.configuration.validation.constraints.violations;
+
+public interface UrlServerViolation {
+ boolean validate(String url);
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ClientAdapterException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ClientAdapterException.java
new file mode 100644
index 00000000..25ea7eb4
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ClientAdapterException.java
@@ -0,0 +1,28 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Cert Service
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.exception;
+
+public class Cmpv2ClientAdapterException extends Exception {
+
+ public Cmpv2ClientAdapterException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ServerNotFoundException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ServerNotFoundException.java
new file mode 100644
index 00000000..304b52a1
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/Cmpv2ServerNotFoundException.java
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.exception;
+
+public class Cmpv2ServerNotFoundException extends RuntimeException {
+ public Cmpv2ServerNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/CsrDecryptionException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/CsrDecryptionException.java
new file mode 100644
index 00000000..80e1b912
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/CsrDecryptionException.java
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.exception;
+
+public class CsrDecryptionException extends DecryptionException {
+ public CsrDecryptionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CsrDecryptionException(String message) {
+ super(message);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/DecryptionException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/DecryptionException.java
new file mode 100644
index 00000000..784009fb
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/DecryptionException.java
@@ -0,0 +1,33 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.exception;
+
+public class DecryptionException extends Exception {
+
+ public DecryptionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DecryptionException(String message) {
+ super(message);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/ErrorResponseModel.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/ErrorResponseModel.java
new file mode 100644
index 00000000..db659412
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/ErrorResponseModel.java
@@ -0,0 +1,36 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.exception;
+
+public class ErrorResponseModel {
+
+ private final String errorMessage;
+
+ public ErrorResponseModel(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+}
+
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/KeyDecryptionException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/KeyDecryptionException.java
new file mode 100644
index 00000000..eeb21da4
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/KeyDecryptionException.java
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.exception;
+
+public class KeyDecryptionException extends DecryptionException {
+ public KeyDecryptionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public KeyDecryptionException(String message) {
+ super(message);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificationModel.java b/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificationModel.java
new file mode 100644
index 00000000..08a09a75
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificationModel.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.model;
+
+import java.util.Collections;
+import java.util.List;
+
+public class CertificationModel {
+
+ private final List<String> certificateChain;
+ private final List<String> trustedCertificates;
+
+ public CertificationModel(List<String> certificateChain, List<String> trustedCertificates) {
+ this.certificateChain = certificateChain;
+ this.trustedCertificates = trustedCertificates;
+ }
+
+ public List<String> getCertificateChain() {
+ return Collections.unmodifiableList(certificateChain);
+ }
+
+ public List<String> getTrustedCertificates() {
+ return Collections.unmodifiableList(trustedCertificates);
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java b/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java
new file mode 100644
index 00000000..7cba1949
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java
@@ -0,0 +1,170 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.certification.model;
+
+import java.io.IOException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.util.io.pem.PemObject;
+
+import org.onap.oom.certservice.certification.exception.CsrDecryptionException;
+import org.onap.oom.certservice.certification.exception.DecryptionException;
+import org.onap.oom.certservice.certification.exception.KeyDecryptionException;
+
+
+public class CsrModel {
+
+ private final PKCS10CertificationRequest csr;
+ private final X500Name subjectData;
+ private final PrivateKey privateKey;
+ private final PublicKey publicKey;
+ private final List<String> sans;
+
+ public CsrModel(PKCS10CertificationRequest csr, X500Name subjectData, PrivateKey privateKey, PublicKey publicKey,
+ List<String> sans) {
+ this.csr = csr;
+ this.subjectData = subjectData;
+ this.privateKey = privateKey;
+ this.publicKey = publicKey;
+ this.sans = sans;
+ }
+
+ public PKCS10CertificationRequest getCsr() {
+ return csr;
+ }
+
+ public X500Name getSubjectData() {
+ return subjectData;
+ }
+
+ public PrivateKey getPrivateKey() {
+ return privateKey;
+ }
+
+ public PublicKey getPublicKey() {
+ return publicKey;
+ }
+
+ public List<String> getSans() {
+ return sans;
+ }
+
+ @Override
+ public String toString() {
+ return "Subject: { " + subjectData + " ,SANs: " + sans + " }";
+ }
+
+ public static class CsrModelBuilder {
+
+ private final PKCS10CertificationRequest csr;
+ private final PemObject privateKey;
+
+ public CsrModel build() throws DecryptionException {
+
+ X500Name subjectData = getSubjectData();
+ PrivateKey javaPrivateKey = convertingPemPrivateKeyToJavaSecurityPrivateKey(getPrivateKey());
+ PublicKey javaPublicKey = convertingPemPublicKeyToJavaSecurityPublicKey(getPublicKey());
+ List<String> sans = getSansData();
+
+ return new CsrModel(csr, subjectData, javaPrivateKey, javaPublicKey, sans);
+ }
+
+ public CsrModelBuilder(PKCS10CertificationRequest csr, PemObject privateKey) {
+ this.csr = csr;
+ this.privateKey = privateKey;
+ }
+
+ private PemObject getPublicKey() throws CsrDecryptionException {
+ try {
+ return new PemObject("PUBLIC KEY", csr.getSubjectPublicKeyInfo().getEncoded());
+ } catch (IOException e) {
+ throw new CsrDecryptionException("Reading Public Key from CSR failed", e.getCause());
+ }
+ }
+
+ private PemObject getPrivateKey() {
+ return privateKey;
+ }
+
+ private X500Name getSubjectData() {
+ return csr.getSubject();
+ }
+
+ private List<String> getSansData() {
+ if (!isAttrsEmpty() && !isAttrsValuesEmpty()) {
+ Extensions extensions = Extensions.getInstance(csr.getAttributes()[0].getAttrValues().getObjectAt(0));
+ GeneralName[] arrayOfAlternativeNames =
+ GeneralNames.fromExtensions(extensions, Extension.subjectAlternativeName).getNames();
+ return Arrays.stream(arrayOfAlternativeNames).map(GeneralName::getName).map(Objects::toString)
+ .collect(Collectors.toList());
+ }
+ return Collections.emptyList();
+ }
+
+ private boolean isAttrsValuesEmpty() {
+ return csr.getAttributes()[0].getAttrValues().size() == 0;
+ }
+
+ private boolean isAttrsEmpty() {
+ return csr.getAttributes().length == 0;
+ }
+
+ private PrivateKey convertingPemPrivateKeyToJavaSecurityPrivateKey(PemObject privateKey)
+ throws KeyDecryptionException {
+ try {
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getContent());
+ return factory.generatePrivate(keySpec);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new KeyDecryptionException("Converting Private Key failed", e.getCause());
+ }
+ }
+
+ private PublicKey convertingPemPublicKeyToJavaSecurityPublicKey(PemObject publicKey)
+ throws KeyDecryptionException {
+ try {
+ KeyFactory factory = KeyFactory.getInstance("RSA");
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getContent());
+ return factory.generatePublic(keySpec);
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+ throw new KeyDecryptionException("Converting Public Key from CSR failed", e.getCause());
+ }
+ }
+ }
+
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java
new file mode 100644
index 00000000..d525c6e3
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.api;
+
+import java.util.Date;
+
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.model.CsrModel;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
+
+/**
+ * This class represent CmpV2Client Interface for obtaining X.509 Digital Certificates in a Public
+ * Key Infrastructure (PKI), making use of Certificate Management Protocol (CMPv2) operating on
+ * newest version: cmp2000(2).
+ */
+public interface CmpClient {
+
+ /**
+ * Requests for a External Root CA Certificate to be created for the passed public keyPair wrapped
+ * in a CSRMeta with common details, accepts self-signed certificate. Basic Authentication using
+ * IAK/RV, Verification of the signature (proof-of-possession) on the request is performed and an
+ * Exception thrown if verification fails or issue encountered in fetching certificate from CA.
+ *
+ * @param csrModel Certificate Signing Request model. Must not be {@code null}.
+ * @param server CMPv2 Server. Must not be {@code null}.
+ * @param notBefore An optional validity to set in the created certificate, Certificate not valid
+ * before this date.
+ * @param notAfter An optional validity to set in the created certificate, Certificate not valid
+ * after this date.
+ * @return model for certification containing certificate chain and trusted certificates
+ * @throws CmpClientException if client error occurs.
+ */
+ Cmpv2CertificationModel createCertificate(
+ CsrModel csrModel,
+ Cmpv2Server server,
+ Date notBefore,
+ Date notAfter)
+ throws CmpClientException;
+
+ /**
+ * Requests for a External Root CA Certificate to be created for the passed public keyPair wrapped
+ * in a CSRMeta with common details, accepts self-signed certificate. Basic Authentication using
+ * IAK/RV, Verification of the signature (proof-of-possession) on the request is performed and an
+ * Exception thrown if verification fails or issue encountered in fetching certificate from CA.
+ *
+ * @param csrModel Certificate Signing Request Model. Must not be {@code null}.
+ * @param server CMPv2 server. Must not be {@code null}.
+ * @return model for certification containing certificate chain and trusted certificates
+ * @throws CmpClientException if client error occurs.
+ */
+ Cmpv2CertificationModel createCertificate(
+ CsrModel csrModel,
+ Cmpv2Server server)
+ throws CmpClientException;
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/CmpClientException.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/CmpClientException.java
new file mode 100644
index 00000000..866377fc
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/CmpClientException.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.exceptions;
+
+/**
+ * The CmpClientException wraps all exceptions occur internally to Cmpv2Client Api code.
+ */
+public class CmpClientException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance with detail message.
+ */
+ public CmpClientException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new instance with detail Throwable cause.
+ */
+ public CmpClientException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new instance with detail message and Throwable cause.
+ */
+ public CmpClientException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/PkiErrorException.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/PkiErrorException.java
new file mode 100644
index 00000000..5dabf064
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/exceptions/PkiErrorException.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.exceptions;
+
+public class PkiErrorException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance with detail message.
+ */
+ public PkiErrorException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new instance with detail Throwable cause.
+ */
+ public PkiErrorException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new instance with detail message and Throwable cause.
+ */
+ public PkiErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java
new file mode 100644
index 00000000..f5eddb58
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java
@@ -0,0 +1,242 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.checkIfCmpResponseContainsError;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.getCertFromByteArray;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.checkImplicitConfirm;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifyPasswordBasedProtection;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifySignature;
+
+import java.io.IOException;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.bouncycastle.asn1.cmp.CMPCertificate;
+import org.bouncycastle.asn1.cmp.CertRepMessage;
+import org.bouncycastle.asn1.cmp.CertResponse;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIHeader;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.onap.oom.certservice.certification.configuration.model.CaMode;
+import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.model.CsrModel;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.onap.oom.certservice.cmpv2client.api.CmpClient;
+import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of the CmpClient Interface conforming to RFC4210 (Certificate Management Protocol
+ * (CMP)) and RFC4211 (Certificate Request Message Format (CRMF)) standards.
+ */
+public class CmpClientImpl implements CmpClient {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CmpClientImpl.class);
+ private final CloseableHttpClient httpClient;
+
+ private static final String DEFAULT_CA_NAME = "Certification Authority";
+ private static final String DEFAULT_PROFILE = CaMode.RA.getProfile();
+
+ public CmpClientImpl(CloseableHttpClient httpClient) {
+ this.httpClient = httpClient;
+ }
+
+ @Override
+ public Cmpv2CertificationModel createCertificate(
+ CsrModel csrModel,
+ Cmpv2Server server,
+ Date notBefore,
+ Date notAfter)
+ throws CmpClientException {
+
+ validate(csrModel, server, httpClient, notBefore, notAfter);
+ KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
+
+ final CreateCertRequest certRequest =
+ CmpMessageBuilder.of(CreateCertRequest::new)
+ .with(CreateCertRequest::setIssuerDn, server.getIssuerDN())
+ .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
+ .with(CreateCertRequest::setSansList, csrModel.getSans())
+ .with(CreateCertRequest::setSubjectKeyPair, keyPair)
+ .with(CreateCertRequest::setNotBefore, notBefore)
+ .with(CreateCertRequest::setNotAfter, notAfter)
+ .with(CreateCertRequest::setInitAuthPassword, server.getAuthentication().getIak())
+ .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv())
+ .build();
+
+ final PKIMessage pkiMessage = certRequest.generateCertReq();
+ Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
+ return retrieveCertificates(csrModel, server, pkiMessage, cmpv2HttpClient);
+ }
+
+ @Override
+ public Cmpv2CertificationModel createCertificate(CsrModel csrModel, Cmpv2Server server)
+ throws CmpClientException {
+ return createCertificate(csrModel, server, null, null);
+ }
+
+ private void checkCmpResponse(
+ final PKIMessage respPkiMessage, final PublicKey publicKey, final String initAuthPassword)
+ throws CmpClientException {
+ final PKIHeader header = respPkiMessage.getHeader();
+ final AlgorithmIdentifier protectionAlgo = header.getProtectionAlg();
+ verifySignatureWithPublicKey(respPkiMessage, publicKey);
+ verifyProtectionWithProtectionAlgo(respPkiMessage, initAuthPassword, header, protectionAlgo);
+ }
+
+ private void verifySignatureWithPublicKey(PKIMessage respPkiMessage, PublicKey publicKey)
+ throws CmpClientException {
+ if (Objects.nonNull(publicKey)) {
+ LOG.debug("Verifying signature of the response.");
+ verifySignature(respPkiMessage, publicKey);
+ } else {
+ LOG.error("Public Key is not available, therefore cannot verify signature");
+ throw new CmpClientException(
+ "Public Key is not available, therefore cannot verify signature");
+ }
+ }
+
+ private void verifyProtectionWithProtectionAlgo(
+ PKIMessage respPkiMessage,
+ String initAuthPassword,
+ PKIHeader header,
+ AlgorithmIdentifier protectionAlgo)
+ throws CmpClientException {
+ if (Objects.nonNull(protectionAlgo)) {
+ LOG.debug("Verifying PasswordBased Protection of the Response.");
+ verifyPasswordBasedProtection(respPkiMessage, initAuthPassword, protectionAlgo);
+ checkImplicitConfirm(header);
+ } else {
+ LOG.error(
+ "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm");
+ throw new CmpClientException(
+ "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm");
+ }
+ }
+
+ private Cmpv2CertificationModel checkCmpCertRepMessage(final PKIMessage respPkiMessage)
+ throws CmpClientException {
+ final PKIBody pkiBody = respPkiMessage.getBody();
+ if (Objects.nonNull(pkiBody) && pkiBody.getContent() instanceof CertRepMessage) {
+ final CertRepMessage certRepMessage = (CertRepMessage) pkiBody.getContent();
+ if (Objects.nonNull(certRepMessage)) {
+ final CertResponse certResponse =
+ getCertificateResponseContainingNewCertificate(certRepMessage);
+ try {
+ return verifyReturnCertChainAndTrustStore(respPkiMessage, certRepMessage, certResponse);
+ } catch (IOException | CertificateParsingException ex) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Exception occurred while retrieving Certificates from response", ex);
+ LOG.error("Exception occurred while retrieving Certificates from response", ex);
+ throw cmpClientException;
+ }
+ } else {
+ return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
+ }
+ }
+ return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
+ }
+
+ private Cmpv2CertificationModel verifyReturnCertChainAndTrustStore(
+ PKIMessage respPkiMessage, CertRepMessage certRepMessage, CertResponse certResponse)
+ throws CertificateParsingException, CmpClientException, IOException {
+ LOG.info("Verifying certificates returned as part of CertResponse.");
+ final CMPCertificate cmpCertificate =
+ certResponse.getCertifiedKeyPair().getCertOrEncCert().getCertificate();
+ final Optional<X509Certificate> leafCertificate =
+ getCertFromByteArray(cmpCertificate.getEncoded(), X509Certificate.class);
+ if (leafCertificate.isPresent()) {
+ return verifyAndReturnCertChainAndTrustSTore(
+ respPkiMessage, certRepMessage, leafCertificate.get());
+ }
+ return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
+ }
+
+ private CertResponse getCertificateResponseContainingNewCertificate(
+ CertRepMessage certRepMessage) {
+ return certRepMessage.getResponse()[0];
+ }
+
+ /**
+ * Validate inputs for Certificate Creation.
+ *
+ * @param csrModel Certificate Signing Request model. Must not be {@code null}.
+ * @param server CMPv2 Server. Must not be {@code null}.
+ * @throws IllegalArgumentException if Before Date is set after the After Date.
+ */
+ private static void validate(
+ final CsrModel csrModel,
+ final Cmpv2Server server,
+ final CloseableHttpClient httpClient,
+ final Date notBefore,
+ final Date notAfter) {
+
+ String caName = CmpUtil.isNullOrEmpty(server.getCaName()) ? server.getCaName() : DEFAULT_CA_NAME;
+ String profile = server.getCaMode() != null ? server.getCaMode().getProfile() : DEFAULT_PROFILE;
+ LOG.info(
+ "Validate before creating Certificate Request for CA :{} in Mode {} ", caName, profile);
+
+ CmpUtil.notNull(csrModel, "CsrModel Instance");
+ CmpUtil.notNull(csrModel.getSubjectData(), "Subject DN");
+ CmpUtil.notNull(csrModel.getPrivateKey(), "Subject private key");
+ CmpUtil.notNull(csrModel.getPublicKey(), "Subject public key");
+ CmpUtil.notNull(server.getIssuerDN(), "Issuer DN");
+ CmpUtil.notNull(server.getUrl(), "External CA URL");
+ CmpUtil.notNull(server.getAuthentication().getIak(), "IAK/RV Password");
+ CmpUtil.notNull(httpClient, "Closeable Http Client");
+
+ if (notBefore != null && notAfter != null && notBefore.compareTo(notAfter) > 0) {
+ throw new IllegalArgumentException("Before Date is set after the After Date");
+ }
+ }
+
+ private Cmpv2CertificationModel retrieveCertificates(
+ CsrModel csrModel, Cmpv2Server server, PKIMessage pkiMessage, Cmpv2HttpClient cmpv2HttpClient)
+ throws CmpClientException {
+ final byte[] respBytes = cmpv2HttpClient.postRequest(pkiMessage, server.getUrl(), server.getCaName());
+ try {
+ final PKIMessage respPkiMessage = PKIMessage.getInstance(respBytes);
+ LOG.info("Received response from Server");
+ checkIfCmpResponseContainsError(respPkiMessage);
+ checkCmpResponse(respPkiMessage, csrModel.getPublicKey(), server.getAuthentication().getIak());
+ return checkCmpCertRepMessage(respPkiMessage);
+ } catch (IllegalArgumentException iae) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Error encountered while processing response from CA server ", iae);
+ LOG.error("Error encountered while processing response from CA server ", iae);
+ throw cmpClientException;
+ }
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageBuilder.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageBuilder.java
new file mode 100644
index 00000000..8ef15a12
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageBuilder.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Generic Builder Class for creating CMP Message.
+ */
+public final class CmpMessageBuilder<T> {
+
+ private final Supplier<T> instantiator;
+ private final List<Consumer<T>> instanceModifiers = new ArrayList<>();
+
+ public CmpMessageBuilder(Supplier<T> instantiator) {
+ this.instantiator = instantiator;
+ }
+
+ public static <T> CmpMessageBuilder<T> of(Supplier<T> instantiator) {
+ return new CmpMessageBuilder<>(instantiator);
+ }
+
+ public <U> CmpMessageBuilder<T> with(BiConsumer<T, U> consumer, U value) {
+ Consumer<T> valueConsumer = instance -> consumer.accept(instance, value);
+ instanceModifiers.add(valueConsumer);
+ return this;
+ }
+
+ public T build() {
+ T value = instantiator.get();
+ instanceModifiers.forEach(modifier -> modifier.accept(value));
+ instanceModifiers.clear();
+ return value;
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageHelper.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageHelper.java
new file mode 100644
index 00000000..844f85be
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpMessageHelper.java
@@ -0,0 +1,246 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import static org.onap.oom.certservice.cmpv2client.impl.CmpUtil.generateProtectedBytes;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+import org.bouncycastle.asn1.cmp.PBMParameter;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIHeader;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.crmf.CertRequest;
+import org.bouncycastle.asn1.crmf.OptionalValidity;
+import org.bouncycastle.asn1.crmf.POPOSigningKey;
+import org.bouncycastle.asn1.crmf.ProofOfPossession;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.Time;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class CmpMessageHelper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CmpMessageHelper.class);
+ private static final AlgorithmIdentifier OWF_ALGORITHM =
+ new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.3.14.3.2.26"));
+ private static final AlgorithmIdentifier MAC_ALGORITHM =
+ new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.2.840.113549.2.9"));
+ private static final ASN1ObjectIdentifier PASSWORD_BASED_MAC =
+ new ASN1ObjectIdentifier("1.2.840.113533.7.66.13");
+
+ private CmpMessageHelper() {
+ }
+
+ /**
+ * Creates an Optional Validity, which is used to specify how long the returned cert should be
+ * valid for.
+ *
+ * @param notBefore Date specifying certificate is not valid before this date.
+ * @param notAfter Date specifying certificate is not valid after this date.
+ * @return {@link OptionalValidity} that can be set for certificate on external CA.
+ */
+ public static OptionalValidity generateOptionalValidity(
+ final Date notBefore, final Date notAfter) {
+ LOG.info("Generating Optional Validity from Date objects");
+ ASN1EncodableVector optionalValidityV = new ASN1EncodableVector();
+ if (notBefore != null) {
+ Time nb = new Time(notBefore);
+ optionalValidityV.add(new DERTaggedObject(true, 0, nb));
+ }
+ if (notAfter != null) {
+ Time na = new Time(notAfter);
+ optionalValidityV.add(new DERTaggedObject(true, 1, na));
+ }
+ return OptionalValidity.getInstance(new DERSequence(optionalValidityV));
+ }
+
+ /**
+ * Create Extensions from Subject Alternative Names.
+ *
+ * @return {@link Extensions}.
+ */
+ public static Extensions generateExtension(final List<String> sansList)
+ throws CmpClientException {
+ LOG.info("Generating Extensions from Subject Alternative Names");
+ final ExtensionsGenerator extGenerator = new ExtensionsGenerator();
+ final GeneralName[] sansGeneralNames = getGeneralNames(sansList);
+ // KeyUsage
+ try {
+ final KeyUsage keyUsage =
+ new KeyUsage(
+ KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.nonRepudiation);
+ extGenerator.addExtension(Extension.keyUsage, false, new DERBitString(keyUsage));
+ extGenerator.addExtension(
+ Extension.subjectAlternativeName, false, new GeneralNames(sansGeneralNames));
+ } catch (IOException ioe) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Exception occurred while creating extensions for PKIMessage", ioe);
+ LOG.error("Exception occurred while creating extensions for PKIMessage");
+ throw cmpClientException;
+ }
+ return extGenerator.generate();
+ }
+
+ public static GeneralName[] getGeneralNames(List<String> sansList) {
+ final List<GeneralName> nameList = new ArrayList<>();
+ for (String san : sansList) {
+ nameList.add(new GeneralName(GeneralName.dNSName, san));
+ }
+ final GeneralName[] sansGeneralNames = new GeneralName[nameList.size()];
+ nameList.toArray(sansGeneralNames);
+ return sansGeneralNames;
+ }
+
+ /**
+ * Method generates Proof-of-Possession (POP) of Private Key. To allow a CA/RA to properly
+ * validity binding between an End Entity and a Key Pair, the PKI Operations specified here make
+ * it possible for an End Entity to prove that it has possession of the Private Key corresponding
+ * to the Public Key for which a Certificate is requested.
+ *
+ * @param certRequest Certificate request that requires proof of possession
+ * @param keypair keypair associated with the subject sending the certificate request
+ * @return {@link ProofOfPossession}.
+ * @throws CmpClientException A general-purpose Cmp client exception.
+ */
+ public static ProofOfPossession generateProofOfPossession(
+ final CertRequest certRequest, final KeyPair keypair) throws CmpClientException {
+ ProofOfPossession proofOfPossession;
+ try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+ final DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
+ derOutputStream.writeObject(certRequest);
+
+ byte[] popoProtectionBytes = byteArrayOutputStream.toByteArray();
+ final String sigalg = PKCSObjectIdentifiers.sha256WithRSAEncryption.getId();
+ final Signature signature = Signature.getInstance(sigalg, BouncyCastleProvider.PROVIDER_NAME);
+ signature.initSign(keypair.getPrivate());
+ signature.update(popoProtectionBytes);
+ DERBitString bs = new DERBitString(signature.sign());
+
+ proofOfPossession =
+ new ProofOfPossession(
+ new POPOSigningKey(
+ null, new AlgorithmIdentifier(new ASN1ObjectIdentifier(sigalg)), bs));
+ } catch (IOException
+ | NoSuchProviderException
+ | NoSuchAlgorithmException
+ | InvalidKeyException
+ | SignatureException ex) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Exception occurred while creating proof of possession for PKIMessage", ex);
+ LOG.error("Exception occurred while creating proof of possession for PKIMessage");
+ throw cmpClientException;
+ }
+ return proofOfPossession;
+ }
+
+ /**
+ * Generic code to create Algorithm Identifier for protection of PKIMessage.
+ *
+ * @return Algorithm Identifier
+ */
+ public static AlgorithmIdentifier protectionAlgoIdentifier(int iterations, byte[] salt) {
+ ASN1Integer iteration = new ASN1Integer(iterations);
+ DEROctetString derSalt = new DEROctetString(salt);
+
+ PBMParameter pp = new PBMParameter(derSalt, OWF_ALGORITHM, iteration, MAC_ALGORITHM);
+ return new AlgorithmIdentifier(PASSWORD_BASED_MAC, pp);
+ }
+
+ /**
+ * Adds protection to the PKIMessage via a specified protection algorithm.
+ *
+ * @param password password used to authenticate PkiMessage with external CA
+ * @param pkiHeader Header of PKIMessage containing generic details for any PKIMessage
+ * @param pkiBody Body of PKIMessage containing specific details for certificate request
+ * @return Protected Pki Message
+ * @throws CmpClientException Wraps several exceptions into one general-purpose exception.
+ */
+ public static PKIMessage protectPkiMessage(
+ PKIHeader pkiHeader, PKIBody pkiBody, String password, int iterations, byte[] salt)
+ throws CmpClientException {
+
+ byte[] raSecret = password.getBytes();
+ byte[] basekey = new byte[raSecret.length + salt.length];
+ System.arraycopy(raSecret, 0, basekey, 0, raSecret.length);
+ System.arraycopy(salt, 0, basekey, raSecret.length, salt.length);
+ byte[] out;
+ try {
+ MessageDigest dig =
+ MessageDigest.getInstance(
+ OWF_ALGORITHM.getAlgorithm().getId(), BouncyCastleProvider.PROVIDER_NAME);
+ for (int i = 0; i < iterations; i++) {
+ basekey = dig.digest(basekey);
+ dig.reset();
+ }
+ byte[] protectedBytes = generateProtectedBytes(pkiHeader, pkiBody);
+ Mac mac =
+ Mac.getInstance(MAC_ALGORITHM.getAlgorithm().getId(), BouncyCastleProvider.PROVIDER_NAME);
+ SecretKey key = new SecretKeySpec(basekey, MAC_ALGORITHM.getAlgorithm().getId());
+ mac.init(key);
+ mac.reset();
+ mac.update(protectedBytes, 0, protectedBytes.length);
+ out = mac.doFinal();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeyException ex) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Exception occurred while generating proof of possession for PKIMessage", ex);
+ LOG.error("Exception occured while generating the proof of possession for PKIMessage");
+ throw cmpClientException;
+ }
+ DERBitString bs = new DERBitString(out);
+
+ return new PKIMessage(pkiHeader, pkiBody, bs);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java
new file mode 100644
index 00000000..db508603
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java
@@ -0,0 +1,335 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.bouncycastle.asn1.cmp.CMPCertificate;
+import org.bouncycastle.asn1.cmp.CertRepMessage;
+import org.bouncycastle.asn1.cmp.ErrorMsgContent;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.onap.oom.certservice.cmpv2client.exceptions.PkiErrorException;
+import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class CmpResponseHelper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CmpResponseHelper.class);
+
+ private CmpResponseHelper() {
+ }
+
+ static void checkIfCmpResponseContainsError(PKIMessage respPkiMessage)
+ throws CmpClientException {
+ if (respPkiMessage.getBody().getType() == PKIBody.TYPE_ERROR) {
+ final ErrorMsgContent errorMsgContent =
+ (ErrorMsgContent) respPkiMessage.getBody().getContent();
+ PkiErrorException pkiErrorException =
+ new PkiErrorException(
+ errorMsgContent.getPKIStatusInfo().getStatusString().getStringAt(0).getString());
+ CmpClientException cmpClientException =
+ new CmpClientException("Error in the PkiMessage response", pkiErrorException);
+ LOG.error("Error in the PkiMessage response: {} ", pkiErrorException.getMessage());
+ throw cmpClientException;
+ }
+ }
+
+
+ /**
+ * Puts together certChain and Trust store and verifies the certChain
+ *
+ * @param respPkiMessage PKIMessage that may contain extra certs used for certchain
+ * @param certRepMessage CertRepMessage that should contain rootCA for certchain
+ * @param leafCertificate certificate returned from our original Cert Request
+ * @return model for certification containing certificate chain and trusted certificates
+ * @throws CertificateParsingException thrown if error occurs while parsing certificate
+ * @throws IOException thrown if IOException occurs while parsing certificate
+ * @throws CmpClientException thrown if error occurs during the verification of the certChain
+ */
+ static Cmpv2CertificationModel verifyAndReturnCertChainAndTrustSTore(
+ PKIMessage respPkiMessage, CertRepMessage certRepMessage, X509Certificate leafCertificate)
+ throws CertificateParsingException, IOException, CmpClientException {
+ Map<X500Name, X509Certificate> certificates = mapAllCertificates(respPkiMessage, certRepMessage);
+ return extractCertificationModel(certificates, leafCertificate);
+ }
+
+ private static Map<X500Name, X509Certificate> mapAllCertificates(
+ PKIMessage respPkiMessage, CertRepMessage certRepMessage
+ )
+ throws IOException, CertificateParsingException, CmpClientException {
+
+ Map<X500Name, X509Certificate> certificates = new HashMap<>();
+
+ CMPCertificate[] extraCerts = respPkiMessage.getExtraCerts();
+ certificates.putAll(mapCertificates(extraCerts));
+
+ CMPCertificate[] caPubsCerts = certRepMessage.getCaPubs();
+ certificates.putAll(mapCertificates(caPubsCerts));
+
+ return certificates;
+ }
+
+ private static Map<X500Name, X509Certificate> mapCertificates(
+ CMPCertificate[] cmpCertificates)
+ throws CertificateParsingException, CmpClientException, IOException {
+
+ Map<X500Name, X509Certificate> certificates = new HashMap<>();
+ if (cmpCertificates != null) {
+ for (CMPCertificate certificate : cmpCertificates) {
+ getCertFromByteArray(certificate.getEncoded(), X509Certificate.class)
+ .ifPresent(x509Certificate ->
+ certificates.put(extractSubjectDn(x509Certificate), x509Certificate)
+ );
+ }
+ }
+
+ return certificates;
+ }
+
+ private static Cmpv2CertificationModel extractCertificationModel(
+ Map<X500Name, X509Certificate> certificates, X509Certificate leafCertificate
+ )
+ throws CmpClientException {
+ List<X509Certificate> certificateChain = new ArrayList<>();
+ X509Certificate previousCertificateInChain;
+ X509Certificate nextCertificateInChain = leafCertificate;
+ do {
+ certificateChain.add(nextCertificateInChain);
+ certificates.remove(extractSubjectDn(nextCertificateInChain));
+ previousCertificateInChain = nextCertificateInChain;
+ nextCertificateInChain = certificates.get(extractIssuerDn(nextCertificateInChain));
+ verify(previousCertificateInChain, nextCertificateInChain, null);
+ }
+ while (!isSelfSign(nextCertificateInChain));
+ List<X509Certificate> trustedCertificates = new ArrayList<>(certificates.values());
+
+ return new Cmpv2CertificationModel(certificateChain, trustedCertificates);
+ }
+
+ private static boolean isSelfSign(X509Certificate certificate) {
+ return extractIssuerDn(certificate).equals(extractSubjectDn(certificate));
+ }
+
+ private static X500Name extractIssuerDn(X509Certificate x509Certificate) {
+ return X500Name.getInstance(x509Certificate.getIssuerDN());
+ }
+
+ private static X500Name extractSubjectDn(X509Certificate x509Certificate) {
+ return X500Name.getInstance(x509Certificate.getSubjectDN());
+ }
+
+
+ /**
+ * Check the certificate with CA certificate.
+ *
+ * @param certificate X.509 certificate to verify. May not be null.
+ * @param caCertChain Collection of X509Certificates. May not be null, an empty list or a
+ * Collection with null entries.
+ * @param date Date to verify at, or null to use current time.
+ * @param pkixCertPathCheckers optional PKIXCertPathChecker implementations to use during cert
+ * path validation
+ * @throws CmpClientException if certificate could not be validated
+ */
+ private static void verify(
+ X509Certificate certificate,
+ X509Certificate caCertChain,
+ Date date,
+ PKIXCertPathChecker... pkixCertPathCheckers)
+ throws CmpClientException {
+ try {
+ verifyCertificates(certificate, caCertChain, date, pkixCertPathCheckers);
+ } catch (CertPathValidatorException cpve) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Invalid certificate or certificate not issued by specified CA: ", cpve);
+ LOG.error("Invalid certificate or certificate not issued by specified CA: ", cpve);
+ throw cmpClientException;
+ } catch (CertificateException ce) {
+ CmpClientException cmpClientException =
+ new CmpClientException("Something was wrong with the supplied certificate", ce);
+ LOG.error("Something was wrong with the supplied certificate", ce);
+ throw cmpClientException;
+ } catch (NoSuchProviderException nspe) {
+ CmpClientException cmpClientException =
+ new CmpClientException("BouncyCastle provider not found.", nspe);
+ LOG.error("BouncyCastle provider not found.", nspe);
+ throw cmpClientException;
+ } catch (NoSuchAlgorithmException nsae) {
+ CmpClientException cmpClientException =
+ new CmpClientException("Algorithm PKIX was not found.", nsae);
+ LOG.error("Algorithm PKIX was not found.", nsae);
+ throw cmpClientException;
+ } catch (InvalidAlgorithmParameterException iape) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Either ca certificate chain was empty,"
+ + " or the certificate was on an inappropriate type for a PKIX path checker.",
+ iape);
+ LOG.error(
+ "Either ca certificate chain was empty, "
+ + "or the certificate was on an inappropriate type for a PKIX path checker.",
+ iape);
+ throw cmpClientException;
+ }
+ }
+
+ private static void verifyCertificates(
+ X509Certificate certificate,
+ X509Certificate caCertChain,
+ Date date,
+ PKIXCertPathChecker[] pkixCertPathCheckers)
+ throws CertificateException, NoSuchProviderException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, CertPathValidatorException {
+ if (caCertChain == null) {
+ final String noRootCaCertificateMessage = "Server response does not contain proper root CA certificate";
+ throw new CertificateException(noRootCaCertificateMessage);
+ }
+ LOG.debug(
+ "Verifying certificate {} as part of cert chain with certificate {}",
+ certificate.getSubjectDN().getName(),
+ caCertChain.getSubjectDN().getName());
+ CertPath cp = getCertPath(certificate);
+ PKIXParameters params = getPkixParameters(caCertChain, date, pkixCertPathCheckers);
+ CertPathValidator cpv =
+ CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME);
+ PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Certificate verify result:{} ", result);
+ }
+ }
+
+ private static PKIXParameters getPkixParameters(
+ X509Certificate caCertChain, Date date, PKIXCertPathChecker[] pkixCertPathCheckers)
+ throws InvalidAlgorithmParameterException {
+ TrustAnchor anchor = new TrustAnchor(caCertChain, null);
+ PKIXParameters params = new PKIXParameters(Collections.singleton(anchor));
+ for (final PKIXCertPathChecker pkixCertPathChecker : pkixCertPathCheckers) {
+ params.addCertPathChecker(pkixCertPathChecker);
+ }
+ params.setRevocationEnabled(false);
+ params.setDate(date);
+ return params;
+ }
+
+ private static CertPath getCertPath(X509Certificate certificate)
+ throws CertificateException, NoSuchProviderException {
+ ArrayList<X509Certificate> certlist = new ArrayList<>();
+ certlist.add(certificate);
+ return CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME)
+ .generateCertPath(certlist);
+ }
+
+ /**
+ * Returns a CertificateFactory that can be used to create certificates from byte arrays and such.
+ *
+ * @param provider Security provider that should be used to create certificates, default BC is
+ * null is passed.
+ * @return CertificateFactory for creating certificate
+ */
+ private static CertificateFactory getCertificateFactory(final String provider)
+ throws CmpClientException {
+ LOG.debug("Creating certificate Factory to generate certificate using provider {}", provider);
+ final String prov;
+ prov = Objects.requireNonNullElse(provider, BouncyCastleProvider.PROVIDER_NAME);
+ try {
+ return CertificateFactory.getInstance("X.509", prov);
+ } catch (NoSuchProviderException nspe) {
+ CmpClientException cmpClientException = new CmpClientException("NoSuchProvider: ", nspe);
+ LOG.error("NoSuchProvider: ", nspe);
+ throw cmpClientException;
+ } catch (CertificateException ce) {
+ CmpClientException cmpClientException = new CmpClientException("CertificateException: ", ce);
+ LOG.error("CertificateException: ", ce);
+ throw cmpClientException;
+ }
+ }
+
+ /**
+ * @param cert byte array that contains certificate
+ * @param returnType the type of Certificate to be returned, for example X509Certificate.class.
+ * Certificate.class can be used if certificate type is unknown.
+ * @throws CertificateParsingException if the byte array does not contain a proper certificate.
+ */
+ static <T extends Certificate> Optional<X509Certificate> getCertFromByteArray(
+ byte[] cert, Class<T> returnType) throws CertificateParsingException, CmpClientException {
+ LOG.debug("Retrieving certificate of type {} from byte array.", returnType);
+ String prov = BouncyCastleProvider.PROVIDER_NAME;
+
+ if (returnType.equals(X509Certificate.class)) {
+ return parseX509Certificate(prov, cert);
+ } else {
+ LOG.debug("Certificate of type {} was skipped, because type of certificate is not 'X509Certificate'.", returnType);
+ return Optional.empty();
+ }
+ }
+
+
+ /**
+ * Parse a X509Certificate from an array of bytes
+ *
+ * @param provider a provider name
+ * @param cert a byte array containing an encoded certificate
+ * @return a decoded X509Certificate
+ * @throws CertificateParsingException if the byte array wasn't valid, or contained a certificate
+ * other than an X509 Certificate.
+ */
+ private static Optional<X509Certificate> parseX509Certificate(String provider, byte[] cert)
+ throws CertificateParsingException, CmpClientException {
+ LOG.debug("Parsing X509Certificate from bytes with provider {}", provider);
+ final CertificateFactory cf = getCertificateFactory(provider);
+ X509Certificate result;
+ try {
+ result = (X509Certificate) Objects.requireNonNull(cf).generateCertificate(new ByteArrayInputStream(cert));
+ return Optional.ofNullable(result);
+ } catch (CertificateException ce) {
+ throw new CertificateParsingException("Could not parse byte array as X509Certificate ", ce);
+ }
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseValidationHelper.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseValidationHelper.java
new file mode 100644
index 00000000..c699b110
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseValidationHelper.java
@@ -0,0 +1,241 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.util.Arrays;
+import java.util.Objects;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
+import org.bouncycastle.asn1.cmp.InfoTypeAndValue;
+import org.bouncycastle.asn1.cmp.PBMParameter;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIHeader;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class CmpResponseValidationHelper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CmpResponseValidationHelper.class);
+
+ private CmpResponseValidationHelper() {
+ }
+
+ /**
+ * Create a base key to use for verifying the PasswordBasedMac on a PKIMessage
+ *
+ * @param pbmParamSeq parameters recieved in PKIMessage used with password
+ * @param initAuthPassword password used to decrypt the basekey
+ * @return bytes representing the basekey
+ * @throws CmpClientException thrown if algorithem exceptions occur for the message digest
+ */
+ public static byte[] getBaseKeyFromPbmParameters(
+ PBMParameter pbmParamSeq, String initAuthPassword) throws CmpClientException {
+ final int iterationCount = pbmParamSeq.getIterationCount().getPositiveValue().intValue();
+ LOG.info("Iteration count is: {}", iterationCount);
+ final AlgorithmIdentifier owfAlg = pbmParamSeq.getOwf();
+ LOG.info("One Way Function type is: {}", owfAlg.getAlgorithm().getId());
+ final byte[] salt = pbmParamSeq.getSalt().getOctets();
+ final byte[] raSecret = initAuthPassword != null ? initAuthPassword.getBytes() : new byte[0];
+ byte[] basekey = new byte[raSecret.length + salt.length];
+ System.arraycopy(raSecret, 0, basekey, 0, raSecret.length);
+ System.arraycopy(salt, 0, basekey, raSecret.length, salt.length);
+ try {
+ final MessageDigest messageDigest =
+ MessageDigest.getInstance(
+ owfAlg.getAlgorithm().getId(), BouncyCastleProvider.PROVIDER_NAME);
+ for (int i = 0; i < iterationCount; i++) {
+ basekey = messageDigest.digest(basekey);
+ messageDigest.reset();
+ }
+ } catch (NoSuchAlgorithmException | NoSuchProviderException ex) {
+ LOG.error("ProtectionBytes don't match passwordBasedProtection, authentication failed");
+ throw new CmpClientException(
+ "ProtectionBytes don't match passwordBasedProtection, authentication failed", ex);
+ }
+ return basekey;
+ }
+
+ /**
+ * Verifies the signature of the response message using our public key
+ *
+ * @param respPkiMessage PKIMessage we wish to verify signature for
+ * @param pk public key used to verify signature.
+ * @throws CmpClientException
+ */
+ public static void verifySignature(PKIMessage respPkiMessage, PublicKey pk)
+ throws CmpClientException {
+ final byte[] protBytes = getProtectedBytes(respPkiMessage);
+ final DERBitString derBitString = respPkiMessage.getProtection();
+ try {
+ final Signature signature =
+ Signature.getInstance(
+ PKCSObjectIdentifiers.sha256WithRSAEncryption.getId(),
+ BouncyCastleProvider.PROVIDER_NAME);
+ signature.initVerify(pk);
+ signature.update(protBytes);
+ signature.verify(derBitString.getBytes());
+ } catch (NoSuchAlgorithmException
+ | NoSuchProviderException
+ | InvalidKeyException
+ | SignatureException e) {
+ CmpClientException clientException =
+ new CmpClientException("Signature Verification failed", e);
+ LOG.error("Signature Verification failed", e);
+ throw clientException;
+ }
+ }
+
+ /**
+ * Converts the header and the body of a PKIMessage to an ASN1Encodable and returns the as a byte
+ * array
+ *
+ * @param msg PKIMessage to get protected bytes from
+ * @return the PKIMessage's header and body in byte array
+ */
+ public static byte[] getProtectedBytes(PKIMessage msg) throws CmpClientException {
+ return getProtectedBytes(msg.getHeader(), msg.getBody());
+ }
+
+ /**
+ * Converts the header and the body of a PKIMessage to an ASN1Encodable and returns the as a byte
+ * array
+ *
+ * @param header PKIHeader to be converted
+ * @param body PKIMessage to be converted
+ * @return the PKIMessage's header and body in byte array
+ */
+ public static byte[] getProtectedBytes(PKIHeader header, PKIBody body) throws CmpClientException {
+ byte[] res;
+ ASN1EncodableVector encodableVector = new ASN1EncodableVector();
+ encodableVector.add(header);
+ encodableVector.add(body);
+ ASN1Encodable protectedPart = new DERSequence(encodableVector);
+ try {
+ ByteArrayOutputStream bao = new ByteArrayOutputStream();
+ DEROutputStream out = new DEROutputStream(bao);
+ out.writeObject(protectedPart);
+ res = bao.toByteArray();
+ } catch (IOException ioe) {
+ CmpClientException cmpClientException =
+ new CmpClientException("Error occured while getting protected bytes", ioe);
+ LOG.error("Error occured while getting protected bytes", ioe);
+ throw cmpClientException;
+ }
+ return res;
+ }
+
+ /**
+ * verify the password based protection within the response message
+ *
+ * @param respPkiMessage PKIMessage we want to verify password based protection for
+ * @param initAuthPassword password used to decrypt protection
+ * @param protectionAlgo protection algorithm we can use to decrypt protection
+ * @throws CmpClientException
+ */
+ public static void verifyPasswordBasedProtection(
+ PKIMessage respPkiMessage, String initAuthPassword, AlgorithmIdentifier protectionAlgo)
+ throws CmpClientException {
+ final byte[] protectedBytes = getProtectedBytes(respPkiMessage);
+ final PBMParameter pbmParamSeq = PBMParameter.getInstance(protectionAlgo.getParameters());
+ if (Objects.nonNull(pbmParamSeq)) {
+ try {
+ byte[] basekey = getBaseKeyFromPbmParameters(pbmParamSeq, initAuthPassword);
+ final Mac mac = getMac(protectedBytes, pbmParamSeq, basekey);
+ final byte[] outBytes = mac.doFinal();
+ final byte[] protectionBytes = respPkiMessage.getProtection().getBytes();
+ if (!Arrays.equals(outBytes, protectionBytes)) {
+ LOG.error("protectionBytes don't match passwordBasedProtection, authentication failed");
+ throw new CmpClientException(
+ "protectionBytes don't match passwordBasedProtection, authentication failed");
+ }
+ } catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidKeyException ex) {
+ CmpClientException cmpClientException =
+ new CmpClientException("Error while validating CMP response ", ex);
+ LOG.error("Error while validating CMP response ", ex);
+ throw cmpClientException;
+ }
+ }
+ }
+
+ public static void checkImplicitConfirm(PKIHeader header) {
+ InfoTypeAndValue[] infos = header.getGeneralInfo();
+ if (Objects.nonNull(infos)) {
+ if (CMPObjectIdentifiers.it_implicitConfirm.equals(getImplicitConfirm(infos))) {
+ LOG.info("Implicit Confirm on certificate from server.");
+ } else {
+ LOG.debug("No Implicit confirm in Response");
+ }
+ } else {
+ LOG.debug("No general Info in header of response, cannot verify implicit confirm");
+ }
+ }
+
+ public static ASN1ObjectIdentifier getImplicitConfirm(InfoTypeAndValue[] info) {
+ return info[0].getInfoType();
+ }
+
+ /**
+ * Get cryptographical Mac we can use to decrypt our PKIMessage
+ *
+ * @param protectedBytes Protected bytes representing the PKIMessage
+ * @param pbmParamSeq Parameters used to decrypt PKIMessage, including mac algorithm used
+ * @param basekey Key used alongside mac Oid to create secret key for decrypting PKIMessage
+ * @return Mac that's ready to return decrypted bytes
+ * @throws NoSuchAlgorithmException Possibly thrown trying to get mac instance
+ * @throws NoSuchProviderException Possibly thrown trying to get mac instance
+ * @throws InvalidKeyException Possibly thrown trying to initialize mac using secretkey
+ */
+ public static Mac getMac(byte[] protectedBytes, PBMParameter pbmParamSeq, byte[] basekey)
+ throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException {
+ final AlgorithmIdentifier macAlg = pbmParamSeq.getMac();
+ LOG.info("Mac type is: {}", macAlg.getAlgorithm().getId());
+ final String macOid = macAlg.getAlgorithm().getId();
+ final Mac mac = Mac.getInstance(macOid, BouncyCastleProvider.PROVIDER_NAME);
+ final SecretKey key = new SecretKeySpec(basekey, macOid);
+ mac.init(key);
+ mac.reset();
+ mac.update(protectedBytes, 0, protectedBytes.length);
+ return mac;
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpUtil.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpUtil.java
new file mode 100644
index 00000000..281f6f5e
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpUtil.java
@@ -0,0 +1,153 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.Date;
+import java.util.Objects;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.CMPObjectIdentifiers;
+import org.bouncycastle.asn1.cmp.InfoTypeAndValue;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIHeader;
+import org.bouncycastle.asn1.cmp.PKIHeaderBuilder;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class CmpUtil {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CmpUtil.class);
+ private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+ public static final int RANDOM_BYTE_LENGTH = 16;
+ public static final int RANDOM_SEED = 1000;
+
+ private CmpUtil() {
+ }
+
+ /**
+ * Validates specified object reference is not null.
+ *
+ * @param argument T - the type of the reference.
+ * @param message message - detail message to be used in the event that a NullPointerException is
+ * thrown.
+ * @return The Object if not null
+ */
+ public static <T> T notNull(T argument, String message) {
+ return Objects.requireNonNull(argument, message + " must not be null");
+ }
+
+ /**
+ * Validates String object reference is not null and not empty.
+ *
+ * @param stringArg String Object that need to be validated.
+ * @return boolean
+ */
+ public static boolean isNullOrEmpty(String stringArg) {
+ return (stringArg != null && !stringArg.trim().isEmpty());
+ }
+
+ /**
+ * Creates a random number than can be used for sendernonce, transactionId and salts.
+ *
+ * @return bytes containing a random number string representing a nonce
+ */
+ static byte[] createRandomBytes() {
+ LOGGER.info("Generating random array of bytes");
+ byte[] randomBytes = new byte[RANDOM_BYTE_LENGTH];
+ SECURE_RANDOM.nextBytes(randomBytes);
+ return randomBytes;
+ }
+
+ /**
+ * Creates a random integer than can be used to represent a transactionId or determine the number
+ * iterations in a protection algorithm.
+ *
+ * @return bytes containing a random number string representing a nonce
+ */
+ static int createRandomInt(int range) {
+ LOGGER.info("Generating random integer");
+ return SECURE_RANDOM.nextInt(range) + RANDOM_SEED;
+ }
+
+ /**
+ * Generates protected bytes of a combined PKIHeader and PKIBody.
+ *
+ * @param header Header of PKIMessage containing common parameters
+ * @param body Body of PKIMessage containing specific information for message
+ * @return bytes representing the PKIHeader and PKIBody thats to be protected
+ */
+ static byte[] generateProtectedBytes(PKIHeader header, PKIBody body) throws CmpClientException {
+ LOGGER.info("Generating array of bytes representing PkiHeader and PkiBody");
+ byte[] res;
+ ASN1EncodableVector vector = new ASN1EncodableVector();
+ vector.add(header);
+ vector.add(body);
+ ASN1Encodable protectedPart = new DERSequence(vector);
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ DEROutputStream out = new DEROutputStream(baos);
+ out.writeObject(protectedPart);
+ res = baos.toByteArray();
+ } catch (IOException ioe) {
+ CmpClientException cmpClientException =
+ new CmpClientException("IOException occurred while creating protectedBytes", ioe);
+ LOGGER.error("IOException occurred while creating protectedBytes");
+ throw cmpClientException;
+ }
+ return res;
+ }
+
+ /**
+ * Generates a PKIHeader Builder object.
+ *
+ * @param subjectDn distinguished name of Subject
+ * @param issuerDn distinguished name of external CA
+ * @param protectionAlg protection Algorithm used to protect PKIMessage
+ * @return PKIHeaderBuilder
+ */
+ static PKIHeader generatePkiHeader(
+ X500Name subjectDn, X500Name issuerDn, AlgorithmIdentifier protectionAlg, String senderKid) {
+ LOGGER.info("Generating a Pki Header Builder");
+ PKIHeaderBuilder pkiHeaderBuilder =
+ new PKIHeaderBuilder(
+ PKIHeader.CMP_2000, new GeneralName(subjectDn), new GeneralName(issuerDn));
+
+ pkiHeaderBuilder.setMessageTime(new ASN1GeneralizedTime(new Date()));
+ pkiHeaderBuilder.setSenderNonce(new DEROctetString(createRandomBytes()));
+ pkiHeaderBuilder.setTransactionID(new DEROctetString(createRandomBytes()));
+ pkiHeaderBuilder.setProtectionAlg(protectionAlg);
+ pkiHeaderBuilder.setGeneralInfo(new InfoTypeAndValue(CMPObjectIdentifiers.it_implicitConfirm));
+ pkiHeaderBuilder.setSenderKID(new DEROctetString(senderKid.getBytes()));
+
+ return pkiHeaderBuilder.build();
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/Cmpv2HttpClient.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/Cmpv2HttpClient.java
new file mode 100644
index 00000000..0f32c668
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/Cmpv2HttpClient.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class Cmpv2HttpClient {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Cmpv2HttpClient.class);
+
+ private static final String CONTENT_TYPE = "Content-type";
+ private static final String CMP_REQUEST_MIMETYPE = "application/pkixcmp";
+ private final CloseableHttpClient httpClient;
+
+ /**
+ * constructor for Cmpv2HttpClient
+ *
+ * @param httpClient CloseableHttpClient used for sending/recieve request.
+ */
+ Cmpv2HttpClient(CloseableHttpClient httpClient) {
+ this.httpClient = httpClient;
+ }
+
+ /**
+ * Send Post Request to Server
+ *
+ * @param pkiMessage PKIMessage to send to server
+ * @param urlString url for the server we're sending request
+ * @param caName name of CA server
+ * @return PKIMessage received from CMPServer
+ * @throws CmpClientException thrown if problems with connecting or parsing response to server
+ */
+ public byte[] postRequest(
+ final PKIMessage pkiMessage, final String urlString, final String caName)
+ throws CmpClientException {
+ try (ByteArrayOutputStream byteArrOutputStream = new ByteArrayOutputStream()) {
+ final HttpPost postRequest = new HttpPost(urlString);
+ final byte[] requestBytes = pkiMessage.getEncoded();
+
+ postRequest.setEntity(new ByteArrayEntity(requestBytes));
+ postRequest.setHeader(CONTENT_TYPE, CMP_REQUEST_MIMETYPE);
+
+ try (CloseableHttpResponse response = httpClient.execute(postRequest)) {
+ response.getEntity().writeTo(byteArrOutputStream);
+ }
+ return byteArrOutputStream.toByteArray();
+ } catch (IOException ioe) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ String.format("IOException error while trying to connect CA %s", caName), ioe);
+ LOG.error("IOException error {}, while trying to connect CA {}", ioe.getMessage(), caName);
+ throw cmpClientException;
+ }
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java
new file mode 100644
index 00000000..a0ba13d6
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.impl;
+
+import static org.onap.oom.certservice.cmpv2client.impl.CmpUtil.createRandomBytes;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpUtil.createRandomInt;
+import static org.onap.oom.certservice.cmpv2client.impl.CmpUtil.generatePkiHeader;
+
+import java.security.KeyPair;
+import java.util.Date;
+import java.util.List;
+
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIHeader;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.crmf.CertReqMessages;
+import org.bouncycastle.asn1.crmf.CertReqMsg;
+import org.bouncycastle.asn1.crmf.CertRequest;
+import org.bouncycastle.asn1.crmf.CertTemplateBuilder;
+import org.bouncycastle.asn1.crmf.ProofOfPossession;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+
+/**
+ * Implementation of the CmpClient Interface conforming to RFC4210 (Certificate Management Protocol
+ * (CMP)) and RFC4211 (Certificate Request Message Format (CRMF)) standards.
+ */
+class CreateCertRequest {
+
+ private X500Name issuerDn;
+ private X500Name subjectDn;
+ private List<String> sansList;
+ private KeyPair subjectKeyPair;
+ private Date notBefore;
+ private Date notAfter;
+ private String initAuthPassword;
+ private String senderKid;
+
+ private static final int ITERATIONS = createRandomInt(5000);
+ private static final byte[] SALT = createRandomBytes();
+ private final int certReqId = createRandomInt(Integer.MAX_VALUE);
+
+ public void setIssuerDn(X500Name issuerDn) {
+ this.issuerDn = issuerDn;
+ }
+
+ public void setSubjectDn(X500Name subjectDn) {
+ this.subjectDn = subjectDn;
+ }
+
+ public void setSansList(List<String> sansList) {
+ this.sansList = sansList;
+ }
+
+ public void setSubjectKeyPair(KeyPair subjectKeyPair) {
+ this.subjectKeyPair = subjectKeyPair;
+ }
+
+ public void setNotBefore(Date notBefore) {
+ this.notBefore = notBefore;
+ }
+
+ public void setNotAfter(Date notAfter) {
+ this.notAfter = notAfter;
+ }
+
+ public void setInitAuthPassword(String initAuthPassword) {
+ this.initAuthPassword = initAuthPassword;
+ }
+
+ public void setSenderKid(String senderKid) {
+ this.senderKid = senderKid;
+ }
+
+ /**
+ * Method to create {@link PKIMessage} from {@link CertRequest},{@link ProofOfPossession}, {@link
+ * CertReqMsg}, {@link CertReqMessages}, {@link PKIHeader} and {@link PKIBody}.
+ *
+ * @return {@link PKIMessage}
+ */
+ public PKIMessage generateCertReq() throws CmpClientException {
+ final CertTemplateBuilder certTemplateBuilder =
+ new CertTemplateBuilder()
+ .setIssuer(issuerDn)
+ .setSubject(subjectDn)
+ .setExtensions(CmpMessageHelper.generateExtension(sansList))
+ .setValidity(CmpMessageHelper.generateOptionalValidity(notBefore, notAfter))
+ .setPublicKey(
+ SubjectPublicKeyInfo.getInstance(subjectKeyPair.getPublic().getEncoded()));
+
+ final CertRequest certRequest = new CertRequest(certReqId, certTemplateBuilder.build(), null);
+ final ProofOfPossession proofOfPossession =
+ CmpMessageHelper.generateProofOfPossession(certRequest, subjectKeyPair);
+
+ final CertReqMsg certReqMsg = new CertReqMsg(certRequest, proofOfPossession, null);
+ final CertReqMessages certReqMessages = new CertReqMessages(certReqMsg);
+
+ final PKIHeader pkiHeader =
+ generatePkiHeader(
+ subjectDn,
+ issuerDn,
+ CmpMessageHelper.protectionAlgoIdentifier(ITERATIONS, SALT),
+ senderKid);
+ final PKIBody pkiBody = new PKIBody(PKIBody.TYPE_INIT_REQ, certReqMessages);
+
+ return CmpMessageHelper.protectPkiMessage(
+ pkiHeader, pkiBody, initAuthPassword, ITERATIONS, SALT);
+ }
+}
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/model/Cmpv2CertificationModel.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/model/Cmpv2CertificationModel.java
new file mode 100644
index 00000000..a40a7708
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/model/Cmpv2CertificationModel.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.oom.certservice.cmpv2client.model;
+
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+
+public class Cmpv2CertificationModel {
+
+ private final List<X509Certificate> certificateChain;
+ private final List<X509Certificate> trustedCertificates;
+
+ public Cmpv2CertificationModel(List<X509Certificate> certificateChain, List<X509Certificate> trustedCertificates) {
+ this.certificateChain = certificateChain;
+ this.trustedCertificates = trustedCertificates;
+ }
+
+ public List<X509Certificate> getCertificateChain() {
+ return Collections.unmodifiableList(certificateChain);
+ }
+
+ public List<X509Certificate> getTrustedCertificates() {
+ return Collections.unmodifiableList(trustedCertificates);
+ }
+}