2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019, Nordix Foundation. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.openecomp.sdcrests.vsp.rest.data;
22 import java.security.cert.CertificateException;
23 import java.util.List;
25 import java.util.Optional;
26 import org.apache.commons.io.FilenameUtils;
27 import org.apache.commons.lang3.tuple.Pair;
28 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
29 import org.openecomp.core.utilities.file.FileContentHandler;
30 import org.openecomp.sdc.common.utils.CommonUtil;
31 import org.openecomp.sdc.common.zip.exception.ZipException;
32 import org.openecomp.sdc.logging.api.Logger;
33 import org.openecomp.sdc.logging.api.LoggerFactory;
34 import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager;
35 import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException;
38 * Class responsible for processing zip archive and verify if this package corresponds SOL004 option 2 signed package
39 * format, verifies the cms signature if package is signed
41 public class PackageArchive {
43 private static final Logger LOG = LoggerFactory.getLogger(PackageArchive.class);
44 private static final String[] ALLOWED_ARCHIVE_EXTENSIONS = {"csar", "zip"};
45 private static final String[] ALLOWED_SIGNATURE_EXTENSIONS = {"cms"};
46 private static final String[] ALLOWED_CERTIFICATE_EXTENSIONS = {"cert"};
47 private static final int NUMBER_OF_FILES_FOR_SIGNATURE_WITH_CERT_INSIDE = 2;
48 private static final int NUMBER_OF_FILES_FOR_SIGNATURE_WITHOUT_CERT_INSIDE = 3;
49 private final SecurityManager securityManager;
50 private final byte[] outerPackageFileBytes;
51 private Pair<FileContentHandler, List<String>> handlerPair;
52 private Boolean signatureValid;
54 public PackageArchive(Attachment uploadedFile) {
55 this(uploadedFile.getObject(byte[].class));
58 public PackageArchive(byte[] outerPackageFileBytes) {
59 this.outerPackageFileBytes = outerPackageFileBytes;
60 this.securityManager = SecurityManager.getInstance();
62 handlerPair = CommonUtil.getFileContentMapFromOrchestrationCandidateZip(
63 outerPackageFileBytes);
64 } catch (final ZipException exception) {
65 LOG.error("Error reading files inside archive", exception);
70 * Checks if package matches required format {package.csar/zip, package.cms, package.cert(optional)}
72 * @return true if structure matches sol004 option 2 structure
74 public boolean isSigned() {
75 return isPackageSizeMatches() && getSignatureFileName().isPresent();
79 * Gets csar/zip package name with extension only if package is signed
81 * @return csar package name
83 public Optional<String> getArchiveFileName() {
85 return getFileByExtension(ALLOWED_ARCHIVE_EXTENSIONS);
87 return Optional.empty();
91 * Gets csar/zip package content from zip archive
93 * @return csar package content
94 * @throws SecurityManagerException
96 public byte[] getPackageFileContents() throws SecurityManagerException {
98 if (isSignatureValid()) {
99 return handlerPair.getKey().getFiles().get(getArchiveFileName().orElseThrow(CertificateException::new));
101 } catch (CertificateException exception) {
102 LOG.info("Error verifying signature ", exception);
104 return outerPackageFileBytes;
108 * Validates package signature against trusted certificates
110 * @return true if signature verified
111 * @throws SecurityManagerException
113 public boolean isSignatureValid() throws SecurityManagerException {
114 if (signatureValid == null) {
115 final Map<String, byte[]> files = handlerPair.getLeft().getFiles();
116 final Optional<String> signatureFileName = getSignatureFileName();
117 final Optional<String> archiveFileName = getArchiveFileName();
118 if (files.isEmpty() || !signatureFileName.isPresent() || !archiveFileName.isPresent()) {
119 signatureValid = false;
121 final Optional<String> certificateFile = getCertificateFileName();
122 signatureValid = securityManager.verifySignedData(files.get(signatureFileName.get()),
123 certificateFile.map(files::get).orElse(null), files.get(archiveFileName.get()));
127 return signatureValid;
130 private boolean isPackageSizeMatches() {
131 return handlerPair.getRight().isEmpty()
132 && (handlerPair.getLeft().getFiles().size() == NUMBER_OF_FILES_FOR_SIGNATURE_WITH_CERT_INSIDE
133 || handlerPair.getLeft().getFiles().size() == NUMBER_OF_FILES_FOR_SIGNATURE_WITHOUT_CERT_INSIDE);
136 private Optional<String> getSignatureFileName() {
137 return getFileByExtension(ALLOWED_SIGNATURE_EXTENSIONS);
140 private Optional<String> getFileByExtension(String[] extensions) {
141 for (String fileName : handlerPair.getLeft().getFileList()) {
142 for (String extension : extensions) {
143 if (extension.equalsIgnoreCase(FilenameUtils.getExtension(fileName))) {
144 return Optional.of(fileName);
148 return Optional.empty();
151 private Optional<String> getCertificateFileName() {
152 Optional<String> certFileName = getFileByExtension(ALLOWED_CERTIFICATE_EXTENSIONS);
153 if (!certFileName.isPresent()) {
154 return Optional.empty();
156 String certNameWithoutExtension = FilenameUtils.removeExtension(certFileName.get());
157 if (certNameWithoutExtension.equals(FilenameUtils.removeExtension(getArchiveFileName().orElse("")))) {
160 //cert file name should be the same as package name, e.g. vnfpackage.scar-->vnfpackage.cert
161 return Optional.empty();