diff options
55 files changed, 2336 insertions, 80 deletions
diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java index 38f03f12f5..73be4a2100 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilder.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation @@ -25,6 +24,7 @@ import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; import java.util.TreeSet; +import org.apache.commons.lang.StringUtils; /** * Builder for the manifest (.mf) file in a NSD CSAR @@ -45,6 +45,7 @@ public class NsdCsarManifestBuilder { private final MetadataHeader metadataHeader; private final Set<String> sources; private final Set<String> compatibleSpecificationVersions; + private String signature; public NsdCsarManifestBuilder() { metadataHeader = new MetadataHeader(); @@ -122,6 +123,13 @@ public class NsdCsarManifestBuilder { return this; } + public NsdCsarManifestBuilder withSignature(final String signature) { + if (signature != null) { + this.signature = signature.trim(); + } + return this; + } + /** * Builds a string representing the manifest content based on provided values. * @@ -142,7 +150,14 @@ public class NsdCsarManifestBuilder { .append(String.join(",", compatibleSpecificationVersions)).append(NEW_LINE); } final StringBuilder builder = new StringBuilder(); - builder.append(metadataBuilder).append(compatibleSpecificationVersionsBuilder).append(NEW_LINE).append(sourceBuilder); + + builder.append(metadataBuilder) + .append(compatibleSpecificationVersionsBuilder) + .append(NEW_LINE) + .append(sourceBuilder); + if (StringUtils.isNotBlank(signature)) { + builder.append(signature); + } return builder.toString(); } diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/factory/EtsiNfvNsdCsarGeneratorFactory.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/factory/EtsiNfvNsdCsarGeneratorFactory.java index fb08f56ac2..c51dc51854 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/factory/EtsiNfvNsdCsarGeneratorFactory.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/factory/EtsiNfvNsdCsarGeneratorFactory.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation @@ -25,6 +24,7 @@ import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsdCsarGenerat import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.VnfDescriptorGenerator; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.EtsiVersion; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.NsDescriptorConfig; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; @@ -35,20 +35,24 @@ public class EtsiNfvNsdCsarGeneratorFactory { private final NsDescriptorGeneratorFactory nsDescriptorGeneratorFactory; private final ArtifactCassandraDao artifactCassandraDao; private final ObjectProvider<EtsiNfvNsdCsarGeneratorImpl> etsiNfvNsdCsarGeneratorObjectProvider; + private final NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer; public EtsiNfvNsdCsarGeneratorFactory(final VnfDescriptorGenerator vnfDescriptorGenerator, final NsDescriptorGeneratorFactory nsDescriptorGeneratorFactory, final ArtifactCassandraDao artifactCassandraDao, - final ObjectProvider<EtsiNfvNsdCsarGeneratorImpl> etsiNfvNsdCsarGeneratorObjectProvider) { + final ObjectProvider<EtsiNfvNsdCsarGeneratorImpl> etsiNfvNsdCsarGeneratorObjectProvider, + final NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer) { this.vnfDescriptorGenerator = vnfDescriptorGenerator; this.nsDescriptorGeneratorFactory = nsDescriptorGeneratorFactory; this.artifactCassandraDao = artifactCassandraDao; this.etsiNfvNsdCsarGeneratorObjectProvider = etsiNfvNsdCsarGeneratorObjectProvider; + this.nsdCsarEtsiOption2Signer = nsdCsarEtsiOption2Signer; } public EtsiNfvNsdCsarGenerator create(final EtsiVersion version) { final NsDescriptorConfig nsDescriptorConfig = new NsDescriptorConfig(version); return etsiNfvNsdCsarGeneratorObjectProvider - .getObject(nsDescriptorConfig, vnfDescriptorGenerator, nsDescriptorGeneratorFactory, artifactCassandraDao); + .getObject(nsDescriptorConfig, vnfDescriptorGenerator, nsDescriptorGeneratorFactory, artifactCassandraDao + , nsdCsarEtsiOption2Signer); } } diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java index 90359a550d..9a7312b0fb 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGenerator.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation @@ -30,6 +29,7 @@ import org.openecomp.sdc.be.plugins.CsarEntryGenerator; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.factory.EtsiNfvNsdCsarGeneratorFactory; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.EtsiVersion; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +40,9 @@ import org.slf4j.LoggerFactory; public class EtsiNfvNsCsarEntryGenerator implements CsarEntryGenerator { static final String ETSI_NS_COMPONENT_CATEGORY = "ETSI NFV Network Service"; - static final String NSD_FILE_PATH_FORMAT = "Artifacts/%s/%s.csar"; + static final String NSD_FILE_PATH_FORMAT = "Artifacts/%s/%s.%s"; + static final String SIGNED_CSAR_EXTENSION = "zip"; + static final String UNSIGNED_CSAR_EXTENSION = "csar"; static final String ETSI_VERSION_METADATA = "ETSI Version"; private static final Logger LOGGER = LoggerFactory.getLogger(EtsiNfvNsCsarEntryGenerator.class); private final EtsiNfvNsdCsarGeneratorFactory etsiNfvNsdCsarGeneratorFactory; @@ -69,7 +71,8 @@ public class EtsiNfvNsCsarEntryGenerator implements CsarEntryGenerator { ETSI_NS_COMPONENT_CATEGORY); return Collections.emptyMap(); } - final byte[] nsdCsar; + + final NsdCsar nsdCsar; try { final EtsiVersion etsiVersion = getComponentEtsiVersion(component); final EtsiNfvNsdCsarGenerator etsiNfvNsdCsarGenerator = etsiNfvNsdCsarGeneratorFactory.create(etsiVersion); @@ -81,7 +84,8 @@ public class EtsiNfvNsCsarEntryGenerator implements CsarEntryGenerator { LOGGER.error("Could not create NSD CSAR entry for component '{}'. An unexpected exception occurred", component.getName(), e); return Collections.emptyMap(); } - return createEntry(component.getNormalizedName(), nsdCsar); + + return createEntry(nsdCsar); } private EtsiVersion getComponentEtsiVersion(Component component) { @@ -89,10 +93,11 @@ public class EtsiNfvNsCsarEntryGenerator implements CsarEntryGenerator { return EtsiVersion.convertOrNull(etsiVersion); } - private Map<String, byte[]> createEntry(final String csarName, final byte[] nsdCsar) { + private Map<String, byte[]> createEntry(final NsdCsar nsdCsar) { final Map<String, byte[]> entryMap = new HashMap<>(); - final String entryKey = String.format(NSD_FILE_PATH_FORMAT, ETSI_PACKAGE, csarName); - entryMap.put(entryKey, nsdCsar); + final String extension = nsdCsar.isSigned() ? SIGNED_CSAR_EXTENSION : UNSIGNED_CSAR_EXTENSION; + final String entryKey = String.format(NSD_FILE_PATH_FORMAT, ETSI_PACKAGE, nsdCsar.getFileName(), extension); + entryMap.put(entryKey, nsdCsar.getCsarPackage()); return entryMap; } } diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java index 072c4c5a89..1ad6e82481 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGenerator.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation @@ -21,6 +20,7 @@ package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator; import org.openecomp.sdc.be.model.Component; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; /** * Generator for a ETSI NFV NSD CSAR @@ -33,5 +33,5 @@ public interface EtsiNfvNsdCsarGenerator { * @param component the service component * @return the CSAR package content */ - byte[] generateNsdCsar(Component component) throws NsdException; + NsdCsar generateNsdCsar(Component component) throws NsdException; } diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java index e7d30197d7..f9e0970ba9 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImpl.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation @@ -23,10 +22,13 @@ import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ETSI_PACKAGE; import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ONBOARDED_PACKAGE; import fj.data.Either; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -36,9 +38,11 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.commons.lang.StringUtils; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao; import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus; import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields; @@ -53,7 +57,9 @@ import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.EtsiVersion; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.NsDescriptorConfig; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.NsDescriptorVersionComparator; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer; import org.openecomp.sdc.be.resources.data.DAOArtifactData; import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil; import org.slf4j.Logger; @@ -74,6 +80,8 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { private static final String MANIFEST_EXT = "mf"; private static final String SLASH = "/"; private static final String DOT = "."; + private static final String SIGNATURE_EXTENSION = ".sig.cms"; + private static final String CSAR_EXTENSION = ".csar"; private static final String DOT_YAML = DOT + "yaml"; private static final String DEFINITION = "Definitions"; private static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta"; @@ -81,18 +89,21 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { private final NsDescriptorGeneratorFactory nsDescriptorGeneratorFactory; private final ArtifactCassandraDao artifactCassandraDao; private final NsDescriptorConfig nsDescriptorConfig; + private final NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer; public EtsiNfvNsdCsarGeneratorImpl(final NsDescriptorConfig nsDescriptorConfig, final VnfDescriptorGenerator vnfDescriptorGenerator, final NsDescriptorGeneratorFactory nsDescriptorGeneratorFactory, - final ArtifactCassandraDao artifactCassandraDao) { + final ArtifactCassandraDao artifactCassandraDao, + final NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer) { this.nsDescriptorConfig = nsDescriptorConfig; this.vnfDescriptorGenerator = vnfDescriptorGenerator; this.nsDescriptorGeneratorFactory = nsDescriptorGeneratorFactory; this.artifactCassandraDao = artifactCassandraDao; + this.nsdCsarEtsiOption2Signer = nsdCsarEtsiOption2Signer; } @Override - public byte[] generateNsdCsar(final Component component) throws NsdException { + public NsdCsar generateNsdCsar(final Component component) throws NsdException { if (component == null) { throw new NsdException("Could not generate the NSD CSAR, invalid component argument"); } @@ -101,23 +112,43 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { final String componentName = component.getName(); try { LOGGER.debug("Starting NSD CSAR generation for component '{}'", componentName); - final Map<String, byte[]> nsdCsarFiles = new HashMap<>(); - final List<VnfDescriptor> vnfDescriptorList = generateVnfPackages(component); - vnfDescriptorList.forEach(vnfPackage -> nsdCsarFiles.putAll(vnfPackage.getDefinitionFiles())); final String nsdFileName = getNsdFileName(component); + final NsdCsar nsdCsar = new NsdCsar(nsdFileName); + + final List<VnfDescriptor> vnfDescriptorList = generateVnfPackages(component); + vnfDescriptorList.forEach(vnfPackage -> nsdCsar.addAllFiles(vnfPackage.getDefinitionFiles())); + final EtsiVersion etsiVersion = nsDescriptorConfig.getNsVersion(); final Nsd nsd = generateNsd(component, vnfDescriptorList); - nsdCsarFiles.put(getNsdPath(nsdFileName), nsd.getContents()); - nsdCsarFiles.put(TOSCA_META_PATH, buildToscaMetaContent(nsdFileName).getBytes()); - addEtsiSolNsdTypes(etsiVersion, nsdCsarFiles); + + nsdCsar.addFile(getNsdPath(nsdFileName), nsd.getContents()); + nsdCsar.addFile(TOSCA_META_PATH, buildToscaMetaContent(nsdFileName).getBytes()); + + nsdCsar.addAllFiles(createEtsiSolNsdTypeEntries(etsiVersion)); for (final String referencedFile : nsd.getArtifactReferences()) { getReferencedArtifact(component, referencedFile) - .ifPresent(artifactDefinition -> nsdCsarFiles.put(referencedFile, artifactDefinition.getPayloadData())); + .ifPresent(artifactDefinition -> nsdCsar.addFile(referencedFile, artifactDefinition.getPayloadData()) + ); + } + final boolean isCertificateConfigured = nsdCsarEtsiOption2Signer.isCertificateConfigured(); + final String manifestPath = getManifestPath(nsdFileName); + final NsdCsarManifestBuilder manifestBuilder = + createManifestBuilder(nsd, etsiVersion, manifestPath, nsdCsar.getFileMap().keySet(), isCertificateConfigured); + + nsdCsar.addManifest(manifestBuilder); + + if (isCertificateConfigured) { + nsdCsarEtsiOption2Signer.signArtifacts(nsdCsar); + } + + byte[] package1 = buildCsarPackage(nsdCsar.getFileMap()); + if (isCertificateConfigured) { + package1 = buildZipWithCsarAndSignature(nsdCsar.getFileName(), package1); + nsdCsar.setSigned(true); } - nsdCsarFiles.put(getManifestPath(nsdFileName), getManifestFileContent(nsd, etsiVersion, nsdCsarFiles.keySet()).getBytes()); - final byte[] csar = buildCsarPackage(nsdCsarFiles); + nsdCsar.setCsarPackage(package1); LOGGER.debug("Successfully generated NSD CSAR package"); - return csar; + return nsdCsar; } catch (final Exception exception) { throw new NsdException("Could not generate the NSD CSAR file", exception); } @@ -205,29 +236,41 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { }).findFirst(); } - private void addEtsiSolNsdTypes(final EtsiVersion etsiVersion, final Map<String, byte[]> nsdCsarFileMap) { + private Map<String, byte[]> createEtsiSolNsdTypeEntries(final EtsiVersion etsiVersion) { final EtsiVersion currentVersion = etsiVersion == null ? EtsiVersion.getDefaultVersion() : etsiVersion; + final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { final Resource[] resources = resolver.getResources(String.format("classpath:etsi-nfv-types/%s/*.*", currentVersion.getVersion())); if (resources.length > 0) { + final Map<String, byte[]> entryMap = new HashMap<>(); for (final Resource resource : resources) { - addToCsarFileMap(resource, nsdCsarFileMap); + readResource(resource).ifPresent(resourceBytes -> { + final String entryName = createDefinitionEntryName(resource.getFilename()); + entryMap.put(entryName, resourceBytes); + }); } + return entryMap; } } catch (final IOException e) { LOGGER.error("Could not find types files for the version '{}'", currentVersion.getVersion(), e); } + + return Collections.emptyMap(); + } + + private String createDefinitionEntryName(final String fileName) { + return DEFINITION + "/" + fileName; } - private void addToCsarFileMap(final Resource resource, final Map<String, byte[]> nsdCsarFileMap) { + private Optional<byte[]> readResource(final Resource resource) { try { - nsdCsarFileMap.put(DEFINITION + "/" + resource.getFilename(), - IOUtils.toByteArray(resource.getInputStream())); + return Optional.ofNullable(IOUtils.toByteArray(resource.getInputStream())); } catch (final IOException exception) { LOGGER.error("Error adding '{}' to NSD CSAR", resource.getFilename(), exception); } + return Optional.empty(); } private Nsd generateNsd(final Component component, @@ -294,18 +337,24 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { return toscaMetadata; } - private String getManifestFileContent(final Nsd nsd, - final EtsiVersion nsdVersion, - final Set<String> files) { + private NsdCsarManifestBuilder createManifestBuilder(final Nsd nsd, final EtsiVersion nsdVersion, + final String manifestFilePath, final Set<String> filePath, + final boolean addSignatureFiles) { LOGGER.debug("Creating NS manifest file content"); + final Set<String> filesToAdd = new HashSet<>(filePath); + if (addSignatureFiles) { + filePath.forEach(file -> filesToAdd.add(file + SIGNATURE_EXTENSION)); + } + filesToAdd.add(manifestFilePath); + final NsdCsarManifestBuilder nsdCsarManifestBuilder = new NsdCsarManifestBuilder(); nsdCsarManifestBuilder.withDesigner(nsd.getDesigner()) .withInvariantId(nsd.getInvariantId()) .withName(nsd.getName()) .withNowReleaseDateTime() .withFileStructureVersion(nsd.getVersion()) - .withSources(files); + .withSources(filesToAdd); final NsDescriptorVersionComparator nsdVersionComparator = new NsDescriptorVersionComparator(); @@ -313,10 +362,10 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { nsdCsarManifestBuilder.withCompatibleSpecificationVersion(nsdVersion.getVersion()); } - final String manifest = nsdCsarManifestBuilder.build(); - LOGGER.debug("Successfully created NS CSAR manifest file content:\n {}", manifest); - return manifest; - + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Successfully created NS CSAR manifest file content:\n {}", nsdCsarManifestBuilder.build()); + } + return nsdCsarManifestBuilder; } private String getManifestPath(final String nsdFileName) { @@ -358,6 +407,37 @@ public class EtsiNfvNsdCsarGeneratorImpl implements EtsiNfvNsdCsarGenerator { } } + private byte[] buildZipWithCsarAndSignature(final String csarFileName, final byte[] csarPackageBytes) throws NsdException { + final byte[] signature; + try { + signature = nsdCsarEtsiOption2Signer.sign(csarPackageBytes); + } catch (final Exception e) { + throw new NsdException(String.format("Could not sign the CSAR '%s'", csarFileName), e); + } + final Optional<CertificateInfo> certificateInfoOpt = nsdCsarEtsiOption2Signer.getSigningCertificate(); + if (certificateInfoOpt.isEmpty()) { + throw new NsdException(String.format("Could not sign the CSAR '%s'. No certificate configured.", csarFileName)); + } + final CertificateInfo certificateInfo = certificateInfoOpt.get(); + try (final ByteArrayOutputStream out = new ByteArrayOutputStream(); + final ZipOutputStream zip = new ZipOutputStream(out)) { + zip.putNextEntry(new ZipEntry(csarFileName + CSAR_EXTENSION)); + zip.write(csarPackageBytes); + zip.putNextEntry(new ZipEntry(csarFileName + SIGNATURE_EXTENSION)); + zip.write(signature); + final File certificateFile = certificateInfo.getCertificateFile(); + zip.putNextEntry(new ZipEntry(csarFileName + "." + FilenameUtils.getExtension(certificateFile.getName()))); + zip.write(Files.readAllBytes(certificateFile.toPath())); + zip.flush(); + zip.finish(); + LOGGER.debug("NSD signed CSAR zip file was successfully built"); + + return out.toByteArray(); + } catch (final IOException e) { + throw new NsdException("Could not build the NSD signed CSAR zip file", e); + } + } + } diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/NsdCsar.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/NsdCsar.java new file mode 100644 index 0000000000..733604529a --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/NsdCsar.java @@ -0,0 +1,110 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.Setter; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdCsarManifestBuilder; + +/** + * Represents a NSD CSAR package + */ +public class NsdCsar { + + private static final String MANIFEST_EXTENSION = "mf"; + private static final String YAML_EXTENSION = "yaml"; + private static final String DOT = "."; + private static final String SLASH = "/"; + private static final String DEFINITIONS = "Definitions"; + + private final Map<String, byte[]> fileMap = new HashMap<>(); + @Getter + private final String fileName; + @Getter + @Setter + private byte[] csarPackage; + @Getter + @Setter + private boolean isSigned; + @Getter + private NsdCsarManifestBuilder manifestBuilder; + + public NsdCsar(final String fileName) { + this.fileName = fileName; + manifestBuilder = new NsdCsarManifestBuilder(); + } + + public void addFile(final String filePath, final byte[] fileBytes) { + fileMap.put(filePath, fileBytes); + } + + public byte[] getFile(final String filePath) { + return fileMap.get(filePath); + } + + public byte[] getManifest() { + return fileMap.get(getManifestPath()); + } + + public byte[] getMainDefinition() { + return fileMap.get(getMainDefinitionPath()); + } + + public Map<String, byte[]> getFileMap() { + return new HashMap<>(fileMap); + } + + public String getManifestPath() { + return fileName + DOT + MANIFEST_EXTENSION; + } + + public boolean isManifest(final String filePath) { + return getManifestPath().equals(filePath); + } + + public String getMainDefinitionPath() { + return DEFINITIONS + SLASH + fileName + YAML_EXTENSION; + } + + public void addAllFiles(final Map<String, byte[]> definitionFiles) { + fileMap.putAll(definitionFiles); + } + + /** + * Sets a manifest builder and build it, adding its content to the to the CSAR files. Ignores {@code null} manifest builders. + * + * @param manifestBuilder the manifest builder + */ + public void addManifest(final NsdCsarManifestBuilder manifestBuilder) { + if (manifestBuilder == null) { + return; + } + this.manifestBuilder = manifestBuilder; + final String manifestContent = manifestBuilder.build(); + addFile(getManifestPath(), manifestContent.getBytes(StandardCharsets.UTF_8)); + } + + public boolean isEmpty() { + return fileMap.isEmpty(); + } +} diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/NsdCsarEtsiOption2Signer.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/NsdCsarEtsiOption2Signer.java new file mode 100644 index 0000000000..abba32ad8d --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/NsdCsarEtsiOption2Signer.java @@ -0,0 +1,151 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security; + +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.cert.Certificate; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.stream.Collectors; +import org.openecomp.sdc.be.csar.security.api.CertificateManager; +import org.openecomp.sdc.be.csar.security.api.CmsContentSigner; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; +import org.openecomp.sdc.be.csar.security.exception.CmsSignatureException; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdCsarManifestBuilder; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureException; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureExceptionSupplier; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +/** + * Handles NSD CSAR file and package signature, following the ETSI SOL007 v3.3.1, section 5.1, signing option 2. + * + * @see <a href="https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/007/03.03.01_60/gs_NFV-SOL007v030301p.pdf">ETSI SOL007 v3.3.1 documentation</a> + */ +@Component +public class NsdCsarEtsiOption2Signer { + + public static final String SDC_NSD_CERT_NAME = "SDC_NSD_CERT_NAME"; + public static final String SIGNATURE_EXTENSION = ".sig.cms"; + + private final CertificateManager certificateManager; + private final CmsContentSigner cmsContentSigner; + private final Environment environment; + + public NsdCsarEtsiOption2Signer(final CertificateManager certificateManager, + final CmsContentSigner cmsContentSigner, + final Environment environment) { + this.certificateManager = certificateManager; + this.cmsContentSigner = cmsContentSigner; + this.environment = environment; + } + + /** + * Sign each NSD CSAR artifact (files), generating a cms file for each. The manifest, though, have its signature added in its body instead of a separate + * CMS file. Modifies the given NSD CSAR by adding the file signatures and the modified manifest. + * + * @param nsdCsar the NSD CSAR + * @throws NsdSignatureException when there was a problem while creating a file signature + */ + public void signArtifacts(final NsdCsar nsdCsar) throws NsdSignatureException { + if (nsdCsar == null) { + return; + } + //ignore the manifest, the signature of the manifest goes inside the manifest itself ETSI 3.3.1 section 5.3 + final Map<String, byte[]> fileMap = nsdCsar.getFileMap().entrySet().stream() + .filter(entry -> !nsdCsar.isManifest(entry.getKey())) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + + for (final Entry<String, byte[]> fileEntry : fileMap.entrySet()) { + final byte[] signatureBytes = sign(fileEntry.getValue()); + final String filePath = fileEntry.getKey(); + nsdCsar.addFile(filePath + SIGNATURE_EXTENSION, signatureBytes); + } + + signManifest(nsdCsar); + } + + private void signManifest(final NsdCsar nsdCsar) throws NsdSignatureException { + final Optional<Entry<String, byte[]>> manifestEntryOpt = nsdCsar.getFileMap().entrySet().stream() + .filter(entry -> nsdCsar.isManifest(entry.getKey())).findFirst(); + if (manifestEntryOpt.isEmpty()) { + return; + } + final CertificateInfo certificateInfo = getValidCertificate(); + + final Entry<String, byte[]> manifestEntry = manifestEntryOpt.get(); + final String pemSignature = createFileSignature(certificateInfo.getCertificate(), + certificateInfo.getPrivateKey(), manifestEntry.getValue()); + final NsdCsarManifestBuilder manifestBuilder = nsdCsar.getManifestBuilder(); + manifestBuilder.withSignature(pemSignature); + nsdCsar.addManifest(manifestBuilder); + } + + /** + * Sign a file, creating the PEM format signature and returning its bytes. + * + * @param fileBytes the file to sign + * @return the bytes of the PEM format signature created from the given file and the NSD certificate + * @throws NsdSignatureException when it was not possible to retrieve the NSD certificate + * @throws NsdSignatureException when the NSD certificate is invalid + * @throws NsdSignatureException it was not possible to sign the file + */ + public byte[] sign(final byte[] fileBytes) throws NsdSignatureException { + final CertificateInfo certificateInfo = getValidCertificate(); + final String pemSignature = + createFileSignature(certificateInfo.getCertificate(), certificateInfo.getPrivateKey(), fileBytes); + return pemSignature.getBytes(StandardCharsets.UTF_8); + } + + public Optional<CertificateInfo> getSigningCertificate() { + final String sdcNsdCertName = environment.getProperty(SDC_NSD_CERT_NAME); + return certificateManager.getCertificate(sdcNsdCertName); + } + + public boolean isCertificateConfigured() { + return getSigningCertificate().isPresent(); + } + + private CertificateInfo getValidCertificate() throws NsdSignatureException { + final Optional<CertificateInfo> certificateInfoOpt = getSigningCertificate(); + if (certificateInfoOpt.isEmpty()) { + throw NsdSignatureExceptionSupplier.certificateNotConfigured(); + } + final CertificateInfo certificateInfo = certificateInfoOpt.get(); + if (!certificateInfo.isValid()) { + throw NsdSignatureExceptionSupplier.invalidCertificate(certificateInfo.getName()); + } + + return certificateInfo; + } + + private String createFileSignature(final Certificate certificate, final Key privateKey, + final byte[] fileBytes) throws NsdSignatureException { + try { + final byte[] dataSignature = cmsContentSigner.signData(fileBytes, certificate, privateKey); + return cmsContentSigner.formatToPemSignature(dataSignature); + } catch (final CmsSignatureException e) { + throw NsdSignatureExceptionSupplier.unableToCreateSignature(e); + } + } +} diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/exception/NsdSignatureException.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/exception/NsdSignatureException.java new file mode 100644 index 0000000000..1591a5e294 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/exception/NsdSignatureException.java @@ -0,0 +1,31 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception; + +public class NsdSignatureException extends Exception { + + public NsdSignatureException(final String message) { + super(message); + } + + public NsdSignatureException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/exception/NsdSignatureExceptionSupplier.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/exception/NsdSignatureExceptionSupplier.java new file mode 100644 index 0000000000..696a8d8626 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/main/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/exception/NsdSignatureExceptionSupplier.java @@ -0,0 +1,39 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception; + +public final class NsdSignatureExceptionSupplier { + + private NsdSignatureExceptionSupplier() { + } + + public static NsdSignatureException invalidCertificate(final String certificateName) { + return new NsdSignatureException(String.format("The certificate '%s' is invalid", certificateName)); + } + + public static NsdSignatureException certificateNotConfigured() { + return new NsdSignatureException("No certificate configured"); + } + + public static NsdSignatureException unableToCreateSignature(final Exception e) { + return new NsdSignatureException("Could create file signature", e); + } + +}
\ No newline at end of file diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilderTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilderTest.java index 1ff3a6d52b..caca932929 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilderTest.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/builder/NsdCsarManifestBuilderTest.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation @@ -43,6 +42,10 @@ class NsdCsarManifestBuilderTest { nsdCsarManifestBuilder.withFileStructureVersion("fileStructureVersion"); nsdCsarManifestBuilder.withCompatibleSpecificationVersion("1.0.0"); nsdCsarManifestBuilder.withCompatibleSpecificationVersion("1.0.1"); + final String signature = "-----BEGIN CMS-----\n" + + "12d08j19d981928129dj129j1\n" + + "-----END CMS-----"; + nsdCsarManifestBuilder.withSignature(signature); final List<String> sourceList = new ArrayList<>(); final String source1 = "Definitions/aSource1.yaml"; sourceList.add(source1); @@ -53,9 +56,16 @@ class NsdCsarManifestBuilderTest { assertSource(manifest, source1); assertSource(manifest, source2); assertCompatibleSpecificationVersions(manifest, "1.0.0,1.0.1"); - final String expectedManifest = "metadata: \n" + "nsd_designer: designer\n" + "nsd_invariant_id: invariantId\n" + "nsd_name: name\n" - + "nsd_file_structure_version: fileStructureVersion\n" + "compatible_specification_versions: 1.0.0,1.0.1\n" + "\n" - + "Source: Definitions/aSource1.yaml\n" + "Source: Definitions/aSource2.yaml\n" + ""; + final String expectedManifest = "metadata: \n" + + "nsd_designer: designer\n" + + "nsd_invariant_id: invariantId\n" + + "nsd_name: name\n" + + "nsd_file_structure_version: fileStructureVersion\n" + + "compatible_specification_versions: 1.0.0,1.0.1\n" + + "\n" + + "Source: Definitions/aSource1.yaml\n" + + "Source: Definitions/aSource2.yaml\n" + + signature; assertEquals(expectedManifest, manifest); } diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java index 8ebd1df190..837179d50d 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsCsarEntryGeneratorTest.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation @@ -27,6 +26,7 @@ import static org.mockito.Mockito.when; import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.ETSI_NS_COMPONENT_CATEGORY; import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.ETSI_VERSION_METADATA; import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.NSD_FILE_PATH_FORMAT; +import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.UNSIGNED_CSAR_EXTENSION; import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ETSI_PACKAGE; import java.util.ArrayList; @@ -44,6 +44,7 @@ import org.openecomp.sdc.be.model.category.CategoryDefinition; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.exception.NsdException; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.factory.EtsiNfvNsdCsarGeneratorFactory; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.EtsiVersion; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; class EtsiNfvNsCsarEntryGeneratorTest { @@ -68,12 +69,14 @@ class EtsiNfvNsCsarEntryGeneratorTest { @Test void successfullyEntryGenerationTest() throws NsdException { mockServiceComponent(); - final byte[] expectedNsdCsar = new byte[5]; - when(etsiNfvNsdCsarGenerator.generateNsdCsar(service)).thenReturn(expectedNsdCsar); + final NsdCsar nsdCsar = new NsdCsar(SERVICE_NORMALIZED_NAME); + nsdCsar.setCsarPackage(new byte[5]); + when(etsiNfvNsdCsarGenerator.generateNsdCsar(service)).thenReturn(nsdCsar); final Map<String, byte[]> entryMap = etsiNfvNsCsarEntryGenerator.generateCsarEntries(service); assertThat("Csar Entries should contain only one entry", entryMap.size(), is(1)); assertThat("Csar Entries should contain the expected entry", entryMap, - hasEntry(String.format(NSD_FILE_PATH_FORMAT, ETSI_PACKAGE, SERVICE_NORMALIZED_NAME), expectedNsdCsar)); + hasEntry(String.format(NSD_FILE_PATH_FORMAT, ETSI_PACKAGE, SERVICE_NORMALIZED_NAME, UNSIGNED_CSAR_EXTENSION), + nsdCsar.getCsarPackage())); } @Test diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java index b498c45d1f..b1b8157534 100644 --- a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/generator/EtsiNfvNsdCsarGeneratorImplTest.java @@ -1,4 +1,3 @@ - /* * ============LICENSE_START======================================================= * Copyright (C) 2020 Nordix Foundation @@ -23,12 +22,16 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.EtsiNfvNsCsarEntryGenerator.ETSI_NS_COMPONENT_CATEGORY; import static org.openecomp.sdc.common.api.ArtifactTypeEnum.ONBOARDED_PACKAGE; import fj.data.Either; +import java.io.File; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -39,6 +42,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.openecomp.sdc.be.csar.security.model.CertificateInfoImpl; import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao; import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields; @@ -52,7 +56,10 @@ import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.factory.NsDescriptorGeneratorFa import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.EtsiVersion; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.generator.config.NsDescriptorConfig; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.Nsd; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.VnfDescriptor; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureException; import org.openecomp.sdc.be.resources.data.DAOArtifactData; class EtsiNfvNsdCsarGeneratorImplTest { @@ -67,6 +74,8 @@ class EtsiNfvNsdCsarGeneratorImplTest { @Mock private ArtifactCassandraDao artifactCassandraDao; @Mock + private NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer; + @Mock private Service service; private EtsiNfvNsdCsarGeneratorImpl etsiNfvNsdCsarGenerator; @@ -74,8 +83,8 @@ class EtsiNfvNsdCsarGeneratorImplTest { void setUp() { MockitoAnnotations.initMocks(this); final EtsiVersion version2_5_1 = EtsiVersion.VERSION_2_5_1; - etsiNfvNsdCsarGenerator = new EtsiNfvNsdCsarGeneratorImpl(new NsDescriptorConfig(version2_5_1), vnfDescriptorGenerator, - nsDescriptorGeneratorFactory, artifactCassandraDao); + etsiNfvNsdCsarGenerator = new EtsiNfvNsdCsarGeneratorImpl(new NsDescriptorConfig(version2_5_1), + vnfDescriptorGenerator, nsDescriptorGeneratorFactory, artifactCassandraDao, nsdCsarEtsiOption2Signer); when(nsDescriptorGeneratorFactory.create()).thenReturn(nsDescriptorGeneratorImpl); } @@ -83,8 +92,29 @@ class EtsiNfvNsdCsarGeneratorImplTest { void generateNsdCsarSuccessfulTest() throws VnfDescriptorException, NsdException { mockServiceComponent(); mockServiceComponentArtifacts(); - final byte[] nsdCsar = etsiNfvNsdCsarGenerator.generateNsdCsar(service); - assertThat("", nsdCsar, is(notNullValue())); + final NsdCsar nsdCsar = etsiNfvNsdCsarGenerator.generateNsdCsar(service); + assertThat("The NSD CSAR should not be null", nsdCsar, is(notNullValue())); + assertThat("The NSD CSAR should not be signed", nsdCsar.isSigned(), is(false)); + assertThat("The NSD CSAR content should not be null", nsdCsar.getCsarPackage(), is(notNullValue())); + } + + @Test + void generateSignedNsdCsarSuccessfulTest() throws VnfDescriptorException, NsdException, NsdSignatureException { + mockServiceComponent(); + mockServiceComponentArtifacts(); + when(nsdCsarEtsiOption2Signer.isCertificateConfigured()).thenReturn(true); + final String path = getClass().getClassLoader().getResource("aFile.txt").getPath(); + System.out.println(path); + final CertificateInfoImpl certificateInfo = new CertificateInfoImpl(new File(path), null); + when(nsdCsarEtsiOption2Signer.getSigningCertificate()).thenReturn(Optional.of(certificateInfo)); + when(nsdCsarEtsiOption2Signer.sign(any(byte[].class))).thenReturn("signedCsar".getBytes(StandardCharsets.UTF_8)); + final NsdCsar nsdCsar = etsiNfvNsdCsarGenerator.generateNsdCsar(service); + verify(nsdCsarEtsiOption2Signer).signArtifacts(any(NsdCsar.class)); + assertThat("The NSD CSAR should not be null", nsdCsar, is(notNullValue())); + assertThat("The NSD CSAR should be signed", nsdCsar.isSigned(), is(true)); + assertThat("The NSD CSAR content should not be null", nsdCsar.getCsarPackage(), is(notNullValue())); + assertThat("The NSD CSAR name should be as expected", nsdCsar.getFileName(), is(SERVICE_NORMALIZED_NAME)); + assertThat("The NSD CSAR name should be as expected", nsdCsar.isEmpty(), is(false)); } @Test() @@ -103,6 +133,7 @@ class EtsiNfvNsdCsarGeneratorImplTest { final Nsd nsd = new Nsd(); when(vnfDescriptorGenerator.generate(componentInstance1Name, instanceArtifact1)).thenReturn(Optional.of(vnfDescriptor1)); when(nsDescriptorGeneratorImpl.generate(service, vnfDescriptorList)).thenReturn(Optional.of(nsd)); + final List<CategoryDefinition> categoryDefinitionList = new ArrayList<>(); final CategoryDefinition nsComponentCategoryDefinition = new CategoryDefinition(); nsComponentCategoryDefinition.setName(ETSI_NS_COMPONENT_CATEGORY); diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/NsdCsarTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/NsdCsarTest.java new file mode 100644 index 0000000000..061a52a2dc --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/model/NsdCsarTest.java @@ -0,0 +1,140 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.builder.NsdCsarManifestBuilder; + +class NsdCsarTest { + + @Test + void testAddFile() { + final NsdCsar nsdCsar = new NsdCsar(""); + assertTrue(nsdCsar.getFileMap().isEmpty()); + final String aFilePath = "aFile"; + final byte[] fileContent = aFilePath.getBytes(StandardCharsets.UTF_8); + nsdCsar.addFile(aFilePath, fileContent); + assertEquals(1, nsdCsar.getFileMap().size()); + assertEquals(fileContent, nsdCsar.getFile(aFilePath)); + } + + @Test + void testGetFile() { + final NsdCsar nsdCsar = new NsdCsar(""); + final String aFilePath = "aFile"; + assertNull(nsdCsar.getFile(aFilePath)); + final byte[] fileContent = aFilePath.getBytes(StandardCharsets.UTF_8); + nsdCsar.addFile(aFilePath, fileContent); + assertEquals(1, nsdCsar.getFileMap().size()); + assertEquals(fileContent, nsdCsar.getFile(aFilePath)); + } + + @Test + void testIsManifest() { + final NsdCsar nsdCsar = new NsdCsar(""); + assertTrue(nsdCsar.isManifest(nsdCsar.getManifestPath())); + assertFalse(nsdCsar.isManifest("")); + } + + @Test + void testGetManifest() { + final NsdCsar nsdCsar = new NsdCsar(""); + assertNull(nsdCsar.getManifest()); + final byte[] expectedManifest = "".getBytes(StandardCharsets.UTF_8); + nsdCsar.addFile(nsdCsar.getManifestPath(), expectedManifest); + final byte[] actualManifest = nsdCsar.getManifest(); + assertNotNull(actualManifest); + assertEquals(actualManifest, expectedManifest); + } + + @Test + void testGetMainDefinition() { + final String csarFileName = "csarFileName"; + final NsdCsar nsdCsar = new NsdCsar(csarFileName); + assertNull(nsdCsar.getMainDefinition()); + assertTrue(nsdCsar.getMainDefinitionPath().contains(csarFileName)); + final byte[] expectedMainDefinition = "".getBytes(StandardCharsets.UTF_8); + nsdCsar.addFile(nsdCsar.getMainDefinitionPath(), expectedMainDefinition); + final byte[] actualMainDefinition = nsdCsar.getMainDefinition(); + assertNotNull(actualMainDefinition); + assertEquals(actualMainDefinition, expectedMainDefinition); + } + + @Test + void testFileMapEncapsulation() { + final NsdCsar nsdCsar = new NsdCsar(""); + final Map<String, byte[]> fileMap = nsdCsar.getFileMap(); + fileMap.put("", new byte[]{}); + assertTrue(nsdCsar.getFileMap().isEmpty()); + } + + @Test + void addAllFiles() { + final NsdCsar nsdCsar = new NsdCsar(""); + assertTrue(nsdCsar.isEmpty()); + Map<String, byte[]> fileMap = new HashMap(); + fileMap.put("1", new byte[]{}); + fileMap.put("2", new byte[]{}); + fileMap.put("3", new byte[]{}); + fileMap.put("4", new byte[]{}); + nsdCsar.addAllFiles(fileMap); + assertFalse(nsdCsar.isEmpty()); + assertEquals(nsdCsar.getFileMap().size(), fileMap.size()); + } + + @Test + void testIfStartsEmpty() { + final NsdCsar nsdCsar = new NsdCsar("test"); + assertTrue(nsdCsar.getFileMap().isEmpty(), "Csar should starts empty"); + assertTrue(nsdCsar.isEmpty(), "Csar should starts empty"); + } + + @Test + void testIsEmpty() { + final NsdCsar nsdCsar = new NsdCsar("test"); + assertTrue(nsdCsar.isEmpty()); + nsdCsar.addFile("", new byte[]{}); + assertFalse(nsdCsar.isEmpty()); + } + + @Test + void testAddManifest() { + final NsdCsar nsdCsar = new NsdCsar("test"); + nsdCsar.addManifest(null); + assertTrue(nsdCsar.isEmpty()); + final NsdCsarManifestBuilder manifestBuilder = new NsdCsarManifestBuilder(); + nsdCsar.addManifest(manifestBuilder); + assertFalse(nsdCsar.isEmpty()); + assertEquals(1, nsdCsar.getFileMap().size()); + final byte[] expectedManifestContent = manifestBuilder.build().getBytes(StandardCharsets.UTF_8); + assertThat("Manifest content should be the same", nsdCsar.getManifest(), is(expectedManifestContent)); + } +}
\ No newline at end of file diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/NsdCsarEtsiOption2SignerTest.java b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/NsdCsarEtsiOption2SignerTest.java new file mode 100644 index 0000000000..e6e89574f9 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/java/org/openecomp/sdc/be/plugins/etsi/nfv/nsd/security/NsdCsarEtsiOption2SignerTest.java @@ -0,0 +1,183 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer.SDC_NSD_CERT_NAME; +import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.NsdCsarEtsiOption2Signer.SIGNATURE_EXTENSION; +import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureExceptionSupplier.certificateNotConfigured; +import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureExceptionSupplier.invalidCertificate; +import static org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureExceptionSupplier.unableToCreateSignature; + +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openecomp.sdc.be.csar.security.api.CertificateManager; +import org.openecomp.sdc.be.csar.security.api.CmsContentSigner; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; +import org.openecomp.sdc.be.csar.security.exception.CmsSignatureException; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.model.NsdCsar; +import org.openecomp.sdc.be.plugins.etsi.nfv.nsd.security.exception.NsdSignatureException; +import org.springframework.core.env.Environment; + +class NsdCsarEtsiOption2SignerTest { + + private static final String CERT_NAME = "nsdCert"; + + @Mock + private CertificateManager certificateManager; + @Mock + private CmsContentSigner cmsContentSigner; + @Mock + private Environment environment; + @Mock + private CertificateInfo certificateInfo; + @InjectMocks + private NsdCsarEtsiOption2Signer nsdCsarEtsiOption2Signer; + + @BeforeEach + void setUp() { + MockitoAnnotations.initMocks(this); + when(environment.getProperty(SDC_NSD_CERT_NAME)).thenReturn(CERT_NAME); + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.of(certificateInfo)); + when(certificateInfo.isValid()).thenReturn(true); + } + + @Test + void signNsdTest() throws NsdSignatureException, CmsSignatureException { + final NsdCsar nsdCsar = new NsdCsar(""); + nsdCsar.addFile("aFile", "aFile".getBytes(StandardCharsets.UTF_8)); + final byte[] aFileSigned = "aFileSigned".getBytes(StandardCharsets.UTF_8); + when(cmsContentSigner.signData(eq("aFile".getBytes(StandardCharsets.UTF_8)), any(), any())).thenReturn( + aFileSigned); + final String aFileSignedPemString = "aFileSignedPemString"; + when(cmsContentSigner.formatToPemSignature(aFileSigned)).thenReturn(aFileSignedPemString); + nsdCsarEtsiOption2Signer.signArtifacts(nsdCsar); + assertThat("The NSD CSAR should contain the original file and its signature", + nsdCsar.getFileMap().keySet(), hasSize(2)); + assertThat("The signed file should be as expected", + nsdCsar.getFile("aFile" + SIGNATURE_EXTENSION), is(aFileSignedPemString.getBytes(StandardCharsets.UTF_8))); + } + + @Test + void dontCreateNsdManifestSignatureFileTest() throws NsdSignatureException { + final NsdCsar nsdCsar = new NsdCsar("nsdCsar"); + nsdCsar.addFile(nsdCsar.getManifestPath(), "manifest".getBytes(StandardCharsets.UTF_8)); + nsdCsarEtsiOption2Signer.signArtifacts(nsdCsar); + assertThat("The NSD CSAR should contain only the original file", + nsdCsar.getFileMap().keySet(), hasSize(1)); + assertThat("The NSD CSAR should not contain the manifest signature file", + nsdCsar.getFile(nsdCsar.getManifestPath() + SIGNATURE_EXTENSION), is(nullValue())); + } + + @Test + void signEmptyNsdTest() throws NsdSignatureException { + final NsdCsar nsdCsar = new NsdCsar(""); + nsdCsarEtsiOption2Signer.signArtifacts(nsdCsar); + assertThat("The NSD CSAR should continue empty", nsdCsar.isEmpty(), is(true)); + } + + @Test + void signNsdNoCertificateTest() { + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.empty()); + final NsdCsar nsdCsar = new NsdCsar(""); + nsdCsar.addFile("anyFile", "anyFile".getBytes()); + final NsdSignatureException actualException = assertThrows(NsdSignatureException.class, + () -> nsdCsarEtsiOption2Signer.signArtifacts(nsdCsar)); + assertThat(actualException.getMessage(), is(certificateNotConfigured().getMessage())); + } + + @Test + void signWholeNoCertificateTest() { + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.empty()); + final NsdSignatureException actualException = + Assertions.assertThrows(NsdSignatureException.class, + () -> nsdCsarEtsiOption2Signer.sign(new byte[]{})); + assertThat(actualException.getMessage(), is(certificateNotConfigured().getMessage())); + } + + @Test + void signWithInvalidCertificateTest() { + when(certificateInfo.isValid()).thenReturn(false); + final NsdSignatureException actualException = + Assertions.assertThrows(NsdSignatureException.class, + () -> nsdCsarEtsiOption2Signer.sign(new byte[]{})); + assertThat(actualException.getMessage(), is(invalidCertificate(null).getMessage())); + } + + @Test + void signWholeFileTest() throws NsdSignatureException, CmsSignatureException { + final byte[] nsdCsarBytes = "nsdCsarBytes".getBytes(StandardCharsets.UTF_8); + final NsdCsar nsdCsar = new NsdCsar(""); + nsdCsar.addFile("aFile", "aFile".getBytes(StandardCharsets.UTF_8)); + final byte[] nsdCsarBytesSigned = "nsdCsarBytesSigned".getBytes(StandardCharsets.UTF_8); + when(cmsContentSigner.signData(eq(nsdCsarBytes), any(), any())).thenReturn(nsdCsarBytesSigned); + final String nsdCsarBytesSignedPemString = "nsdCsarBytesSignedPemString"; + when(cmsContentSigner.formatToPemSignature(nsdCsarBytesSigned)).thenReturn(nsdCsarBytesSignedPemString); + final byte[] actualNsdSignedCsar = nsdCsarEtsiOption2Signer.sign(nsdCsarBytes); + assertThat("Signature should be as expected", + actualNsdSignedCsar, is(nsdCsarBytesSignedPemString.getBytes(StandardCharsets.UTF_8))); + } + + @Test + void signatureCreationErrorTest() throws CmsSignatureException { + final byte[] nsdCsarBytes = "nsdCsarBytes".getBytes(StandardCharsets.UTF_8); + final NsdCsar nsdCsar = new NsdCsar(""); + nsdCsar.addFile("aFile", "aFile".getBytes(StandardCharsets.UTF_8)); + when(cmsContentSigner.signData(eq(nsdCsarBytes), any(), any())) + .thenThrow(new CmsSignatureException(null, null)); + final NsdSignatureException actualException = assertThrows(NsdSignatureException.class, + () -> nsdCsarEtsiOption2Signer.sign(nsdCsarBytes)); + assertThat(actualException.getMessage(), is(unableToCreateSignature(null).getMessage())); + } + + @Test + void getSigningCertificateTest() { + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.empty()); + Optional<CertificateInfo> signingCertificate = nsdCsarEtsiOption2Signer.getSigningCertificate(); + assertThat("Certificate should not be present", signingCertificate.isEmpty(), is(true)); + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.of(certificateInfo)); + signingCertificate = nsdCsarEtsiOption2Signer.getSigningCertificate(); + assertThat("Certificate should be present", signingCertificate.isPresent(), is(true)); + assertThat("Certificate should be as expected", signingCertificate.get(), is(certificateInfo)); + } + + @Test + void isCertificateConfiguredTest() { + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.empty()); + boolean isCertificateConfigured = nsdCsarEtsiOption2Signer.isCertificateConfigured(); + assertThat("Certificate should not be configured", isCertificateConfigured, is(false)); + when(certificateManager.getCertificate(CERT_NAME)).thenReturn(Optional.of(certificateInfo)); + isCertificateConfigured = nsdCsarEtsiOption2Signer.isCertificateConfigured(); + assertThat("Certificate should be configured", isCertificateConfigured, is(true)); + } +}
\ No newline at end of file diff --git a/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/aFile.txt b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/aFile.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/catalog-be-plugins/etsi-nfv-nsd-csar-plugin/src/test/resources/aFile.txt diff --git a/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java b/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java index 2f72b771e1..0c8d0fc012 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java @@ -55,6 +55,7 @@ import org.springframework.core.annotation.Order; "org.openecomp.sdc.be.components.merge", "org.openecomp.sdc.be.components.csar", "org.openecomp.sdc.be.components.property", + "org.openecomp.sdc.be.csar.security", "org.openecomp.sdc.be.datamodel.utils", "org.openecomp.sdc.be.components.upgrade", "org.openecomp.sdc.be.externalapi.servlet", diff --git a/common-be/pom.xml b/common-be/pom.xml index 94da6cf7c2..c4489b0e17 100644 --- a/common-be/pom.xml +++ b/common-be/pom.xml @@ -74,6 +74,13 @@ </dependency> <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpkix-jdk15on</artifactId> + <version>${bouncycastle.version}</version> + <scope>compile</scope> + </dependency> + + <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest</artifactId> <version>${hamcrest.version}</version> diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/CertificateManagerImpl.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/CertificateManagerImpl.java new file mode 100644 index 0000000000..9ec8ea864e --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/CertificateManagerImpl.java @@ -0,0 +1,167 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.Key; +import java.security.Security; +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.commons.io.FilenameUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.openecomp.sdc.be.csar.security.api.CertificateManager; +import org.openecomp.sdc.be.csar.security.api.CertificateReader; +import org.openecomp.sdc.be.csar.security.api.PrivateKeyReader; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; +import org.openecomp.sdc.be.csar.security.exception.CertificateNotFoundException; +import org.openecomp.sdc.be.csar.security.model.CertificateInfoImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +public class CertificateManagerImpl implements CertificateManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(CertificateManagerImpl.class); + + private final PrivateKeyReader privateKeyReader; + private final CertificateReader certificateReader; + private final Environment environment; + + private Path certificateDirectoryPath; + private File certificateDirectory; + private final Map<String, CertificateInfo> certificateMap = new HashMap<>(); + + public static final String CERT_DIR_ENV_VARIABLE = "SDC_CERT_DIR"; + + public CertificateManagerImpl(final PrivateKeyReader privateKeyReader, + final CertificateReader certificateReader, + final Environment environment) { + this.certificateReader = certificateReader; + this.privateKeyReader = privateKeyReader; + this.environment = environment; + init(); + } + + private void init() { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + + final String certificateDir = environment.getProperty(CERT_DIR_ENV_VARIABLE); + if (certificateDir == null) { + LOGGER.warn("Environment variable '{}' was not provided. Could not load certificates.", CERT_DIR_ENV_VARIABLE); + return; + } + try { + this.certificateDirectoryPath = Paths.get(certificateDir); + } catch (final Exception e) { + LOGGER.error("Invalid path '{}' provided in the environment variable '{}'. Could not load certificates.", + certificateDir, CERT_DIR_ENV_VARIABLE, e); + return; + } + try { + loadCertificateDirectory(); + } catch (final Exception e) { + LOGGER.error("Could not load certificate directory", e); + return; + } + try { + loadCertificates(); + } catch (final Exception e) { + LOGGER.error("Could not load certificates", e); + } + } + + private void loadCertificates() { + final File[] files = certificateDirectory.listFiles(); + if (files == null || files.length == 0) { + LOGGER.warn("Certificate directory is empty. No trusted certificate found."); + return; + } + + final List<File> certFileList = Arrays.stream(files) + .filter(file -> "cert".equals(FilenameUtils.getExtension(file.getName()))) + .collect(Collectors.toList()); + final List<File> keyFileList = Arrays.stream(files) + .filter(file -> "key".equals(FilenameUtils.getExtension(file.getName()))) + .collect(Collectors.toList()); + + if (certFileList.isEmpty()) { + LOGGER.error("Certificate directory is empty. No trusted certificate found."); + return; + } + + certFileList.forEach(certFile -> { + final String baseFileName = FilenameUtils.getBaseName(certFile.getName()); + final Certificate certificate = loadCertificate(certFile); + final Optional<File> keyFileOptional = keyFileList.stream().filter( + keyFile1 -> FilenameUtils.getBaseName(keyFile1.getName()) + .equals(baseFileName)).findFirst(); + keyFileOptional.ifPresentOrElse( + keyFile -> { + final CertificateInfoImpl certificateInfo = + new CertificateInfoImpl(certFile, certificate, keyFile, loadPrivateKey(keyFile)); + if (certificateInfo.isValid()) { + certificateMap.put(baseFileName, certificateInfo); + } + }, + () -> { + final CertificateInfoImpl certificateInfo = new CertificateInfoImpl(certFile, certificate); + if (certificateInfo.isValid()) { + certificateMap.put(baseFileName, new CertificateInfoImpl(certFile, certificate)); + } + } + ); + }); + } + + private void loadCertificateDirectory() { + final File file = certificateDirectoryPath.toFile(); + if (!file.exists() || !file.isDirectory()) { + final String errorMsg = + String.format("Provided certificate path '%s' is not a directory or does not exist", + certificateDirectoryPath); + throw new CertificateNotFoundException(errorMsg); + } + this.certificateDirectory = file; + } + + private Certificate loadCertificate(final File certFile) { + return certificateReader.loadCertificate(certFile); + } + + private Key loadPrivateKey(final File privateKeyFile) { + return privateKeyReader.loadPrivateKey(privateKeyFile); + } + + @Override + public Optional<CertificateInfo> getCertificate(final String certName) { + return Optional.ofNullable(certificateMap.get(certName)); + } +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/PrivateKeyReaderImpl.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/PrivateKeyReaderImpl.java new file mode 100644 index 0000000000..a6ee61d680 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/PrivateKeyReaderImpl.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import java.io.File; +import java.io.FileReader; +import java.security.Key; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.openecomp.sdc.be.csar.security.api.PrivateKeyReader; +import org.openecomp.sdc.be.csar.security.exception.LoadPrivateKeyException; +import org.openecomp.sdc.be.csar.security.exception.UnsupportedKeyFormatException; +import org.springframework.stereotype.Component; + +@Component +public class PrivateKeyReaderImpl implements PrivateKeyReader { + + @Override + public Key loadPrivateKey(final File privateKeyFile) { + try (final PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile))) { + final Object pemObject = pemParser.readObject(); + final JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME); + if (pemObject instanceof PrivateKeyInfo) { + return converter.getPrivateKey((PrivateKeyInfo) pemObject); + } + } catch (final Exception e) { + final String errorMsg = "Could not load the private key from given file '%s'"; + throw new LoadPrivateKeyException(String.format(errorMsg, privateKeyFile), e); + } + final String errorMsg = "Could not load the private key from given file '%s'. Unsupported format."; + throw new UnsupportedKeyFormatException(String.format(errorMsg, privateKeyFile)); + } + +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/Sha256WithRsaCmsContentSigner.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/Sha256WithRsaCmsContentSigner.java new file mode 100644 index 0000000000..7b7273e810 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/Sha256WithRsaCmsContentSigner.java @@ -0,0 +1,98 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import java.io.IOException; +import java.io.StringWriter; +import java.security.Key; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Collections; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.cms.ContentInfo; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.CMSSignedDataGenerator; +import org.bouncycastle.cms.CMSTypedData; +import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.openecomp.sdc.be.csar.security.api.CmsContentSigner; +import org.openecomp.sdc.be.csar.security.exception.CmsSignatureException; +import org.springframework.stereotype.Component; + +@Component +public class Sha256WithRsaCmsContentSigner implements CmsContentSigner { + + @Override + public byte[] signData(final byte[] data, final Certificate signingCertificate, final Key signingKey) + throws CmsSignatureException { + + final CMSTypedData cmsData = new CMSProcessableByteArray(data); + final JcaCertStore certStore = createCertificateStore(signingCertificate); + try { + final ContentSigner contentSigner + = new JcaContentSignerBuilder("SHA256withRSA") + .setProvider(BouncyCastleProvider.PROVIDER_NAME).build((PrivateKey) signingKey); + + final CMSSignedDataGenerator cmsGenerator = new CMSSignedDataGenerator(); + cmsGenerator.addSignerInfoGenerator( + new JcaSignerInfoGeneratorBuilder( + new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build() + ).build(contentSigner, (X509Certificate) signingCertificate) + ); + cmsGenerator.addCertificates(certStore); + + final CMSSignedData cms = cmsGenerator.generate(cmsData, false); + return cms.getEncoded(); + } catch (final Exception e) { + throw new CmsSignatureException("Could not sign the given data", e); + } + } + + @Override + public String formatToPemSignature(final byte[] signedData) throws CmsSignatureException { + final StringWriter sw = new StringWriter(); + try (final JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(sw)) { + final ContentInfo ci = ContentInfo.getInstance(ASN1Primitive.fromByteArray(signedData)); + jcaPEMWriter.writeObject(ci); + } catch (final IOException e) { + throw new CmsSignatureException("Could not convert signed data to PEM format", e); + } + return sw.toString(); + } + + private JcaCertStore createCertificateStore(final Certificate signingCertificate) throws CmsSignatureException { + try { + return new JcaCertStore(Collections.singletonList(signingCertificate)); + } catch (final CertificateEncodingException e) { + final String errorMsg = String + .format("Could not create certificate store from certificate '%s'", signingCertificate); + throw new CmsSignatureException(errorMsg, e); + } + } + +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/X509CertificateReader.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/X509CertificateReader.java new file mode 100644 index 0000000000..b8e95e7b18 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/X509CertificateReader.java @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import org.openecomp.sdc.be.csar.security.api.CertificateReader; +import org.openecomp.sdc.be.csar.security.exception.LoadCertificateException; +import org.springframework.stereotype.Component; + +@Component +public class X509CertificateReader implements CertificateReader { + + /** + * Reads X.509 certificate file. + * + * @param certFile the certificate file + * @return the read certificate + * @throws LoadCertificateException when an error has occurred while reading the certificate + */ + @Override + public Certificate loadCertificate(final File certFile) { + try (final FileInputStream fi = new FileInputStream(certFile)) { + return buildCertificate(fi); + } catch (final Exception e) { + final String errorMsg = "Could not load the certificate from given file '%s'"; + throw new LoadCertificateException(String.format(errorMsg, certFile), e); + } + } + + private Certificate buildCertificate(final InputStream certificateInputStream) throws CertificateException { + final CertificateFactory factory = CertificateFactory.getInstance("X.509"); + return factory.generateCertificate(certificateInputStream); + } + +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CertificateManager.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CertificateManager.java new file mode 100644 index 0000000000..53437f399f --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CertificateManager.java @@ -0,0 +1,29 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.api; + +import java.util.Optional; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; + +public interface CertificateManager { + + Optional<CertificateInfo> getCertificate(String certName); + +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CertificateReader.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CertificateReader.java new file mode 100644 index 0000000000..4c32fa1cee --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CertificateReader.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.api; + +import java.io.File; +import java.security.cert.Certificate; + +public interface CertificateReader { + + /** + * Reads a certificate file. + * + * @param certFile the certificate file + * @return the read certificate + */ + Certificate loadCertificate(File certFile); +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CmsContentSigner.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CmsContentSigner.java new file mode 100644 index 0000000000..37bd988e50 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/CmsContentSigner.java @@ -0,0 +1,32 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.api; + +import java.security.Key; +import java.security.cert.Certificate; +import org.openecomp.sdc.be.csar.security.exception.CmsSignatureException; + +public interface CmsContentSigner { + + byte[] signData(byte[] data, Certificate signingCertificate, Key signingKey) + throws CmsSignatureException; + + String formatToPemSignature(byte[] signedData) throws CmsSignatureException; +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/PrivateKeyReader.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/PrivateKeyReader.java new file mode 100644 index 0000000000..3e8c406b74 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/PrivateKeyReader.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.api; + +import java.io.File; +import java.security.Key; +import org.openecomp.sdc.be.csar.security.exception.LoadPrivateKeyException; +import org.openecomp.sdc.be.csar.security.exception.UnsupportedKeyFormatException; + +public interface PrivateKeyReader { + + /** + * Reads a given private file. + * + * @param privateKeyFile the private key file + * @return the read private key + * @throws LoadPrivateKeyException when an error has occurred while reading the private key + * @throws UnsupportedKeyFormatException when the private key is not supported + */ + Key loadPrivateKey(final File privateKeyFile); +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/model/CertificateInfo.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/model/CertificateInfo.java new file mode 100644 index 0000000000..5b234cc661 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/api/model/CertificateInfo.java @@ -0,0 +1,46 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.api.model; + +import java.io.File; +import java.security.Key; +import java.security.cert.Certificate; + +public interface CertificateInfo { + + String getName(); + + File getCertificateFile(); + + Certificate getCertificate(); + + File getPrivateKeyFile(); + + Key getPrivateKey(); + + /** + * Check if the certificate is valid. + * + * @return {@code true} if the certificate is valid. {@code false} otherwise. + * @throws UnsupportedOperationException when the certificate is not supported + */ + boolean isValid(); + +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/CertificateNotFoundException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/CertificateNotFoundException.java new file mode 100644 index 0000000000..a2175f379c --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/CertificateNotFoundException.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.exception; + +public class CertificateNotFoundException extends RuntimeException { + + public CertificateNotFoundException(final String message) { + super(message); + } +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/CmsSignatureException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/CmsSignatureException.java new file mode 100644 index 0000000000..6bc49d6e4d --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/CmsSignatureException.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.exception; + +public class CmsSignatureException extends Exception { + + public CmsSignatureException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/LoadCertificateException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/LoadCertificateException.java new file mode 100644 index 0000000000..3cd10628f5 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/LoadCertificateException.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.exception; + +public class LoadCertificateException extends RuntimeException { + + public LoadCertificateException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/LoadPrivateKeyException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/LoadPrivateKeyException.java new file mode 100644 index 0000000000..00681bc842 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/LoadPrivateKeyException.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.exception; + +public class LoadPrivateKeyException extends RuntimeException { + + public LoadPrivateKeyException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/UnsupportedKeyFormatException.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/UnsupportedKeyFormatException.java new file mode 100644 index 0000000000..d30f6f274a --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/exception/UnsupportedKeyFormatException.java @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.exception; + +public class UnsupportedKeyFormatException extends RuntimeException { + + public UnsupportedKeyFormatException(final String message) { + super(message); + } +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/csar/security/model/CertificateInfoImpl.java b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/model/CertificateInfoImpl.java new file mode 100644 index 0000000000..f7b2fafb3c --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/csar/security/model/CertificateInfoImpl.java @@ -0,0 +1,70 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.model; + +import java.io.File; +import java.security.Key; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import lombok.Getter; +import org.apache.commons.io.FilenameUtils; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Getter +public class CertificateInfoImpl implements CertificateInfo { + + private static final Logger LOGGER = LoggerFactory.getLogger(CertificateInfoImpl.class); + + private final String name; + private final File certificateFile; + private final Certificate certificate; + private File privateKeyFile; + private Key privateKey; + + public CertificateInfoImpl(final File certificateFile, final Certificate certificate) { + this.certificateFile = certificateFile; + this.certificate = certificate; + this.name = FilenameUtils.getBaseName(certificateFile.getName()); + } + + public CertificateInfoImpl(final File certificateFile, final Certificate certificate, + final File privateKeyFile, final Key privateKey) { + this(certificateFile, certificate); + this.privateKeyFile = privateKeyFile; + this.privateKey = privateKey; + } + + @Override + public boolean isValid() { + if("X.509".equals(certificate.getType())) { + try { + ((X509Certificate) certificate).checkValidity(); + return true; + } catch (final Exception e) { + LOGGER.warn("Invalid certificate '{}'", certificateFile.getAbsolutePath(), e); + return false; + } + } + throw new UnsupportedOperationException(String.format("Certificate type '%s' not supported", certificate.getType())); + } + +} diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/security/CertificateManagerImplTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/CertificateManagerImplTest.java new file mode 100644 index 0000000000..6287c0e85b --- /dev/null +++ b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/CertificateManagerImplTest.java @@ -0,0 +1,141 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; +import static org.openecomp.sdc.be.csar.security.CertificateManagerImpl.CERT_DIR_ENV_VARIABLE; + +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Optional; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openecomp.sdc.be.csar.security.api.CertificateReader; +import org.openecomp.sdc.be.csar.security.api.PrivateKeyReader; +import org.openecomp.sdc.be.csar.security.api.model.CertificateInfo; +import org.springframework.core.env.Environment; + +class CertificateManagerImplTest { + + @Mock + private Environment environment; + @Mock + private PrivateKeyReader privateKeyReader; + @Mock + private CertificateReader certificateReader; + @Mock + private X509Certificate certificateMock; + private CertificateManagerImpl certificateManager; + + static Path certificateFolderPath; + + @BeforeAll + static void beforeAll() { + final String resourceFolder = "certificateManager"; + final URL certificateManager = CertificateManagerImplTest.class.getClassLoader().getResource(resourceFolder); + if (certificateManager == null) { + fail("Could not find resource folder " + resourceFolder); + } + certificateFolderPath = Paths.get(certificateManager.getPath()); + } + + @BeforeEach + void setUp() throws CertificateNotYetValidException, CertificateExpiredException { + MockitoAnnotations.initMocks(this); + when(environment.getProperty(CERT_DIR_ENV_VARIABLE)).thenReturn(certificateFolderPath.toString()); + when(certificateMock.getType()).thenReturn("X.509"); + doNothing().when(certificateMock).checkValidity(); + when(certificateReader.loadCertificate(ArgumentMatchers.any())).thenReturn(certificateMock); + certificateManager = new CertificateManagerImpl(privateKeyReader, certificateReader, environment); + } + + @Test + void getCertificateSuccessTest() { + final String certificateName = "fakeCert1"; + final Optional<CertificateInfo> certificateOpt = certificateManager.getCertificate(certificateName); + assertThat(certificateOpt.isPresent(), is(true)); + final CertificateInfo certificateInfo = certificateOpt.get(); + assertThat(certificateInfo.getName(), is(certificateName)); + assertThat(certificateInfo.getPrivateKeyFile(), is(notNullValue())); + assertThat(certificateInfo.getPrivateKeyFile().getAbsolutePath(), + is(certificateFolderPath.resolve(certificateName + ".key").toString())); + assertThat(certificateInfo.getCertificateFile(), is(notNullValue())); + assertThat(certificateInfo.getCertificateFile().getAbsolutePath(), + is(certificateFolderPath.resolve(certificateName + ".cert").toString())); + } + + @Test + void initCertificateSuccessTest() { + final String certificateName1 = "fakeCert1"; + final String certificateName2 = "fakeCert2"; + final String certificateName3 = "fakeCert3"; + assertThat("Certificate " + certificateName1 + " should be present", + certificateManager.getCertificate(certificateName1).isPresent(), is(true)); + assertThat("Certificate " + certificateName2 + " should be present", + certificateManager.getCertificate(certificateName2).isPresent(), is(true)); + assertThat("Certificate " + certificateName3 + " should not be present", + certificateManager.getCertificate(certificateName3).isEmpty(), is(true)); + } + + @Test + void invalidCertificateFolderTest() { + final String certificateName1 = "fakeCert1"; + when(environment.getProperty(CERT_DIR_ENV_VARIABLE)).thenReturn("/an/invalid/folder"); + final CertificateManagerImpl certificateManager = + new CertificateManagerImpl(privateKeyReader, certificateReader, environment); + assertThat("Certificate " + certificateName1 + " should be present", + certificateManager.getCertificate(certificateName1).isPresent(), is(false)); + } + + @Test + void noEnvironmentVariableConfiguredTest() { + final String certificateName1 = "fakeCert1"; + when(environment.getProperty(CERT_DIR_ENV_VARIABLE)).thenReturn(null); + final CertificateManagerImpl certificateManager = + new CertificateManagerImpl(privateKeyReader, certificateReader, environment); + assertThat("Certificate " + certificateName1 + " should be present", + certificateManager.getCertificate(certificateName1).isPresent(), is(false)); + } + + @Test + void loadCertificateExceptionTest() { + final String certificateName1 = "fakeCert1"; + when(certificateReader.loadCertificate(any())).thenThrow(new RuntimeException()); + final CertificateManagerImpl certificateManager = + new CertificateManagerImpl(privateKeyReader, certificateReader, environment); + assertThat("Certificate " + certificateName1 + " should be present", + certificateManager.getCertificate(certificateName1).isPresent(), is(false)); + } + +}
\ No newline at end of file diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/security/PrivateKeyReaderImplTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/PrivateKeyReaderImplTest.java new file mode 100644 index 0000000000..7bd44cf9c1 --- /dev/null +++ b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/PrivateKeyReaderImplTest.java @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.Key; +import java.security.Security; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openecomp.sdc.be.csar.security.exception.LoadPrivateKeyException; +import org.openecomp.sdc.be.csar.security.exception.UnsupportedKeyFormatException; + +class PrivateKeyReaderImplTest { + + private PrivateKeyReaderImpl privateKeyReader; + + @BeforeEach + void setUp() { + privateKeyReader = new PrivateKeyReaderImpl(); + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + @AfterEach + void tearDown() { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) != null) { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + } + + @Test + void loadPrivateKeySuccessTest() { + final Path certPath = Paths.get("certificateManager", "realCert", "realCert1.key"); + final URL resource = getClass().getClassLoader().getResource(certPath.toString()); + if (resource == null) { + fail("Could not find resource " + certPath.toString()); + } + final Key privateKey = privateKeyReader.loadPrivateKey(new File(resource.getPath())); + assertNotNull(privateKey); + } + + @Test + void loadInvalidKeyFilePathTest() { + final String invalidFilePath = "aaaa"; + final File keyFile = new File(invalidFilePath); + final LoadPrivateKeyException actualException = assertThrows(LoadPrivateKeyException.class, + () -> privateKeyReader.loadPrivateKey(keyFile)); + assertThat(actualException.getMessage(), + is(String.format("Could not load the private key from given file '%s'", invalidFilePath))); + } + + @Test + void loadInvalidKeyFileTest() { + final Path certPath = Paths.get("certificateManager", "fakeCert1.key"); + final URL resource = getClass().getClassLoader().getResource(certPath.toString()); + if (resource == null) { + fail("Could not find resource " + certPath.toString()); + } + final File keyFile = new File(resource.getPath()); + final UnsupportedKeyFormatException actualException = assertThrows(UnsupportedKeyFormatException.class, + () -> privateKeyReader.loadPrivateKey(keyFile)); + assertThat(actualException.getMessage(), + is(String.format("Could not load the private key from given file '%s'. Unsupported format.", + resource.getPath()))); + } +}
\ No newline at end of file diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/security/Sha256WithRsaCmsContentSignerTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/Sha256WithRsaCmsContentSignerTest.java new file mode 100644 index 0000000000..2f0031d6e1 --- /dev/null +++ b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/Sha256WithRsaCmsContentSignerTest.java @@ -0,0 +1,119 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.Key; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSProcessableByteArray; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.SignerInformationStore; +import org.bouncycastle.cms.SignerInformationVerifier; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.OperatorCreationException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openecomp.sdc.be.csar.security.api.CertificateReader; +import org.openecomp.sdc.be.csar.security.api.PrivateKeyReader; +import org.openecomp.sdc.be.csar.security.exception.CmsSignatureException; + +class Sha256WithRsaCmsContentSignerTest { + + private Sha256WithRsaCmsContentSigner cmsContentSigner; + private PrivateKeyReader privateKeyReader; + private CertificateReader certificateReader; + + private static final Path testFilesPath = Path.of("certificateManager", "signerTest"); + private static final Path certFilesPath = Path.of("certificateManager", "realCert"); + + @BeforeEach + void setUp() { + Security.addProvider(new BouncyCastleProvider()); + cmsContentSigner = new Sha256WithRsaCmsContentSigner(); + privateKeyReader = new PrivateKeyReaderImpl(); + certificateReader = new X509CertificateReader(); + } + + @AfterEach + void tearDown() { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + @Test + void signDataSuccessTest() throws OperatorCreationException, CMSException, IOException, CmsSignatureException { + final File certFile = getResourceFile(certFilesPath.resolve("realCert1.cert")); + final File keyFile = getResourceFile(certFilesPath.resolve("realCert1.key")); + final File fileToSign = getResourceFile(testFilesPath.resolve("fileToSign.txt")); + final Key privateKey = privateKeyReader.loadPrivateKey(keyFile); + final Certificate certificate = certificateReader.loadCertificate(certFile); + final byte[] actualSignatureBytes = cmsContentSigner + .signData(Files.readAllBytes(fileToSign.toPath()), certificate, privateKey); + + assertTrue(verifySignature(Files.readAllBytes(fileToSign.toPath()), actualSignatureBytes, + (X509Certificate) certificate)); + } + + @Test + void signDataInvalidCertAndKeyTest() { + assertThrows(CmsSignatureException.class, + () -> cmsContentSigner.signData(null, null, null)); + } + + private boolean verifySignature(byte[] contentBytes, byte[] signatureBytes, X509Certificate certificate) + throws CMSException, OperatorCreationException { + + final CMSSignedData cms = new CMSSignedData(new CMSProcessableByteArray(contentBytes), signatureBytes); + final SignerInformationStore signers = cms.getSignerInfos(); + final SignerInformationVerifier signerInformationVerifier = + new JcaSimpleSignerInfoVerifierBuilder() + .setProvider(BouncyCastleProvider.PROVIDER_NAME).build(certificate); + for (final SignerInformation signer : signers.getSigners()) { + if (!signer.verify(signerInformationVerifier)) { + return false; + } + } + + return true; + } + + private File getResourceFile(final Path testResourcePath) { + final URL resource = getClass().getClassLoader().getResource(testResourcePath.toString()); + if (resource == null) { + fail("Could not load the file " + testResourcePath.toString()); + } + + return new File(resource.getPath()); + } + +}
\ No newline at end of file diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/security/X509CertificateReaderTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/X509CertificateReaderTest.java new file mode 100644 index 0000000000..3235739780 --- /dev/null +++ b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/X509CertificateReaderTest.java @@ -0,0 +1,81 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.cert.Certificate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openecomp.sdc.be.csar.security.exception.LoadCertificateException; + +class X509CertificateReaderTest { + + private X509CertificateReader certificateReader; + + @BeforeEach + void setUp() { + certificateReader = new X509CertificateReader(); + } + + @Test + void loadCertificateSuccessTest() { + final Path certPath = Paths.get("certificateManager", "realCert", "realCert1.cert"); + final URL resource = getClass().getClassLoader().getResource(certPath.toString()); + if (resource == null) { + fail("Could not find resource " + certPath.toString()); + } + final Certificate certificate = certificateReader.loadCertificate(new File(resource.getPath())); + assertNotNull(certificate); + } + + @Test + void loadInvalidCertificateFilePathTest() { + final String invalidFilePath = "aaaa"; + final File certFile = new File(invalidFilePath); + final LoadCertificateException actualException = assertThrows(LoadCertificateException.class, + () -> certificateReader.loadCertificate(certFile)); + assertThat(actualException.getMessage(), + is(String.format("Could not load the certificate from given file '%s'", invalidFilePath))); + } + + @Test + void loadInvalidCertificateFileTest() { + final Path certPath = Paths.get("certificateManager", "fakeCert1.cert"); + System.out.println(certPath.toString()); + final URL resource = getClass().getClassLoader().getResource(certPath.toString()); + if (resource == null) { + fail("Could not find resource " + certPath.toString()); + } + final File certFile = new File(resource.getPath()); + final LoadCertificateException actualException = assertThrows(LoadCertificateException.class, + () -> certificateReader.loadCertificate(certFile)); + assertThat(actualException.getMessage(), + is(String.format("Could not load the certificate from given file '%s'", resource.getPath()))); + } +}
\ No newline at end of file diff --git a/common-be/src/test/java/org/openecomp/sdc/be/csar/security/model/CertificateInfoImplTest.java b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/model/CertificateInfoImplTest.java new file mode 100644 index 0000000000..6b094130b0 --- /dev/null +++ b/common-be/src/test/java/org/openecomp/sdc/be/csar/security/model/CertificateInfoImplTest.java @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.csar.security.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +class CertificateInfoImplTest { + + @Mock + private X509Certificate certificate; + + @BeforeEach + void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + void isValidTest() throws CertificateNotYetValidException, CertificateExpiredException { + when(certificate.getType()).thenReturn("X.509"); + doNothing().when(certificate).checkValidity(); + final CertificateInfoImpl certificateInfo = new CertificateInfoImpl(new File(""), certificate); + assertTrue(certificateInfo.isValid()); + doThrow(CertificateExpiredException.class).when(certificate).checkValidity(); + assertFalse(certificateInfo.isValid()); + } + + @Test + void unsupportedCertificateTypeTest() { + final String certificateType = "unknown"; + when(certificate.getType()).thenReturn(certificateType); + final CertificateInfoImpl certificateInfo = new CertificateInfoImpl(new File(""), certificate); + final UnsupportedOperationException actualException = + assertThrows(UnsupportedOperationException.class, certificateInfo::isValid); + assertEquals(actualException.getMessage(), + String.format("Certificate type '%s' not supported", certificateType)); + } +} diff --git a/common-be/src/test/resources/certificateManager/fakeCert1.cert b/common-be/src/test/resources/certificateManager/fakeCert1.cert new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/common-be/src/test/resources/certificateManager/fakeCert1.cert diff --git a/common-be/src/test/resources/certificateManager/fakeCert1.key b/common-be/src/test/resources/certificateManager/fakeCert1.key new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/common-be/src/test/resources/certificateManager/fakeCert1.key diff --git a/common-be/src/test/resources/certificateManager/fakeCert2.cert b/common-be/src/test/resources/certificateManager/fakeCert2.cert new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/common-be/src/test/resources/certificateManager/fakeCert2.cert diff --git a/common-be/src/test/resources/certificateManager/fakeCert3.key b/common-be/src/test/resources/certificateManager/fakeCert3.key new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/common-be/src/test/resources/certificateManager/fakeCert3.key diff --git a/common-be/src/test/resources/certificateManager/realCert/realCert1.cert b/common-be/src/test/resources/certificateManager/realCert/realCert1.cert new file mode 100644 index 0000000000..ae7d518ab0 --- /dev/null +++ b/common-be/src/test/resources/certificateManager/realCert/realCert1.cert @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUZqdsy8mtirp4AGnTopg9rb3NXcEwDQYJKoZIhvcNAQEL +BQAwgY8xCzAJBgNVBAYTAkVVMRAwDgYDVQQIDAdOb3doZXJlMRIwEAYDVQQHDAlT +b21ld2hlcmUxDTALBgNVBAoMBEFDTUUxDDAKBgNVBAsMA1NEQzEVMBMGA1UEAwwM +c2RjLm9uYXAub3JnMSYwJAYJKoZIhvcNAQkBFhdvbmFwLXNkY0BsaXN0cy5vbmFw +Lm9yZzAgFw0yMTAyMTYxNDQ2NThaGA8yMDcxMDIwNDE0NDY1OFowgY8xCzAJBgNV +BAYTAkVVMRAwDgYDVQQIDAdOb3doZXJlMRIwEAYDVQQHDAlTb21ld2hlcmUxDTAL +BgNVBAoMBEFDTUUxDDAKBgNVBAsMA1NEQzEVMBMGA1UEAwwMc2RjLm9uYXAub3Jn +MSYwJAYJKoZIhvcNAQkBFhdvbmFwLXNkY0BsaXN0cy5vbmFwLm9yZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANAWK6fjuOq6Wgxwh6QqK0YB9ahjPRj0 +ik/l86AokwyawpgY/iC2xXw+9Etq91C6plYy8BlbYIcv/+KoiteL4YgME9Hk1lwK +GEceNGwvLKzU5/fTX1BP4qpVi3aB5zCAWa9MhCk+UI/aIeERKFP9XHAx/wuAUq/x +OP4HLxgLuX/A7Trld3RjjaMYOLjbfJjPFN3lrvfQ02fy5DlJfWG3ASTOOEfdHt1g +wfOLuzl2l5R34p+zwQKid+2cyKXYs0owZu7Dz9OTWLVxZcLiYEGz8Y3gmmHVhXy+ +TPpvl5wPEajOQBUMksdOric0dIfKjJCMJqFzILv1dfZCr3843MYN3oMCAwEAAaNT +MFEwHQYDVR0OBBYEFJ6mFarA/5ISm7nZimLshzCH/0F8MB8GA1UdIwQYMBaAFJ6m +FarA/5ISm7nZimLshzCH/0F8MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAGUzF4W1vHfksW9qSYqtc8xuM+lV1WrdOW7pX7JkIihUv3Z4jMqysrU4 +nj2iVBG5dPNm9v5GtaeK1ESnVilcdMSYTj/F34/MXJ0iZy1UR0j0IBSrR7JjbTAD +aYH1sx00hH13U8GMZInnmenmwziOQgBwhhCjLMblUY2vQ4O5MGwZG0VW2e3mUcq1 +HBC69yRMx9jQ+Iof6+rHnCLZXTjcl+65IxXSbKhofvpPJMVXFlmV3TdDdONuvKpS +051z/ISD6/SEWEvF9ZwAnZvJ/5yPqGzKgyC7rp8zJL+N3VLfr+la1D1w7qgoyFTi +drptEMCzLOk4OuM8PX8l2kY0G+gQD1o= +-----END CERTIFICATE----- diff --git a/common-be/src/test/resources/certificateManager/realCert/realCert1.key b/common-be/src/test/resources/certificateManager/realCert/realCert1.key new file mode 100644 index 0000000000..a31579a407 --- /dev/null +++ b/common-be/src/test/resources/certificateManager/realCert/realCert1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQFiun47jquloM +cIekKitGAfWoYz0Y9IpP5fOgKJMMmsKYGP4gtsV8PvRLavdQuqZWMvAZW2CHL//i +qIrXi+GIDBPR5NZcChhHHjRsLyys1Of3019QT+KqVYt2gecwgFmvTIQpPlCP2iHh +EShT/VxwMf8LgFKv8Tj+By8YC7l/wO065Xd0Y42jGDi423yYzxTd5a730NNn8uQ5 +SX1htwEkzjhH3R7dYMHzi7s5dpeUd+Kfs8EConftnMil2LNKMGbuw8/Tk1i1cWXC +4mBBs/GN4Jph1YV8vkz6b5ecDxGozkAVDJLHTq4nNHSHyoyQjCahcyC79XX2Qq9/ +ONzGDd6DAgMBAAECggEANLj5VK+JIcgXmsFETN72WeWTNZf3WgRTqwzLXpAJOg9Y +MKtccDN+9A0LXrR6dzTjgkGjvfj+CyKpReeITja97PeKagr+GRHhtts7UxHc0uma +4JrosnObLadBD8S4K/zJPHY5oi2MwfX10Y3EVwuByVeRlHtt9/A5jXuKfiAyXuCw +42AT6GUFxntwcKhJSQEcy2B9FwvnF5BDDQKyRVD1McrIj/g7+fiyOpqBXGxk+VYX +6GjYb0kWR+lVexPBP4fgSs6Yx9EroDqzXAolH94NQfb93sOJuawtNVhkEgjE9hec +NumdpHMXhXGP0WI/jhicz4gaBDGyEwuSqUxMnfqNyQKBgQDyFx715F3iz2yfnsUQ +xU7KheUjN0g/vhrcir31L9NuhRpWoBJyRUDeXekuCaDF2ElUXJAdUTMGWDwXxlLx +aa5b8LPBL4pGvNRakfT1TJrViUm3A9wC4EO8l0IIpoYAEt8nstaw4pMEWLOSSKH/ +O2wG+v1h1/Xfd+ruTbmcBn6yLwKBgQDcCuSQoheMZrb6Hq+i+99LF3ZHVqJKUUCI +XRnH3fhwhnMrsR9EshOVRtex0aAdl1GUWCAoVt4ZNom5m7b2qRji0gx8npwA0jxD +CSDUw68Jy3MzQxDMPWNRI38P2r46CslNqCkEGlebbgHEJ0qzp2tIfN25pO6EXfjx +wk9afY5n7QKBgQCG0Wtzgn7qfZs/dTrHsSnEzTYjG3lHzkXFRhqtfbngVY2qajB7 +pKeQbnoaIlB/fYiwy2+SdBLXWLH2h4LPYIwyNWTVk/UMmcIkwh3JsaSUgIUlv6d5 +jo0KbK3ghWQgjGHsCMNY9ITtKbyvHXXh3qS1andLUuphTbXuiihwhIlwDwKBgD9P +QwP6HxxeUTcVrSMPpOdOENHlszv+tLqHTuuaieiWRnzDsWNqeQfyIg0faxoYd3hf +AqGYnL5UWrv0eWfuryJTnRQd7nSuCHihH7kXtDz1NGgDW8nnv7OQqvY80Y6Rm+mk +AGkVyy8FL6zoQS3/dXadto27ToT3JLEqXvqCNX7hAoGAPfNcJtXJXrXh9P3JZqkM +pa8nRqUW0MeRt3v8rLqi4D7J6kW1Rkz5AhjiKkp1EwtE0IbGRI1EPhxT5DCMKlIy +UX7VfP8nf+wb6dLHQ0s4oB20y5J6T7FAkD+50dE6Q/6fAl/2MkE+/R0npKRVhOlc +eXIeBGzSc7p92K1p/vRMOZg= +-----END PRIVATE KEY----- diff --git a/common-be/src/test/resources/certificateManager/signerTest/fileToSign.txt b/common-be/src/test/resources/certificateManager/signerTest/fileToSign.txt new file mode 100644 index 0000000000..cf3472103d --- /dev/null +++ b/common-be/src/test/resources/certificateManager/signerTest/fileToSign.txt @@ -0,0 +1 @@ +This is a file to be signed. diff --git a/common-be/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/common-be/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..ca6ee9cea8 --- /dev/null +++ b/common-be/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline
\ No newline at end of file diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 98d827d04c..5506ecfb7a 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -531,7 +531,7 @@ limitations under the License. <volumes> <bind> <volume>${it.chef.config}:/var/lib/jetty/chef-solo/environments</volume> - <volume>${project.basedir}/src/test/resources/cert:/var/lib/jetty/onap/cert + <volume>${project.basedir}/src/test/resources/cert/onboarding-be:/var/lib/jetty/onap/cert </volume> <volume>${it.shared.volume}:/var/lib/jetty/logs</volume> </bind> @@ -572,12 +572,15 @@ limitations under the License. <ENVNAME>${it.env.name}</ENVNAME> <JAVA_OPTIONS>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:4000 -Xmx1536m -Xms1536m</JAVA_OPTIONS> + <SDC_CERT_DIR>onap/cert</SDC_CERT_DIR> + <SDC_NSD_CERT_NAME>nsdCert</SDC_NSD_CERT_NAME> </env> <hostname>sdc-BE</hostname> <volumes> <bind> <volume>${it.chef.config}:/var/lib/jetty/chef-solo/environments</volume> <volume>${it.shared.volume}:/var/lib/jetty/logs</volume> + <volume>${project.basedir}/src/test/resources/cert/catalog-be:/var/lib/jetty/onap/cert</volume> </bind> </volumes> <wait> diff --git a/integration-tests/src/test/resources/cert/catalog-be/nsdCert.cert b/integration-tests/src/test/resources/cert/catalog-be/nsdCert.cert new file mode 100644 index 0000000000..ae7d518ab0 --- /dev/null +++ b/integration-tests/src/test/resources/cert/catalog-be/nsdCert.cert @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUZqdsy8mtirp4AGnTopg9rb3NXcEwDQYJKoZIhvcNAQEL +BQAwgY8xCzAJBgNVBAYTAkVVMRAwDgYDVQQIDAdOb3doZXJlMRIwEAYDVQQHDAlT +b21ld2hlcmUxDTALBgNVBAoMBEFDTUUxDDAKBgNVBAsMA1NEQzEVMBMGA1UEAwwM +c2RjLm9uYXAub3JnMSYwJAYJKoZIhvcNAQkBFhdvbmFwLXNkY0BsaXN0cy5vbmFw +Lm9yZzAgFw0yMTAyMTYxNDQ2NThaGA8yMDcxMDIwNDE0NDY1OFowgY8xCzAJBgNV +BAYTAkVVMRAwDgYDVQQIDAdOb3doZXJlMRIwEAYDVQQHDAlTb21ld2hlcmUxDTAL +BgNVBAoMBEFDTUUxDDAKBgNVBAsMA1NEQzEVMBMGA1UEAwwMc2RjLm9uYXAub3Jn +MSYwJAYJKoZIhvcNAQkBFhdvbmFwLXNkY0BsaXN0cy5vbmFwLm9yZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANAWK6fjuOq6Wgxwh6QqK0YB9ahjPRj0 +ik/l86AokwyawpgY/iC2xXw+9Etq91C6plYy8BlbYIcv/+KoiteL4YgME9Hk1lwK +GEceNGwvLKzU5/fTX1BP4qpVi3aB5zCAWa9MhCk+UI/aIeERKFP9XHAx/wuAUq/x +OP4HLxgLuX/A7Trld3RjjaMYOLjbfJjPFN3lrvfQ02fy5DlJfWG3ASTOOEfdHt1g +wfOLuzl2l5R34p+zwQKid+2cyKXYs0owZu7Dz9OTWLVxZcLiYEGz8Y3gmmHVhXy+ +TPpvl5wPEajOQBUMksdOric0dIfKjJCMJqFzILv1dfZCr3843MYN3oMCAwEAAaNT +MFEwHQYDVR0OBBYEFJ6mFarA/5ISm7nZimLshzCH/0F8MB8GA1UdIwQYMBaAFJ6m +FarA/5ISm7nZimLshzCH/0F8MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAGUzF4W1vHfksW9qSYqtc8xuM+lV1WrdOW7pX7JkIihUv3Z4jMqysrU4 +nj2iVBG5dPNm9v5GtaeK1ESnVilcdMSYTj/F34/MXJ0iZy1UR0j0IBSrR7JjbTAD +aYH1sx00hH13U8GMZInnmenmwziOQgBwhhCjLMblUY2vQ4O5MGwZG0VW2e3mUcq1 +HBC69yRMx9jQ+Iof6+rHnCLZXTjcl+65IxXSbKhofvpPJMVXFlmV3TdDdONuvKpS +051z/ISD6/SEWEvF9ZwAnZvJ/5yPqGzKgyC7rp8zJL+N3VLfr+la1D1w7qgoyFTi +drptEMCzLOk4OuM8PX8l2kY0G+gQD1o= +-----END CERTIFICATE----- diff --git a/integration-tests/src/test/resources/cert/catalog-be/nsdCert.key b/integration-tests/src/test/resources/cert/catalog-be/nsdCert.key new file mode 100644 index 0000000000..a31579a407 --- /dev/null +++ b/integration-tests/src/test/resources/cert/catalog-be/nsdCert.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQFiun47jquloM +cIekKitGAfWoYz0Y9IpP5fOgKJMMmsKYGP4gtsV8PvRLavdQuqZWMvAZW2CHL//i +qIrXi+GIDBPR5NZcChhHHjRsLyys1Of3019QT+KqVYt2gecwgFmvTIQpPlCP2iHh +EShT/VxwMf8LgFKv8Tj+By8YC7l/wO065Xd0Y42jGDi423yYzxTd5a730NNn8uQ5 +SX1htwEkzjhH3R7dYMHzi7s5dpeUd+Kfs8EConftnMil2LNKMGbuw8/Tk1i1cWXC +4mBBs/GN4Jph1YV8vkz6b5ecDxGozkAVDJLHTq4nNHSHyoyQjCahcyC79XX2Qq9/ +ONzGDd6DAgMBAAECggEANLj5VK+JIcgXmsFETN72WeWTNZf3WgRTqwzLXpAJOg9Y +MKtccDN+9A0LXrR6dzTjgkGjvfj+CyKpReeITja97PeKagr+GRHhtts7UxHc0uma +4JrosnObLadBD8S4K/zJPHY5oi2MwfX10Y3EVwuByVeRlHtt9/A5jXuKfiAyXuCw +42AT6GUFxntwcKhJSQEcy2B9FwvnF5BDDQKyRVD1McrIj/g7+fiyOpqBXGxk+VYX +6GjYb0kWR+lVexPBP4fgSs6Yx9EroDqzXAolH94NQfb93sOJuawtNVhkEgjE9hec +NumdpHMXhXGP0WI/jhicz4gaBDGyEwuSqUxMnfqNyQKBgQDyFx715F3iz2yfnsUQ +xU7KheUjN0g/vhrcir31L9NuhRpWoBJyRUDeXekuCaDF2ElUXJAdUTMGWDwXxlLx +aa5b8LPBL4pGvNRakfT1TJrViUm3A9wC4EO8l0IIpoYAEt8nstaw4pMEWLOSSKH/ +O2wG+v1h1/Xfd+ruTbmcBn6yLwKBgQDcCuSQoheMZrb6Hq+i+99LF3ZHVqJKUUCI +XRnH3fhwhnMrsR9EshOVRtex0aAdl1GUWCAoVt4ZNom5m7b2qRji0gx8npwA0jxD +CSDUw68Jy3MzQxDMPWNRI38P2r46CslNqCkEGlebbgHEJ0qzp2tIfN25pO6EXfjx +wk9afY5n7QKBgQCG0Wtzgn7qfZs/dTrHsSnEzTYjG3lHzkXFRhqtfbngVY2qajB7 +pKeQbnoaIlB/fYiwy2+SdBLXWLH2h4LPYIwyNWTVk/UMmcIkwh3JsaSUgIUlv6d5 +jo0KbK3ghWQgjGHsCMNY9ITtKbyvHXXh3qS1andLUuphTbXuiihwhIlwDwKBgD9P +QwP6HxxeUTcVrSMPpOdOENHlszv+tLqHTuuaieiWRnzDsWNqeQfyIg0faxoYd3hf +AqGYnL5UWrv0eWfuryJTnRQd7nSuCHihH7kXtDz1NGgDW8nnv7OQqvY80Y6Rm+mk +AGkVyy8FL6zoQS3/dXadto27ToT3JLEqXvqCNX7hAoGAPfNcJtXJXrXh9P3JZqkM +pa8nRqUW0MeRt3v8rLqi4D7J6kW1Rkz5AhjiKkp1EwtE0IbGRI1EPhxT5DCMKlIy +UX7VfP8nf+wb6dLHQ0s4oB20y5J6T7FAkD+50dE6Q/6fAl/2MkE+/R0npKRVhOlc +eXIeBGzSc7p92K1p/vRMOZg= +-----END PRIVATE KEY----- diff --git a/integration-tests/src/test/resources/cert/root.cert b/integration-tests/src/test/resources/cert/onboarding-be/root.cert index 11c827ac6f..11c827ac6f 100644 --- a/integration-tests/src/test/resources/cert/root.cert +++ b/integration-tests/src/test/resources/cert/onboarding-be/root.cert 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 3b24c4feb6..b5479e0868 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 @@ -20,27 +20,27 @@ package org.openecomp.sdc.vendorsoftwareproduct.security; -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 static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertTrue; +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; public class SecurityManagerTest { + private File certDir; private String cerDirPath = "/tmp/cert/"; private SecurityManager securityManager; - private File PrepareCertFiles(String origFilePath, String newFilePath) throws IOException, URISyntaxException { + private File prepareCertFiles(String origFilePath, String newFilePath) throws IOException, URISyntaxException { File origFile = new File(getClass().getResource(origFilePath).toURI()); File newFile = new File(newFilePath); newFile.createNewFile(); @@ -72,7 +72,7 @@ public class SecurityManagerTest { @Test public void testGetCertificates() throws IOException, SecurityManagerException, URISyntaxException { - File newFile = PrepareCertFiles("/cert/root-certificate.pem", cerDirPath + "/root-certificate.pem"); + File newFile = prepareCertFiles("/cert/root-certificate.pem", cerDirPath + "/root-certificate.pem"); assertEquals(1, securityManager.getTrustedCertificates().size()); newFile.delete(); assertEquals(0, securityManager.getTrustedCertificates().size()); @@ -98,9 +98,9 @@ public class SecurityManagerTest { @Test public void testGetCertificatesUpdated() throws IOException, SecurityManagerException, URISyntaxException { - File newFile = PrepareCertFiles("/cert/root-certificate.pem", cerDirPath + "root-certificate.pem"); + File newFile = prepareCertFiles("/cert/root-certificate.pem", cerDirPath + "root-certificate.pem"); assertTrue(securityManager.getTrustedCertificates().size() == 1); - File otherNewFile = PrepareCertFiles("/cert/package-certificate.pem", cerDirPath + "package-certificate.pem"); + File otherNewFile = prepareCertFiles("/cert/package-certificate.pem", cerDirPath + "package-certificate.pem"); assertEquals(2, securityManager.getTrustedCertificates().size()); otherNewFile.delete(); assertEquals(1, securityManager.getTrustedCertificates().size()); @@ -110,7 +110,7 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestCertIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException { - PrepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); + 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"); assertTrue(securityManager.verifySignedData(signature, null, archive)); @@ -119,7 +119,7 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestCertNotIncludedIntoSignatureButExpected() throws IOException, URISyntaxException, SecurityManagerException { Assertions.assertThrows(SecurityManagerException.class, () -> { - PrepareCertFiles("/cert/root.cert", cerDirPath + "root.cert"); + prepareCertFiles("/cert/root.cert", cerDirPath + "root.cert"); byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms"); byte[] archive = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.csar"); securityManager.verifySignedData(signature, null, archive); @@ -129,7 +129,7 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestCertNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException { - PrepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); + 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"); byte[] cert = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cert"); @@ -138,8 +138,8 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestCertIntermediateNotIncludedIntoSignature() throws IOException, URISyntaxException, SecurityManagerException { - PrepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); - PrepareCertFiles("/cert/package2.cert", cerDirPath + "signing-ca2.crt"); + 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"); byte[] archive = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.csar"); byte[] cert = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cert"); @@ -149,8 +149,8 @@ public class SecurityManagerTest { @Test public 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"); + prepareCertFiles("/cert/root.cert", cerDirPath + "root.cert"); + prepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt"); byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms"); byte[] archive = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.csar"); byte[] cert = readAllBytes("/cert/3-file-signed-package/dummyPnfv4-no-intermediate.cert"); @@ -160,9 +160,10 @@ public class SecurityManagerTest { } @Test - public void verifySignedDataTestCertIncludedIntoSignatureWithWrongIntermediateInDirectory() throws IOException, URISyntaxException, SecurityManagerException { - PrepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); - PrepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt"); + public void verifySignedDataTestCertIncludedIntoSignatureWithWrongIntermediateInDirectory() + throws IOException, URISyntaxException, SecurityManagerException { + prepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); + prepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt"); byte[] signature = readAllBytes("/cert/2-file-signed-package/dummyPnfv4.cms"); byte[] archive = readAllBytes("/cert/2-file-signed-package/dummyPnfv4.csar"); assertTrue(securityManager.verifySignedData(signature, null, archive)); @@ -170,8 +171,8 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestCertWrongIntermediateInDirectory() throws IOException, URISyntaxException, SecurityManagerException { - PrepareCertFiles("/cert/rootCA.cert", cerDirPath + "root.cert"); - PrepareCertFiles("/cert/signing-ca1.crt", cerDirPath + "signing-ca1.crt"); + 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"); byte[] archive = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.csar"); byte[] cert = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cert"); @@ -181,7 +182,7 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestWrongCertificate() throws IOException, URISyntaxException, SecurityManagerException { Assertions.assertThrows(SecurityManagerException.class, () -> { - PrepareCertFiles("/cert/root-certificate.pem", cerDirPath + "root-certificate.cert"); + prepareCertFiles("/cert/root-certificate.pem", cerDirPath + "root-certificate.cert"); byte[] signature = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cms"); byte[] archive = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.csar"); byte[] cert = readAllBytes("/cert/3-file-signed-package/dummyPnfv4.cert"); @@ -193,7 +194,7 @@ public class SecurityManagerTest { @Test public void verifySignedDataTestChangedArchive() throws IOException, URISyntaxException, SecurityManagerException { Assertions.assertThrows(SecurityManagerException.class, () -> { - PrepareCertFiles("/cert/root.cert", cerDirPath + "root.cert"); + prepareCertFiles("/cert/root.cert", cerDirPath + "root.cert"); byte[] signature = readAllBytes("/cert/tampered-signed-package/dummyPnfv4.cms"); byte[] archive = readAllBytes("/cert/tampered-signed-package/dummyPnfv4.csar"); securityManager.verifySignedData(signature, null, archive); diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/fileToSign.txt b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/fileToSign.txt new file mode 100644 index 0000000000..cf3472103d --- /dev/null +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/fileToSign.txt @@ -0,0 +1 @@ +This is a file to be signed. diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/fileToSign.txt.cms b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/fileToSign.txt.cms new file mode 100644 index 0000000000..c0ac6467da --- /dev/null +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/fileToSign.txt.cms @@ -0,0 +1,18 @@ +-----BEGIN CMS----- +MIIC8gYJKoZIhvcNAQcCoIIC4zCCAt8CAQExDTALBglghkgBZQMEAgEwCwYJKoZI +hvcNAQcBMYICvDCCArgCAQEwgaswgZIxCzAJBgNVBAYTAklSMRIwEAYDVQQIDAlX +ZXN0bWVhdGgxEDAOBgNVBAcMB0F0aGxvbmUxETAPBgNVBAoMCGVzdC50ZWNoMREw +DwYDVQQLDAhlc3QudGVjaDERMA8GA1UEAwwIZXN0LnRlY2gxJDAiBgkqhkiG9w0B +CQEWFWFuZHJlLnNjaG1pZEBlc3QudGVjaAIUPr/6LGtQEr6HkS9OPY3VkUJocycw +CwYJYIZIAWUDBAIBoIHkMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZI +hvcNAQkFMQ8XDTIxMDEyODE2NTY0MlowLwYJKoZIhvcNAQkEMSIEIKJ0GRZdnzHz +edaHW6QPdArXCWESAmfwrhzusQh4iDofMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZI +AWUDBAEqMAsGCWCGSAFlAwQBFjALBglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYI +KoZIhvcNAwICAgCAMA0GCCqGSIb3DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMC +AgEoMA0GCSqGSIb3DQEBAQUABIIBAFDH2MqTh2j7hhctKghJ0QJFyE2u8zkrWlzD +WSVhVBJGiVHIuDe5bBGbT5h0Vvwde2bFNvb448p7Ymps68g8Wh+oFHS4wEEfmGtX +rCjO+fw8JSol4/y0R49xBhACMgVzuUo7v6uaHMy13sHNsyFbA6k/QmV2nPYaD0VY +6/qQN50soiNA/XGt6iDEwKsPMVv45Fl/tt/ldQ9MEpWaet3nvPS5/o2DMNQbB8c9 +moHi+05QLltoAQBn9dcipOuMXSyDUKFf0VsOBeHekTJ9BZdn4m938Vznr4zcE02P +AvOTghecINTJEVCxwZlHf4sv96RjDLohMBX2VGY+6tjdgMCDDEg= +-----END CMS----- diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/rootCA.cert b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/rootCA.cert new file mode 100644 index 0000000000..24511f8456 --- /dev/null +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/rootCA.cert @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEBzCCAu+gAwIBAgIUPr/6LGtQEr6HkS9OPY3VkUJocycwDQYJKoZIhvcNAQEL +BQAwgZIxCzAJBgNVBAYTAklSMRIwEAYDVQQIDAlXZXN0bWVhdGgxEDAOBgNVBAcM +B0F0aGxvbmUxETAPBgNVBAoMCGVzdC50ZWNoMREwDwYDVQQLDAhlc3QudGVjaDER +MA8GA1UEAwwIZXN0LnRlY2gxJDAiBgkqhkiG9w0BCQEWFWFuZHJlLnNjaG1pZEBl +c3QudGVjaDAeFw0yMTAxMjgxNDIwMDBaFw0yMTAyMjcxNDIwMDBaMIGSMQswCQYD +VQQGEwJJUjESMBAGA1UECAwJV2VzdG1lYXRoMRAwDgYDVQQHDAdBdGhsb25lMREw +DwYDVQQKDAhlc3QudGVjaDERMA8GA1UECwwIZXN0LnRlY2gxETAPBgNVBAMMCGVz +dC50ZWNoMSQwIgYJKoZIhvcNAQkBFhVhbmRyZS5zY2htaWRAZXN0LnRlY2gwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDc2nm2hCmwkN0CHn4xtdERaUY5 +NnypzPbond32qhRQAfJodLqRJVHx6p7moHmYCZJyYQxxSaAHXGsg64NvkM3OUoIv +S/9wVh3S7PX7ZdxBxntK8iLxVxMoqJ0rzboU0PAvMgBWKl5pI0YQRvnwZkOCCEbp +5CaBmT+j8x526mbyozr8fyaxpVDEnbYNp464DPOuVtdlXAz8+xNP935bOYDhvt0N +EL8Eb+8peGvxowHXwYwFRiySzu13Lx6yR5UP+KP5PidGzm8jAKlkxPLZg3yGH23q +/6Lw/4inS/gsl0j+REzZLl1hWcMsXwYWn9K457Xryyv9vYdImi47w9cikIbHAgMB +AAGjUzBRMB0GA1UdDgQWBBQTRLbprizfJqO3nTYCgAuIbEpEfzAfBgNVHSMEGDAW +gBQTRLbprizfJqO3nTYCgAuIbEpEfzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQB3MDFS6LMogHjYxhfgFJsvbpvSu9QNLNezHIdwOpJlz+iNMMr6 +OqqVLMVBjAjdtXOUcY0QSmpL5Jo25/5LY4vXpRoJRBDcZDyWUjuDGksBuPr+vxeD +lA/JQ+xT8XOkNi75KcgARw8FUEAuOtbZtPGycOQj+ckeHcywod+ZOB3lyKgUk1Jl +9S0QGF+6PI7WgCh9NMRXmSyFz9pskUeZxT73QNmknUz88XG3HDOTT4wEwOABeOV4 +xjMnyTiIWO+MsO0Jf51TcdRIhoxDylGLyHV1ihkFd5XBLVaSxkbHr65N0pR97nee +nnoSqS+jd965dR1Ixwnoap2tLegACXsNDgC+ +-----END CERTIFICATE----- diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/rootCA.key b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/rootCA.key new file mode 100644 index 0000000000..779e7e1655 --- /dev/null +++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/test/resources/cert/certificateManager/rootCA.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDc2nm2hCmwkN0C +Hn4xtdERaUY5NnypzPbond32qhRQAfJodLqRJVHx6p7moHmYCZJyYQxxSaAHXGsg +64NvkM3OUoIvS/9wVh3S7PX7ZdxBxntK8iLxVxMoqJ0rzboU0PAvMgBWKl5pI0YQ +RvnwZkOCCEbp5CaBmT+j8x526mbyozr8fyaxpVDEnbYNp464DPOuVtdlXAz8+xNP +935bOYDhvt0NEL8Eb+8peGvxowHXwYwFRiySzu13Lx6yR5UP+KP5PidGzm8jAKlk +xPLZg3yGH23q/6Lw/4inS/gsl0j+REzZLl1hWcMsXwYWn9K457Xryyv9vYdImi47 +w9cikIbHAgMBAAECggEBAKasDpzKYvgIa3I09wGm+C/Opht5Xl16/H+S3DdSJeA3 +h+yC42W0p4INkXv1ca5m0umVCiHY8Xd8rLB8bIXe3yKtK+hrz0vFAd7zy7O9LJtL +IjLuR6ahEyllDrBHIv6grBLCBnUApp1r7G1OU0O0uY/BlVrgPpshv2CIkt4liinj +1Ix7Hy7bu2dp8JETon3sH4sqQ+/AJOalvR97FK1Dr6m2O3zF6b+kfb/9ZiVaork7 +kh03KblopQ8SG3N4YHGBcAGHRtRvfxCUYZ/CD1Ym8oeH/xRiYPJETeil6ogf2o5h +3zbXVe1I1cOR5W2XVoSCvwbnH0lywx2jnvK+DtdYjLECgYEA/JV8a1DxOG+PEDmZ +760bI5FTHwaq3aLVT9xnSfnRZzLpR1/dTLdS0R2plAggAcmp9OU2bSB17vmgUjg0 +2AmKNm2lyhCbP7H3IS5pBa4qmOn9i/HZpLkWMnS1h7wYyr0Q/6hfrJvCpkseFN9s +2KWPGd/ehdVl1VJi/1uxh25q/bkCgYEA39chOw89IjJsPDY9sBhFBR03poiGMJB8 +sMPQ8fxxDiDHh+Hqn2GgAD7RFXtksxFd9iRrFCjMxL+Y+39zs29BpHklfGtZw8Ef +jLxGORRam/NoTJDeyPgxjSlupSl436p2a72st2d948/2YHIH4tS3EaBjEJAcYA9O +FXmI6/B16H8CgYEA+BcQDQyE0izpNUOOipbZyDaukRPocifFI1rV7Rg+cQHVlfB4 +GikaZCGhdbABWCAvGkalDczLaX2w1fE1HllIcyuBoxZqlsaDeQjvyM+IMYfrLTDQ +2HWfctGll/vN0hygfKxxxMV9C/9OPawyah07kGuOUqAHFN+GM1Fp7x8qYakCgYEA +g6Vy1dlQixw8q+z6uuaLMUbNxBPnWAiiTRPdf3SIyVZeA3zLEJHKGFrZfVjlEKvW +Jz2j/GdChzRoNQHwekekw6a8dUBtusOKWqsgYdGmuLGfVeieHnRaMDt954VMob8P +hq43NmaXk7Rroq1uFeolASjjGoqnJZfaxKaF8L2rmuECgYEAqjNkDrhmokEJ9Tj/ +Kj/bvFi9Hc67LPYNi24HqEHWFBu9Ss9SFMR5wyx+5sGmEffqoK6pQLhiivVCSJ+J +4m6WCWRoXJWZsonUvVb68AajLw/FHjzMpxOehPVTRvwYFn77/0KmCnK40fe6y/dl +yiV5bMP+AuBKc4+WITet1tYHM/c= +-----END PRIVATE KEY----- @@ -171,6 +171,7 @@ Modifications copyright (c) 2018-2019 Nokia <surefire.skip.tests>false</surefire.skip.tests> <docker.api.version>1.35</docker.api.version> + <bouncycastle.version>1.68</bouncycastle.version> </properties> <dependencyManagement> |