summaryrefslogtreecommitdiffstats
path: root/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java')
-rw-r--r--certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java335
1 files changed, 335 insertions, 0 deletions
diff --git a/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java
new file mode 100644
index 00000000..db508603
--- /dev/null
+++ b/certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpResponseHelper.java
@@ -0,0 +1,335 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 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.onap.oom.certservice.cmpv2client.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.bouncycastle.asn1.cmp.CMPCertificate;
+import org.bouncycastle.asn1.cmp.CertRepMessage;
+import org.bouncycastle.asn1.cmp.ErrorMsgContent;
+import org.bouncycastle.asn1.cmp.PKIBody;
+import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
+import org.onap.oom.certservice.cmpv2client.exceptions.PkiErrorException;
+import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class CmpResponseHelper {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CmpResponseHelper.class);
+
+ private CmpResponseHelper() {
+ }
+
+ static void checkIfCmpResponseContainsError(PKIMessage respPkiMessage)
+ throws CmpClientException {
+ if (respPkiMessage.getBody().getType() == PKIBody.TYPE_ERROR) {
+ final ErrorMsgContent errorMsgContent =
+ (ErrorMsgContent) respPkiMessage.getBody().getContent();
+ PkiErrorException pkiErrorException =
+ new PkiErrorException(
+ errorMsgContent.getPKIStatusInfo().getStatusString().getStringAt(0).getString());
+ CmpClientException cmpClientException =
+ new CmpClientException("Error in the PkiMessage response", pkiErrorException);
+ LOG.error("Error in the PkiMessage response: {} ", pkiErrorException.getMessage());
+ throw cmpClientException;
+ }
+ }
+
+
+ /**
+ * Puts together certChain and Trust store and verifies the certChain
+ *
+ * @param respPkiMessage PKIMessage that may contain extra certs used for certchain
+ * @param certRepMessage CertRepMessage that should contain rootCA for certchain
+ * @param leafCertificate certificate returned from our original Cert Request
+ * @return model for certification containing certificate chain and trusted certificates
+ * @throws CertificateParsingException thrown if error occurs while parsing certificate
+ * @throws IOException thrown if IOException occurs while parsing certificate
+ * @throws CmpClientException thrown if error occurs during the verification of the certChain
+ */
+ static Cmpv2CertificationModel verifyAndReturnCertChainAndTrustSTore(
+ PKIMessage respPkiMessage, CertRepMessage certRepMessage, X509Certificate leafCertificate)
+ throws CertificateParsingException, IOException, CmpClientException {
+ Map<X500Name, X509Certificate> certificates = mapAllCertificates(respPkiMessage, certRepMessage);
+ return extractCertificationModel(certificates, leafCertificate);
+ }
+
+ private static Map<X500Name, X509Certificate> mapAllCertificates(
+ PKIMessage respPkiMessage, CertRepMessage certRepMessage
+ )
+ throws IOException, CertificateParsingException, CmpClientException {
+
+ Map<X500Name, X509Certificate> certificates = new HashMap<>();
+
+ CMPCertificate[] extraCerts = respPkiMessage.getExtraCerts();
+ certificates.putAll(mapCertificates(extraCerts));
+
+ CMPCertificate[] caPubsCerts = certRepMessage.getCaPubs();
+ certificates.putAll(mapCertificates(caPubsCerts));
+
+ return certificates;
+ }
+
+ private static Map<X500Name, X509Certificate> mapCertificates(
+ CMPCertificate[] cmpCertificates)
+ throws CertificateParsingException, CmpClientException, IOException {
+
+ Map<X500Name, X509Certificate> certificates = new HashMap<>();
+ if (cmpCertificates != null) {
+ for (CMPCertificate certificate : cmpCertificates) {
+ getCertFromByteArray(certificate.getEncoded(), X509Certificate.class)
+ .ifPresent(x509Certificate ->
+ certificates.put(extractSubjectDn(x509Certificate), x509Certificate)
+ );
+ }
+ }
+
+ return certificates;
+ }
+
+ private static Cmpv2CertificationModel extractCertificationModel(
+ Map<X500Name, X509Certificate> certificates, X509Certificate leafCertificate
+ )
+ throws CmpClientException {
+ List<X509Certificate> certificateChain = new ArrayList<>();
+ X509Certificate previousCertificateInChain;
+ X509Certificate nextCertificateInChain = leafCertificate;
+ do {
+ certificateChain.add(nextCertificateInChain);
+ certificates.remove(extractSubjectDn(nextCertificateInChain));
+ previousCertificateInChain = nextCertificateInChain;
+ nextCertificateInChain = certificates.get(extractIssuerDn(nextCertificateInChain));
+ verify(previousCertificateInChain, nextCertificateInChain, null);
+ }
+ while (!isSelfSign(nextCertificateInChain));
+ List<X509Certificate> trustedCertificates = new ArrayList<>(certificates.values());
+
+ return new Cmpv2CertificationModel(certificateChain, trustedCertificates);
+ }
+
+ private static boolean isSelfSign(X509Certificate certificate) {
+ return extractIssuerDn(certificate).equals(extractSubjectDn(certificate));
+ }
+
+ private static X500Name extractIssuerDn(X509Certificate x509Certificate) {
+ return X500Name.getInstance(x509Certificate.getIssuerDN());
+ }
+
+ private static X500Name extractSubjectDn(X509Certificate x509Certificate) {
+ return X500Name.getInstance(x509Certificate.getSubjectDN());
+ }
+
+
+ /**
+ * Check the certificate with CA certificate.
+ *
+ * @param certificate X.509 certificate to verify. May not be null.
+ * @param caCertChain Collection of X509Certificates. May not be null, an empty list or a
+ * Collection with null entries.
+ * @param date Date to verify at, or null to use current time.
+ * @param pkixCertPathCheckers optional PKIXCertPathChecker implementations to use during cert
+ * path validation
+ * @throws CmpClientException if certificate could not be validated
+ */
+ private static void verify(
+ X509Certificate certificate,
+ X509Certificate caCertChain,
+ Date date,
+ PKIXCertPathChecker... pkixCertPathCheckers)
+ throws CmpClientException {
+ try {
+ verifyCertificates(certificate, caCertChain, date, pkixCertPathCheckers);
+ } catch (CertPathValidatorException cpve) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Invalid certificate or certificate not issued by specified CA: ", cpve);
+ LOG.error("Invalid certificate or certificate not issued by specified CA: ", cpve);
+ throw cmpClientException;
+ } catch (CertificateException ce) {
+ CmpClientException cmpClientException =
+ new CmpClientException("Something was wrong with the supplied certificate", ce);
+ LOG.error("Something was wrong with the supplied certificate", ce);
+ throw cmpClientException;
+ } catch (NoSuchProviderException nspe) {
+ CmpClientException cmpClientException =
+ new CmpClientException("BouncyCastle provider not found.", nspe);
+ LOG.error("BouncyCastle provider not found.", nspe);
+ throw cmpClientException;
+ } catch (NoSuchAlgorithmException nsae) {
+ CmpClientException cmpClientException =
+ new CmpClientException("Algorithm PKIX was not found.", nsae);
+ LOG.error("Algorithm PKIX was not found.", nsae);
+ throw cmpClientException;
+ } catch (InvalidAlgorithmParameterException iape) {
+ CmpClientException cmpClientException =
+ new CmpClientException(
+ "Either ca certificate chain was empty,"
+ + " or the certificate was on an inappropriate type for a PKIX path checker.",
+ iape);
+ LOG.error(
+ "Either ca certificate chain was empty, "
+ + "or the certificate was on an inappropriate type for a PKIX path checker.",
+ iape);
+ throw cmpClientException;
+ }
+ }
+
+ private static void verifyCertificates(
+ X509Certificate certificate,
+ X509Certificate caCertChain,
+ Date date,
+ PKIXCertPathChecker[] pkixCertPathCheckers)
+ throws CertificateException, NoSuchProviderException, InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, CertPathValidatorException {
+ if (caCertChain == null) {
+ final String noRootCaCertificateMessage = "Server response does not contain proper root CA certificate";
+ throw new CertificateException(noRootCaCertificateMessage);
+ }
+ LOG.debug(
+ "Verifying certificate {} as part of cert chain with certificate {}",
+ certificate.getSubjectDN().getName(),
+ caCertChain.getSubjectDN().getName());
+ CertPath cp = getCertPath(certificate);
+ PKIXParameters params = getPkixParameters(caCertChain, date, pkixCertPathCheckers);
+ CertPathValidator cpv =
+ CertPathValidator.getInstance("PKIX", BouncyCastleProvider.PROVIDER_NAME);
+ PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Certificate verify result:{} ", result);
+ }
+ }
+
+ private static PKIXParameters getPkixParameters(
+ X509Certificate caCertChain, Date date, PKIXCertPathChecker[] pkixCertPathCheckers)
+ throws InvalidAlgorithmParameterException {
+ TrustAnchor anchor = new TrustAnchor(caCertChain, null);
+ PKIXParameters params = new PKIXParameters(Collections.singleton(anchor));
+ for (final PKIXCertPathChecker pkixCertPathChecker : pkixCertPathCheckers) {
+ params.addCertPathChecker(pkixCertPathChecker);
+ }
+ params.setRevocationEnabled(false);
+ params.setDate(date);
+ return params;
+ }
+
+ private static CertPath getCertPath(X509Certificate certificate)
+ throws CertificateException, NoSuchProviderException {
+ ArrayList<X509Certificate> certlist = new ArrayList<>();
+ certlist.add(certificate);
+ return CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME)
+ .generateCertPath(certlist);
+ }
+
+ /**
+ * Returns a CertificateFactory that can be used to create certificates from byte arrays and such.
+ *
+ * @param provider Security provider that should be used to create certificates, default BC is
+ * null is passed.
+ * @return CertificateFactory for creating certificate
+ */
+ private static CertificateFactory getCertificateFactory(final String provider)
+ throws CmpClientException {
+ LOG.debug("Creating certificate Factory to generate certificate using provider {}", provider);
+ final String prov;
+ prov = Objects.requireNonNullElse(provider, BouncyCastleProvider.PROVIDER_NAME);
+ try {
+ return CertificateFactory.getInstance("X.509", prov);
+ } catch (NoSuchProviderException nspe) {
+ CmpClientException cmpClientException = new CmpClientException("NoSuchProvider: ", nspe);
+ LOG.error("NoSuchProvider: ", nspe);
+ throw cmpClientException;
+ } catch (CertificateException ce) {
+ CmpClientException cmpClientException = new CmpClientException("CertificateException: ", ce);
+ LOG.error("CertificateException: ", ce);
+ throw cmpClientException;
+ }
+ }
+
+ /**
+ * @param cert byte array that contains certificate
+ * @param returnType the type of Certificate to be returned, for example X509Certificate.class.
+ * Certificate.class can be used if certificate type is unknown.
+ * @throws CertificateParsingException if the byte array does not contain a proper certificate.
+ */
+ static <T extends Certificate> Optional<X509Certificate> getCertFromByteArray(
+ byte[] cert, Class<T> returnType) throws CertificateParsingException, CmpClientException {
+ LOG.debug("Retrieving certificate of type {} from byte array.", returnType);
+ String prov = BouncyCastleProvider.PROVIDER_NAME;
+
+ if (returnType.equals(X509Certificate.class)) {
+ return parseX509Certificate(prov, cert);
+ } else {
+ LOG.debug("Certificate of type {} was skipped, because type of certificate is not 'X509Certificate'.", returnType);
+ return Optional.empty();
+ }
+ }
+
+
+ /**
+ * Parse a X509Certificate from an array of bytes
+ *
+ * @param provider a provider name
+ * @param cert a byte array containing an encoded certificate
+ * @return a decoded X509Certificate
+ * @throws CertificateParsingException if the byte array wasn't valid, or contained a certificate
+ * other than an X509 Certificate.
+ */
+ private static Optional<X509Certificate> parseX509Certificate(String provider, byte[] cert)
+ throws CertificateParsingException, CmpClientException {
+ LOG.debug("Parsing X509Certificate from bytes with provider {}", provider);
+ final CertificateFactory cf = getCertificateFactory(provider);
+ X509Certificate result;
+ try {
+ result = (X509Certificate) Objects.requireNonNull(cf).generateCertificate(new ByteArrayInputStream(cert));
+ return Optional.ofNullable(result);
+ } catch (CertificateException ce) {
+ throw new CertificateParsingException("Could not parse byte array as X509Certificate ", ce);
+ }
+ }
+}