summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvasraz <vasyl.razinkov@est.tech>2021-07-29 14:41:18 +0100
committerMichael Morris <michael.morris@est.tech>2021-08-05 11:25:09 +0000
commit36ff777984fbd728737b264d7aa3933794716519 (patch)
tree242f8ddac4aa07c7f3e7702b611afcb7061b5af1
parent95b22d8d074f294e997c27d79d369b0eb3bee9e2 (diff)
Implement 'Signed Large CSAR' support
Change-Id: I33cc381b86c6a10e20d521c0d3dcc76c28344b8f Signed-off-by: Vasyl Razinkov <vasyl.razinkov@est.tech> Issue-ID: SDC-3652 Issue-ID: SDC-3653 Signed-off-by: André Schmid <andre.schmid@est.tech>
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java153
-rw-r--r--common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java55
-rw-r--r--common-be/src/test/resources/csarSizeReducer/dummyToNotReduce.csarbin0 -> 24301 bytes
-rw-r--r--common-be/src/test/resources/csarSizeReducer/dummyToReduce.csar (renamed from common-be/src/test/resources/csarSizeReducer/dummy.csar)bin25876 -> 25876 bytes
-rw-r--r--common-be/src/test/resources/csarSizeReducer/dummyToReduce.zipbin0 -> 23905 bytes
-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.java24
-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.java12
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateCSARHandler.java10
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java34
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java188
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java112
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManagerTest.java66
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/2-file-signed-package.zipbin0 -> 3154 bytes
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/3-file-signed-package.zipbin0 -> 3446 bytes
14 files changed, 509 insertions, 145 deletions
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java
index cf35c8c4d7..1fef373362 100644
--- a/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java
+++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducer.java
@@ -20,14 +20,24 @@
package org.openecomp.sdc.be.csar.storage;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.List;
+import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
+import lombok.Getter;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.FilenameUtils;
import org.openecomp.sdc.be.csar.storage.exception.CsarSizeReducerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,6 +45,12 @@ import org.slf4j.LoggerFactory;
public class CsarSizeReducer implements PackageSizeReducer {
private static final Logger LOGGER = LoggerFactory.getLogger(CsarSizeReducer.class);
+ private static final Set<String> ALLOWED_SIGNATURE_EXTENSIONS = Set.of("cms");
+ private static final Set<String> ALLOWED_CERTIFICATE_EXTENSIONS = Set.of("cert", "crt");
+ private static final String CSAR_EXTENSION = "csar";
+ private static final String UNEXPECTED_PROBLEM_HAPPENED_WHILE_READING_THE_CSAR = "An unexpected problem happened while reading the CSAR '%s'";
+ @Getter
+ private final AtomicBoolean reduced = new AtomicBoolean(false);
private final CsarPackageReducerConfiguration configuration;
@@ -44,38 +60,31 @@ public class CsarSizeReducer implements PackageSizeReducer {
@Override
public byte[] reduce(final Path csarPackagePath) {
+ if (hasSignedPackageStructure(csarPackagePath)) {
+ return reduce(csarPackagePath, this::signedZipProcessingConsumer);
+ } else {
+ return reduce(csarPackagePath, this::unsignedZipProcessingConsumer);
+ }
+ }
+
+ private byte[] reduce(final Path csarPackagePath, final ZipProcessFunction zipProcessingFunction) {
final var reducedCsarPath = Path.of(csarPackagePath + "." + UUID.randomUUID());
try (final var zf = new ZipFile(csarPackagePath.toString());
final var zos = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(reducedCsarPath)))) {
-
- zf.entries().asIterator().forEachRemaining(entry -> {
- final var entryName = entry.getName();
- try {
- if (!entry.isDirectory()) {
- zos.putNextEntry(new ZipEntry(entryName));
- if (isCandidateToRemove(entry)) {
- // replace with EMPTY string to avoid package description inconsistency/validation errors
- zos.write("".getBytes());
- } else {
- zos.write(zf.getInputStream(entry).readAllBytes());
- }
- }
- zos.closeEntry();
- } catch (final IOException ei) {
- final var errorMsg = String.format("Failed to extract '%s' from zip '%s'", entryName, csarPackagePath);
- throw new CsarSizeReducerException(errorMsg, ei);
- }
- });
-
+ zf.entries().asIterator().forEachRemaining(zipProcessingFunction.getProcessZipConsumer(csarPackagePath, zf, zos));
} catch (final IOException ex1) {
rollback(reducedCsarPath);
- final var errorMsg = String.format("An unexpected problem happened while reading the CSAR '%s'", csarPackagePath);
+ final var errorMsg = String.format(UNEXPECTED_PROBLEM_HAPPENED_WHILE_READING_THE_CSAR, csarPackagePath);
throw new CsarSizeReducerException(errorMsg, ex1);
}
final byte[] reducedCsarBytes;
try {
- reducedCsarBytes = Files.readAllBytes(reducedCsarPath);
+ if (reduced.get()) {
+ reducedCsarBytes = Files.readAllBytes(reducedCsarPath);
+ } else {
+ reducedCsarBytes = Files.readAllBytes(csarPackagePath);
+ }
} catch (final IOException e) {
final var errorMsg = String.format("Could not read bytes of file '%s'", csarPackagePath);
throw new CsarSizeReducerException(errorMsg, e);
@@ -90,6 +99,51 @@ public class CsarSizeReducer implements PackageSizeReducer {
return reducedCsarBytes;
}
+ private Consumer<ZipEntry> signedZipProcessingConsumer(final Path csarPackagePath, final ZipFile zf, final ZipOutputStream zos) {
+ return zipEntry -> {
+ final var entryName = zipEntry.getName();
+ try {
+ zos.putNextEntry(new ZipEntry(entryName));
+ if (!zipEntry.isDirectory()) {
+ if (entryName.toLowerCase().endsWith(CSAR_EXTENSION)) {
+ final var internalCsarExtractPath = Path.of(csarPackagePath + "." + UUID.randomUUID());
+ Files.copy(zf.getInputStream(zipEntry), internalCsarExtractPath, REPLACE_EXISTING);
+ zos.write(reduce(internalCsarExtractPath, this::unsignedZipProcessingConsumer));
+ Files.delete(internalCsarExtractPath);
+ } else {
+ zos.write(zf.getInputStream(zipEntry).readAllBytes());
+ }
+ }
+ zos.closeEntry();
+ } catch (final IOException ei) {
+ final var errorMsg = String.format("Failed to extract '%s' from zip '%s'", entryName, csarPackagePath);
+ throw new CsarSizeReducerException(errorMsg, ei);
+ }
+ };
+ }
+
+ private Consumer<ZipEntry> unsignedZipProcessingConsumer(final Path csarPackagePath, final ZipFile zf, final ZipOutputStream zos) {
+ return zipEntry -> {
+ final var entryName = zipEntry.getName();
+ try {
+ zos.putNextEntry(new ZipEntry(entryName));
+ if (!zipEntry.isDirectory()) {
+ if (isCandidateToRemove(zipEntry)) {
+ // replace with EMPTY string to avoid package description inconsistency/validation errors
+ zos.write("".getBytes());
+ reduced.set(true);
+ } else {
+ zos.write(zf.getInputStream(zipEntry).readAllBytes());
+ }
+ }
+ zos.closeEntry();
+ } catch (final IOException ei) {
+ final var errorMsg = String.format("Failed to extract '%s' from zip '%s'", entryName, csarPackagePath);
+ throw new CsarSizeReducerException(errorMsg, ei);
+ }
+ };
+ }
+
private void rollback(final Path reducedCsarPath) {
if (Files.exists(reducedCsarPath)) {
try {
@@ -106,4 +160,59 @@ public class CsarSizeReducer implements PackageSizeReducer {
|| zipEntry.getSize() > configuration.getSizeLimit();
}
+ private boolean hasSignedPackageStructure(final Path csarPackagePath) {
+ final List<Path> packagePathList;
+ try (final var zf = new ZipFile(csarPackagePath.toString())) {
+ packagePathList = zf.stream()
+ .filter(zipEntry -> !zipEntry.isDirectory())
+ .map(ZipEntry::getName).map(Path::of)
+ .collect(Collectors.toList());
+ } catch (final IOException e) {
+ final var errorMsg = String.format(UNEXPECTED_PROBLEM_HAPPENED_WHILE_READING_THE_CSAR, csarPackagePath);
+ throw new CsarSizeReducerException(errorMsg, e);
+ }
+
+ if (CollectionUtils.isEmpty(packagePathList)) {
+ return false;
+ }
+ final int numberOfFiles = packagePathList.size();
+ if (numberOfFiles == 2) {
+ return hasOneInternalPackageFile(packagePathList) && hasOneSignatureFile(packagePathList);
+ }
+ if (numberOfFiles == 3) {
+ return hasOneInternalPackageFile(packagePathList) && hasOneSignatureFile(packagePathList) && hasOneCertificateFile(packagePathList);
+ }
+ return false;
+ }
+
+ private boolean hasOneInternalPackageFile(final List<Path> packagePathList) {
+ return packagePathList.parallelStream()
+ .map(Path::toString)
+ .map(FilenameUtils::getExtension)
+ .map(String::toLowerCase)
+ .filter(extension -> extension.endsWith(CSAR_EXTENSION)).count() == 1;
+ }
+
+ private boolean hasOneSignatureFile(final List<Path> packagePathList) {
+ return packagePathList.parallelStream()
+ .map(Path::toString)
+ .map(FilenameUtils::getExtension)
+ .map(String::toLowerCase)
+ .filter(ALLOWED_SIGNATURE_EXTENSIONS::contains).count() == 1;
+ }
+
+ private boolean hasOneCertificateFile(final List<Path> packagePathList) {
+ return packagePathList.parallelStream()
+ .map(Path::toString)
+ .map(FilenameUtils::getExtension)
+ .map(String::toLowerCase)
+ .filter(ALLOWED_CERTIFICATE_EXTENSIONS::contains).count() == 1;
+ }
+
+ @FunctionalInterface
+ private interface ZipProcessFunction {
+
+ Consumer<ZipEntry> getProcessZipConsumer(Path csarPackagePath, ZipFile zf, ZipOutputStream zos);
+ }
+
}
diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java
index c7586446f7..eaa5ffeda2 100644
--- a/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java
+++ b/common-be/src/test/java/org/openecomp/sdc/be/csar/storage/CsarSizeReducerTest.java
@@ -23,7 +23,9 @@ package org.openecomp.sdc.be.csar.storage;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.when;
import java.nio.charset.StandardCharsets;
@@ -32,7 +34,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -51,15 +54,16 @@ class CsarSizeReducerTest {
MockitoAnnotations.openMocks(this);
}
- @Test
- void reduceByPathAndSizeTest() throws ZipException {
+ @ParameterizedTest
+ @ValueSource(strings = {"dummyToReduce.zip", "dummyToReduce.csar", "dummyToNotReduce.csar"})
+ void reduceByPathAndSizeTest(String fileName) throws ZipException {
final var pathToReduce1 = Path.of("Files/images");
final var pathToReduce2 = Path.of("Files/Scripts/my_script.sh");
final var sizeLimit = 150000L;
when(csarPackageReducerConfiguration.getSizeLimit()).thenReturn(sizeLimit);
when(csarPackageReducerConfiguration.getFoldersToStrip()).thenReturn(Set.of(pathToReduce1, pathToReduce2));
- final var csarPath = Path.of("src/test/resources/csarSizeReducer/dummy.csar");
+ final var csarPath = Path.of("src/test/resources/csarSizeReducer/" + fileName);
final Map<String, byte[]> originalCsar = ZipUtils.readZip(csarPath.toFile(), false);
@@ -74,14 +78,47 @@ class CsarSizeReducerTest {
assertTrue(reducedCsar.containsKey(originalFilePath),
String.format("No file should be removed, but it is missing original file '%s'", originalFilePath));
- if (originalFilePath.startsWith(pathToReduce1.toString()) || originalFilePath.startsWith(pathToReduce2.toString())
- || originalBytes.length > sizeLimit) {
- assertArrayEquals("".getBytes(StandardCharsets.UTF_8), reducedCsar.get(originalFilePath),
- String.format("File '%s' expected to be reduced to empty string", originalFilePath));
+ final String extention = fileName.substring(fileName.lastIndexOf('.') + 1);
+ switch (extention.toLowerCase()) {
+ case "zip":
+ verifyZIP(pathToReduce1, pathToReduce2, sizeLimit, reducedCsar, originalFilePath, originalBytes);
+ break;
+ case "csar":
+ verifyCSAR(pathToReduce1, pathToReduce2, sizeLimit, reducedCsar, originalFilePath, originalBytes);
+ break;
+ default:
+ fail("Unexpected file extention");
+ break;
+ }
+ }
+ }
+
+ private void verifyCSAR(final Path pathToReduce1, final Path pathToReduce2, final long sizeLimit, final Map<String, byte[]> reducedCsar,
+ final String originalFilePath, final byte[] originalBytes) {
+ if (originalFilePath.startsWith(pathToReduce1.toString()) || originalFilePath.startsWith(pathToReduce2.toString())
+ || originalBytes.length > sizeLimit) {
+ assertArrayEquals("".getBytes(StandardCharsets.UTF_8), reducedCsar.get(originalFilePath),
+ String.format("File '%s' expected to be reduced to empty string", originalFilePath));
+ } else {
+ assertArrayEquals(originalBytes, reducedCsar.get(originalFilePath),
+ String.format("File '%s' expected to be equal", originalFilePath));
+ }
+ }
+
+ private void verifyZIP(final Path pathToReduce1, final Path pathToReduce2, final long sizeLimit, final Map<String, byte[]> reducedCsar,
+ final String originalFilePath, final byte[] originalBytes) {
+ if (originalFilePath.startsWith(pathToReduce1.toString()) || originalFilePath.startsWith(pathToReduce2.toString())
+ || originalBytes.length > sizeLimit) {
+ assertArrayEquals("".getBytes(StandardCharsets.UTF_8), reducedCsar.get(originalFilePath),
+ String.format("File '%s' expected to be reduced to empty string", originalFilePath));
+ } else {
+ if (originalFilePath.endsWith(".csar") && csarSizeReducer.getReduced().get()) {
+ assertNotEquals(originalBytes.length, reducedCsar.get(originalFilePath).length,
+ String.format("File '%s' expected to be NOT equal", originalFilePath));
} else {
assertArrayEquals(originalBytes, reducedCsar.get(originalFilePath),
String.format("File '%s' expected to be equal", originalFilePath));
}
}
}
-} \ No newline at end of file
+}
diff --git a/common-be/src/test/resources/csarSizeReducer/dummyToNotReduce.csar b/common-be/src/test/resources/csarSizeReducer/dummyToNotReduce.csar
new file mode 100644
index 0000000000..d44041382e
--- /dev/null
+++ b/common-be/src/test/resources/csarSizeReducer/dummyToNotReduce.csar
Binary files differ
diff --git a/common-be/src/test/resources/csarSizeReducer/dummy.csar b/common-be/src/test/resources/csarSizeReducer/dummyToReduce.csar
index 73b28f52fd..73b28f52fd 100644
--- a/common-be/src/test/resources/csarSizeReducer/dummy.csar
+++ b/common-be/src/test/resources/csarSizeReducer/dummyToReduce.csar
Binary files differ
diff --git a/common-be/src/test/resources/csarSizeReducer/dummyToReduce.zip b/common-be/src/test/resources/csarSizeReducer/dummyToReduce.zip
new file mode 100644
index 0000000000..fecb45aaaf
--- /dev/null
+++ b/common-be/src/test/resources/csarSizeReducer/dummyToReduce.zip
Binary files differ
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 10f6012a76..19f2c5df87 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
@@ -160,17 +160,21 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
try {
packageInputStream = fileToUpload.getDataHandler().getInputStream();
} catch (final IOException e) {
- return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING.formatMessage(filename)))).build();
+ return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(
+ new ErrorMessage(ErrorLevel.ERROR, UNEXPECTED_PROBLEM_HAPPENED_WHILE_GETTING.formatMessage(filename)))).build();
}
try {
artifactInfo = artifactStorageManager.upload(vspId, versionId, packageInputStream);
} catch (final BusinessException e) {
- return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename)))).build();
+ return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(
+ new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_PERSISTING_THE_ARTIFACT.formatMessage(filename)))).build();
}
try {
fileToUploadBytes = packageSizeReducer.reduce(artifactInfo.getPath());
} catch (final BusinessException e) {
- return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(artifactInfo.getPath())))).build();
+ return Response.status(INTERNAL_SERVER_ERROR).entity(buildUploadResponseWithError(
+ new ErrorMessage(ErrorLevel.ERROR, ERROR_HAS_OCCURRED_WHILE_REDUCING_THE_ARTIFACT_SIZE.formatMessage(artifactInfo.getPath()))))
+ .build();
}
} else {
fileToUploadBytes = fileToUpload.getObject(byte[].class);
@@ -183,10 +187,12 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
}
final var onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
if (onboardPackageInfo == null) {
- return Response.ok(buildUploadResponseWithError(new ErrorMessage(ErrorLevel.ERROR, PACKAGE_PROCESS_ERROR.formatMessage(filename)))).build();
+ final UploadFileResponseDto uploadFileResponseDto = buildUploadResponseWithError(
+ new ErrorMessage(ErrorLevel.ERROR, PACKAGE_PROCESS_ERROR.formatMessage(filename)));
+ return Response.ok(uploadFileResponseDto).build();
}
- final var vspDetails = new VspDetails(ValidationUtils.sanitizeInputString(vspId),
- new Version(ValidationUtils.sanitizeInputString(versionId)));
+ final var version = new Version(ValidationUtils.sanitizeInputString(versionId));
+ final var vspDetails = new VspDetails(ValidationUtils.sanitizeInputString(vspId), version);
return processOnboardPackage(onboardPackageInfo, vspDetails, errorMessages);
}
@@ -224,7 +230,8 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
} else {
zipFile = vendorSoftwareProductManager.get(vspId, new Version((versionId)));
if (!zipFile.isPresent()) {
- ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR, getErrorWithParameters(NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(), ""));
+ ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR,
+ getErrorWithParameters(NO_FILE_WAS_UPLOADED_OR_FILE_NOT_EXIST.getErrorMessage(), ""));
LOGGER.error(errorMessage.getMessage());
return Response.status(NOT_FOUND).build();
}
@@ -255,7 +262,8 @@ public class OrchestrationTemplateCandidateImpl implements OrchestrationTemplate
FilesDataStructure fileDataStructure = copyFilesDataStructureDtoToFilesDataStructure(fileDataStructureDto);
ValidationResponse response = candidateManager.updateFilesDataStructure(vspId, new Version(versionId), fileDataStructure);
if (!response.isValid()) {
- return Response.status(EXPECTATION_FAILED).entity(new MapValidationResponseToDto().applyMapping(response, ValidationResponseDto.class)).build();
+ return Response.status(EXPECTATION_FAILED).entity(new MapValidationResponseToDto().applyMapping(response, ValidationResponseDto.class))
+ .build();
}
return Response.ok(fileDataStructureDto).build();
}
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
index b89756e501..edf29b75c5 100644
--- 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
@@ -74,7 +74,10 @@ import org.openecomp.sdcrests.vendorsoftwareproducts.types.UploadFileResponseDto
class OrchestrationTemplateCandidateImplTest {
private final Logger logger = LoggerFactory.getLogger(OrchestrationTemplateCandidateImplTest.class);
-
+ private final String candidateId = UUID.randomUUID().toString();
+ private final String softwareProductId = UUID.randomUUID().toString();
+ private final String versionId = UUID.randomUUID().toString();
+ private final String user = "cs0008";
@Mock
private OrchestrationTemplateCandidateManager candidateManager;
@Mock
@@ -85,15 +88,8 @@ class OrchestrationTemplateCandidateImplTest {
private ArtifactStorageManager artifactStorageManager;
@Mock
private PackageSizeReducer packageSizeReducer;
-
private OrchestrationTemplateCandidateImpl orchestrationTemplateCandidate;
- private final String candidateId = UUID.randomUUID().toString();
- private final String softwareProductId = UUID.randomUUID().toString();
- private final String versionId = UUID.randomUUID().toString();
-
- private final String user = "cs0008";
-
@BeforeEach
public void setUp() {
try {
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateCSARHandler.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateCSARHandler.java
index f1062395af..f1cab482f2 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateCSARHandler.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/OrchestrationTemplateCSARHandler.java
@@ -25,6 +25,7 @@ import java.io.IOException;
import java.util.Optional;
import org.openecomp.core.utilities.file.FileContentHandler;
import org.openecomp.core.utilities.orchestration.OnboardingTypesEnum;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
import org.openecomp.sdc.common.errors.CoreException;
import org.openecomp.sdc.common.errors.Messages;
import org.openecomp.sdc.common.utils.SdcCommon;
@@ -49,7 +50,8 @@ public class OrchestrationTemplateCSARHandler extends BaseOrchestrationTemplateH
final UploadFileResponse uploadFileResponse = new UploadFileResponse();
if (onboardPackageInfo.getPackageType() == OnboardingTypesEnum.SIGNED_CSAR) {
final OnboardSignedPackage originalOnboardPackage = (OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage();
- validatePackageSecurity(originalOnboardPackage).ifPresent(packageSignatureResponse -> {
+ final ArtifactInfo artifactInfo = onboardPackageInfo.getArtifactInfo();
+ validatePackageSecurity(originalOnboardPackage, artifactInfo).ifPresent(packageSignatureResponse -> {
if (packageSignatureResponse.hasErrors()) {
uploadFileResponse.addStructureErrors(packageSignatureResponse.getErrors());
}
@@ -74,11 +76,11 @@ public class OrchestrationTemplateCSARHandler extends BaseOrchestrationTemplateH
return uploadFileResponse;
}
- private Optional<UploadFileResponse> validatePackageSecurity(final OnboardSignedPackage originalOnboardPackage) {
+ private Optional<UploadFileResponse> validatePackageSecurity(final OnboardSignedPackage signedPackage, final ArtifactInfo artifactInfo) {
final UploadFileResponse uploadFileResponseDto = new UploadFileResponse();
try {
final CsarSecurityValidator csarSecurityValidator = new CsarSecurityValidator();
- if (!csarSecurityValidator.verifyPackageSignature(originalOnboardPackage)) {
+ if (!csarSecurityValidator.verifyPackageSignature(signedPackage, artifactInfo)) {
final ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR, Messages.FAILED_TO_VERIFY_SIGNATURE.getErrorMessage());
logger.error(errorMessage.getMessage());
uploadFileResponseDto.addStructureError(SdcCommon.UPLOAD_FILE, errorMessage);
@@ -86,7 +88,7 @@ public class OrchestrationTemplateCSARHandler extends BaseOrchestrationTemplateH
}
} catch (final SecurityManagerException e) {
final ErrorMessage errorMessage = new ErrorMessage(ErrorLevel.ERROR, e.getMessage());
- logger.error("Could not validate package signature {}", originalOnboardPackage.getFilename(), e);
+ logger.error("Could not validate package signature {}", signedPackage.getFilename(), e);
uploadFileResponseDto.addStructureError(SdcCommon.UPLOAD_FILE, errorMessage);
return Optional.of(uploadFileResponseDto);
}
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java
index 81a17f333b..bf5abe3737 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidator.java
@@ -19,7 +19,8 @@
package org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation;
import java.util.Optional;
-import org.openecomp.core.utilities.file.FileContentHandler;
+import lombok.NoArgsConstructor;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
@@ -27,13 +28,11 @@ import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
/**
* Validates the package security
*/
+@NoArgsConstructor
public class CsarSecurityValidator {
private SecurityManager securityManager = SecurityManager.getInstance();
- public CsarSecurityValidator() {
- }
-
//for tests purpose
CsarSecurityValidator(final SecurityManager securityManager) {
this.securityManager = securityManager;
@@ -45,15 +44,24 @@ public class CsarSecurityValidator {
* @return true if signature verified
* @throws SecurityManagerException when a certificate error occurs.
*/
- public boolean verifyPackageSignature(final OnboardSignedPackage signedPackage) throws SecurityManagerException {
- final FileContentHandler fileContentHandler = signedPackage.getFileContentHandler();
- final byte[] signatureBytes = fileContentHandler.getFileContent(signedPackage.getSignatureFilePath());
- final byte[] archiveBytes = fileContentHandler.getFileContent(signedPackage.getInternalPackageFilePath());
- byte[] certificateBytes = null;
- final Optional<String> certificateFilePath = signedPackage.getCertificateFilePath();
- if (certificateFilePath.isPresent()) {
- certificateBytes = fileContentHandler.getFileContent(certificateFilePath.get());
+ public boolean verifyPackageSignature(final OnboardSignedPackage signedPackage, final ArtifactInfo artifactInfo) throws SecurityManagerException {
+ if (isArtifactInfoPresent(artifactInfo)) {
+ return securityManager.verifyPackageSignedData(signedPackage, artifactInfo);
+ } else {
+ final var fileContentHandler = signedPackage.getFileContentHandler();
+ final byte[] signatureBytes = fileContentHandler.getFileContent(signedPackage.getSignatureFilePath());
+ final byte[] archiveBytes = fileContentHandler.getFileContent(signedPackage.getInternalPackageFilePath());
+ byte[] certificateBytes = null;
+ final Optional<String> certificateFilePath = signedPackage.getCertificateFilePath();
+ if (certificateFilePath.isPresent()) {
+ certificateBytes = fileContentHandler.getFileContent(certificateFilePath.get());
+ }
+ return securityManager.verifySignedData(signatureBytes, certificateBytes, archiveBytes);
}
- return securityManager.verifySignedData(signatureBytes, certificateBytes, archiveBytes);
}
+
+ private boolean isArtifactInfoPresent(final ArtifactInfo artifactInfo) {
+ return artifactInfo != null && artifactInfo.getPath() != null;
+ }
+
}
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 d60b54b5e1..fec15b5fcc 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
@@ -19,14 +19,17 @@
*/
package org.openecomp.sdc.vendorsoftwareproduct.security;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
import com.google.common.collect.ImmutableSet;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@@ -48,22 +51,28 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.zip.ZipFile;
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.CMSProcessableFile;
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.openecomp.sdc.be.csar.storage.ArtifactInfo;
+import org.openecomp.sdc.common.errors.SdcRuntimeException;
import org.openecomp.sdc.logging.api.Logger;
import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
/**
* This is temporary solution. When AAF provides functionality for verifying trustedCertificates, this class should be reviewed Class is responsible
@@ -71,13 +80,12 @@ import org.openecomp.sdc.logging.api.LoggerFactory;
*/
public class SecurityManager {
+ public static final Set<String> ALLOWED_SIGNATURE_EXTENSIONS = Set.of("cms");
+ public static final Set<String> ALLOWED_CERTIFICATE_EXTENSIONS = Set.of("cert", "crt");
private static final String CERTIFICATE_DEFAULT_LOCATION = "cert";
- public static final Set<String> ALLOWED_SIGNATURE_EXTENSIONS = ImmutableSet.of("cms");
- public static final Set<String> ALLOWED_CERTIFICATE_EXTENSIONS = ImmutableSet.of("cert", "crt");
- private Logger logger = LoggerFactory.getLogger(SecurityManager.class);
- private Set<X509Certificate> trustedCertificates = new HashSet<>();
- private Set<X509Certificate> trustedCertificatesFromPackage = new HashSet<>();
- private File certificateDirectory;
+ private static final Logger logger = LoggerFactory.getLogger(SecurityManager.class);
+ private static final String UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION = "Unexpected error occurred during signature validation!";
+ private static final String COULD_NOT_VERIFY_SIGNATURE = "Could not verify signature!";
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
@@ -85,6 +93,10 @@ public class SecurityManager {
}
}
+ private Set<X509Certificate> trustedCertificates = new HashSet<>();
+ private Set<X509Certificate> trustedCertificatesFromPackage = new HashSet<>();
+ private File certificateDirectory;
+
private SecurityManager() {
certificateDirectory = this.getcertDirectory(System.getenv("SDC_CERT_DIR"));
}
@@ -99,20 +111,12 @@ public class SecurityManager {
}
/**
- * Initialization on demand class / synchronized singleton pattern.
- */
- private static class SecurityManagerInstanceHolder {
-
- private static final SecurityManager instance = new SecurityManager();
- }
-
- /**
* Checks the configured location for available trustedCertificates
*
* @return set of trustedCertificates
* @throws SecurityManagerException
*/
- public Set<X509Certificate> getTrustedCertificates() throws SecurityManagerException, FileNotFoundException {
+ public Set<X509Certificate> getTrustedCertificates() throws SecurityManagerException {
//if file number in certificate directory changed reload certs
String[] certFiles = certificateDirectory.list();
if (certFiles == null) {
@@ -145,43 +149,121 @@ public class SecurityManager {
* @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); final PEMParser pemParser = new PEMParser(
- new InputStreamReader(signatureStream))) {
+ public boolean verifySignedData(final byte[] messageSyntaxSignature,
+ final byte[] packageCert,
+ final byte[] innerPackageFile) throws SecurityManagerException {
+ try (final ByteArrayInputStream signatureStream = new ByteArrayInputStream(messageSyntaxSignature);
+ final PEMParser pemParser = new PEMParser(new InputStreamReader(signatureStream))) {
final Object parsedObject = pemParser.readObject();
if (!(parsedObject instanceof ContentInfo)) {
throw new SecurityManagerException("Signature is not recognized");
}
- final ContentInfo signature = ContentInfo.getInstance(parsedObject);
- final CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFile);
- final CMSSignedData signedData = new CMSSignedData(signedContent, signature);
- final Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
- final SignerInformation firstSigner = signers.iterator().next();
- final X509Certificate cert;
- Collection<X509CertificateHolder> certs;
- if (packageCert == null) {
- certs = signedData.getCertificates().getMatches(null);
- cert = readSignCert(certs, firstSigner)
- .orElseThrow(() -> new SecurityManagerException("No certificate found in cms signature that should contain one!"));
- } else {
- certs = parseCertsFromPem(packageCert);
- cert = readSignCert(certs, firstSigner)
- .orElseThrow(() -> new SecurityManagerException("No matching certificate found in certificate file that should contain one!"));
+ return verify(packageCert, new CMSSignedData(new CMSProcessableByteArray(innerPackageFile), ContentInfo.getInstance(parsedObject)));
+ } catch (final IOException | CMSException e) {
+ logger.error(e.getMessage(), e);
+ throw new SecurityManagerException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e);
+ }
+ }
+
+ public boolean verifyPackageSignedData(final OnboardSignedPackage signedPackage, final ArtifactInfo artifactInfo)
+ throws SecurityManagerException {
+ boolean fail = false;
+ final var fileContentHandler = signedPackage.getFileContentHandler();
+ byte[] packageCert = null;
+ final Optional<String> certificateFilePath = signedPackage.getCertificateFilePath();
+ if (certificateFilePath.isPresent()) {
+ packageCert = fileContentHandler.getFileContent(certificateFilePath.get());
+ }
+ final var path = artifactInfo.getPath();
+ final var target = Path.of(path.toString() + "." + UUID.randomUUID());
+
+ try (final var signatureStream = new ByteArrayInputStream(fileContentHandler.getFileContent(signedPackage.getSignatureFilePath()));
+ final var pemParser = new PEMParser(new InputStreamReader(signatureStream))) {
+ final var parsedObject = pemParser.readObject();
+ if (!(parsedObject instanceof ContentInfo)) {
+ fail = true;
+ throw new SecurityManagerException("Signature is not recognized");
}
- trustedCertificatesFromPackage = readTrustedCerts(certs, firstSigner);
- if (verifyCertificate(cert, getTrustedCertificates()) == null) {
+
+ if (!findCSARandExtract(path, target)) {
+ fail = true;
return false;
}
- return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
- } catch (OperatorCreationException | IOException | CMSException e) {
+ final var verify = verify(packageCert, new CMSSignedData(new CMSProcessableFile(target.toFile()), ContentInfo.getInstance(parsedObject)));
+ fail = !verify;
+ return verify;
+ } catch (final IOException e) {
+ fail = true;
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);
+ throw new SecurityManagerException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e);
+ } catch (final CMSException e) {
+ fail = true;
+ throw new SecurityManagerException(COULD_NOT_VERIFY_SIGNATURE, e);
+ } catch (final SecurityManagerException e) {
+ fail = true;
+ throw e;
+ } finally {
+ deleteFile(target);
+ if (fail) {
+ deleteFile(path);
+ }
+ }
+ }
+
+ private void deleteFile(final Path filePath) {
+ try {
+ Files.delete(filePath);
+ } catch (final IOException e) {
+ logger.warn("Failed to delete '{}' after verifying package signed data", filePath, e);
}
}
+ private boolean verify(final byte[] packageCert, final CMSSignedData signedData) throws SecurityManagerException {
+ final SignerInformation firstSigner = signedData.getSignerInfos().getSigners().iterator().next();
+ final X509Certificate cert;
+ Collection<X509CertificateHolder> certs;
+ if (packageCert == null) {
+ certs = signedData.getCertificates().getMatches(null);
+ cert = readSignCert(certs, firstSigner)
+ .orElseThrow(() -> new SecurityManagerException("No certificate found in cms signature that should contain one!"));
+ } else {
+ try {
+ certs = parseCertsFromPem(packageCert);
+ } catch (final IOException e) {
+ throw new SecurityManagerException("Failed to parse certificate from PEM", e);
+ }
+ cert = readSignCert(certs, firstSigner)
+ .orElseThrow(() -> new SecurityManagerException("No matching certificate found in certificate file that should contain one!"));
+ }
+ trustedCertificatesFromPackage = readTrustedCerts(certs, firstSigner);
+ if (verifyCertificate(cert, getTrustedCertificates()) == null) {
+ return false;
+ }
+ try {
+ return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ } catch (CMSException | OperatorCreationException e) {
+ throw new SecurityManagerException("Failed to verify package signed data", e);
+ }
+ }
+
+ private boolean findCSARandExtract(final Path path, final Path target) throws IOException {
+ final AtomicBoolean found = new AtomicBoolean(false);
+ try (final var zf = new ZipFile(path.toString())) {
+ zf.entries().asIterator().forEachRemaining(entry -> {
+ final var entryName = entry.getName();
+ if (!entry.isDirectory() && entryName.toLowerCase().endsWith(".csar")) {
+ try {
+ Files.copy(zf.getInputStream(entry), target, REPLACE_EXISTING);
+ } catch (final IOException e) {
+ throw new SdcRuntimeException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e);
+ }
+ found.set(true);
+ }
+ });
+ }
+ return found.get();
+ }
+
private Optional<X509Certificate> readSignCert(final Collection<X509CertificateHolder> certs, final SignerInformation firstSigner) {
return certs.stream().filter(crt -> firstSigner.getSID().match(crt)).findAny().map(this::loadCertificate);
}
@@ -205,7 +287,7 @@ public class SecurityManager {
return allCerts;
}
- private void processCertificateDir() throws SecurityManagerException, FileNotFoundException {
+ private void processCertificateDir() throws SecurityManagerException {
if (!certificateDirectory.exists() || !certificateDirectory.isDirectory()) {
logger.error("Issue with certificate directory, check if exists!");
return;
@@ -253,8 +335,8 @@ public class SecurityManager {
}
}
- private PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set<X509Certificate> additionalCerts)
- throws GeneralSecurityException, SecurityManagerException {
+ private PKIXCertPathBuilderResult verifyCertificate(final X509Certificate cert,
+ final Set<X509Certificate> additionalCerts) throws SecurityManagerException {
if (null == cert) {
throw new SecurityManagerException("The certificate is empty!");
}
@@ -273,7 +355,11 @@ public class SecurityManager {
intermediateCerts.add(additionalCert);
}
}
- return verifyCertificate(cert, trustedRootCerts, intermediateCerts);
+ try {
+ return verifyCertificate(cert, trustedRootCerts, intermediateCerts);
+ } catch (final GeneralSecurityException e) {
+ throw new SecurityManagerException("Failed to verify certificate", e);
+ }
}
private PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set<X509Certificate> allTrustedRootCerts,
@@ -325,4 +411,12 @@ public class SecurityManager {
private boolean isSelfSigned(X509Certificate cert) {
return cert.getIssuerDN().equals(cert.getSubjectDN());
}
+
+ /**
+ * Initialization on demand class / synchronized singleton pattern.
+ */
+ private static class SecurityManagerInstanceHolder {
+
+ private static final SecurityManager instance = new SecurityManager();
+ }
}
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java
index 5f5f9eb7dc..96d11eb148 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/java/org/openecomp/sdc/vendorsoftwareproduct/impl/orchestration/csar/validation/CsarSecurityValidatorTest.java
@@ -19,6 +19,7 @@
package org.openecomp.sdc.vendorsoftwareproduct.impl.orchestration.csar.validation;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@@ -27,12 +28,21 @@ import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import java.io.IOException;
+import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
-import org.junit.Before;
-import org.junit.Test;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.mockito.Mock;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
+import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo;
import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageProcessor;
import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPackageValidator;
import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
@@ -40,37 +50,88 @@ import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException
import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
-public class CsarSecurityValidatorTest {
+class CsarSecurityValidatorTest {
- private static final String BASE_DIR = "/vspmanager.csar/";
+ private static final String BASE_DIR = "/vspmanager.csar/signing/";
+ private static final String DELIMITER = "---";
private CsarSecurityValidator csarSecurityValidator;
@Mock
- SecurityManager securityManager;
+ private SecurityManager securityManager;
- @Before
- public void setUp() {
+ @AfterEach
+ void tearDown() throws Exception {
+ restore();
+ }
+
+ private void restore() throws Exception {
+ final URI uri = CsarSecurityValidatorTest.class.getResource(BASE_DIR).toURI();
+ final List<Path> list = Files.list(Path.of(uri.getPath())).filter(path -> path.toString().contains(DELIMITER)).collect(Collectors.toList());
+ for (final Path path : list) {
+ final String[] split = path.toString().split(DELIMITER);
+ Files.move(path, Path.of(split[0]), REPLACE_EXISTING);
+ }
+ }
+
+ @BeforeEach
+ public void setUp() throws Exception {
initMocks(this);
csarSecurityValidator = new CsarSecurityValidator(securityManager);
+ backup();
+ }
+
+ private void backup() throws Exception {
+ final URI uri = CsarSecurityValidatorTest.class.getResource(BASE_DIR).toURI();
+ final List<Path> list = Files.list(Path.of(uri.getPath())).collect(Collectors.toList());
+ for (final Path path : list) {
+ Files.copy(path, Path.of(path.toString() + DELIMITER + UUID.randomUUID()), REPLACE_EXISTING);
+ }
}
@Test
- public void isSignatureValidTestCorrectStructureAndValidSignatureExists() throws SecurityManagerException {
- final byte[] packageBytes = getFileBytesOrFail("signing/signed-package.zip");
- final OnboardSignedPackage onboardSignedPackage = loadSignedPackage("signed-package.zip",
+ void isSignatureValidTestCorrectStructureAndValidSignatureExists() throws SecurityManagerException, IOException {
+ final byte[] packageBytes = getFileBytesOrFail("signed-package.zip");
+ final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithArtifactInfo("signed-package.zip", packageBytes, null);
+ when(securityManager.verifyPackageSignedData(any(OnboardSignedPackage.class), any(ArtifactInfo.class))).thenReturn(true);
+ final boolean isSignatureValid = csarSecurityValidator
+ .verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo());
+ assertThat("Signature should be valid", isSignatureValid, is(true));
+ }
+
+ @Test
+ void isSignatureValidTestCorrectStructureAndNotValidSignatureExists() throws SecurityManagerException {
+ final byte[] packageBytes = getFileBytesOrFail("signed-package-tampered-data.zip");
+ final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithArtifactInfo("signed-package-tampered-data.zip", packageBytes, null);
+ //no mocked securityManager
+ csarSecurityValidator = new CsarSecurityValidator();
+ Assertions.assertThrows(SecurityManagerException.class, () -> {
+ csarSecurityValidator
+ .verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo());
+ });
+ }
+
+ @Test
+ void isSignatureValidTestCorrectStructureAndValidSignatureExistsArtifactStorageManagerIsEnabled() throws SecurityManagerException {
+ final byte[] packageBytes = getFileBytesOrFail("signed-package.zip");
+ final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithoutArtifactInfo("signed-package.zip",
packageBytes, null);
when(securityManager.verifySignedData(any(), any(), any())).thenReturn(true);
- final boolean isSignatureValid = csarSecurityValidator.verifyPackageSignature(onboardSignedPackage);
+ final boolean isSignatureValid = csarSecurityValidator
+ .verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo());
+
assertThat("Signature should be valid", isSignatureValid, is(true));
}
- @Test(expected = SecurityManagerException.class)
- public void isSignatureValidTestCorrectStructureAndNotValidSignatureExists() throws SecurityManagerException {
- final byte[] packageBytes = getFileBytesOrFail("signing/signed-package-tampered-data.zip");
- final OnboardSignedPackage onboardSignedPackage = loadSignedPackage("signed-package-tampered-data.zip",
+ @Test
+ void isSignatureValidTestCorrectStructureAndNotValidSignatureExistsArtifactStorageManagerIsEnabled() throws SecurityManagerException {
+ final byte[] packageBytes = getFileBytesOrFail("signed-package-tampered-data.zip");
+ final OnboardPackageInfo onboardPackageInfo = loadSignedPackageWithoutArtifactInfo("signed-package-tampered-data.zip",
packageBytes, null);
//no mocked securityManager
csarSecurityValidator = new CsarSecurityValidator();
- csarSecurityValidator.verifyPackageSignature(onboardSignedPackage);
+ Assertions.assertThrows(SecurityManagerException.class, () -> {
+ csarSecurityValidator
+ .verifyPackageSignature((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo());
+ });
}
private byte[] getFileBytesOrFail(final String path) {
@@ -87,8 +148,21 @@ public class CsarSecurityValidatorTest {
CsarSecurityValidatorTest.class.getResource(BASE_DIR + path).toURI()));
}
- private OnboardSignedPackage loadSignedPackage(final String packageName, final byte[] packageBytes,
- CnfPackageValidator cnfPackageValidator) {
+ private OnboardPackageInfo loadSignedPackageWithArtifactInfo(final String packageName, final byte[] packageBytes,
+ final CnfPackageValidator cnfPackageValidator) {
+ final OnboardingPackageProcessor onboardingPackageProcessor =
+ new OnboardingPackageProcessor(packageName, packageBytes, cnfPackageValidator,
+ new PersistentStorageArtifactInfo(Path.of("src/test/resources/vspmanager.csar/signing/signed-package.zip")));
+ final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
+ if (onboardPackageInfo == null) {
+ fail("Unexpected error. Could not load original package");
+ }
+
+ return onboardPackageInfo;
+ }
+
+ private OnboardPackageInfo loadSignedPackageWithoutArtifactInfo(final String packageName, final byte[] packageBytes,
+ final CnfPackageValidator cnfPackageValidator) {
final OnboardingPackageProcessor onboardingPackageProcessor =
new OnboardingPackageProcessor(packageName, packageBytes, cnfPackageValidator, null);
final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
@@ -96,6 +170,6 @@ public class CsarSecurityValidatorTest {
fail("Unexpected error. Could not load original package");
}
- return (OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage();
+ return onboardPackageInfo;
}
}
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 b5479e0868..6dc5517c45 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
@@ -27,14 +27,20 @@ import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.be.csar.storage.PersistentStorageArtifactInfo;
+import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.OnboardingPackageProcessor;
+import org.openecomp.sdc.vendorsoftwareproduct.impl.onboarding.validation.CnfPackageValidator;
+import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardPackageInfo;
+import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
-public class SecurityManagerTest {
+class SecurityManagerTest {
private File certDir;
private String cerDirPath = "/tmp/cert/";
@@ -71,7 +77,7 @@ public class SecurityManagerTest {
}
@Test
- public void testGetCertificates() throws IOException, SecurityManagerException, URISyntaxException {
+ void testGetCertificates() throws IOException, SecurityManagerException, URISyntaxException {
File newFile = prepareCertFiles("/cert/root-certificate.pem", cerDirPath + "/root-certificate.pem");
assertEquals(1, securityManager.getTrustedCertificates().size());
newFile.delete();
@@ -79,13 +85,13 @@ public class SecurityManagerTest {
}
@Test
- public void testGetCertificatesNoDirectory() throws IOException, SecurityManagerException {
+ void testGetCertificatesNoDirectory() throws IOException, SecurityManagerException {
certDir.delete();
assertEquals(0, securityManager.getTrustedCertificates().size());
}
@Test
- public void testGetCertificatesException() throws IOException, SecurityManagerException {
+ void testGetCertificatesException() throws IOException, SecurityManagerException {
File newFile = new File(cerDirPath + "root-certificate.pem");
newFile.createNewFile();
Assertions.assertThrows(SecurityManagerException.class, () -> {
@@ -97,9 +103,9 @@ public class SecurityManagerTest {
}
@Test
- public void testGetCertificatesUpdated() throws IOException, SecurityManagerException, URISyntaxException {
+ void testGetCertificatesUpdated() throws IOException, SecurityManagerException, URISyntaxException {
File newFile = prepareCertFiles("/cert/root-certificate.pem", cerDirPath + "root-certificate.pem");
- assertTrue(securityManager.getTrustedCertificates().size() == 1);
+ assertEquals(1, securityManager.getTrustedCertificates().size());
File otherNewFile = prepareCertFiles("/cert/package-certificate.pem", cerDirPath + "package-certificate.pem");
assertEquals(2, securityManager.getTrustedCertificates().size());
otherNewFile.delete();
@@ -109,7 +115,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestCertIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
byte[] signature = readAllBytes("/cert/2-file-signed-package/dummyPnfv4.cms");
byte[] archive = readAllBytes("/cert/2-file-signed-package/dummyPnfv4.csar");
@@ -117,7 +123,22 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertNotIncludedIntoSignatureButExpected() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestCertIncludedIntoSignatureArtifactStorageManagerIsEnabled()
+ throws IOException, URISyntaxException, SecurityManagerException {
+ prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
+ byte[] fileToUploadBytes = readAllBytes("/cert/2-file-signed-package/2-file-signed-package.zip");
+
+ final var onboardingPackageProcessor = new OnboardingPackageProcessor("2-file-signed-package.zip", fileToUploadBytes,
+ new CnfPackageValidator(),
+ new PersistentStorageArtifactInfo(Path.of("src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip")));
+ final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
+
+ assertTrue(securityManager
+ .verifyPackageSignedData((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo()));
+ }
+
+ @Test
+ void verifySignedDataTestCertNotIncludedIntoSignatureButExpected() throws IOException, URISyntaxException, SecurityManagerException {
Assertions.assertThrows(SecurityManagerException.class, () -> {
prepareCertFiles("/cert/root.cert", cerDirPath + "root.cert");
byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms");
@@ -128,7 +149,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestCertNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms");
byte[] archive = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.csar");
@@ -137,7 +158,22 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertIntermediateNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestCertNotIncludedIntoSignatureArtifactStorageManagerIsEnabled()
+ throws IOException, URISyntaxException, SecurityManagerException {
+ prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
+ byte[] fileToUploadBytes = readAllBytes("/cert/3-file-signed-package/3-file-signed-package.zip");
+
+ final var onboardingPackageProcessor = new OnboardingPackageProcessor("3-file-signed-package.zip", fileToUploadBytes,
+ new CnfPackageValidator(),
+ new PersistentStorageArtifactInfo(Path.of("src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip")));
+ final OnboardPackageInfo onboardPackageInfo = onboardingPackageProcessor.getOnboardPackageInfo().orElse(null);
+
+ assertTrue(securityManager
+ .verifyPackageSignedData((OnboardSignedPackage) onboardPackageInfo.getOriginalOnboardPackage(), onboardPackageInfo.getArtifactInfo()));
+ }
+
+ @Test
+ void verifySignedDataTestCertIntermediateNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException {
prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
prepareCertFiles("/cert/package2.cert", cerDirPath + "signing-ca2.crt");
byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms");
@@ -147,7 +183,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertWrongIntermediate() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestCertWrongIntermediate() throws IOException, URISyntaxException, SecurityManagerException {
Assertions.assertThrows(SecurityManagerException.class, () -> {
prepareCertFiles("/cert/root.cert", cerDirPath + "root.cert");
prepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt");
@@ -160,7 +196,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertIncludedIntoSignatureWithWrongIntermediateInDirectory()
+ void verifySignedDataTestCertIncludedIntoSignatureWithWrongIntermediateInDirectory()
throws IOException, URISyntaxException, SecurityManagerException {
prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
prepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt");
@@ -170,7 +206,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestCertWrongIntermediateInDirectory() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestCertWrongIntermediateInDirectory() throws IOException, URISyntaxException, SecurityManagerException {
prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert");
prepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt");
byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms");
@@ -180,7 +216,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestWrongCertificate() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestWrongCertificate() throws IOException, URISyntaxException, SecurityManagerException {
Assertions.assertThrows(SecurityManagerException.class, () -> {
prepareCertFiles("/cert/root-certificate.pem", cerDirPath + "root-certificate.cert");
byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms");
@@ -192,7 +228,7 @@ public class SecurityManagerTest {
}
@Test
- public void verifySignedDataTestChangedArchive() throws IOException, URISyntaxException, SecurityManagerException {
+ void verifySignedDataTestChangedArchive() throws IOException, URISyntaxException, SecurityManagerException {
Assertions.assertThrows(SecurityManagerException.class, () -> {
prepareCertFiles("/cert/root.cert", cerDirPath + "root.cert");
byte[] signature = readAllBytes("/cert/tampered-signed-package/dummyPnfv4.cms");
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip
new file mode 100644
index 0000000000..be48e8a674
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip
Binary files differ
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip
new file mode 100644
index 0000000000..7f2eacbe10
--- /dev/null
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip
Binary files differ