aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkooper <sergey.sachkov@est.tech>2019-04-02 09:22:01 +0000
committerkooper <sergey.sachkov@est.tech>2019-04-02 09:22:01 +0000
commitb2f9dc5d3bc02564b4d952caa0bf2ccd20dfc6af (patch)
tree9d26cfd0a4771c38bc1f662d697bce77190d5e4c
parentddaa4ab7cbefb3c765b6d5732bef568a447f134a (diff)
Verify signature
Change-Id: I8fc5d50d74d3dd8031c96ee16708489dc7c789b8 Issue-ID: SDC-2163 Signed-off-by: kooper <sergey.sachkov@est.tech>
-rw-r--r--onboarding/pom.xml1
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/pom.xml18
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java160
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateException.java27
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java55
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchiveTest.java99
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java121
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/notCsar.txt0
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-directories-in-root.zipbin0 -> 290 bytes
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-directory-with-contents-in-root.zipbin0 -> 558 bytes
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-empty-directory-in-root.zipbin0 -> 420 bytes
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-files-in-root.zipbin0 -> 286 bytes
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/csar-and-cms-in-root.zipbin0 -> 304 bytes
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package-tampered-data.zipbin0 -> 4242 bytes
-rw-r--r--openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package.zipbin0 -> 4242 bytes
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml5
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java269
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerException.java27
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java108
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.cms34
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.csarbin0 -> 3866 bytes
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cert20
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cms17
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.csarbin0 -> 3866 bytes
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/root.cert22
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.cms34
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.csarbin0 -> 3877 bytes
-rw-r--r--openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java3
28 files changed, 956 insertions, 64 deletions
diff --git a/onboarding/pom.xml b/onboarding/pom.xml
index 25db1ac9de..35be7440fc 100644
--- a/onboarding/pom.xml
+++ b/onboarding/pom.xml
@@ -125,6 +125,7 @@
<zusammen-index-store.version>1.0.0</zusammen-index-store.version>
<build.tools.version>${project.version}</build.tools.version>
<togglz.version>2.4.1.Final</togglz.version>
+ <bouncycastle.version>1.61</bouncycastle.version>
</properties>
<dependencyManagement>
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/pom.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/pom.xml
index 26a7c15ff2..b1cee443a8 100644
--- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/pom.xml
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/pom.xml
@@ -130,6 +130,24 @@
<artifactId>unique-type-rest-types</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4-common</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito2</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java
new file mode 100644
index 0000000000..bf029fb29e
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java
@@ -0,0 +1,160 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2019, Nordix Foundation. 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.openecomp.sdcrests.vsp.rest.data;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.openecomp.core.utilities.file.FileContentHandler;
+import org.openecomp.sdc.common.utils.CommonUtil;
+import org.openecomp.sdc.logging.api.Logger;
+import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
+import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Class responsible for processing zip archive and verify if this package corresponds
+ * SOL004 option 2 signed package format, verifies the cms signature if package is signed
+ */
+public class PackageArchive {
+ private static final Logger LOG = LoggerFactory.getLogger(PackageArchive.class);
+ private static final String[] ALLOWED_ARCHIVE_EXTENSIONS = {"csar", "zip"};
+ private static final String[] ALLOWED_SIGNATURE_EXTENSIONS = {"cms"};
+ private static final String[] ALLOWED_CERTIFICATE_EXTENSIONS = {"cert"};
+ private static final int NUMBER_OF_FILES_FOR_SIGNATURE_WITH_CERT_INSIDE = 2;
+ private static final int NUMBER_OF_FILES_FOR_SIGNATURE_WITHOUT_CERT_INSIDE = 3;
+ private final SecurityManager securityManager;
+ private final byte[] outerPackageFileBytes;
+ private Pair<FileContentHandler, List<String>> handlerPair;
+
+ public PackageArchive(Attachment uploadedFile) {
+ this(uploadedFile.getObject(byte[].class));
+ }
+
+ public PackageArchive(byte[] outerPackageFileBytes) {
+ this.outerPackageFileBytes = outerPackageFileBytes;
+ this.securityManager = SecurityManager.getInstance();
+ try {
+ handlerPair = CommonUtil.getFileContentMapFromOrchestrationCandidateZip(
+ outerPackageFileBytes);
+ } catch (IOException exception) {
+ LOG.error("Error reading files inside archive", exception);
+ }
+ }
+
+ /**
+ * Checks if package matches required format {package.csar/zip, package.cms, package.cert(optional)}
+ *
+ * @return true if structure matches sol004 option 2 structure
+ */
+ public boolean isSigned() {
+ return isPackageSizeMatches() && getSignatureFileName().isPresent();
+ }
+
+ /**
+ * Gets csar/zip package name with extension only if package is signed
+ *
+ * @return csar package name
+ */
+ public Optional<String> getArchiveFileName() {
+ if (isSigned()) {
+ return getFileByExtension(ALLOWED_ARCHIVE_EXTENSIONS);
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Gets csar/zip package content from zip archive
+ * @return csar package content
+ * @throws SecurityManagerException
+ */
+ public byte[] getPackageFileContents() throws SecurityManagerException {
+ try {
+ if (isSignatureValid()) {
+ return handlerPair.getKey().getFiles().get(getArchiveFileName().orElseThrow(CertificateException::new));
+ }
+ } catch (CertificateException exception) {
+ LOG.info("Error verifying signature " + exception);
+ }
+ return outerPackageFileBytes;
+ }
+
+ /**
+ * Validates package signature against trusted certificates
+ * @return true if signature verified
+ * @throws SecurityManagerException
+ */
+ public boolean isSignatureValid() throws SecurityManagerException {
+ Map<String, byte[]> files = handlerPair.getLeft().getFiles();
+ Optional<String> signatureFileName = getSignatureFileName();
+ Optional<String> archiveFileName = getArchiveFileName();
+ if (files.isEmpty() || !signatureFileName.isPresent() || !archiveFileName.isPresent()) {
+ return false;
+ }
+ Optional<String> certificateFile = getCertificateFileName();
+ if(certificateFile.isPresent()){
+ return securityManager.verifySignedData(files.get(signatureFileName.get()),
+ files.get(certificateFile.get()), files.get(archiveFileName.get()));
+ }else {
+ return securityManager.verifySignedData(files.get(signatureFileName.get()),
+ null, files.get(archiveFileName.get()));
+ }
+ }
+
+ private boolean isPackageSizeMatches() {
+ return handlerPair.getRight().isEmpty()
+ && (handlerPair.getLeft().getFiles().size() == NUMBER_OF_FILES_FOR_SIGNATURE_WITH_CERT_INSIDE
+ || handlerPair.getLeft().getFiles().size() == NUMBER_OF_FILES_FOR_SIGNATURE_WITHOUT_CERT_INSIDE);
+ }
+
+ private Optional<String> getSignatureFileName() {
+ return getFileByExtension(ALLOWED_SIGNATURE_EXTENSIONS);
+ }
+
+ private Optional<String> getFileByExtension(String[] extensions) {
+ for (String fileName : handlerPair.getLeft().getFileList()) {
+ for (String extension : extensions) {
+ if (extension.equalsIgnoreCase(FilenameUtils.getExtension(fileName))) {
+ return Optional.of(fileName);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Optional<String> getCertificateFileName() {
+ Optional<String> certFileName = getFileByExtension(ALLOWED_CERTIFICATE_EXTENSIONS);
+ if(!certFileName.isPresent()){
+ return Optional.empty();
+ }
+ String certNameWithoutExtension = FilenameUtils.removeExtension(certFileName.get());
+ if (certNameWithoutExtension.equals(FilenameUtils.removeExtension(getArchiveFileName().orElse("")))) {
+ return certFileName;
+ }
+ //cert file name should be the same as package name, e.g. vnfpackage.scar-->vnfpackage.cert
+ return Optional.empty();
+ }
+}
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateException.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateException.java
new file mode 100644
index 0000000000..a7e65a7e6f
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateException.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2019, Nordix Foundation. 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.openecomp.sdcrests.vsp.rest.services;
+
+public class OrchestrationTemplateCandidateException extends Exception{
+
+ public OrchestrationTemplateCandidateException(String message, Throwable t){
+ super(message, t);
+ }
+}
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java
index bdc422d580..4978b3fb34 100644
--- a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImpl.java
@@ -25,6 +25,7 @@ import org.openecomp.sdc.activitylog.ActivityLogManagerFactory;
import org.openecomp.sdc.activitylog.dao.type.ActivityLogEntity;
import org.openecomp.sdc.activitylog.dao.type.ActivityType;
import org.openecomp.sdc.common.errors.Messages;
+import org.openecomp.sdc.common.utils.SdcCommon;
import org.openecomp.sdc.datatypes.error.ErrorLevel;
import org.openecomp.sdc.datatypes.error.ErrorMessage;
import org.openecomp.sdc.logging.api.Logger;
@@ -33,6 +34,7 @@ import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateMan
import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManagerFactory;
import org.openecomp.sdc.vendorsoftwareproduct.VendorSoftwareProductManager;
import org.openecomp.sdc.vendorsoftwareproduct.VspManagerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
import org.openecomp.sdc.vendorsoftwareproduct.types.OrchestrationTemplateActionResponse;
import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
import org.openecomp.sdc.vendorsoftwareproduct.types.ValidationResponse;
@@ -43,6 +45,7 @@ import org.openecomp.sdcrests.vendorsoftwareproducts.types.OrchestrationTemplate
import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
import org.openecomp.sdcrests.vendorsoftwareproducts.types.ValidationResponseDto;
import org.openecomp.sdcrests.vsp.rest.OrchestrationTemplateCandidate;
+import org.openecomp.sdcrests.vsp.rest.data.PackageArchive;
import org.openecomp.sdcrests.vsp.rest.mapping.MapFilesDataStructureToDto;
import org.openecomp.sdcrests.vsp.rest.mapping.MapUploadFileResponseToUploadFileResponseDto;
import org.openecomp.sdcrests.vsp.rest.mapping.MapValidationResponseToDto;
@@ -51,9 +54,13 @@ import org.springframework.stereotype.Service;
import javax.inject.Named;
import javax.ws.rs.core.Response;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
import static org.openecomp.core.utilities.file.FileUtils.getFileExtension;
@@ -75,18 +82,46 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
@Override
public Response upload(String vspId, String versionId, Attachment fileToUpload, String user) {
+ PackageArchive archive = new PackageArchive(fileToUpload.getObject(byte[].class));
+ UploadFileResponseDto uploadFileResponseDto;
+ try {
+ if (archive.isSigned() && !archive.isSignatureValid()) {
+ ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR,
+ getErrorWithParameters(Messages.FAILED_TO_VERIFY_SIGNATURE.getErrorMessage(), ""));
+ LOGGER.error(errorMessage.getMessage());
+ uploadFileResponseDto = buildUploadResponseWithError(errorMessage);
+ //returning OK as SDC UI won't show error message if NOT OK error code.
+ return Response.ok(uploadFileResponseDto).build();
+ }
- String filename = fileToUpload.getContentDisposition().getParameter("filename");
- UploadFileResponse uploadFileResponse = candidateManager
- .upload(vspId, new Version(versionId), fileToUpload.getObject(InputStream.class),
- getFileExtension(filename), getNetworkPackageName(filename));
-
- UploadFileResponseDto uploadFileResponseDto = new MapUploadFileResponseToUploadFileResponseDto()
- .applyMapping(uploadFileResponse, UploadFileResponseDto.class);
-
+ String filename = archive.getArchiveFileName().orElse(fileToUpload.getContentDisposition().getFilename());
+ UploadFileResponse uploadFileResponse = candidateManager
+ .upload(vspId, new Version(versionId), new ByteArrayInputStream(archive.getPackageFileContents()),
+ getFileExtension(filename), getNetworkPackageName(filename));
+
+ uploadFileResponseDto = new MapUploadFileResponseToUploadFileResponseDto()
+ .applyMapping(uploadFileResponse, UploadFileResponseDto.class);
+ } catch (SecurityManagerException e) {
+ ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR,
+ getErrorWithParameters(e.getMessage(), ""));
+ LOGGER.error(errorMessage.getMessage(), e);
+ uploadFileResponseDto = buildUploadResponseWithError(errorMessage);
+ //returning OK as SDC UI won't show error message if NOT OK error code.
+ return Response.ok(uploadFileResponseDto).build();
+ }
return Response.ok(uploadFileResponseDto).build();
}
+ private UploadFileResponseDto buildUploadResponseWithError(ErrorMessage errorMessage) {
+ UploadFileResponseDto uploadFileResponseDto = new UploadFileResponseDto();
+ Map<String, List<ErrorMessage>> errorMap = new HashMap<>();
+ List<ErrorMessage> errorMessages = new ArrayList<>();
+ errorMessages.add(errorMessage);
+ errorMap.put(SdcCommon.UPLOAD_FILE, errorMessages);
+ uploadFileResponseDto.setErrors(errorMap);
+ return uploadFileResponseDto;
+ }
+
@Override
public Response get(String vspId, String versionId, String user) throws IOException {
Optional<Pair<String, byte[]>> zipFile = candidateManager.get(vspId, new Version(versionId));
@@ -146,7 +181,7 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
String errorWithParameters = ErrorMessagesFormatBuilder
.getErrorWithParameters(Messages.MAPPING_OBJECTS_FAILURE.getErrorMessage(),
fileDataStructureDto.toString(), fileDataStructure.toString());
- throw new Exception(errorWithParameters, exception);
+ throw new OrchestrationTemplateCandidateException(errorWithParameters, exception);
}
ValidationResponse response = candidateManager
.updateFilesDataStructure(vspId, new Version(versionId), fileDataStructure);
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchiveTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchiveTest.java
new file mode 100644
index 0000000000..6458a65d17
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchiveTest.java
@@ -0,0 +1,99 @@
+package org.openecomp.sdcrests.vsp.rest.data;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
+import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
+import org.powermock.reflect.Whitebox;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+public class PackageArchiveTest {
+ private static final String BASE_DIR = "/vspmanager.csar/";
+
+ @Mock
+ SecurityManager manager;
+
+ @Before
+ public void setUp(){
+ initMocks(this);
+ }
+
+
+ @Test
+ public void isSignedTestCheckingWrongFile() throws IOException,
+ URISyntaxException {
+ PackageArchive packageArchive = getArchive("notCsar.txt");
+ assertFalse("2 or 3 files expected for signed package present or signature valid for " +
+ "empty file", packageArchive.isSigned());
+ }
+
+ @Test
+ public void isSignedTestWrongPackageStructure2EmptyDirInRoot() throws IOException,
+ URISyntaxException {
+ PackageArchive packageArchive = getArchive("signing/2-empty-directories-in-root.zip");
+ assertFalse(packageArchive.isSigned());
+ }
+
+ @Test
+ public void isSignedTestWrongPackageStructure2EmptyFilesAndEmptyDirInRoot() throws IOException,
+ URISyntaxException {
+ PackageArchive packageArchive = getArchive("signing/2-empty-files-1-empty-directory-in-root.zip");
+ assertFalse(packageArchive.isSigned());
+ }
+
+ @Test
+ public void isSignedTestWrongPackageStructure2EmptyFilesAndDirWithContentInRoot() throws IOException,
+ URISyntaxException {
+ PackageArchive packageArchive = getArchive("signing/2-empty-files-1-directory-with-contents-in-root.zip");
+ assertFalse(packageArchive.isSigned());
+ }
+
+ @Test
+ public void isSignedTestCorrectStructureNoSignature() throws IOException,
+ URISyntaxException {
+ PackageArchive packageArchive = getArchive("signing/2-files-in-root.zip");
+ assertFalse(packageArchive.isSigned());
+ }
+
+ @Test
+ public void isSignedTestCorrectStructureAndSignatureExists() throws IOException,
+ URISyntaxException {
+ PackageArchive packageArchive = getArchive("signing/csar-and-cms-in-root.zip");
+ assertTrue(packageArchive.isSigned());
+ }
+
+ @Test
+ public void isSignatureValidTestCorrectStructureAndValidSignatureExists() throws IOException,
+ URISyntaxException, SecurityManagerException {
+ PackageArchive packageArchive = getArchive("signing/signed-package.zip");
+ Whitebox.setInternalState(packageArchive, "securityManager", manager);
+ when(manager.verifySignedData(any(), any(), any())).thenReturn(true);
+ assertTrue("Signature invalid for signed package",
+ packageArchive.isSignatureValid());
+ }
+
+ @Test(expected = SecurityManagerException.class)
+ public void isSignatureValidTestCorrectStructureAndNotValidSignatureExists() throws IOException,
+ URISyntaxException, SecurityManagerException {
+ PackageArchive packageArchive = getArchive("signing/signed-package-tampered-data.zip");
+ Whitebox.setInternalState(packageArchive, "securityManager", manager);
+ when(manager.verifySignedData(any(), any(), any())).thenThrow(new SecurityManagerException("error!"));
+ packageArchive.isSignatureValid();
+ }
+
+ private PackageArchive getArchive(String path) throws URISyntaxException, IOException {
+ return new PackageArchive(Files.readAllBytes(Paths.get(
+ PackageArchiveTest.class.getResource(BASE_DIR + path).toURI())));
+ }
+}
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java
new file mode 100644
index 0000000000..2dc6cd737c
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/java/org/openecomp/sdcrests/vsp/rest/services/OrchestrationTemplateCandidateImplTest.java
@@ -0,0 +1,121 @@
+package org.openecomp.sdcrests.vsp.rest.services;
+
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
+import org.openecomp.sdc.activitylog.ActivityLogManager;
+import org.openecomp.sdc.activitylog.ActivityLogManagerFactory;
+import org.openecomp.sdc.logging.api.Logger;
+import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManager;
+import org.openecomp.sdc.vendorsoftwareproduct.OrchestrationTemplateCandidateManagerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.VendorSoftwareProductManager;
+import org.openecomp.sdc.vendorsoftwareproduct.VspManagerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
+import org.openecomp.sdc.vendorsoftwareproduct.types.UploadFileResponse;
+import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto;
+import org.openecomp.sdcrests.vsp.rest.data.PackageArchive;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import javax.ws.rs.core.Response;
+
+import java.util.Optional;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+import static org.powermock.api.mockito.PowerMockito.whenNew;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({VspManagerFactory.class, ActivityLogManagerFactory.class,
+ OrchestrationTemplateCandidateManagerFactory.class, OrchestrationTemplateCandidateImpl.class})
+public class OrchestrationTemplateCandidateImplTest {
+
+ Logger logger = LoggerFactory.getLogger(OrchestrationTemplateCandidateImplTest.class);
+ @Mock
+ private OrchestrationTemplateCandidateManager candidateManager;
+ @Mock
+ private VendorSoftwareProductManager vendorSoftwareProductManager;
+ @Mock
+ private PackageArchive packageArchive;
+ @Mock
+ private VspManagerFactory vspManagerFactory;
+ @Mock
+ private ActivityLogManager activityLogManager;
+ @Mock
+ private ActivityLogManagerFactory activityLogManagerFactory;
+ @Mock
+ OrchestrationTemplateCandidateManagerFactory orchestrationTemplateCandidateManagerFactory;
+
+ private OrchestrationTemplateCandidateImpl orchestrationTemplateCandidate;
+
+ @Before
+ public void setUp(){
+ try {
+ initMocks(this);
+ packageArchive = mock(PackageArchive.class);
+ mockStatic(VspManagerFactory.class);
+ when(VspManagerFactory.getInstance()).thenReturn(vspManagerFactory);
+ when(vspManagerFactory.createInterface()).thenReturn(vendorSoftwareProductManager);
+ mockStatic(ActivityLogManagerFactory.class);
+ when(ActivityLogManagerFactory.getInstance()).thenReturn(activityLogManagerFactory);
+ when(activityLogManagerFactory.createInterface()).thenReturn(activityLogManager);
+ whenNew(PackageArchive.class).withAnyArguments().thenReturn(packageArchive);
+ mockStatic(OrchestrationTemplateCandidateManagerFactory.class);
+ when(OrchestrationTemplateCandidateManagerFactory.getInstance()).thenReturn(orchestrationTemplateCandidateManagerFactory);
+ when(orchestrationTemplateCandidateManagerFactory.createInterface()).thenReturn(candidateManager);
+ when(packageArchive.getArchiveFileName()).thenReturn(Optional.of("test"));
+ when(packageArchive.getPackageFileContents()).thenReturn(new byte[0]);
+ UploadFileResponse uploadFileResponse = new UploadFileResponse();
+ uploadFileResponse.setOnboardingType(OnboardingTypesEnum.ZIP);
+ uploadFileResponse.setNetworkPackageName("test");
+ when(candidateManager.upload(any(), any(), any(), any(), any())).thenReturn(uploadFileResponse);
+ }catch (Exception e){
+ logger.error(e.getMessage(), e);
+ }
+ }
+
+ @Test
+ public void uploadSignedTest() throws SecurityManagerException {
+ when(packageArchive.isSigned()).thenReturn(true);
+ when(packageArchive.isSignatureValid()).thenReturn(true);
+ orchestrationTemplateCandidate = new OrchestrationTemplateCandidateImpl();
+ Attachment attachment = mock(Attachment.class);
+ when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test"));
+ Response response = orchestrationTemplateCandidate.upload("1", "1", attachment, "1");
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ }
+
+ @Test
+ public void uploadNotSignedTest(){
+ when(packageArchive.isSigned()).thenReturn(false);
+ orchestrationTemplateCandidate = new OrchestrationTemplateCandidateImpl();
+ Attachment attachment = mock(Attachment.class);
+ when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test"));
+ Response response = orchestrationTemplateCandidate.upload("1", "1", attachment, "1");
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+ }
+
+ @Test
+ public void uploadSignNotValidTest() throws SecurityManagerException {
+ when(packageArchive.isSigned()).thenReturn(true);
+ when(packageArchive.isSignatureValid()).thenReturn(false);
+ orchestrationTemplateCandidate = new OrchestrationTemplateCandidateImpl();
+ Attachment attachment = mock(Attachment.class);
+ when(attachment.getContentDisposition()).thenReturn(new ContentDisposition("test"));
+ Response response = orchestrationTemplateCandidate.upload("1", "1", attachment, "1");
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertFalse(((UploadFileResponseDto)response.getEntity()).getErrors().isEmpty());
+
+ }
+}
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/notCsar.txt b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/notCsar.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/notCsar.txt
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-directories-in-root.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-directories-in-root.zip
new file mode 100644
index 0000000000..d0f1fd09dc
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-directories-in-root.zip
Binary files differ
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-directory-with-contents-in-root.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-directory-with-contents-in-root.zip
new file mode 100644
index 0000000000..0f10af262f
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-directory-with-contents-in-root.zip
Binary files differ
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-empty-directory-in-root.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-empty-directory-in-root.zip
new file mode 100644
index 0000000000..6ded8b1d57
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-empty-files-1-empty-directory-in-root.zip
Binary files differ
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-files-in-root.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-files-in-root.zip
new file mode 100644
index 0000000000..d1e80ae132
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/2-files-in-root.zip
Binary files differ
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/csar-and-cms-in-root.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/csar-and-cms-in-root.zip
new file mode 100644
index 0000000000..07331466df
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/csar-and-cms-in-root.zip
Binary files differ
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package-tampered-data.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package-tampered-data.zip
new file mode 100644
index 0000000000..0cfb9e0265
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package-tampered-data.zip
Binary files differ
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package.zip b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package.zip
new file mode 100644
index 0000000000..a64ddd9be2
--- /dev/null
+++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/test/resources/vspmanager.csar/signing/signed-package.zip
Binary files differ
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml
index 66f04f1ba7..74a691b373 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/pom.xml
@@ -204,6 +204,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ <version>${bouncycastle.version}</version>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
index d2da7ef20f..7b1890dcaa 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
@@ -20,83 +20,292 @@
package org.openecomp.sdc.vendorsoftwareproduct.security;
import com.google.common.collect.ImmutableSet;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Store;
import org.openecomp.sdc.logging.api.Logger;
import org.openecomp.sdc.logging.api.LoggerFactory;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.PKIXCertPathBuilderResult;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
- * This is temporary solution. When AAF provides functionality for verifying certificates, this class should be reviewed
- * Class is responsible for providing root certificates from configured location in onboarding container.
+ * This is temporary solution. When AAF provides functionality for verifying trustedCertificates, this class should be reviewed
+ * Class is responsible for providing root trustedCertificates from configured location in onboarding container.
*/
public class SecurityManager {
- private static final String CERTIFICATE_DEFAULT_LOCATION = "/root/cert";
+ private static final String CERTIFICATE_DEFAULT_LOCATION = "cert";
+ private static final SecurityManager INSTANCE = new SecurityManager();
private Logger logger = LoggerFactory.getLogger(SecurityManager.class);
- private Set<Certificate> certificates = new HashSet<>();
+ private Set<X509Certificate> trustedCertificates = new HashSet<>();
private File certificateDirectory;
+ static {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
- public SecurityManager(){
+ private SecurityManager() {
certificateDirectory = this.getcertDirectory();
}
- private void processCertificateDir() {
- if(!certificateDirectory.exists() || !certificateDirectory.isDirectory()){
+ public static SecurityManager getInstance(){
+ return INSTANCE;
+ }
+
+ /**
+ *
+ * Checks the configured location for available trustedCertificates
+ *
+ * @return set of trustedCertificates
+ * @throws SecurityManagerException
+ */
+ public Set<X509Certificate> getTrustedCertificates() throws SecurityManagerException {
+ //if file number in certificate directory changed reload certs
+ String[] certFiles = certificateDirectory.list();
+ if (certFiles == null) {
+ logger.error("Certificate directory is empty!");
+ return ImmutableSet.copyOf(new HashSet<>());
+ }
+ if (trustedCertificates.size() != certFiles.length) {
+ trustedCertificates = new HashSet<>();
+ processCertificateDir();
+ }
+ return ImmutableSet.copyOf(trustedCertificates);
+ }
+
+ /**
+ * Cleans certificate collection
+ */
+ public void cleanTrustedCertificates(){
+ trustedCertificates.clear();
+ }
+
+ /**
+ *
+ * Verifies if packaged signed with trusted certificate
+ *
+ * @param messageSyntaxSignature - signature data in cms format
+ * @param packageCert - package certificate if not part of cms signature, can be null
+ * @param innerPackageFile data package signed with cms signature
+ * @return true if signature verified
+ * @throws SecurityManagerException
+ */
+ public boolean verifySignedData(final byte[] messageSyntaxSignature, final byte[] packageCert,
+ final byte[] innerPackageFile) throws SecurityManagerException{
+ try (ByteArrayInputStream signatureStream = new ByteArrayInputStream(messageSyntaxSignature)) {
+ Object parsedObject = new PEMParser(new InputStreamReader(signatureStream)).readObject();
+ if (!(parsedObject instanceof ContentInfo)) {
+ throw new SecurityManagerException("Signature is not recognized");
+ }
+ ContentInfo signature = ContentInfo.getInstance(parsedObject);
+ CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFile);
+ CMSSignedData signedData = new CMSSignedData(signedContent, signature);
+
+ Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
+ SignerInformation firstSigner = signers.iterator().next();
+ Store certificates = signedData.getCertificates();
+ X509Certificate cert;
+ if (packageCert == null) {
+ Collection<X509CertificateHolder> firstSignerCertificates = certificates.getMatches(firstSigner.getSID());
+ if(!firstSignerCertificates.iterator().hasNext()){
+ throw new SecurityManagerException("No certificate found in cms signature that should contain one!");
+ }
+ X509CertificateHolder firstSignerFirstCertificate = firstSignerCertificates.iterator().next();
+ cert = loadCertificate(firstSignerFirstCertificate.getEncoded());
+ } else {
+ cert = loadCertificate(packageCert);
+ }
+
+ PKIXCertPathBuilderResult result = verifyCertificate(cert, getTrustedCertificates());
+
+ if (result == null) {
+ return false;
+ }
+
+ return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ } catch (OperatorCreationException | IOException | CMSException e) {
+ logger.error(e.getMessage(), e);
+ throw new SecurityManagerException("Unexpected error occurred during signature validation!", e);
+ } catch (GeneralSecurityException e){
+ throw new SecurityManagerException("Could not verify signature!", e);
+ }
+ }
+
+ private void processCertificateDir() throws SecurityManagerException {
+ if (!certificateDirectory.exists() || !certificateDirectory.isDirectory()) {
logger.error("Issue with certificate directory, check if exists!");
return;
}
- File [] files = certificateDirectory.listFiles();
- if(files == null){
+ File[] files = certificateDirectory.listFiles();
+ if (files == null) {
logger.error("Certificate directory is empty!");
return;
}
- for(File f : files) {
- certificates.add(loadCertificate(f));
+ for (File f : files) {
+ trustedCertificates.add(loadCertificate(f));
}
}
private File getcertDirectory() {
String certDirLocation = System.getenv("SDC_CERT_DIR");
- if(certDirLocation == null){
+ if (certDirLocation == null) {
certDirLocation = CERTIFICATE_DEFAULT_LOCATION;
}
return new File(certDirLocation);
}
- private Certificate loadCertificate(File certFile){
- try (InputStream fileInputStream = new FileInputStream(certFile)){
+ private X509Certificate loadCertificate(File certFile) throws SecurityManagerException {
+ try (InputStream fileInputStream = new FileInputStream(certFile)) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
- return factory.generateCertificate(fileInputStream);
- } catch (CertificateException|IOException e) {
+ return (X509Certificate) factory.generateCertificate(fileInputStream);
+ } catch (CertificateException | IOException e) {
throw new SecurityManagerException("Error during loading Certificate file!", e);
}
}
- /**
- * Checks the configured location for available certificates
- * @return set of certificates
- */
- public Set<Certificate> getCertificates() {
- //if file number in certificate directory changed reload certs
- String[] certFiles = certificateDirectory.list();
- if(certFiles == null){
- logger.error("Certificate directory is empty!");
- return ImmutableSet.copyOf(new HashSet<>());
+ private X509Certificate loadCertificate(byte[] certFile) throws SecurityManagerException {
+ try (InputStream in = new ByteArrayInputStream(certFile)) {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) factory.generateCertificate(in);
+ } catch (CertificateException | IOException e) {
+ throw new SecurityManagerException("Error during loading Certificate from bytes!", e);
}
- if(certificates.size() != certFiles.length){
- certificates = new HashSet<>();
- processCertificateDir();
+ }
+
+ private PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert,
+ Set<X509Certificate> additionalCerts) throws GeneralSecurityException, SecurityManagerException {
+ if (null == cert) {
+ throw new SecurityManagerException("The certificate is empty!");
+ }
+
+ if (isExpired(cert)) {
+ throw new SecurityManagerException("The certificate expired on: " + cert.getNotAfter());
+ }
+
+ if (isSelfSigned(cert)) {
+ throw new SecurityManagerException("The certificate is self-signed.");
+ }
+
+ Set<X509Certificate> trustedRootCerts = new HashSet<>();
+ Set<X509Certificate> intermediateCerts = new HashSet<>();
+ for (X509Certificate additionalCert : additionalCerts) {
+ if (isSelfSigned(additionalCert)) {
+ trustedRootCerts.add(additionalCert);
+ } else {
+ intermediateCerts.add(additionalCert);
+ }
+ }
+
+ return verifyCertificate(cert, trustedRootCerts, intermediateCerts);
+ }
+
+ private PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert,
+ Set<X509Certificate> allTrustedRootCerts,
+ Set<X509Certificate> allIntermediateCerts)
+ throws GeneralSecurityException {
+
+ // Create the selector that specifies the starting certificate
+ X509CertSelector selector = new X509CertSelector();
+ selector.setCertificate(cert);
+
+ // Create the trust anchors (set of root CA certificates)
+ Set<TrustAnchor> trustAnchors = new HashSet<>();
+ for (X509Certificate trustedRootCert : allTrustedRootCerts) {
+ trustAnchors.add(new TrustAnchor(trustedRootCert, null));
+ }
+
+ // Configure the PKIX certificate builder algorithm parameters
+ PKIXBuilderParameters pkixParams;
+ try {
+ pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
+ } catch (InvalidAlgorithmParameterException ex) {
+ throw new InvalidAlgorithmParameterException("No root CA has been found for this certificate", ex);
+ }
+
+ // Not supporting CRL checks for now
+ pkixParams.setRevocationEnabled(false);
+
+ Set<X509Certificate> certSet = new HashSet<>();
+ certSet.add(cert);
+ pkixParams.addCertStore(createCertStore(certSet));
+ pkixParams.addCertStore(createCertStore(allIntermediateCerts));
+ pkixParams.addCertStore(createCertStore(allTrustedRootCerts));
+
+ CertPathBuilder builder = CertPathBuilder.getInstance(CertPathBuilder.getDefaultType(), BouncyCastleProvider.PROVIDER_NAME);
+ return (PKIXCertPathBuilderResult) builder.build(pkixParams);
+ }
+
+ private CertStore createCertStore(Set<X509Certificate> certificateSet) throws InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchProviderException {
+ return CertStore.getInstance("Collection", new CollectionCertStoreParameters(certificateSet), BouncyCastleProvider.PROVIDER_NAME);
+ }
+
+ private boolean isExpired(X509Certificate cert) {
+ try {
+ cert.checkValidity();
+ } catch (CertificateExpiredException e) {
+ logger.error(e.getMessage(), e);
+ return true;
+ } catch (CertificateNotYetValidException e) {
+ logger.error(e.getMessage(), e);
+ return false;
+ }
+ return false;
+ }
+
+ private boolean isSelfSigned(Certificate cert)
+ throws CertificateException, NoSuchAlgorithmException,
+ NoSuchProviderException {
+ try {
+ // Try to verify certificate signature with its own public key
+ PublicKey key = cert.getPublicKey();
+ cert.verify(key);
+ return true;
+ } catch (SignatureException | InvalidKeyException e) {
+ logger.error(e.getMessage(), e);
+ //not self-signed
+ return false;
}
- return ImmutableSet.copyOf(certificates);
}
}
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerException.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerException.java
index 5c5a23a5f8..cdba2f8f0b 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerException.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerException.java
@@ -1,8 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2019, Nordix Foundation. 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.openecomp.sdc.vendorsoftwareproduct.security;
-public class SecurityManagerException extends RuntimeException {
+public class SecurityManagerException extends Exception {
- public SecurityManagerException(String s, Throwable t) {
+ public SecurityManagerException(String s) {
super(s);
}
+
+ public SecurityManagerException(String s, Throwable t) {
+ super(s, t);
+ }
}
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java
index c693015791..eea8a3a186 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java
@@ -6,11 +6,15 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.io.File;
import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
@@ -18,67 +22,129 @@ import static org.mockito.ArgumentMatchers.eq;
@RunWith(PowerMockRunner.class)
@PrepareForTest(SecurityManager.class)
+@PowerMockIgnore("javax.security.auth.x500.X500Principal")
public class SecurityManagerTest {
- File certDir;
+ private File certDir;
+ private SecurityManager securityManager;
@Before
- public void setUp(){
+ public void setUp() throws IOException {
certDir = new File("/tmp/cert");
+ if(certDir.exists()){
+ tearDown();
+ }
certDir.mkdirs();
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getenv(eq("SDC_CERT_DIR"))).thenReturn(certDir.getPath());
+ securityManager = SecurityManager.getInstance();
}
@After
- public void tearDown(){
- certDir.delete();
+ public void tearDown() throws IOException {
+ if(certDir.exists()) {
+ FileUtils.deleteDirectory(certDir);
+ }
+ securityManager.cleanTrustedCertificates();
}
@Test
- public void testGetCertificates() throws IOException {
+ public void testGetCertificates() throws IOException, SecurityManagerException {
File origFile = new File("src/test/resources/cert/root-certificate.pem");
File newFile = new File("/tmp/cert/root-certificate.pem");
newFile.createNewFile();
FileUtils.copyFile(origFile, newFile);
- SecurityManager securityManager = new SecurityManager();
- assertEquals(1, securityManager.getCertificates().size());
+ assertEquals(1, securityManager.getTrustedCertificates().size());
newFile.delete();
- assertEquals(0, securityManager.getCertificates().size());
+ assertEquals(0, securityManager.getTrustedCertificates().size());
}
@Test
- public void testGetCertificatesNoDirectory() throws IOException {
+ public void testGetCertificatesNoDirectory() throws IOException, SecurityManagerException {
certDir.delete();
- SecurityManager securityManager = new SecurityManager();
- assertEquals(0, securityManager.getCertificates().size());
+ assertEquals(0, securityManager.getTrustedCertificates().size());
}
@Test(expected = SecurityManagerException.class)
- public void testGetCertificatesException() throws IOException {
+ public void testGetCertificatesException() throws IOException, SecurityManagerException {
File newFile = new File("/tmp/cert/root-certificate.pem");
newFile.createNewFile();
- SecurityManager securityManager = new SecurityManager();
- assertEquals(1, securityManager.getCertificates().size());
+ assertEquals(1, securityManager.getTrustedCertificates().size());
newFile.delete();
- assertEquals(0, securityManager.getCertificates().size());
+ assertEquals(0, securityManager.getTrustedCertificates().size());
}
@Test
- public void testGetCertificatesUpdated() throws IOException {
+ public void testGetCertificatesUpdated() throws IOException, SecurityManagerException {
File origFile = new File("src/test/resources/cert/root-certificate.pem");
File newFile = new File("/tmp/cert/root-certificate.pem");
newFile.createNewFile();
FileUtils.copyFile(origFile, newFile);
- SecurityManager securityManager = new SecurityManager();
- assertTrue(securityManager.getCertificates().size() == 1);
+ assertTrue(securityManager.getTrustedCertificates().size() == 1);
File otherOrigFile = new File("src/test/resources/cert/package-certificate.pem");
File otherNewFile = new File("/tmp/cert/package-certificate.pem");
newFile.createNewFile();
FileUtils.copyFile(otherOrigFile, otherNewFile);
- assertEquals(2, securityManager.getCertificates().size());
+ assertEquals(2, securityManager.getTrustedCertificates().size());
otherNewFile.delete();
- assertEquals(1, securityManager.getCertificates().size());
+ assertEquals(1, securityManager.getTrustedCertificates().size());
newFile.delete();
- assertEquals(0, securityManager.getCertificates().size());
+ assertEquals(0, securityManager.getTrustedCertificates().size());
+ }
+
+ @Test
+ public void verifySignedDataTestCertIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
+ File origFile = new File("src/test/resources/cert/root.cert");
+ File newFile = new File("/tmp/cert/root.cert");
+ newFile.createNewFile();
+ FileUtils.copyFile(origFile, newFile);
+ byte[] signature = Files.readAllBytes(Paths.get(getClass().getResource("/cert/2-file-signed-package/dummyPnfv3.cms").toURI()));
+ byte[] archive = Files.readAllBytes(Paths.get(getClass().getResource("/cert/2-file-signed-package/dummyPnfv3.csar").toURI()));
+ assertTrue(securityManager.verifySignedData(signature, null, archive));
+ }
+
+ @Test(expected = SecurityManagerException.class)
+ public void verifySignedDataTestCertNotIncludedIntoSignatureButExpected() throws IOException, URISyntaxException, SecurityManagerException {
+ File origFile = new File("src/test/resources/cert/root.cert");
+ File newFile = new File("/tmp/cert/root.cert");
+ newFile.createNewFile();
+ FileUtils.copyFile(origFile, newFile);
+ byte[] signature = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.cms").toURI()));
+ byte[] archive = Files.readAllBytes(Paths.get(getClass().getResource("/cert/2-file-signed-package/dummyPnfv3.csar").toURI()));
+ securityManager.verifySignedData(signature, null, archive);
+ }
+
+ @Test
+ public void verifySignedDataTestCertNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
+ File origFile = new File("src/test/resources/cert/root.cert");
+ File newFile = new File("/tmp/cert/root.cert");
+ newFile.createNewFile();
+ FileUtils.copyFile(origFile, newFile);
+ byte[] signature = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.cms").toURI()));
+ byte[] archive = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.csar").toURI()));
+ byte[] cert = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.cert").toURI()));
+ assertTrue(securityManager.verifySignedData(signature, cert, archive));
+ }
+
+ @Test(expected = SecurityManagerException.class)
+ public void verifySignedDataTestWrongCertificate() throws IOException, URISyntaxException, SecurityManagerException {
+ File origFile = new File("src/test/resources/cert/root-certificate.pem");
+ File newFile = new File("/tmp/cert/root-certificate.cert");
+ newFile.createNewFile();
+ FileUtils.copyFile(origFile, newFile);
+ byte[] signature = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.cms").toURI()));
+ byte[] archive = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.csar").toURI()));
+ byte[] cert = Files.readAllBytes(Paths.get(getClass().getResource("/cert/3-file-signed-package/dummyPnfv3.cert").toURI()));
+ securityManager.verifySignedData(signature, cert, archive);
+ }
+
+ @Test(expected = SecurityManagerException.class)
+ public void verifySignedDataTestChangedArchive() throws IOException, URISyntaxException, SecurityManagerException {
+ File origFile = new File("src/test/resources/cert/root.cert");
+ File newFile = new File("/tmp/cert/root.cert");
+ newFile.createNewFile();
+ FileUtils.copyFile(origFile, newFile);
+ byte[] signature = Files.readAllBytes(Paths.get(getClass().getResource("/cert/tampered-signed-package/dummyPnfv3.cms").toURI()));
+ byte[] archive = Files.readAllBytes(Paths.get(getClass().getResource("/cert/tampered-signed-package/dummyPnfv3.csar").toURI()));
+ securityManager.verifySignedData(signature, null, archive);
}
}
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.cms b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.cms
new file mode 100644
index 0000000000..fca5faca8e
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.cms
@@ -0,0 +1,34 @@
+-----BEGIN CMS-----
+MIIF9AYJKoZIhvcNAQcCoIIF5TCCBeECAQExDTALBglghkgBZQMEAgEwCwYJKoZI
+hvcNAQcBoIIDPjCCAzowggIiAgkAmTZc6pj8rWYwDQYJKoZIhvcNAQELBQAwXzEL
+MAkGA1UEBhMCSUUxEjAQBgNVBAgMCVdlc3RtZWF0aDEQMA4GA1UEBwwHQXRobG9u
+ZTEMMAoGA1UECgwDRVNZMQ8wDQYDVQQLDAZUZWNobm8xCzAJBgNVBAMMAlNTMB4X
+DTE5MDMyODEzMDQ0NloXDTE5MDQyNzEzMDQ0NlowXzELMAkGA1UEBhMCSUUxEjAQ
+BgNVBAgMCVdlc3RtZWF0aDEQMA4GA1UEBwwHQXRobG9uZTEMMAoGA1UECgwDRVNZ
+MQ8wDQYDVQQLDAZUZWNobm8xCzAJBgNVBAMMAlNTMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAqzpc/mRJZe5fxh9yo2ZmFCrNCynrbtLujp2GJwW40Nh0
+89jUBb49zFRwHrUUTlmIZRMrW8XDopX1LDajE+pzNxv+skdpZaPHhEjYcqbFIL1I
+KiWxo1PTBi/9KgSFlzc5eewolrwV+NX76p2+xkLDwt6rnZy8UiubVH7U4mUnPtxy
+Wx/W7uVGaZDKo0g2PNcFayRcL5skbm0Una2TjjAunwGP3FkxKigw+LukLE+w2fvE
+C7b8ndIk10WER9rCIeMCf1571Ub8WJzR/80PfhJxbxoroRaiGESFh3kNNfqanLcS
+Q4I9KHWeijOhSW0pHkqL2KPAee35FtfEUpL5aN0OcwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQBlm8RMspc6cwcktqJXDLZLZiHSoapQqcq3TI3dkhU2uEFTstnxnXa3
+r4eTVF8tre2BjvxJtgmM7qMnoDTFo+uUjkvuBBalLARbQM+gF6PAeRLYRHMLSkN/
+yOfnyQ3ypYAQMpEHVG0Er6B5+KbQwFr2G0XBW0zE8au9oGzqBUNg7e0O22AyXqQk
+uhHzXXVhz6sWxJVv51gjPoWtr/1YbsGmJPimFIuz9GvrZD1MKGQ4sotZvRkfofHz
+ePg0y8taAcdXHJwfmAeiJdc0S9SsYxKLAz1OB+n4oQTsk+31cnKflp+wVfeNyaRP
+sdFf4KLicluzbwIRJ/x0h2r/lTorGGUcMYICfDCCAngCAQEwbDBfMQswCQYDVQQG
+EwJJRTESMBAGA1UECAwJV2VzdG1lYXRoMRAwDgYDVQQHDAdBdGhsb25lMQwwCgYD
+VQQKDANFU1kxDzANBgNVBAsMBlRlY2hubzELMAkGA1UEAwwCU1MCCQCZNlzqmPyt
+ZjALBglghkgBZQMEAgGggeQwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkq
+hkiG9w0BCQUxDxcNMTkwMzI4MTMwODUwWjAvBgkqhkiG9w0BCQQxIgQg9ya6QcX9
+J6hp+zfK1gceoLlpApp92mfxGoX3eZ1dMUwweQYJKoZIhvcNAQkPMWwwajALBglg
+hkgBZQMEASowCwYJYIZIAWUDBAEWMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzAO
+BggqhkiG9w0DAgICAIAwDQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcN
+AwICASgwDQYJKoZIhvcNAQEBBQAEggEAAmmSdu8W5zr8DVrkASlujCCSLwKq1XE+
+knlrR84UkkpRz8SacfxtoQL2/T6H0LyOnlJTOGQj3M8w2CaYKKWamnp/2jLZFvUn
+aaPbCdKeKvwPiL99iBIqXWcHXJKk5Ch3fIfcWyAfl48HAB7MFE3TlKk0qUQVXlZP
+7/c4PGaqtbfB7pDuJx6k+Bd2dqG4Xe8RDdvKDEK33HzkAZ72ZPuEL3Zw77eeWZS6
+vyAQTxEkFKERiC1AkmGUdAfTolzYGn1LlTcqb1P59nzs/AZ16JKx6ZITumhaSG6Q
+JvkvodxD99bhOh3pHaLkTkkcLxEEE9OscYEtWvIdIGyfjrpGIFP31g==
+-----END CMS-----
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.csar b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.csar
new file mode 100644
index 0000000000..2c626ed90b
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/dummyPnfv3.csar
Binary files differ
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cert b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cert
new file mode 100644
index 0000000000..d7da41db94
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cert
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDOjCCAiICCQCZNlzqmPytZjANBgkqhkiG9w0BAQsFADBfMQswCQYDVQQGEwJJ
+RTESMBAGA1UECAwJV2VzdG1lYXRoMRAwDgYDVQQHDAdBdGhsb25lMQwwCgYDVQQK
+DANFU1kxDzANBgNVBAsMBlRlY2hubzELMAkGA1UEAwwCU1MwHhcNMTkwMzI4MTMw
+NDQ2WhcNMTkwNDI3MTMwNDQ2WjBfMQswCQYDVQQGEwJJRTESMBAGA1UECAwJV2Vz
+dG1lYXRoMRAwDgYDVQQHDAdBdGhsb25lMQwwCgYDVQQKDANFU1kxDzANBgNVBAsM
+BlRlY2hubzELMAkGA1UEAwwCU1MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQCrOlz+ZEll7l/GH3KjZmYUKs0LKetu0u6OnYYnBbjQ2HTz2NQFvj3MVHAe
+tRROWYhlEytbxcOilfUsNqMT6nM3G/6yR2llo8eESNhypsUgvUgqJbGjU9MGL/0q
+BIWXNzl57CiWvBX41fvqnb7GQsPC3qudnLxSK5tUftTiZSc+3HJbH9bu5UZpkMqj
+SDY81wVrJFwvmyRubRSdrZOOMC6fAY/cWTEqKDD4u6QsT7DZ+8QLtvyd0iTXRYRH
+2sIh4wJ/XnvVRvxYnNH/zQ9+EnFvGiuhFqIYRIWHeQ01+pqctxJDgj0odZ6KM6FJ
+bSkeSovYo8B57fkW18RSkvlo3Q5zAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGWb
+xEyylzpzByS2olcMtktmIdKhqlCpyrdMjd2SFTa4QVOy2fGddrevh5NUXy2t7YGO
+/Em2CYzuoyegNMWj65SOS+4EFqUsBFtAz6AXo8B5EthEcwtKQ3/I5+fJDfKlgBAy
+kQdUbQSvoHn4ptDAWvYbRcFbTMTxq72gbOoFQ2Dt7Q7bYDJepCS6EfNddWHPqxbE
+lW/nWCM+ha2v/VhuwaYk+KYUi7P0a+tkPUwoZDiyi1m9GR+h8fN4+DTLy1oBx1cc
+nB+YB6Il1zRL1KxjEosDPU4H6fihBOyT7fVycp+Wn7BV943JpE+x0V/gouJyW7Nv
+AhEn/HSHav+VOisYZRw=
+-----END CERTIFICATE-----
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cms b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cms
new file mode 100644
index 0000000000..eeee6a977b
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.cms
@@ -0,0 +1,17 @@
+-----BEGIN CMS-----
+MIICsgYJKoZIhvcNAQcCoIICozCCAp8CAQExDTALBglghkgBZQMEAgEwCwYJKoZI
+hvcNAQcBMYICfDCCAngCAQEwbDBfMQswCQYDVQQGEwJJRTESMBAGA1UECAwJV2Vz
+dG1lYXRoMRAwDgYDVQQHDAdBdGhsb25lMQwwCgYDVQQKDANFU1kxDzANBgNVBAsM
+BlRlY2hubzELMAkGA1UEAwwCU1MCCQCZNlzqmPytZjALBglghkgBZQMEAgGggeQw
+GAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMzI4
+MTMxMDI2WjAvBgkqhkiG9w0BCQQxIgQg9ya6QcX9J6hp+zfK1gceoLlpApp92mfx
+GoX3eZ1dMUwweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUD
+BAEWMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYI
+KoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcNAQEB
+BQAEggEAGGYZ4DsMUDzjMVpJU9zwLzTtxO1wCnouTVw8FJT2utGnUds+OexbKQoj
+pCCfuAL1k9UaP3uyNXOjuMx8tzlQY0gZJzaKpYJ7vh0q6P9IZs0hjcvEXPhRTI/y
+vI8mHP3WIXwuh36ehRmqALnGbBcOj46k578gAf/p1hHD3/ceQfB1MSkSVMwvf+yP
+3YwJyvKHYYlGaaAbSjnIK+7g2tuRIvFdXGk30CU2mnldvb3JltfxB5MkZgEM6hPz
+ZhjgNDtmFDZzoblEOCvFJnpXg2IF7bAPjObNaPd20ZRvRSRhQODktT5EHARRT53Y
+p+03N4IUz89hw/roOnq0nlbetQSKvg==
+-----END CMS-----
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.csar b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.csar
new file mode 100644
index 0000000000..2c626ed90b
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/dummyPnfv3.csar
Binary files differ
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/root.cert b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/root.cert
new file mode 100644
index 0000000000..767804ede4
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/root.cert
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIJANxs5zQCT2zPMA0GCSqGSIb3DQEBCwUAMF8xCzAJBgNV
+BAYTAklFMRIwEAYDVQQIDAlXZXN0bWVhdGgxEDAOBgNVBAcMB0F0aGxvbmUxDDAK
+BgNVBAoMA0VTWTEPMA0GA1UECwwGVGVjaG5vMQswCQYDVQQDDAJTUzAeFw0xOTAz
+MjgxMzAyMDVaFw0xOTA0MjcxMzAyMDVaMF8xCzAJBgNVBAYTAklFMRIwEAYDVQQI
+DAlXZXN0bWVhdGgxEDAOBgNVBAcMB0F0aGxvbmUxDDAKBgNVBAoMA0VTWTEPMA0G
+A1UECwwGVGVjaG5vMQswCQYDVQQDDAJTUzCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBALwd8mRaVTPIiyJEGuscMulTg7EyQGUcVgRUJDrcEsubK9vgDEqh
+0BTps1xO01LX7RaXSe4KWTcsJG41QsdX9lo94VoYZFfR0tVKCkPjWoaynl0cZEAZ
+r6vADWwQkWWi1Czwr9fTX9NBu68IexLATuS387gafonlzvpa4TLVwi69ogNlVa91
+pKkeZCBWbhgDgYDz5pEbKPJ6TRab/sFxZOx/HBIM9i7INvwNhdnZF77eZVgNUX2z
+XKFcXOklmY9gEr9HQtsFIyTxlOdL2DF7JspgN0Yfb6hqAKE/sfOgQ6h3A+n4AuA1
+gtgC6k0OVps2ZM3jlmpYatKorz22zp3nhzECAwEAAaNTMFEwHQYDVR0OBBYEFGci
+Qjw5QhCSvwl86i6weBl++bQvMB8GA1UdIwQYMBaAFGciQjw5QhCSvwl86i6weBl+
++bQvMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHtMPlNaUJPy
+IXBOjROu0LlRXWJ/u7TVLaLaLnok5Sy/9QAz/FBKzOvMP1cmavsZiZC/9ISEaWFv
+KlTOeZrhUl7WGk8pJPkkfATxt7HtRxO/c0RNrJin1AWWjQnUxjCB+nuqKS2h/itG
+fHyHzzB3kjzxaK73kVuh8fzdxRDkg6QgLyW83BJ8T/U/VOuM3HRNIF86cazgae7E
+7c9SrnXZ67IS7w3gxm/L/k5Rpd4XuuumaDuDz3NhGj1HFh323x11jheMmfl559SK
+qU5NIC2qwKYGhzDojgLUJeL9g52DeS4eZ3DmINFRK2g0UMrHrypKq5aQ2v1kac6X
+Io5o3F3L2DE=
+-----END CERTIFICATE-----
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.cms b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.cms
new file mode 100644
index 0000000000..fca5faca8e
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.cms
@@ -0,0 +1,34 @@
+-----BEGIN CMS-----
+MIIF9AYJKoZIhvcNAQcCoIIF5TCCBeECAQExDTALBglghkgBZQMEAgEwCwYJKoZI
+hvcNAQcBoIIDPjCCAzowggIiAgkAmTZc6pj8rWYwDQYJKoZIhvcNAQELBQAwXzEL
+MAkGA1UEBhMCSUUxEjAQBgNVBAgMCVdlc3RtZWF0aDEQMA4GA1UEBwwHQXRobG9u
+ZTEMMAoGA1UECgwDRVNZMQ8wDQYDVQQLDAZUZWNobm8xCzAJBgNVBAMMAlNTMB4X
+DTE5MDMyODEzMDQ0NloXDTE5MDQyNzEzMDQ0NlowXzELMAkGA1UEBhMCSUUxEjAQ
+BgNVBAgMCVdlc3RtZWF0aDEQMA4GA1UEBwwHQXRobG9uZTEMMAoGA1UECgwDRVNZ
+MQ8wDQYDVQQLDAZUZWNobm8xCzAJBgNVBAMMAlNTMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAqzpc/mRJZe5fxh9yo2ZmFCrNCynrbtLujp2GJwW40Nh0
+89jUBb49zFRwHrUUTlmIZRMrW8XDopX1LDajE+pzNxv+skdpZaPHhEjYcqbFIL1I
+KiWxo1PTBi/9KgSFlzc5eewolrwV+NX76p2+xkLDwt6rnZy8UiubVH7U4mUnPtxy
+Wx/W7uVGaZDKo0g2PNcFayRcL5skbm0Una2TjjAunwGP3FkxKigw+LukLE+w2fvE
+C7b8ndIk10WER9rCIeMCf1571Ub8WJzR/80PfhJxbxoroRaiGESFh3kNNfqanLcS
+Q4I9KHWeijOhSW0pHkqL2KPAee35FtfEUpL5aN0OcwIDAQABMA0GCSqGSIb3DQEB
+CwUAA4IBAQBlm8RMspc6cwcktqJXDLZLZiHSoapQqcq3TI3dkhU2uEFTstnxnXa3
+r4eTVF8tre2BjvxJtgmM7qMnoDTFo+uUjkvuBBalLARbQM+gF6PAeRLYRHMLSkN/
+yOfnyQ3ypYAQMpEHVG0Er6B5+KbQwFr2G0XBW0zE8au9oGzqBUNg7e0O22AyXqQk
+uhHzXXVhz6sWxJVv51gjPoWtr/1YbsGmJPimFIuz9GvrZD1MKGQ4sotZvRkfofHz
+ePg0y8taAcdXHJwfmAeiJdc0S9SsYxKLAz1OB+n4oQTsk+31cnKflp+wVfeNyaRP
+sdFf4KLicluzbwIRJ/x0h2r/lTorGGUcMYICfDCCAngCAQEwbDBfMQswCQYDVQQG
+EwJJRTESMBAGA1UECAwJV2VzdG1lYXRoMRAwDgYDVQQHDAdBdGhsb25lMQwwCgYD
+VQQKDANFU1kxDzANBgNVBAsMBlRlY2hubzELMAkGA1UEAwwCU1MCCQCZNlzqmPyt
+ZjALBglghkgBZQMEAgGggeQwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkq
+hkiG9w0BCQUxDxcNMTkwMzI4MTMwODUwWjAvBgkqhkiG9w0BCQQxIgQg9ya6QcX9
+J6hp+zfK1gceoLlpApp92mfxGoX3eZ1dMUwweQYJKoZIhvcNAQkPMWwwajALBglg
+hkgBZQMEASowCwYJYIZIAWUDBAEWMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzAO
+BggqhkiG9w0DAgICAIAwDQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcN
+AwICASgwDQYJKoZIhvcNAQEBBQAEggEAAmmSdu8W5zr8DVrkASlujCCSLwKq1XE+
+knlrR84UkkpRz8SacfxtoQL2/T6H0LyOnlJTOGQj3M8w2CaYKKWamnp/2jLZFvUn
+aaPbCdKeKvwPiL99iBIqXWcHXJKk5Ch3fIfcWyAfl48HAB7MFE3TlKk0qUQVXlZP
+7/c4PGaqtbfB7pDuJx6k+Bd2dqG4Xe8RDdvKDEK33HzkAZ72ZPuEL3Zw77eeWZS6
+vyAQTxEkFKERiC1AkmGUdAfTolzYGn1LlTcqb1P59nzs/AZ16JKx6ZITumhaSG6Q
+JvkvodxD99bhOh3pHaLkTkkcLxEEE9OscYEtWvIdIGyfjrpGIFP31g==
+-----END CMS-----
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.csar b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.csar
new file mode 100644
index 0000000000..81cb1f72d2
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/tampered-signed-package/dummyPnfv3.csar
Binary files differ
diff --git a/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java b/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java
index 2111f6d547..7b8fda8c49 100644
--- a/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java
+++ b/openecomp-be/lib/openecomp-common-lib/src/main/java/org/openecomp/sdc/common/errors/Messages.java
@@ -188,7 +188,8 @@ public enum Messages {
/* Notifications */
FAILED_TO_MARK_NOTIFICATION_AS_READ("Failed to mark notifications as read"),
- FAILED_TO_UPDATE_LAST_SEEN_NOTIFICATION("Failed to update last seen notification for user %s");
+ FAILED_TO_UPDATE_LAST_SEEN_NOTIFICATION("Failed to update last seen notification for user %s"),
+ FAILED_TO_VERIFY_SIGNATURE("Cannot verify signature of signed archive!");
private String errorMessage;