From 36ff777984fbd728737b264d7aa3933794716519 Mon Sep 17 00:00:00 2001 From: vasraz Date: Thu, 29 Jul 2021 14:41:18 +0100 Subject: Implement 'Signed Large CSAR' support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I33cc381b86c6a10e20d521c0d3dcc76c28344b8f Signed-off-by: Vasyl Razinkov Issue-ID: SDC-3652 Issue-ID: SDC-3653 Signed-off-by: André Schmid --- .../OrchestrationTemplateCSARHandler.java | 10 +- .../csar/validation/CsarSecurityValidator.java | 34 ++-- .../security/SecurityManager.java | 188 +++++++++++++++------ .../csar/validation/CsarSecurityValidatorTest.java | 112 +++++++++--- .../security/SecurityManagerTest.java | 66 ++++++-- .../2-file-signed-package.zip | Bin 0 -> 3154 bytes .../3-file-signed-package.zip | Bin 0 -> 3446 bytes 7 files changed, 312 insertions(+), 98 deletions(-) create mode 100644 openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip create mode 100644 openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip (limited to 'openecomp-be/backend') 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 validatePackageSecurity(final OnboardSignedPackage originalOnboardPackage) { + private Optional 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 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 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 ALLOWED_SIGNATURE_EXTENSIONS = Set.of("cms"); + public static final Set ALLOWED_CERTIFICATE_EXTENSIONS = Set.of("cert", "crt"); private static final String CERTIFICATE_DEFAULT_LOCATION = "cert"; - public static final Set ALLOWED_SIGNATURE_EXTENSIONS = ImmutableSet.of("cms"); - public static final Set ALLOWED_CERTIFICATE_EXTENSIONS = ImmutableSet.of("cert", "crt"); - private Logger logger = LoggerFactory.getLogger(SecurityManager.class); - private Set trustedCertificates = new HashSet<>(); - private Set 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 trustedCertificates = new HashSet<>(); + private Set trustedCertificatesFromPackage = new HashSet<>(); + private File certificateDirectory; + private SecurityManager() { certificateDirectory = this.getcertDirectory(System.getenv("SDC_CERT_DIR")); } @@ -98,21 +110,13 @@ public class SecurityManager { return SecurityManagerInstanceHolder.instance; } - /** - * 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 getTrustedCertificates() throws SecurityManagerException, FileNotFoundException { + public Set 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 signers = signedData.getSignerInfos().getSigners(); - final SignerInformation firstSigner = signers.iterator().next(); - final X509Certificate cert; - Collection 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 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 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 readSignCert(final Collection 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 additionalCerts) - throws GeneralSecurityException, SecurityManagerException { + private PKIXCertPathBuilderResult verifyCertificate(final X509Certificate cert, + final Set 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 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 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 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 Binary files /dev/null and b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/2-file-signed-package/2-file-signed-package.zip 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 Binary files /dev/null and b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/3-file-signed-package/3-file-signed-package.zip differ -- cgit 1.2.3-korg