summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBogumil Zebek <bogumil.zebek@nokia.com>2019-08-14 10:52:37 +0200
committerZebek Bogumil <bogumil.zebek@nokia.com>2019-08-22 12:10:38 +0200
commit0562debfc5cdd31e61c016aea40272c6c02ad3cb (patch)
tree79e011e5247c1179d784723bb57c6bede0b3fb14
parent870a89675528664aa5c0aca57f50c584b76a8b8f (diff)
CMS signature validation
Change-Id: Ie5d1c835d0e6a760f1b7de651a3833cb87b727e0 Issue-ID: VNFSDK-396 Signed-off-by: Zebek Bogumil <bogumil.zebek@nokia.com>
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java6
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java5
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java (renamed from csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java)117
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java26
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java39
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java62
-rw-r--r--csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java45
-rw-r--r--csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand2
-rw-r--r--csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml (renamed from csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml)2
-rw-r--r--csarvalidation/src/main/resources/vnfreqs.properties6
-rw-r--r--csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java36
-rw-r--r--csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java (renamed from csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java)46
-rw-r--r--csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java22
-rw-r--r--csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java50
-rw-r--r--csarvalidation/src/test/resources/README.txt20
-rw-r--r--csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf10
-rw-r--r--csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf38
-rw-r--r--csarvalidation/src/test/resources/pnf/README.txt6
-rw-r--r--csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar (renamed from csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar)bin5745 -> 5743 bytes
-rw-r--r--csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csarbin0 -> 7324 bytes
-rw-r--r--csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar (renamed from csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar)bin6549 -> 6170 bytes
21 files changed, 442 insertions, 96 deletions
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java b/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java
index 180dadd..beb556d 100644
--- a/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/CSARArchive.java
@@ -1093,7 +1093,7 @@ public class CSARArchive implements AutoCloseable {
this.toscaMeta.getEntryLicense(),
lineNo));
}
- } else if(key.equalsIgnoreCase(TOSCA_METADATA_TOSCA_META_ENTRY_CERTIFICATE)) {
+ } else if(key.equalsIgnoreCase(getEntryCertificateParamName())) {
this.toscaMeta.setEntryCertificate(value);
this.certificatesFile= this.tempDir.resolve(this.toscaMeta.getEntryCertificate()).toFile();
if (!this.certificatesFile.exists()) {
@@ -1247,6 +1247,10 @@ public class CSARArchive implements AutoCloseable {
}
}
+ public String getEntryCertificateParamName() {
+ return TOSCA_METADATA_TOSCA_META_ENTRY_CERTIFICATE;
+ }
+
String getEntryManifestParamName(){
return TOSCA_METADATA_TOSCA_META_ENTRY_MANIFEST;
}
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java
index a6e2745..f3ce960 100644
--- a/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/PnfCSARArchive.java
@@ -66,4 +66,9 @@ public class PnfCSARArchive extends CSARArchive {
String getEntryChangeLogParamName() {
return "ETSI-Entry-Change-Log";
}
+
+ @Override
+ public String getEntryCertificateParamName() {
+ return "ETSI-Entry-Certificate";
+ }
}
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java
index 7a14709..2d85e35 100644
--- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966.java
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206.java
@@ -23,33 +23,40 @@ import org.onap.cli.fw.schema.OnapCommandSchema;
import org.onap.cvc.csar.CSARArchive;
import org.onap.cvc.csar.FileArchive;
import org.onap.cvc.csar.cc.VTPValidateCSARBase;
+import org.onap.cvc.csar.parser.ManifestFileModel;
+import org.onap.cvc.csar.parser.ManifestFileSplitter;
import org.onap.cvc.csar.parser.SourcesParser;
+import org.onap.cvc.csar.security.CmsSignatureValidator;
+import org.onap.cvc.csar.security.CmsSignatureValidatorException;
import org.onap.cvc.csar.security.ShaHashCodeGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
-@OnapCommandSchema(schema = "vtp-validate-csar-r787966.yaml")
-public class VTPValidateCSARR787966 extends VTPValidateCSARBase {
+@OnapCommandSchema(schema = "vtp-validate-csar-r130206.yaml")
+public class VTPValidateCSARR130206 extends VTPValidateCSARBase {
- private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR787966.class);
+ private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR130206.class);
private static final String SHA_256 = "SHA-256";
private static final String SHA_512 = "SHA-512";
private final ShaHashCodeGenerator shaHashCodeGenerator = new ShaHashCodeGenerator();
+ private final ManifestFileSignatureValidator manifestFileSignatureValidator = new ManifestFileSignatureValidator();
public static class CSARErrorUnableToFindCertificate extends CSARArchive.CSARError {
- CSARErrorUnableToFindCertificate() {
+ CSARErrorUnableToFindCertificate(String paramName) {
super("0x4001");
- this.message = "Unable to find cert file defined by Entry-Certificate!";
+ this.message = String.format("Unable to find cert file defined by %s!", paramName);
}
}
@@ -84,7 +91,14 @@ public class VTPValidateCSARR787966 extends VTPValidateCSARBase {
public static class CSARErrorUnableToFindSource extends CSARArchive.CSARError {
CSARErrorUnableToFindSource(String path) {
super("0x4006");
- this.message = String.format("Source '%s' does not exist!", path);
+ this.message = String.format("Unable to calculate digest - file missing: %s", path);
+ }
+ }
+
+ public static class CSARErrorInvalidSignature extends CSARArchive.CSARError {
+ CSARErrorInvalidSignature() {
+ super("0x4007");
+ this.message = "File has invalid CMS signature!";
}
}
@@ -94,38 +108,54 @@ public class VTPValidateCSARR787966 extends VTPValidateCSARBase {
try {
FileArchive.Workspace workspace = csar.getWorkspace();
final Optional<Path> pathToCsarFolder = workspace.getPathToCsarFolder();
- if(pathToCsarFolder.isPresent()) {
+ if (pathToCsarFolder.isPresent()) {
validate(csar, pathToCsarFolder.get());
} else {
this.errors.add(new CSARErrorUnableToFindCsarContent());
}
} catch (Exception e) {
- LOG.error("Internal VTPValidateCSARR787966 command error", e);
+ LOG.error("Internal VTPValidateCSARR130206 command error", e);
throw new OnapCommandException("0x3000", "Internal VTPValidateCSARR787966 command error. See logs.");
}
}
- private void validate(CSARArchive csar, Path csarRootDirectory ) throws IOException, NoSuchAlgorithmException {
-
+ private void validate(CSARArchive csar, Path csarRootDirectory) throws IOException, NoSuchAlgorithmException {
final CSARArchive.Manifest manifest = csar.getManifest();
- final CSARArchive.TOSCAMeta toscaMeta = csar.getToscaMeta();
- validateSecurityStructure(toscaMeta, csarRootDirectory, manifest);
+
+ validateSecurityStructure(csar, csarRootDirectory);
validateSources(csarRootDirectory, manifest);
+
+ final File manifestMfFile = csar.getManifestMfFile();
+ if (manifestMfFile != null) {
+ validateFileSignature(manifestMfFile);
+ }
}
- private void validateSecurityStructure(CSARArchive.TOSCAMeta toscaMeta , Path csarRootDirectory, CSARArchive.Manifest manifest) {
+ private void validateFileSignature(File manifestMfFile) {
+ final boolean isValid = this.manifestFileSignatureValidator.isValid(manifestMfFile);
+ if (!isValid) {
+ this.errors.add(new CSARErrorInvalidSignature());
+ }
+ }
+
+ private void validateSecurityStructure(CSARArchive csar, Path csarRootDirectory) {
+ final CSARArchive.Manifest manifest = csar.getManifest();
+ final CSARArchive.TOSCAMeta toscaMeta = csar.getToscaMeta();
+ final String entryCertificateParamName = csar.getEntryCertificateParamName();
final Optional<File> entryCertificate = resolveCertificateFilePath(toscaMeta, csarRootDirectory);
- if (!entryCertificate.isPresent() || !entryCertificate.get().exists() && !manifest.getCms().isEmpty()) {
- this.errors.add(new CSARErrorUnableToFindCertificate());
- } else if (entryCertificate.get().exists() && manifest.getCms().isEmpty()) {
+ if (!entryCertificate.isPresent() || !entryCertificate.get().exists()) {
+ this.errors.add(new CSARErrorUnableToFindCertificate(entryCertificateParamName));
+ }
+
+ if (manifest.getCms() == null || manifest.getCms().isEmpty()) {
this.errors.add(new CSARErrorUnableToFindCmsSection());
}
}
private Optional<File> resolveCertificateFilePath(CSARArchive.TOSCAMeta toscaMeta, Path csarRootDirectory) {
final String certificatePath = toscaMeta.getEntryCertificate();
- if(certificatePath == null){
+ if (certificatePath == null) {
return Optional.empty();
} else {
return Optional.of(csarRootDirectory.resolve(certificatePath).toFile());
@@ -134,16 +164,22 @@ public class VTPValidateCSARR787966 extends VTPValidateCSARBase {
private void validateSources(Path csarRootDirectory, CSARArchive.Manifest manifest) throws NoSuchAlgorithmException, IOException {
final List<SourcesParser.Source> sources = manifest.getSources();
- for (SourcesParser.Source source: sources){
- final Path sourcePath = csarRootDirectory.resolve(source.getValue());
- if(!Files.exists(sourcePath)){
- this.errors.add(new CSARErrorUnableToFindSource(source.getValue()));
- } else {
- if (!source.getAlgorithm().isEmpty()) {
- validateSourceHashCode(csarRootDirectory, source);
- } else if (source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()) {
- this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue()));
- }
+ for (SourcesParser.Source source : sources) {
+ if (!source.getAlgorithm().isEmpty() || !source.getHash().isEmpty()) {
+ validateSource(csarRootDirectory, source);
+ }
+ }
+ }
+
+ private void validateSource(Path csarRootDirectory, SourcesParser.Source source) throws NoSuchAlgorithmException, IOException {
+ final Path sourcePath = csarRootDirectory.resolve(source.getValue());
+ if (!sourcePath.toFile().exists()) {
+ this.errors.add(new CSARErrorUnableToFindSource(source.getValue()));
+ } else {
+ if (!source.getAlgorithm().isEmpty()) {
+ validateSourceHashCode(csarRootDirectory, source);
+ } else if (source.getAlgorithm().isEmpty() && !source.getHash().isEmpty()) {
+ this.errors.add(new CSARErrorUnableToFindAlgorithm(source.getValue()));
}
}
}
@@ -159,9 +195,9 @@ public class VTPValidateCSARR787966 extends VTPValidateCSARBase {
final byte[] sourceData = Files.readAllBytes(csarRootDirectory.resolve(source.getValue()));
final String algorithm = source.getAlgorithm();
- if(algorithm.equalsIgnoreCase(SHA_256)) {
+ if (algorithm.equalsIgnoreCase(SHA_256)) {
return this.shaHashCodeGenerator.generateSha256(sourceData);
- } else if(algorithm.equalsIgnoreCase(SHA_512)){
+ } else if (algorithm.equalsIgnoreCase(SHA_512)) {
return this.shaHashCodeGenerator.generateSha512(sourceData);
}
@@ -170,8 +206,29 @@ public class VTPValidateCSARR787966 extends VTPValidateCSARBase {
@Override
protected String getVnfReqsNo() {
- return "R787966";
+ return "R130206";
}
}
+
+class ManifestFileSignatureValidator {
+ private static final Logger LOG = LoggerFactory.getLogger(ManifestFileSignatureValidator.class);
+ private final ManifestFileSplitter manifestFileSplitter = new ManifestFileSplitter();
+ private final CmsSignatureValidator cmsSignatureValidator = new CmsSignatureValidator();
+
+ boolean isValid(File manifestFile) {
+ try {
+ ManifestFileModel mf = manifestFileSplitter.split(manifestFile);
+ return cmsSignatureValidator.verifySignedData(toBytes(mf.getCMS()), Optional.empty(), toBytes(mf.getData()));
+ } catch (CmsSignatureValidatorException e) {
+ LOG.error("Unable to verify signed data!", e);
+ return false;
+ }
+ }
+
+ private byte[] toBytes(List<String> data) {
+ final String updatedData = data.stream().map(it -> it + "\r\n").collect(Collectors.joining());
+ return updatedData.getBytes(Charset.defaultCharset());
+ }
+}
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java
index a3ab865..034d35e 100644
--- a/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965.java
@@ -37,6 +37,7 @@ import java.util.Optional;
public class VTPValidateCSARR787965 extends VTPValidateCSARBase {
private static final Logger LOG = LoggerFactory.getLogger(VTPValidateCSARR787965.class);
+ private final CmsSignatureValidator securityManager = new CmsSignatureValidator();
static class CSARErrorInvalidSignature extends CSARArchive.CSARError {
CSARErrorInvalidSignature() {
@@ -52,16 +53,6 @@ public class VTPValidateCSARR787965 extends VTPValidateCSARBase {
}
}
- static class SignatureWithCertificationOnlyWarning extends CSARArchive.CSARError {
- SignatureWithCertificationOnlyWarning() {
- super("0x3003");
- this.message = "Warning. Zip package probably is valid. " +
- "It contains only signature with certification cms and csar package. " +
- "Unable to verify csar signature.";
- }
- }
-
-
static class BrokenZipPackageError extends CSARArchive.CSARError {
BrokenZipPackageError() {
super("0x3004");
@@ -95,7 +86,7 @@ public class VTPValidateCSARR787965 extends VTPValidateCSARBase {
if (pathToCertFile.isPresent() && pathToCmsFile.isPresent()) {
verifyTwoFileCertification(pathToCsarFile.get(), pathToCertFile.get(), pathToCmsFile.get());
} else if (pathToCmsFile.isPresent()) {
- this.errors.add(new SignatureWithCertificationOnlyWarning());
+ verifyOneFileCertification(pathToCsarFile.get(), pathToCmsFile.get());
} else {
this.errors.add(new BrokenZipPackageError());
}
@@ -103,13 +94,20 @@ public class VTPValidateCSARR787965 extends VTPValidateCSARBase {
}
private void verifyTwoFileCertification(Path pathToCsarFile, Path pathToCertFile, Path pathToCmsFile) throws IOException, CmsSignatureValidatorException {
- final CmsSignatureValidator securityManager = new CmsSignatureValidator();
-
byte[] csarContent = Files.readAllBytes(pathToCsarFile);
byte[] signature = Files.readAllBytes(pathToCmsFile);
byte[] publicCertification = Files.readAllBytes(pathToCertFile);
- if (!securityManager.verifySignedData(signature, publicCertification,csarContent)) {
+ if (!securityManager.verifySignedData(signature, Optional.of(publicCertification) ,csarContent)) {
+ this.errors.add(new CSARErrorInvalidSignature());
+ }
+ }
+
+ private void verifyOneFileCertification(Path pathToCsarFile, Path pathToSignatureAndCmsFile) throws IOException, CmsSignatureValidatorException {
+ byte[] csarContent = Files.readAllBytes(pathToCsarFile);
+ byte[] signature = Files.readAllBytes(pathToSignatureAndCmsFile);
+
+ if(!securityManager.verifySignedData(signature, Optional.empty(), csarContent)){
this.errors.add(new CSARErrorInvalidSignature());
}
}
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java
new file mode 100644
index 0000000..f6b42fd
--- /dev/null
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileModel.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 Nokia
+ * <p>
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ *
+ */
+
+package org.onap.cvc.csar.parser;
+
+import java.util.Collections;
+import java.util.List;
+
+public class ManifestFileModel {
+ private final List<String> data;
+ private final List<String> cms;
+
+ public ManifestFileModel(List<String> data, List<String> cms) {
+ this.data = data;
+ this.cms = cms;
+ }
+
+ public List<String> getData() {
+ return Collections.unmodifiableList(data);
+ }
+
+ public List<String> getCMS() {
+ return Collections.unmodifiableList(cms);
+ }
+}
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java
new file mode 100644
index 0000000..03a628c
--- /dev/null
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/parser/ManifestFileSplitter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 Nokia
+ * <p>
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ *
+ */
+
+package org.onap.cvc.csar.parser;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ManifestFileSplitter {
+
+ public ManifestFileModel split(File manifestFile) {
+ String fileName = manifestFile.getAbsolutePath();
+ List<String> data = new ArrayList<>();
+ List<String> cms = new ArrayList<>();
+
+ try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
+ List<String> lines = stream.collect(Collectors.toList());
+ return createManifestFileModel(data, cms, lines);
+
+ } catch (IOException e) {
+ throw new IllegalArgumentException(String.format("Unable to process manifest file! Wrong file path: '%s'", fileName));
+ }
+ }
+
+ private ManifestFileModel createManifestFileModel(List<String> data, List<String> cms, List<String> lines) {
+ boolean isCmsSection = false;
+
+ for (String line : lines) {
+ if (line.contains("BEGIN CMS")) {
+ isCmsSection = true;
+ }
+
+ if (isCmsSection) {
+ cms.add(line);
+ } else {
+ data.add(line);
+ }
+ }
+ return new ManifestFileModel(data, cms);
+ }
+}
diff --git a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java
index a168541..b8b3714 100644
--- a/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java
+++ b/csarvalidation/src/main/java/org/onap/cvc/csar/security/CmsSignatureValidator.java
@@ -18,6 +18,7 @@
package org.onap.cvc.csar.security;
import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
@@ -27,6 +28,7 @@ import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Store;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,23 +36,35 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
+import java.util.Optional;
public class CmsSignatureValidator {
private static final Logger LOG = LoggerFactory.getLogger(CmsSignatureValidator.class);
public boolean verifySignedData(
- final byte[] signature,
- final byte[] certificate,
- final byte[] csarFileContent) throws CmsSignatureValidatorException {
-
- try (ByteArrayInputStream signatureStream = new ByteArrayInputStream(signature)) {
- SignerInformation firstSigner = getSignerInformation(csarFileContent, signatureStream);
- X509Certificate cert = loadCertificate(certificate);
+ final byte[] cmsSignature,
+ final Optional<byte[]> certificate,
+ final byte[] fileContent) throws CmsSignatureValidatorException {
+
+ try (ByteArrayInputStream cmsSignatureStream = new ByteArrayInputStream(cmsSignature)) {
+ CMSSignedData signedData = getCMSSignedData(fileContent, cmsSignatureStream);
+ Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
+ SignerInformation firstSigner = signers.iterator().next();
+
+ Store certificates = signedData.getCertificates();
+ X509Certificate cert;
+ if (!certificate.isPresent()) {
+ X509CertificateHolder firstSignerFirstCertificate = getX509CertificateHolder(firstSigner, certificates);
+ cert = loadCertificate(firstSignerFirstCertificate.getEncoded());
+ } else {
+ cert = loadCertificate(certificate.get());
+ }
return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
} catch (CMSSignerDigestMismatchException e){
@@ -63,17 +77,22 @@ public class CmsSignatureValidator {
}
}
- private SignerInformation getSignerInformation(byte[] innerPackageFileCSAR, ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException, CMSException {
+ private X509CertificateHolder getX509CertificateHolder(SignerInformation firstSigner, Store certificates) throws CmsSignatureValidatorException {
+ Collection<X509CertificateHolder> firstSignerCertificates = certificates.getMatches(firstSigner.getSID());
+ if(!firstSignerCertificates.iterator().hasNext()){
+ throw new CmsSignatureValidatorException("No certificate found in cms signature that should contain one!");
+ }
+ return firstSignerCertificates.iterator().next();
+ }
+
+ private CMSSignedData getCMSSignedData(byte[] innerPackageFileCSAR, ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException, CMSException {
ContentInfo signature = produceSignature(signatureStream);
CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFileCSAR);
- CMSSignedData signedData = new CMSSignedData(signedContent, signature);
-
- Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
- return signers.iterator().next();
+ return new CMSSignedData(signedContent, signature);
}
private ContentInfo produceSignature(ByteArrayInputStream signatureStream) throws IOException, CmsSignatureValidatorException {
- Object parsedObject = new PEMParser(new InputStreamReader(signatureStream)).readObject();
+ Object parsedObject = new PEMParser(new InputStreamReader(signatureStream, Charset.defaultCharset())).readObject();
if (!(parsedObject instanceof ContentInfo)) {
throw new CmsSignatureValidatorException("Signature is not recognized!");
}
diff --git a/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand b/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand
index c6f2934..aadb653 100644
--- a/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand
+++ b/csarvalidation/src/main/resources/META-INF/services/org.onap.cli.fw.cmd.OnapCommand
@@ -47,5 +47,5 @@ org.onap.cvc.csar.cc.sol004.VTPValidateCSARR293901
org.onap.cvc.csar.cc.sol004.VTPValidateCSARR146092
org.onap.cvc.csar.cc.sol004.VTPValidateCSARR57019
org.onap.cvc.csar.cc.sol004.VTPValidateCSARR787965
-org.onap.cvc.csar.cc.sol004.VTPValidateCSARR787966
+org.onap.cvc.csar.cc.sol004.VTPValidateCSARR130206
diff --git a/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml
index 0482836..bf8be04 100644
--- a/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r787966.yaml
+++ b/csarvalidation/src/main/resources/open-cli-schema/sol004/vtp-validate-csar-r130206.yaml
@@ -14,7 +14,7 @@
open_cli_schema_version: 1.0
-name: csar-validate-r787966
+name: csar-validate-r130206
description: |
The VNF/PNF package shall contain a Digest (a.k.a. hash) for each of the components of the VNF package. The table of hashes is included in the manifest file, which is signed with the VNF provider private key. In addition, the VNF provider shall include a signing certificate that includes the VNF provider public key, following a pre-defined naming convention and located either at the root of the archive or in a predefined location (e.g. directory).
diff --git a/csarvalidation/src/main/resources/vnfreqs.properties b/csarvalidation/src/main/resources/vnfreqs.properties
index 8b5d488..abbdb73 100644
--- a/csarvalidation/src/main/resources/vnfreqs.properties
+++ b/csarvalidation/src/main/resources/vnfreqs.properties
@@ -1,5 +1,5 @@
-vnfreqs.enabled=r02454,r04298,r07879,r09467,r13390,r23823,r26881,r27310,r35851,r40293,r43958,r66070,r77707,r77786,r87234,r10087,r21322,r26885,r40820,r35854,r65486,r17852,r46527,r15837,r54356,r67895,r95321,r32155,r01123,r51347,r787965,r787966
-pnfreqs.enabled=r10087,r87234,r35854,r15837,r17852,r293901,r146092,r57019,r787965,r787966
+vnfreqs.enabled=r02454,r04298,r07879,r09467,r13390,r23823,r26881,r27310,r35851,r40293,r43958,r66070,r77707,r77786,r87234,r10087,r21322,r26885,r40820,r35854,r65486,r17852,r46527,r15837,r54356,r67895,r95321,r32155,r01123,r51347,r787965,r130206
+pnfreqs.enabled=r10087,r87234,r35854,r15837,r17852,r293901,r146092,r57019,r787965,r130206
# ignored all chef and ansible related tests
vnferrors.ignored=0x1005,0x1006,r07879-0x1000,r13390-0x1000,r27310-0x1000,r40293-0x1000,r77786-0x1000,r04298-0x1000,r07879-0x1000,r10087-0x1000,r13390-0x1000,r23823-0x1000,r26881-0x1000,r40820-0x1000,r35851-0x1000,r32155-0x1000,r54356-0x1000,r67895-0x1000,r95321-0x1000,r46527-0x1000,r02454-0x1000
-pnferrors.ignored= \ No newline at end of file
+pnferrors.ignored=
diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java
index 6e56959..a708f47 100644
--- a/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java
+++ b/csarvalidation/src/test/java/org/onap/cvc/csar/PnfManifestParserTest.java
@@ -120,9 +120,39 @@ public class PnfManifestParserTest {
List<CSARArchive.CSARError> errors = sourcesPair.getValue();
assertThat(cms).isEqualTo(
- "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"
+ "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"+
+ "4xuyO1o65LlKHoxwnRH9lQ=="
);
assertThat(errors.size()).isEqualTo(0);
}
diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java
index d48869a..90da946 100644
--- a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787966IntegrationTest.java
+++ b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR130206IntegrationTest.java
@@ -18,6 +18,7 @@
package org.onap.cvc.csar.cc.sol004;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.onap.cvc.csar.CSARArchive;
@@ -28,26 +29,30 @@ import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.configureTestCase
import static org.onap.cvc.csar.cc.sol004.IntegrationTestUtils.convertToMessagesList;
-public class VTPValidateCSARR787966IntegrationTest {
+public class VTPValidateCSARR130206IntegrationTest {
private static final boolean IS_PNF = true;
- private VTPValidateCSARR787966 testCase;
+ private VTPValidateCSARR130206 testCase;
@Before
public void setUp() {
- testCase = new VTPValidateCSARR787966();
+ testCase = new VTPValidateCSARR130206();
}
@Test
public void shouldReturnProperRequestNumber() {
- assertThat(testCase.getVnfReqsNo()).isEqualTo("R787966");
+ assertThat(testCase.getVnfReqsNo()).isEqualTo("R130206");
}
@Test
- public void shouldValidateProperCsar() throws Exception {
+ @Ignore("It is impossible to write test which will always pass, because certificate used to sign the file has time validity." +
+ "To verify signed package please please follow instructions from test/resources/README.txt file and comment @Ignore tag. " +
+ "Use instructions for option 1. Test was created for manual verification."
+ )
+ public void manual_shouldValidateProperCsar() throws Exception {
// given
- configureTestCase(testCase, "pnf/r787966/csar-option1-valid.csar", "vtp-validate-csar-r787966.yaml", IS_PNF);
+ configureTestCase(testCase, "pnf/r130206/csar-option1-valid.csar", "vtp-validate-csar-r130206.yaml", IS_PNF);
// when
testCase.execute();
@@ -58,22 +63,40 @@ public class VTPValidateCSARR787966IntegrationTest {
}
@Test
+ public void shouldReportThatOnlySignatureIsInvalid() throws Exception {
+
+ // given
+ configureTestCase(testCase, "pnf/r130206/csar-option1-validSection.csar", "vtp-validate-csar-r130206.yaml", IS_PNF);
+
+ // when
+ testCase.execute();
+
+ // then
+ List<CSARArchive.CSARError> errors = testCase.getErrors();
+ assertThat(errors.size()).isEqualTo(1);
+ assertThat(convertToMessagesList(errors)).contains(
+ "File has invalid CMS signature!"
+ );
+ }
+
+ @Test
public void shouldReportErrorsForInvalidCsar() throws Exception {
// given
- configureTestCase(testCase, "pnf/r787966/csar-option1-invalid.csar", "vtp-validate-csar-r787966.yaml", IS_PNF);
+ configureTestCase(testCase, "pnf/r130206/csar-option1-invalid.csar", "vtp-validate-csar-r130206.yaml", IS_PNF);
// when
testCase.execute();
// then
List<CSARArchive.CSARError> errors = testCase.getErrors();
- assertThat(errors.size()).isEqualTo(4);
+ assertThat(errors.size()).isEqualTo(5);
assertThat(convertToMessagesList(errors)).contains(
"Unable to find CMS section in manifest!",
"Source 'Definitions/MainServiceTemplate.yaml' has wrong hash!",
"Source 'Artifacts/Other/my_script.csh' has hash, but unable to find algorithm tag!",
- "Source 'Artifacts/NonExisting.txt' does not exist!"
+ "Unable to calculate digest - file missing: Artifacts/NonExisting2.txt",
+ "File has invalid CMS signature!"
);
}
@@ -82,7 +105,7 @@ public class VTPValidateCSARR787966IntegrationTest {
public void shouldReportThanInVnfPackageCertFileWasNotDefined() throws Exception {
// given
- configureTestCase(testCase, "sample2.csar", "vtp-validate-csar-r787966.yaml", false);
+ configureTestCase(testCase, "sample2.csar", "vtp-validate-csar-r130206.yaml", false);
// when
testCase.execute();
@@ -91,10 +114,11 @@ public class VTPValidateCSARR787966IntegrationTest {
List<CSARArchive.CSARError> errors = testCase.getErrors();
assertThat(convertToMessagesList(errors)).contains(
"Unable to find cert file defined by Entry-Certificate!",
+ "Unable to find CMS section in manifest!",
"Missing. Entry [tosca_definitions_version]"
);
}
-} \ No newline at end of file
+}
diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java
index eb41d6a..49696e6 100644
--- a/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java
+++ b/csarvalidation/src/test/java/org/onap/cvc/csar/cc/sol004/VTPValidateCSARR787965IntegrationTest.java
@@ -18,6 +18,7 @@
package org.onap.cvc.csar.cc.sol004;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.onap.cvc.csar.CSARArchive;
@@ -61,7 +62,11 @@ public class VTPValidateCSARR787965IntegrationTest {
}
@Test
- public void shouldReportThatZipContainsSignatureWithCertificationFileAndPackageIsProbableValid() throws Exception {
+ @Ignore("It is impossible to write test which will always pass, because certificate used to sign the file has time validity." +
+ "To verify signed package please please follow instructions from test/resources/README.txt file and comment @Ignore tag. " +
+ "Use instructions for option 2. Test was created for manual verification."
+ )
+ public void manual_shouldReportThatZipContainsSignatureWithCertificationFileAndPackageIsValid() throws Exception {
// given
configureTestCase(testCase, "pnf/r787965/signature-and-certificate.zip", "vtp-validate-csar-r787965.yaml", IS_PNF);
@@ -71,12 +76,7 @@ public class VTPValidateCSARR787965IntegrationTest {
// then
List<CSARArchive.CSARError> errors = testCase.getErrors();
- assertThat(errors.size()).isEqualTo(1);
- assertThat(convertToMessagesList(errors)).contains(
- "Warning. Zip package probably is valid. " +
- "It contains only signature with certification cms and csar package. " +
- "Unable to verify csar signature."
- );
+ assertThat(errors.size()).isEqualTo(0);
}
@Test
@@ -97,7 +97,11 @@ public class VTPValidateCSARR787965IntegrationTest {
}
@Test
- public void shouldDoNotReportAnyErrorWhenPackageHasValidSignature() throws Exception {
+ @Ignore("It is impossible to write test which will always pass, because certificate used to sign the file has time validity." +
+ "To verify signed package please please follow instructions from test/resources/README.txt file and comment @Ignore tag. " +
+ "Use instructions for option 2. Test was created for manual verification."
+ )
+ public void manual_shouldDoNotReportAnyErrorWhenPackageHasValidSignature() throws Exception {
// given
configureTestCase(testCase, "pnf/signed-package-valid-signature.zip", "vtp-validate-csar-r787965.yaml", IS_PNF);
@@ -110,4 +114,4 @@ public class VTPValidateCSARR787965IntegrationTest {
assertThat(errors.size()).isEqualTo(0);
}
-} \ No newline at end of file
+}
diff --git a/csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java b/csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java
new file mode 100644
index 0000000..b530691
--- /dev/null
+++ b/csarvalidation/src/test/java/org/onap/cvc/csar/parser/ManifestFileSplitterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 Nokia
+ * <p>
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ *
+ */
+
+package org.onap.cvc.csar.parser;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+
+import java.io.File;
+
+/*
+ How to sing files see to README.txt file into test/resources folder
+ */
+public class ManifestFileSplitterTest {
+
+ @Test
+ public void shouldSplitManifestFileOnDataPartAndCMS() {
+ File file = new File("./src/test/resources/cvc/csar/parser/MainServiceTemplate.mf");
+ ManifestFileSplitter manifestFileSplitter = new ManifestFileSplitter();
+
+ ManifestFileModel manifestFileModel = manifestFileSplitter.split(file);
+
+ Assertions.assertThat(manifestFileModel.getData()).contains("metadata:",
+ " pnfd_name: RadioNode",
+ " pnfd_provider: Ericsson",
+ " pnfd_archive_version: 1.0",
+ " pnfd_release_date_time: 2019-01-14T11:25:00+00:00");
+
+ Assertions.assertThat(manifestFileModel.getCMS()).contains(
+ "-----BEGIN CMS-----",
+ "MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI",
+ "hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG",
+ "-----END CMS-----"
+ );
+ }
+}
diff --git a/csarvalidation/src/test/resources/README.txt b/csarvalidation/src/test/resources/README.txt
new file mode 100644
index 0000000..1360ce9
--- /dev/null
+++ b/csarvalidation/src/test/resources/README.txt
@@ -0,0 +1,20 @@
+How to sign CSAR file - option 2
+---------------------
+openssl req -new -nodes -x509 -keyout root-private-key.pem > root.cert
+openssl req -new -nodes -keyout sample-pnf-private-key.pem > sample-pnf-request.pem
+openssl x509 -req -CA root.cert -CAkey root-private-key.pem -CAcreateserial < sample-pnf-request.pem > sample-pnf.cert
+openssl cms -sign -binary -nocerts -outform pem -signer sample-pnf.cert -inkey sample-pnf-private-key.pem < sample-pnf.csar > sample-pnf.cms
+
+How to sign CSAR file - option 1
+--------------------------------
+openssl req -new -nodes -x509 -keyout root-private-key.pem > root-certificate.cert
+TIP: As a 'Organizational Unit Name' set, for example: Certificate Authority
+
+openssl req -new -nodes -keyout signing-private-key.pem > signing-request.pem
+TIP: As a 'Organizational Unit Name' set, for example: Nokia. Name values must be different!
+
+openssl x509 -req -CA root-certificate.cert -CAkey root-private-key.pem -CAcreateserial < signing-request.pem > signing-certificate.cert
+
+openssl cms -sign -signer signing-certificate.cert -inkey signing-private-key.pem -outform pem -binary < MainServiceTemplate.mf > signature-and-certificate.cms
+
+openssl cms -verify -content MainServiceTemplate.mf -CAfile root-certificate.cert -inform pem -binary < signature-and-certificate.cms > /dev/null
diff --git a/csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf b/csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf
new file mode 100644
index 0000000..556c006
--- /dev/null
+++ b/csarvalidation/src/test/resources/cvc/csar/parser/MainServiceTemplate.mf
@@ -0,0 +1,10 @@
+metadata:
+ pnfd_name: RadioNode
+ pnfd_provider: Ericsson
+ pnfd_archive_version: 1.0
+ pnfd_release_date_time: 2019-01-14T11:25:00+00:00
+
+-----BEGIN CMS-----
+MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI
+hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG
+-----END CMS-----
diff --git a/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf b/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf
index 6987eb1..6bc88b2 100644
--- a/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf
+++ b/csarvalidation/src/test/resources/pnf/MainServiceTemplate.mf
@@ -35,7 +35,37 @@ non_mano_artifact_sets:
source: Artifacts/Other/review_log.txt
-----BEGIN CMS-----
-MIGDBgsqhkiG9w0BCRABCaB0MHICAQAwDQYLKoZIhvcNAQkQAwgwXgYJKoZIhvcN
-AQcBoFEET3icc87PK0nNK9ENqSxItVIoSa0o0S/ISczMs1ZIzkgsKk4tsQ0N1nUM
-dvb05OXi5XLPLEtViMwvLVLwSE0sKlFIVHAqSk3MBkkBAJv0Fx0=
------END CMS----- \ No newline at end of file
+MIIGDAYJKoZIhvcNAQcCoIIF/TCCBfkCAQExDTALBglghkgBZQMEAgEwCwYJKoZI
+hvcNAQcBoIIDRTCCA0EwggIpAhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDANBgkqhkiG
+9w0BAQsFADBlMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G
+A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMR4wHAYDVQQLDBVDZXJ0aWZp
+Y2F0ZSBBdXRob3JpdHkwHhcNMTkwODEzMDg0NDU4WhcNMTkwOTEyMDg0NDU4WjBV
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ4wDAYDVQQLDAVOb2tpYTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAPRCM8g0cm1nuojFuk01Lo1iAbj7STEbiqJn
+Xk4BEEspM4snShj35bO9DHSunXivdCzen4BE7hLpetpbr+7ptqpV7NuR9DgYD399
+eAltb4oLnkLWgODCxhFOnwrKjnXSbP8KX3kmYRJmDzsSjJrpattfxNCa2aHzubyA
+W0Mv9Ni2R0scnBY+ubydwn223d/743T2pfXsiOV6Ucjhz+9XWU96b7e9GxN12EJQ
+R6R4O9dz3CSZmQsiMMYROD5elV59Y9ucSkhdrUjPzjveqjEA9FWc0piBpe42c9Mo
+Lr8S5hKaaC8ONfSUBuEysKC5g6D6OS1Kxii3zbUbNzpxXti8tmUCAwEAATANBgkq
+hkiG9w0BAQsFAAOCAQEAVJGCH8VL/ha1RYmoZBefCT/AQc50GlcIJtPCB8Y7ygkX
+Y2Ybj6SrF66+wq6hQsU9xtxHyn08nfOdGWfNJ9yq4SO8RF7Oz4NxkQ+KFhi2QUGZ
+5TwdWLr0Q+zKTZgpLZm1rtlyyz+2AUwcPPVHhDfJX0kqz/0UPHWFDxXfJyOwmQdN
+E4qhO9uB3zEujJwM8B7wXfDwsNg6xbKBytm67IHQN3OF/Bfcugx7eCVJ08XA8Irj
+CovwPvjxaL32iYTXmiBl+vSb3lEarbinMkMCq80yx3LtIg1goGVO+Tp+yOoVxNUL
+psSXr9kdWncI1venEjk/SvggxtT4RJ6dLH358qFu+TGCAo0wggKJAgEBMH0wZTEL
+MAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy
+bmV0IFdpZGdpdHMgUHR5IEx0ZDEeMBwGA1UECwwVQ2VydGlmaWNhdGUgQXV0aG9y
+aXR5AhRJ6KO7OFR2BuRDZwcd2TT4/wrEqDALBglghkgBZQMEAgGggeQwGAYJKoZI
+hvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwODEzMDg0NTI2
+WjAvBgkqhkiG9w0BCQQxIgQgOtAYNJkSFj5rU8K7Ujz6BdefH7sITKBcMmBcm/hI
+TUAweQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZIAWUDBAEWMAsG
+CWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAwDQYIKoZIhvcN
+AwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcNAQEBBQAEggEA
+Sj+3i3Mcxz6Uqf8WcLNiR3K3QeQUEQJurPHW/BzidjPx+PoZ+6jP8sAkulUu/yeo
+rv3dDQGq0cF6KE3gKi3IXgCOB5nZ/O4BtvPcKOlQk14fMdBnHQMgGb27dNLMheuo
+t4YJVEZNm+1NoYZBMyESm1Ns3DHmq7dqpFMWSad85gMTsbD/q896ZMiua+bLvnlg
+qJXtYrnJPx9KqSzNFhzTqwFMJ9OASaHm+eV9/EWWLJ0rgUmheI0sb2Pa5i93w6dr
+HhE7UbSCHDlDDgrOosJkbuI4UCX/njXrU2ukXbrWz/FjH84Mek039z+w4M6fBnl5
+4xuyO1o65LlKHoxwnRH9lQ==
+-----END CMS-----
diff --git a/csarvalidation/src/test/resources/pnf/README.txt b/csarvalidation/src/test/resources/pnf/README.txt
deleted file mode 100644
index 8984d6f..0000000
--- a/csarvalidation/src/test/resources/pnf/README.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-How to sign CSAR file
----------------------
-openssl req -new -nodes -x509 -keyout root-private-key.pem > root.cert
-openssl req -new -nodes -keyout sample-pnf-private-key.pem > sample-pnf-request.pem
-openssl x509 -req -CA root.cert -CAkey root-private-key.pem -CAcreateserial < sample-pnf-request.pem > sample-pnf.cert
-openssl cms -sign -binary -nocerts -outform pem -signer sample-pnf.cert -inkey sample-pnf-private-key.pem < sample-pnf.csar > sample-pnf.cms \ No newline at end of file
diff --git a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar
index 0213b60..187c008 100644
--- a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-invalid.csar
+++ b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-invalid.csar
Binary files differ
diff --git a/csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar
new file mode 100644
index 0000000..7cca18d
--- /dev/null
+++ b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-valid.csar
Binary files differ
diff --git a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar
index 17aa662..bc90a75 100644
--- a/csarvalidation/src/test/resources/pnf/r787966/csar-option1-valid.csar
+++ b/csarvalidation/src/test/resources/pnf/r130206/csar-option1-validSection.csar
Binary files differ