summaryrefslogtreecommitdiffstats
path: root/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java')
-rw-r--r--openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java188
1 files changed, 141 insertions, 47 deletions
diff --git a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
index d60b54b5e1..fec15b5fcc 100644
--- a/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
+++ b/openecomp-be/backend/openecomp-sdc-vendor-software-product-manager/src/main/java/org/openecomp/sdc/vendorsoftwareproduct/security/SecurityManager.java
@@ -19,14 +19,17 @@
*/
package org.openecomp.sdc.vendorsoftwareproduct.security;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
import com.google.common.collect.ImmutableSet;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
@@ -48,22 +51,28 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.zip.ZipFile;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.OperatorCreationException;
+import org.openecomp.sdc.be.csar.storage.ArtifactInfo;
+import org.openecomp.sdc.common.errors.SdcRuntimeException;
import org.openecomp.sdc.logging.api.Logger;
import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.openecomp.sdc.vendorsoftwareproduct.types.OnboardSignedPackage;
/**
* This is temporary solution. When AAF provides functionality for verifying trustedCertificates, this class should be reviewed Class is responsible
@@ -71,13 +80,12 @@ import org.openecomp.sdc.logging.api.LoggerFactory;
*/
public class SecurityManager {
+ public static final Set<String> ALLOWED_SIGNATURE_EXTENSIONS = Set.of("cms");
+ public static final Set<String> ALLOWED_CERTIFICATE_EXTENSIONS = Set.of("cert", "crt");
private static final String CERTIFICATE_DEFAULT_LOCATION = "cert";
- public static final Set<String> ALLOWED_SIGNATURE_EXTENSIONS = ImmutableSet.of("cms");
- public static final Set<String> ALLOWED_CERTIFICATE_EXTENSIONS = ImmutableSet.of("cert", "crt");
- private Logger logger = LoggerFactory.getLogger(SecurityManager.class);
- private Set<X509Certificate> trustedCertificates = new HashSet<>();
- private Set<X509Certificate> trustedCertificatesFromPackage = new HashSet<>();
- private File certificateDirectory;
+ private static final Logger logger = LoggerFactory.getLogger(SecurityManager.class);
+ private static final String UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION = "Unexpected error occurred during signature validation!";
+ private static final String COULD_NOT_VERIFY_SIGNATURE = "Could not verify signature!";
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
@@ -85,6 +93,10 @@ public class SecurityManager {
}
}
+ private Set<X509Certificate> trustedCertificates = new HashSet<>();
+ private Set<X509Certificate> trustedCertificatesFromPackage = new HashSet<>();
+ private File certificateDirectory;
+
private SecurityManager() {
certificateDirectory = this.getcertDirectory(System.getenv("SDC_CERT_DIR"));
}
@@ -99,20 +111,12 @@ public class SecurityManager {
}
/**
- * Initialization on demand class / synchronized singleton pattern.
- */
- private static class SecurityManagerInstanceHolder {
-
- private static final SecurityManager instance = new SecurityManager();
- }
-
- /**
* Checks the configured location for available trustedCertificates
*
* @return set of trustedCertificates
* @throws SecurityManagerException
*/
- public Set<X509Certificate> getTrustedCertificates() throws SecurityManagerException, FileNotFoundException {
+ public Set<X509Certificate> getTrustedCertificates() throws SecurityManagerException {
//if file number in certificate directory changed reload certs
String[] certFiles = certificateDirectory.list();
if (certFiles == null) {
@@ -145,43 +149,121 @@ public class SecurityManager {
* @return true if signature verified
* @throws SecurityManagerException
*/
- public boolean verifySignedData(final byte[] messageSyntaxSignature, final byte[] packageCert, final byte[] innerPackageFile)
- throws SecurityManagerException {
- try (ByteArrayInputStream signatureStream = new ByteArrayInputStream(messageSyntaxSignature); final PEMParser pemParser = new PEMParser(
- new InputStreamReader(signatureStream))) {
+ public boolean verifySignedData(final byte[] messageSyntaxSignature,
+ final byte[] packageCert,
+ final byte[] innerPackageFile) throws SecurityManagerException {
+ try (final ByteArrayInputStream signatureStream = new ByteArrayInputStream(messageSyntaxSignature);
+ final PEMParser pemParser = new PEMParser(new InputStreamReader(signatureStream))) {
final Object parsedObject = pemParser.readObject();
if (!(parsedObject instanceof ContentInfo)) {
throw new SecurityManagerException("Signature is not recognized");
}
- final ContentInfo signature = ContentInfo.getInstance(parsedObject);
- final CMSTypedData signedContent = new CMSProcessableByteArray(innerPackageFile);
- final CMSSignedData signedData = new CMSSignedData(signedContent, signature);
- final Collection<SignerInformation> signers = signedData.getSignerInfos().getSigners();
- final SignerInformation firstSigner = signers.iterator().next();
- final X509Certificate cert;
- Collection<X509CertificateHolder> certs;
- if (packageCert == null) {
- certs = signedData.getCertificates().getMatches(null);
- cert = readSignCert(certs, firstSigner)
- .orElseThrow(() -> new SecurityManagerException("No certificate found in cms signature that should contain one!"));
- } else {
- certs = parseCertsFromPem(packageCert);
- cert = readSignCert(certs, firstSigner)
- .orElseThrow(() -> new SecurityManagerException("No matching certificate found in certificate file that should contain one!"));
+ return verify(packageCert, new CMSSignedData(new CMSProcessableByteArray(innerPackageFile), ContentInfo.getInstance(parsedObject)));
+ } catch (final IOException | CMSException e) {
+ logger.error(e.getMessage(), e);
+ throw new SecurityManagerException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e);
+ }
+ }
+
+ public boolean verifyPackageSignedData(final OnboardSignedPackage signedPackage, final ArtifactInfo artifactInfo)
+ throws SecurityManagerException {
+ boolean fail = false;
+ final var fileContentHandler = signedPackage.getFileContentHandler();
+ byte[] packageCert = null;
+ final Optional<String> certificateFilePath = signedPackage.getCertificateFilePath();
+ if (certificateFilePath.isPresent()) {
+ packageCert = fileContentHandler.getFileContent(certificateFilePath.get());
+ }
+ final var path = artifactInfo.getPath();
+ final var target = Path.of(path.toString() + "." + UUID.randomUUID());
+
+ try (final var signatureStream = new ByteArrayInputStream(fileContentHandler.getFileContent(signedPackage.getSignatureFilePath()));
+ final var pemParser = new PEMParser(new InputStreamReader(signatureStream))) {
+ final var parsedObject = pemParser.readObject();
+ if (!(parsedObject instanceof ContentInfo)) {
+ fail = true;
+ throw new SecurityManagerException("Signature is not recognized");
}
- trustedCertificatesFromPackage = readTrustedCerts(certs, firstSigner);
- if (verifyCertificate(cert, getTrustedCertificates()) == null) {
+
+ if (!findCSARandExtract(path, target)) {
+ fail = true;
return false;
}
- return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
- } catch (OperatorCreationException | IOException | CMSException e) {
+ final var verify = verify(packageCert, new CMSSignedData(new CMSProcessableFile(target.toFile()), ContentInfo.getInstance(parsedObject)));
+ fail = !verify;
+ return verify;
+ } catch (final IOException e) {
+ fail = true;
logger.error(e.getMessage(), e);
- throw new SecurityManagerException("Unexpected error occurred during signature validation!", e);
- } catch (GeneralSecurityException e) {
- throw new SecurityManagerException("Could not verify signature!", e);
+ throw new SecurityManagerException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e);
+ } catch (final CMSException e) {
+ fail = true;
+ throw new SecurityManagerException(COULD_NOT_VERIFY_SIGNATURE, e);
+ } catch (final SecurityManagerException e) {
+ fail = true;
+ throw e;
+ } finally {
+ deleteFile(target);
+ if (fail) {
+ deleteFile(path);
+ }
+ }
+ }
+
+ private void deleteFile(final Path filePath) {
+ try {
+ Files.delete(filePath);
+ } catch (final IOException e) {
+ logger.warn("Failed to delete '{}' after verifying package signed data", filePath, e);
}
}
+ private boolean verify(final byte[] packageCert, final CMSSignedData signedData) throws SecurityManagerException {
+ final SignerInformation firstSigner = signedData.getSignerInfos().getSigners().iterator().next();
+ final X509Certificate cert;
+ Collection<X509CertificateHolder> certs;
+ if (packageCert == null) {
+ certs = signedData.getCertificates().getMatches(null);
+ cert = readSignCert(certs, firstSigner)
+ .orElseThrow(() -> new SecurityManagerException("No certificate found in cms signature that should contain one!"));
+ } else {
+ try {
+ certs = parseCertsFromPem(packageCert);
+ } catch (final IOException e) {
+ throw new SecurityManagerException("Failed to parse certificate from PEM", e);
+ }
+ cert = readSignCert(certs, firstSigner)
+ .orElseThrow(() -> new SecurityManagerException("No matching certificate found in certificate file that should contain one!"));
+ }
+ trustedCertificatesFromPackage = readTrustedCerts(certs, firstSigner);
+ if (verifyCertificate(cert, getTrustedCertificates()) == null) {
+ return false;
+ }
+ try {
+ return firstSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ } catch (CMSException | OperatorCreationException e) {
+ throw new SecurityManagerException("Failed to verify package signed data", e);
+ }
+ }
+
+ private boolean findCSARandExtract(final Path path, final Path target) throws IOException {
+ final AtomicBoolean found = new AtomicBoolean(false);
+ try (final var zf = new ZipFile(path.toString())) {
+ zf.entries().asIterator().forEachRemaining(entry -> {
+ final var entryName = entry.getName();
+ if (!entry.isDirectory() && entryName.toLowerCase().endsWith(".csar")) {
+ try {
+ Files.copy(zf.getInputStream(entry), target, REPLACE_EXISTING);
+ } catch (final IOException e) {
+ throw new SdcRuntimeException(UNEXPECTED_ERROR_OCCURRED_DURING_SIGNATURE_VALIDATION, e);
+ }
+ found.set(true);
+ }
+ });
+ }
+ return found.get();
+ }
+
private Optional<X509Certificate> readSignCert(final Collection<X509CertificateHolder> certs, final SignerInformation firstSigner) {
return certs.stream().filter(crt -> firstSigner.getSID().match(crt)).findAny().map(this::loadCertificate);
}
@@ -205,7 +287,7 @@ public class SecurityManager {
return allCerts;
}
- private void processCertificateDir() throws SecurityManagerException, FileNotFoundException {
+ private void processCertificateDir() throws SecurityManagerException {
if (!certificateDirectory.exists() || !certificateDirectory.isDirectory()) {
logger.error("Issue with certificate directory, check if exists!");
return;
@@ -253,8 +335,8 @@ public class SecurityManager {
}
}
- private PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set<X509Certificate> additionalCerts)
- throws GeneralSecurityException, SecurityManagerException {
+ private PKIXCertPathBuilderResult verifyCertificate(final X509Certificate cert,
+ final Set<X509Certificate> additionalCerts) throws SecurityManagerException {
if (null == cert) {
throw new SecurityManagerException("The certificate is empty!");
}
@@ -273,7 +355,11 @@ public class SecurityManager {
intermediateCerts.add(additionalCert);
}
}
- return verifyCertificate(cert, trustedRootCerts, intermediateCerts);
+ try {
+ return verifyCertificate(cert, trustedRootCerts, intermediateCerts);
+ } catch (final GeneralSecurityException e) {
+ throw new SecurityManagerException("Failed to verify certificate", e);
+ }
}
private PKIXCertPathBuilderResult verifyCertificate(X509Certificate cert, Set<X509Certificate> allTrustedRootCerts,
@@ -325,4 +411,12 @@ public class SecurityManager {
private boolean isSelfSigned(X509Certificate cert) {
return cert.getIssuerDN().equals(cert.getSubjectDN());
}
+
+ /**
+ * Initialization on demand class / synchronized singleton pattern.
+ */
+ private static class SecurityManagerInstanceHolder {
+
+ private static final SecurityManager instance = new SecurityManager();
+ }
}