From b2f9dc5d3bc02564b4d952caa0bf2ccd20dfc6af Mon Sep 17 00:00:00 2001 From: kooper Date: Tue, 2 Apr 2019 09:22:01 +0000 Subject: Verify signature Change-Id: I8fc5d50d74d3dd8031c96ee16708489dc7c789b8 Issue-ID: SDC-2163 Signed-off-by: kooper --- .../sdcrests/vsp/rest/data/PackageArchive.java | 160 +++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java (limited to 'openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java') diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java new file mode 100644 index 0000000000..bf029fb29e --- /dev/null +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/vendor-software-products-rest/vendor-software-products-rest-services/src/main/java/org/openecomp/sdcrests/vsp/rest/data/PackageArchive.java @@ -0,0 +1,160 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2019, Nordix Foundation. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ +package org.openecomp.sdcrests.vsp.rest.data; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.openecomp.core.utilities.file.FileContentHandler; +import org.openecomp.sdc.common.utils.CommonUtil; +import org.openecomp.sdc.logging.api.Logger; +import org.openecomp.sdc.logging.api.LoggerFactory; +import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManager; +import org.openecomp.sdc.vendorsoftwareproduct.security.SecurityManagerException; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Class responsible for processing zip archive and verify if this package corresponds + * SOL004 option 2 signed package format, verifies the cms signature if package is signed + */ +public class PackageArchive { + private static final Logger LOG = LoggerFactory.getLogger(PackageArchive.class); + private static final String[] ALLOWED_ARCHIVE_EXTENSIONS = {"csar", "zip"}; + private static final String[] ALLOWED_SIGNATURE_EXTENSIONS = {"cms"}; + private static final String[] ALLOWED_CERTIFICATE_EXTENSIONS = {"cert"}; + private static final int NUMBER_OF_FILES_FOR_SIGNATURE_WITH_CERT_INSIDE = 2; + private static final int NUMBER_OF_FILES_FOR_SIGNATURE_WITHOUT_CERT_INSIDE = 3; + private final SecurityManager securityManager; + private final byte[] outerPackageFileBytes; + private Pair> handlerPair; + + public PackageArchive(Attachment uploadedFile) { + this(uploadedFile.getObject(byte[].class)); + } + + public PackageArchive(byte[] outerPackageFileBytes) { + this.outerPackageFileBytes = outerPackageFileBytes; + this.securityManager = SecurityManager.getInstance(); + try { + handlerPair = CommonUtil.getFileContentMapFromOrchestrationCandidateZip( + outerPackageFileBytes); + } catch (IOException exception) { + LOG.error("Error reading files inside archive", exception); + } + } + + /** + * Checks if package matches required format {package.csar/zip, package.cms, package.cert(optional)} + * + * @return true if structure matches sol004 option 2 structure + */ + public boolean isSigned() { + return isPackageSizeMatches() && getSignatureFileName().isPresent(); + } + + /** + * Gets csar/zip package name with extension only if package is signed + * + * @return csar package name + */ + public Optional getArchiveFileName() { + if (isSigned()) { + return getFileByExtension(ALLOWED_ARCHIVE_EXTENSIONS); + } + return Optional.empty(); + } + + /** + * Gets csar/zip package content from zip archive + * @return csar package content + * @throws SecurityManagerException + */ + public byte[] getPackageFileContents() throws SecurityManagerException { + try { + if (isSignatureValid()) { + return handlerPair.getKey().getFiles().get(getArchiveFileName().orElseThrow(CertificateException::new)); + } + } catch (CertificateException exception) { + LOG.info("Error verifying signature " + exception); + } + return outerPackageFileBytes; + } + + /** + * Validates package signature against trusted certificates + * @return true if signature verified + * @throws SecurityManagerException + */ + public boolean isSignatureValid() throws SecurityManagerException { + Map files = handlerPair.getLeft().getFiles(); + Optional signatureFileName = getSignatureFileName(); + Optional archiveFileName = getArchiveFileName(); + if (files.isEmpty() || !signatureFileName.isPresent() || !archiveFileName.isPresent()) { + return false; + } + Optional certificateFile = getCertificateFileName(); + if(certificateFile.isPresent()){ + return securityManager.verifySignedData(files.get(signatureFileName.get()), + files.get(certificateFile.get()), files.get(archiveFileName.get())); + }else { + return securityManager.verifySignedData(files.get(signatureFileName.get()), + null, files.get(archiveFileName.get())); + } + } + + private boolean isPackageSizeMatches() { + return handlerPair.getRight().isEmpty() + && (handlerPair.getLeft().getFiles().size() == NUMBER_OF_FILES_FOR_SIGNATURE_WITH_CERT_INSIDE + || handlerPair.getLeft().getFiles().size() == NUMBER_OF_FILES_FOR_SIGNATURE_WITHOUT_CERT_INSIDE); + } + + private Optional getSignatureFileName() { + return getFileByExtension(ALLOWED_SIGNATURE_EXTENSIONS); + } + + private Optional getFileByExtension(String[] extensions) { + for (String fileName : handlerPair.getLeft().getFileList()) { + for (String extension : extensions) { + if (extension.equalsIgnoreCase(FilenameUtils.getExtension(fileName))) { + return Optional.of(fileName); + } + } + } + return Optional.empty(); + } + + private Optional getCertificateFileName() { + Optional certFileName = getFileByExtension(ALLOWED_CERTIFICATE_EXTENSIONS); + if(!certFileName.isPresent()){ + return Optional.empty(); + } + String certNameWithoutExtension = FilenameUtils.removeExtension(certFileName.get()); + if (certNameWithoutExtension.equals(FilenameUtils.removeExtension(getArchiveFileName().orElse("")))) { + return certFileName; + } + //cert file name should be the same as package name, e.g. vnfpackage.scar-->vnfpackage.cert + return Optional.empty(); + } +} -- cgit 1.2.3-korg