From a999a364ac1d9557decfd3b0157cbe46181006f3 Mon Sep 17 00:00:00 2001 From: Bartosz Gardziejewski Date: Wed, 25 Mar 2020 14:08:49 +0100 Subject: [PATCH] Refactor CmpResponseHelper to support intermediate certificate Issue-ID: AAF-1107 Signed-off-by: Bartosz Gardziejewski Change-Id: Ia2e2f9ba1fbcf0482121ffb5f451c408774481ba --- .../certification/CertificationProvider.java | 7 +- .../aaf/certservice/cmpv2client/api/CmpClient.java | 11 +- .../cmpv2client/impl/CmpClientImpl.java | 27 +- .../cmpv2client/impl/CmpResponseHelper.java | 276 ++++------ .../cmpv2client/model/Cmpv2CertificationModel.java | 44 ++ .../certification/CertificationProviderTest.java | 8 +- .../certservice/cmpv2client/Cmpv2ClientTest.java | 3 +- .../cmpv2client/impl/CmpResponseHelperTest.java | 609 +++++++++++++++++++++ 8 files changed, 800 insertions(+), 185 deletions(-) create mode 100644 certService/src/main/java/org/onap/aaf/certservice/cmpv2client/model/Cmpv2CertificationModel.java create mode 100644 certService/src/test/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelperTest.java diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/CertificationProvider.java b/certService/src/main/java/org/onap/aaf/certservice/certification/CertificationProvider.java index 4435aa75..2478cc58 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/certification/CertificationProvider.java +++ b/certService/src/main/java/org/onap/aaf/certservice/certification/CertificationProvider.java @@ -28,6 +28,7 @@ import org.onap.aaf.certservice.certification.model.CertificationModel; import org.onap.aaf.certservice.certification.model.CsrModel; import org.onap.aaf.certservice.cmpv2client.api.CmpClient; import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -53,9 +54,9 @@ public class CertificationProvider { public CertificationModel signCsr(CsrModel csrModel, Cmpv2Server server) throws CmpClientException { - List> certificates = cmpClient.createCertificate(csrModel, server); - return new CertificationModel(convertFromX509CertificateListToPemList(certificates.get(0)), - convertFromX509CertificateListToPemList(certificates.get(1))); + Cmpv2CertificationModel certificates = cmpClient.createCertificate(csrModel, server); + return new CertificationModel(convertFromX509CertificateListToPemList(certificates.getCertificateChain()), + convertFromX509CertificateListToPemList(certificates.getTrustedCertificates())); } private static List convertFromX509CertificateListToPemList(List certificates) { diff --git a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/api/CmpClient.java b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/api/CmpClient.java index 6ff1bf68..cccb744d 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/api/CmpClient.java +++ b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/api/CmpClient.java @@ -20,13 +20,12 @@ package org.onap.aaf.certservice.cmpv2client.api; -import java.security.cert.X509Certificate; import java.util.Date; -import java.util.List; import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; import org.onap.aaf.certservice.certification.model.CsrModel; import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; /** * This class represent CmpV2Client Interface for obtaining X.509 Digital Certificates in a Public @@ -47,10 +46,10 @@ public interface CmpClient { * before this date. * @param notAfter An optional validity to set in the created certificate, Certificate not valid * after this date. - * @return {@link X509Certificate} The newly created Certificate. + * @return model for certification containing certificate chain and trusted certificates * @throws CmpClientException if client error occurs. */ - List> createCertificate( + Cmpv2CertificationModel createCertificate( CsrModel csrModel, Cmpv2Server server, Date notBefore, @@ -65,10 +64,10 @@ public interface CmpClient { * * @param csrModel Certificate Signing Request Model. Must not be {@code null}. * @param server CMPv2 server. Must not be {@code null}. - * @return {@link X509Certificate} The newly created Certificate. + * @return model for certification containing certificate chain and trusted certificates * @throws CmpClientException if client error occurs. */ - List> createCertificate( + Cmpv2CertificationModel createCertificate( CsrModel csrModel, Cmpv2Server server) throws CmpClientException; diff --git a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpClientImpl.java b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpClientImpl.java index 28731f29..87991132 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpClientImpl.java +++ b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpClientImpl.java @@ -24,7 +24,7 @@ import java.security.KeyPair; import java.security.PublicKey; import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.checkIfCmpResponseContainsError; -import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.getCertfromByteArray; +import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.getCertFromByteArray; import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore; import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseValidationHelper.checkImplicitConfirm; import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifyPasswordBasedProtection; @@ -33,10 +33,8 @@ import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseValidationHel import java.io.IOException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Objects; import java.util.Optional; @@ -53,6 +51,7 @@ import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; import org.onap.aaf.certservice.certification.model.CsrModel; import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException; import org.onap.aaf.certservice.cmpv2client.api.CmpClient; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,7 +72,7 @@ public class CmpClientImpl implements CmpClient { } @Override - public List> createCertificate( + public Cmpv2CertificationModel createCertificate( CsrModel csrModel, Cmpv2Server server, Date notBefore, @@ -101,7 +100,7 @@ public class CmpClientImpl implements CmpClient { } @Override - public List> createCertificate(CsrModel csrModel, Cmpv2Server server) + public Cmpv2CertificationModel createCertificate(CsrModel csrModel, Cmpv2Server server) throws CmpClientException { return createCertificate(csrModel, server, null, null); } @@ -145,7 +144,7 @@ public class CmpClientImpl implements CmpClient { } } - private List> checkCmpCertRepMessage(final PKIMessage respPkiMessage) + private Cmpv2CertificationModel checkCmpCertRepMessage(final PKIMessage respPkiMessage) throws CmpClientException { final PKIBody pkiBody = respPkiMessage.getBody(); if (Objects.nonNull(pkiBody) && pkiBody.getContent() instanceof CertRepMessage) { @@ -163,25 +162,25 @@ public class CmpClientImpl implements CmpClient { throw cmpClientException; } } else { - return new ArrayList<>(Collections.emptyList()); + return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList()); } } - return new ArrayList<>(Collections.emptyList()); + return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList()); } - private List> verifyReturnCertChainAndTrustStore( + private Cmpv2CertificationModel verifyReturnCertChainAndTrustStore( PKIMessage respPkiMessage, CertRepMessage certRepMessage, CertResponse certResponse) throws CertificateParsingException, CmpClientException, IOException { LOG.info("Verifying certificates returned as part of CertResponse."); final CMPCertificate cmpCertificate = certResponse.getCertifiedKeyPair().getCertOrEncCert().getCertificate(); final Optional leafCertificate = - getCertfromByteArray(cmpCertificate.getEncoded(), X509Certificate.class); + getCertFromByteArray(cmpCertificate.getEncoded(), X509Certificate.class); if (leafCertificate.isPresent()) { return verifyAndReturnCertChainAndTrustSTore( respPkiMessage, certRepMessage, leafCertificate.get()); } - return Collections.emptyList(); + return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList()); } private CertResponse getCertificateResponseContainingNewCertificate( @@ -192,8 +191,8 @@ public class CmpClientImpl implements CmpClient { /** * Validate inputs for Certificate Creation. * - * @param csrModel Certificate Signing Request model. Must not be {@code null}. - * @param server CMPv2 Server. Must not be {@code null}. + * @param csrModel Certificate Signing Request model. Must not be {@code null}. + * @param server CMPv2 Server. Must not be {@code null}. * @throws IllegalArgumentException if Before Date is set after the After Date. */ private static void validate( @@ -222,7 +221,7 @@ public class CmpClientImpl implements CmpClient { } } - private List> retrieveCertificates( + private Cmpv2CertificationModel retrieveCertificates( CsrModel csrModel, Cmpv2Server server, PKIMessage pkiMessage, Cmpv2HttpClient cmpv2HttpClient) throws CmpClientException { final byte[] respBytes = cmpv2HttpClient.postRequest(pkiMessage, server.getUrl(), server.getCaName()); diff --git a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelper.java b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelper.java index b2a7b29e..3cb0b0c5 100644 --- a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelper.java +++ b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelper.java @@ -40,7 +40,9 @@ 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; @@ -49,9 +51,11 @@ 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.aaf.certservice.cmpv2client.exceptions.CmpClientException; import org.onap.aaf.certservice.cmpv2client.exceptions.PkiErrorException; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,7 +66,7 @@ public final class CmpResponseHelper { private CmpResponseHelper() { } - public static void checkIfCmpResponseContainsError(PKIMessage respPkiMessage) + static void checkIfCmpResponseContainsError(PKIMessage respPkiMessage) throws CmpClientException { if (respPkiMessage.getBody().getType() == PKIBody.TYPE_ERROR) { final ErrorMsgContent errorMsgContent = @@ -77,54 +81,91 @@ public final class CmpResponseHelper { } } + /** - * @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. + * 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 */ - public static Optional getCertfromByteArray( - byte[] cert, Class returnType) throws CertificateParsingException, CmpClientException { - LOG.debug("Retrieving certificate of type {} from byte array.", returnType); - return getCertfromByteArray(cert, BouncyCastleProvider.PROVIDER_NAME, returnType); + static Cmpv2CertificationModel verifyAndReturnCertChainAndTrustSTore( + PKIMessage respPkiMessage, CertRepMessage certRepMessage, X509Certificate leafCertificate) + throws CertificateParsingException, IOException, CmpClientException { + Map certificates = mapAllCertificates(respPkiMessage, certRepMessage); + return extractCertificationModel(certificates, leafCertificate); } - /** - * @param cert byte array that contains certificate - * @param provider provider used to generate certificate from bytes - * @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. - */ - public static Optional getCertfromByteArray( - byte[] cert, String provider, Class returnType) - throws CertificateParsingException, CmpClientException { - String prov = provider; - if (provider == null) { - prov = BouncyCastleProvider.PROVIDER_NAME; - } + private static Map mapAllCertificates( + PKIMessage respPkiMessage, CertRepMessage certRepMessage + ) + throws IOException, CertificateParsingException, CmpClientException { - if (returnType.equals(X509Certificate.class)) { - return parseX509Certificate(prov, cert); + Map certificates = new HashMap<>(); + + CMPCertificate[] extraCerts = respPkiMessage.getExtraCerts(); + certificates.putAll(mapCertificates(extraCerts)); + + CMPCertificate[] caPubsCerts = certRepMessage.getCaPubs(); + certificates.putAll(mapCertificates(caPubsCerts)); + + return certificates; + } + + private static Map mapCertificates( + CMPCertificate[] cmpCertificates) + throws CertificateParsingException, CmpClientException, IOException { + + Map certificates = new HashMap<>(); + if (cmpCertificates != null) { + for (CMPCertificate certificate : cmpCertificates) { + getCertFromByteArray(certificate.getEncoded(), X509Certificate.class) + .ifPresent(x509Certificate -> + certificates.put(extractSubjectDn(x509Certificate), x509Certificate) + ); + } } - return Optional.empty(); + + return certificates; } - /** - * Check the certificate with CA certificate. - * - * @param caCertChain Collection of X509Certificates. May not be null, an empty list or a - * Collection with null entries. - * @throws CmpClientException if verification failed - */ - public static void verify(List caCertChain) throws CmpClientException { - int iterator = 1; - while (iterator < caCertChain.size()) { - verify(caCertChain.get(iterator - 1), caCertChain.get(iterator), null); - iterator += 1; + private static Cmpv2CertificationModel extractCertificationModel( + Map certificates, X509Certificate leafCertificate + ) + throws CmpClientException { + List 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 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. * @@ -136,7 +177,7 @@ public final class CmpResponseHelper { * path validation * @throws CmpClientException if certificate could not be validated */ - public static void verify( + private static void verify( X509Certificate certificate, X509Certificate caCertChain, Date date, @@ -179,13 +220,17 @@ public final class CmpResponseHelper { } } - public static void verifyCertificates( + 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(), @@ -200,7 +245,7 @@ public final class CmpResponseHelper { } } - public static PKIXParameters getPkixParameters( + private static PKIXParameters getPkixParameters( X509Certificate caCertChain, Date date, PKIXCertPathChecker[] pkixCertPathCheckers) throws InvalidAlgorithmParameterException { TrustAnchor anchor = new TrustAnchor(caCertChain, null); @@ -213,7 +258,7 @@ public final class CmpResponseHelper { return params; } - public static CertPath getCertPath(X509Certificate certificate) + private static CertPath getCertPath(X509Certificate certificate) throws CertificateException, NoSuchProviderException { ArrayList certlist = new ArrayList<>(); certlist.add(certificate); @@ -221,34 +266,6 @@ public final class CmpResponseHelper { .generateCertPath(certlist); } - /** - * 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. - */ - public static Optional 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)); - } catch (CertificateException ce) { - throw new CertificateParsingException("Could not parse byte array as X509Certificate ", ce); - } - if (result != null) { - return Optional.of(result); - } else { - throw new CertificateParsingException("Could not parse byte array as X509Certificate."); - } - } - /** * Returns a CertificateFactory that can be used to create certificates from byte arrays and such. * @@ -256,7 +273,7 @@ public final class CmpResponseHelper { * null is passed. * @return CertificateFactory for creating certificate */ - public static CertificateFactory getCertificateFactory(final String provider) + private static CertificateFactory getCertificateFactory(final String provider) throws CmpClientException { LOG.debug("Creating certificate Factory to generate certificate using provider {}", provider); final String prov; @@ -275,99 +292,44 @@ public final class CmpResponseHelper { } /** - * 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 list of two lists, CertChain and TrustStore - * @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 + * @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. */ - public static List> verifyAndReturnCertChainAndTrustSTore( - PKIMessage respPkiMessage, CertRepMessage certRepMessage, X509Certificate leafCertificate) - throws CertificateParsingException, IOException, CmpClientException { - List certChain = - addExtraCertsToChain(respPkiMessage, certRepMessage, leafCertificate); - List certNames = getNamesOfCerts(certChain); - LOG.debug("Verifying the following certificates in the cert chain: {}", certNames); - verify(certChain); - ArrayList trustStore = new ArrayList<>(); - final int rootCaIndex = certChain.size() - 1; - trustStore.add(certChain.get(rootCaIndex)); - certChain.remove(rootCaIndex); - List> listOfArray = new ArrayList<>(); - listOfArray.add(certChain); - listOfArray.add(trustStore); - return listOfArray; - } + static Optional getCertFromByteArray( + byte[] cert, Class returnType) throws CertificateParsingException, CmpClientException { + LOG.debug("Retrieving certificate of type {} from byte array.", returnType); + String prov = BouncyCastleProvider.PROVIDER_NAME; - public static List getNamesOfCerts(List certChain) { - List certNames = new ArrayList<>(); - certChain.forEach(cert -> certNames.add(cert.getSubjectDN().getName())); - return certNames; + 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(); + } } + /** - * checks whether PKIMessage contains extracerts to create certchain, if not creates from caPubs + * Parse a X509Certificate from an array of bytes * - * @param respPkiMessage PKIMessage that may contain extra certs used for certchain - * @param certRepMessage CertRepMessage that should contain rootCA for certchain - * @param leafCert certificate at top of certChain. - * @throws CertificateParsingException thrown if error occurs while parsing certificate - * @throws IOException thrown if IOException occurs while parsing certificate - * @throws CmpClientException thrown if there are errors creating CertificateFactory + * @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. */ - public static List addExtraCertsToChain( - PKIMessage respPkiMessage, CertRepMessage certRepMessage, X509Certificate leafCert) - throws CertificateParsingException, IOException, CmpClientException { - List certChain = new ArrayList<>(); - certChain.add(leafCert); - if (respPkiMessage.getExtraCerts() != null) { - final CMPCertificate[] extraCerts = respPkiMessage.getExtraCerts(); - for (CMPCertificate cmpCert : extraCerts) { - Optional cert = - getCertfromByteArray(cmpCert.getEncoded(), X509Certificate.class); - certChain = - ifCertPresent( - certChain, - cert, - "Adding certificate from extra certs {} to cert chain", - "Couldn't add certificate from extra certs, certificate wasn't an X509Certificate"); - return certChain; - } - } else { - final CMPCertificate respCmpCaCert = getRootCa(certRepMessage); - Optional cert = - getCertfromByteArray(respCmpCaCert.getEncoded(), X509Certificate.class); - certChain = - ifCertPresent( - certChain, - cert, - "Adding certificate from CaPubs {} to TrustStore", - "Couldn't add certificate from CaPubs, certificate wasn't an X509Certificate"); - return certChain; - } - return Collections.emptyList(); - } - - public static List ifCertPresent( - List certChain, - Optional cert, - String certPresentString, - String certUnavailableString) { - if (cert.isPresent()) { - LOG.debug(certPresentString, cert.get().getSubjectDN().getName()); - certChain.add(cert.get()); - return certChain; - } else { - LOG.debug(certUnavailableString); - return certChain; + private static Optional 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); } } - - private static CMPCertificate getRootCa(CertRepMessage certRepMessage) { - return certRepMessage.getCaPubs()[0]; - } } diff --git a/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/model/Cmpv2CertificationModel.java b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/model/Cmpv2CertificationModel.java new file mode 100644 index 00000000..5d48b978 --- /dev/null +++ b/certService/src/main/java/org/onap/aaf/certservice/cmpv2client/model/Cmpv2CertificationModel.java @@ -0,0 +1,44 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nokia. 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.cmpv2client.model; + +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.List; + +public class Cmpv2CertificationModel { + + private final List certificateChain; + private final List trustedCertificates; + + public Cmpv2CertificationModel(List certificateChain, List trustedCertificates) { + this.certificateChain = certificateChain; + this.trustedCertificates = trustedCertificates; + } + + public List getCertificateChain() { + return Collections.unmodifiableList(certificateChain); + } + + public List getTrustedCertificates() { + return Collections.unmodifiableList(trustedCertificates); + } +} diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/CertificationProviderTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/CertificationProviderTest.java index cf3c7233..a590c5ea 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/certification/CertificationProviderTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/certification/CertificationProviderTest.java @@ -31,6 +31,7 @@ import org.onap.aaf.certservice.certification.model.CertificationModel; import org.onap.aaf.certservice.certification.model.CsrModel; import org.onap.aaf.certservice.cmpv2client.api.CmpClient; import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; import java.io.IOException; import java.io.InputStream; @@ -38,9 +39,7 @@ import java.nio.charset.StandardCharsets; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.Arrays; import java.util.Collections; -import java.util.List; import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; @@ -115,14 +114,15 @@ class CertificationProviderTest { assertThat(exception.getMessage()).isEqualTo(expectedErrorMessage); } - private List> createCorrectClientResponse() + private Cmpv2CertificationModel createCorrectClientResponse() throws CertificateException, NoSuchProviderException { InputStream certificateChain = getClass().getClassLoader().getResourceAsStream("certificateChain.first"); InputStream trustedCertificate = getClass().getClassLoader().getResourceAsStream("trustedCertificates.first"); X509Certificate x509Certificate = new CertificateFactoryProvider().generateCertificate(certificateChain); X509Certificate x509TrustedCertificate = new CertificateFactoryProvider().generateCertificate(trustedCertificate); - return Arrays.asList(Collections.singletonList(x509Certificate), + return new Cmpv2CertificationModel( + Collections.singletonList(x509Certificate), Collections.singletonList(x509TrustedCertificate)); } diff --git a/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/Cmpv2ClientTest.java b/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/Cmpv2ClientTest.java index 06eeecce..05bda54b 100644 --- a/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/Cmpv2ClientTest.java +++ b/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/Cmpv2ClientTest.java @@ -62,6 +62,7 @@ import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server; import org.onap.aaf.certservice.certification.model.CsrModel; import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException; import org.onap.aaf.certservice.cmpv2client.impl.CmpClientImpl; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; class Cmpv2ClientTest { @@ -152,7 +153,7 @@ class Cmpv2ClientTest { } CmpClientImpl cmpClient = spy(new CmpClientImpl(httpClient)); // when - List> cmpClientResult = + Cmpv2CertificationModel cmpClientResult = cmpClient.createCertificate(csrModel, server, notBefore, notAfter); // then assertNotNull(cmpClientResult); diff --git a/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelperTest.java b/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelperTest.java new file mode 100644 index 00000000..c41d6364 --- /dev/null +++ b/certService/src/test/java/org/onap/aaf/certservice/cmpv2client/impl/CmpResponseHelperTest.java @@ -0,0 +1,609 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nokia. 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.aaf.certservice.cmpv2client.impl; + +import org.bouncycastle.asn1.cmp.CMPCertificate; +import org.bouncycastle.asn1.cmp.CertRepMessage; +import org.bouncycastle.asn1.cmp.PKIMessage; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException; +import org.onap.aaf.certservice.cmpv2client.model.Cmpv2CertificationModel; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringReader; +import java.security.NoSuchProviderException; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class CmpResponseHelperTest { + + + private static final String EXPECTED_ERROR_MESSAGE = "Something was wrong with the supplied certificate"; + + private static final String TEST_1LAYER_ENTITY_CERT = "" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEqDCCAxCgAwIBAgIUFioEkVJsxfZGGDMEyCA8Rin3uhQwDQYJKoZIhvcNAQEL\n" + + "BQAwYTEjMCEGCgmSJomT8ixkAQEME2MtMDM1ZDk4NTAwYzhiN2JiMjIxFTATBgNV\n" + + "BAMMDE1hbmFnZW1lbnRDQTEjMCEGA1UECgwaRUpCQ0EgQ29udGFpbmVyIFF1aWNr\n" + + "c3RhcnQwHhcNMjAwMzI0MTEzNTU0WhcNMjIwMzI0MTEzNTU0WjCBljEgMB4GCSqG\n" + + "SIb3DQEJARYRQ29tbW9uTmFtZUBjbi5jb20xDjAMBgNVBAMMBUNsMTIzMQ0wCwYD\n" + + "VQQLDARPTkFQMRkwFwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMRYwFAYDVQQHDA1T\n" + + "YW4tRnJhbmNpc2NvMRMwEQYDVQQIDApDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzCC\n" + + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL94FcmRn/g9Y9ZrEL+jKiud\n" + + "xzDdtVLoF0ijZOGG0rnzyimzzwOjd8LA0jiZlYtpoDef95bbMeZJMKzE3bA8EMFp\n" + + "hynqUHs/KdsLBV+o3J6EzlpYHrwypX7kOriw9o4dmPAxvJHXTu3HC2SejJjHHArk\n" + + "FyahEJ03ypvCJx3iPvGXkLI9tZetobiVXslBJd5t0hQj+JQxzAlTwS0fV+xMowFT\n" + + "css2IlGXfQgd88cdhXBVOE0//qln1ko3G3KeH58iIWLqh9KG660SCeoTCop7bO1N\n" + + "abVrcXlgdE06hAvzTj3FoBxqO5KEWDPo2Dr11qRdq8bLP2T0EbTzAw4DPUwE+H8C\n" + + "AwEAAaOBoTCBnjAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFDPaBc+EX/hCLe5c\n" + + "d+oZIxcQZ1tHMB8GA1UdEQQYMBaCBUNsMTIzgg10ZXN0Lm9uYXAub3JnMB0GA1Ud\n" + + "JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDAdBgNVHQ4EFgQU4dP1HuV9O+sHInl+\n" + + "WuvdDJ63lp8wDgYDVR0PAQH/BAQDAgXgMA0GCSqGSIb3DQEBCwUAA4IBgQBWTF8C\n" + + "sH0ir4bj7rTlJMf5o7apkXFeQ/c7+zXnSLCfXqwM6ad0EDh3FixfTC8IpW5CaENt\n" + + "zTR7IGJr06ccwLgsigR7FxJKnEkxJiBxzkE3zFOEel3KAnV2b7KvOP7cJAzsCdcS\n" + + "iZU475XHOw4Ox3k8fHzhTJJa0Tzw5EjQ3GO99HTiUClGrjJuYDLfen1q7IQSNuTY\n" + + "FzxJZjyqzi34pkKeCNSPRj8Z8Q5aZiWqlmzSJmZRT83xzzeW/pQ1JwvIrWwrbEjR\n" + + "FPXBlUa1n2HztkDgeBQfRyMAj5ixFV+s1Jj+cEYl3pjbugnuHfgBdSJokXFGBo6N\n" + + "8PTd1CnMGWcWiMyhbTwNm2UiSr5KhQbjABjiUzDp4C7jFhIzmu/4/tm2uA+y0xPN\n" + + "342uEZC0ZSZmpCIbQMhPaBNjSHeHj8NaLHjnt5jppLkMxScayRqMvSW07eNew2+k\n" + + "VYJD6z6gfy4y+Y5MSLfvddq1JdPDU86TFprtD1ydcUBS5tduYQG2+1bLgpE=" + + "\n-----END CERTIFICATE-----\n"; + + private static final String TEST_1LAYER_CA_CERT = "" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIEszCCAxugAwIBAgIUEhkh+zJtXZN3K3kzQYcbp2smyIkwDQYJKoZIhvcNAQEL\n" + + "BQAwYTEjMCEGCgmSJomT8ixkAQEME2MtMDM1ZDk4NTAwYzhiN2JiMjIxFTATBgNV\n" + + "BAMMDE1hbmFnZW1lbnRDQTEjMCEGA1UECgwaRUpCQ0EgQ29udGFpbmVyIFF1aWNr\n" + + "c3RhcnQwHhcNMjAwMzI0MTAyODQyWhcNMzAwMzI0MTAyODQyWjBhMSMwIQYKCZIm\n" + + "iZPyLGQBAQwTYy0wMzVkOTg1MDBjOGI3YmIyMjEVMBMGA1UEAwwMTWFuYWdlbWVu\n" + + "dENBMSMwIQYDVQQKDBpFSkJDQSBDb250YWluZXIgUXVpY2tzdGFydDCCAaIwDQYJ\n" + + "KoZIhvcNAQEBBQADggGPADCCAYoCggGBAJyKZyKIRyW6cbga/I1YFJGCEEgs9JVU\n" + + "sV7MD5/yF4SIkJlZqFjJ9kfw8D5thg68zAx2vEWIpNTMroqb1eptIn/XsFoyM//6\n" + + "HzKrY3UUYWHx9sQMDZPenTL8LTRx+4szSen7rzrozH2pJat7kfX4EODEtQ6q7RQ2\n" + + "hmXoo7heeSgiHoeHsPGZixPGzcB27WBaY00Z/sP/n+f0CFaE04MKLw8WeQmq/RkC\n" + + "pj628+eBK0lGtEmUcT7z4CBy4x3hbhn9XHOb0+RlDk7rqFbsc09vHoZK2BfQ/r6e\n" + + "HguZjBQ5Ebqf6PiLF3HqkSW73toIdIy/olvQ2dLbOEyI4OnlObc+8xs/1AC7l9xX\n" + + "FkXY+NBv24KG1C2POXx14+ufHhWY0k2nIRUUlkUIJ7WGMWbuiNUXc1wSE1VrmY/c\n" + + "iXlhsJERqFc6bL/STlhOGuwmkdAD1/K8WS+o/QmIIX6cXlOR0U9bHMbD40F9fur6\n" + + "PV8wSKcQQNd0VHRLhmFwo4kkhZpDpuUp4QIDAQABo2MwYTAPBgNVHRMBAf8EBTAD\n" + + "AQH/MB8GA1UdIwQYMBaAFDPaBc+EX/hCLe5cd+oZIxcQZ1tHMB0GA1UdDgQWBBQz\n" + + "2gXPhF/4Qi3uXHfqGSMXEGdbRzAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL\n" + + "BQADggGBAFGsyu5nWycdk8iva+uY98QnPQe/M6uaUGUis0vGn9UYxoz5ddtpF3Z+\n" + + "MsHgbS51BH9iRYn4ZkQoRoukIjt1iO86d6sgpUS5AStCXsylL4DwAY5G/K5i/Qw5\n" + + "x0lP/tRYwqh2tUhmnx1xZLOWbRFZ63A0YHdguj3CqaXQ/cxafYZe0zcNhX3iH3gf\n" + + "5kHH8E682RT0x4ibb1JtPioQ48+pweyfMlOJkJ7WmZEfiVQitQSSNOnw1hRORiUz\n" + + "oFb0MlYHqe/9lIb9nmzD8QQ9q0H8J6RBCFsntx/Z6oUM8GHr80zAvNjqFfR14lOo\n" + + "jp05w2mr7wxIHFpM6h1HGY1QaeGp6W/fi+N7+gSL3nu1LzXVCYNCTcGkBDeasovB\n" + + "ma70KHGO4ZyRcEMKFCxxE8y4GZnw/EhMhDDevXAVsHEzr6XsBCJkC8e2l3iW5IKH\n" + + "4N/f/k06d4kS5pL290dJ450zx/mBxYGJm+pPHZfDszqVeKn1m1ZhGT80150OePGQ\n" + + "Cc2ir84HwQ==" + + "\n-----END CERTIFICATE-----\n"; + + private static final String TEST_2LAYER_ENTITY_CERT = "" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIDjDCCAnSgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgYQxCzAJBgNVBAYTAlVT\n" + + "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkw\n" + + "FwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMR4wHAYDVQQD\n" + + "DBVpbnRlcm1lZGlhdGUub25hcC5vcmcwHhcNMjAwMjEyMDk1MTI2WhcNMjIxMTA4\n" + + "MDk1MTI2WjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG\n" + + "A1UEBwwNU2FuLUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjEN\n" + + "MAsGA1UECwwET05BUDEVMBMGA1UEAwwMdmlkLm9uYXAub3JnMIIBIjANBgkqhkiG\n" + + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw+GIRzJzUOh0gtc+wzFJEdTnn+q5F10L0Yhr\n" + + "G1xKdjPieHIFGsoiXwcuCU8arNSqlz7ocx62KQRkcA8y6edlOAsYtdOEJvqEI9vc\n" + + "eyTB/HYsbzw3URPGch4AmibrQkKU9QvGwouHtHn4R2Ft2Y0tfEqv9hxj9v4njq4A\n" + + "EiDLAFLl5FmVyCZu/MtKngSgu1smcaFKTYySPMxytgJZexoa/ALZyyE0gRhsvwHm\n" + + "NLGCPt1bmE/PEGZybsCqliyTO0S56ncD55The7+D/UDS4kE1Wg0svlWon/YsE6QW\n" + + "B3oeJDX7Kr8ebDTIAErevIAD7Sm4ee5se2zxYrsYlj0MzHZtvwIDAQABoxAwDjAM\n" + + "BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvQ1pTvjON6vSlcJRKSY4r\n" + + "8q7L4/9ZaVXWJAjzEYJtPIqsgGiPWz0vGfgklowU6tZxp9zRZFXfMil+mPQSe+yo\n" + + "ULrZSQ/z48YHPueE/BNO/nT4aaVBEhPLR5aVwC7uQVX8H+m1V1UGT8lk9vdI9rej\n" + + "CI9l524sLCpdE4dFXiWK2XHEZ0Vfylk221u3IYEogVVA+UMX7BFPSsOnI2vtYK/i\n" + + "lwZtlri8LtTusNe4oiTkYyq+RSyDhtAswg8ANgvfHolhCHoLFj6w1IkG88UCmbwN\n" + + "d7BoGMy06y5MJxyXEZG0vR7eNeLey0TIh+rAszAFPsIQvrOHW+HuA+WLQAj1mhnm\n" + + "-----END CERTIFICATE-----"; + + private static final String TEST_2LAYER_INTERMEDIATE_CERT = "" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIDqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT\n" + + "MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkw\n" + + "FwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMREwDwYDVQQD\n" + + "DAhvbmFwLm9yZzEeMBwGCSqGSIb3DQEJARYPdGVzdGVyQG9uYXAub3JnMB4XDTIw\n" + + "MDIxMjA5NDAxMloXDTIyMTEwODA5NDAxMlowgYQxCzAJBgNVBAYTAlVTMRMwEQYD\n" + + "VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkwFwYDVQQK\n" + + "DBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMR4wHAYDVQQDDBVpbnRl\n" + + "cm1lZGlhdGUub25hcC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\n" + + "AQC1oOYMZ6G+2DGDAizYnzdCNiogivlht1s4oqgem7fM1XFPxD2p31ATIibOdqr/\n" + + "gv1qemO9Q4r1xn6w1Ufq7T1K7PjnMzdSeTqZefurE2JM/HHx2QvW4TjMlz2ILgaD\n" + + "L1LN60kmMQSOi5VxKJpsrCQxbOsxhvefd212gny5AZMcjJe23kUd9OxUrtvpdLEv\n" + + "wI3vFEvT7oRUnEUg/XNz7qeg33vf1C39yMR+6O4s6oevgsEebVKjb+yOoS6zzGtz\n" + + "72wZjm07C54ZlO+4Uy+QAlMjRiU3mgWkKbkOy+4CvwehjhpTikdBs2DX39ZLGHhn\n" + + "L/0a2NYtGulp9XEqmTvRoI+PAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" + + "hvcNAQELBQADggEBADcitdJ6YswiV8jAD9GK0gf3+zqcGegt4kt+79JXlXYbb1sY\n" + + "q3o6prcB7nSUoClgF2xUPCslFGpM0Er9FCSFElQM/ru0l/KVmJS6kSpwEHvsYIH3\n" + + "q5anta+Pyk8JSQWAAw+qrind0uBQMnhR8Tn13tgV+Kjvg/xlH/nZIEdN5YtLB1cA\n" + + "beVsZRyRfVL9DeZU8s/MZ5wC3kgcEp5A4m5lg7HyBxBdqhzFcDr6xiy6OGqW8Yep\n" + + "xrwfc8Fw8a/lOv4U+tBeGNKPQDYaL9hh+oM+qMkNXsHXDqdJsuEGJtU4i3Wcwzoc\n" + + "XGN5NWV//4bP+NFmwgcn7AYCdRvz04A8GU/0Cwg=\n" + + "-----END CERTIFICATE-----"; + + private static final String TEST_2LAYER_CA_CERT = "" + + "-----BEGIN CERTIFICATE-----\n" + + "MIIDtzCCAp8CFAwqQddh4/iyGfP8UZ3dpXlxfAN8MA0GCSqGSIb3DQEBCwUAMIGX\n" + + "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\n" + + "LUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwE\n" + + "T05BUDERMA8GA1UEAwwIb25hcC5vcmcxHjAcBgkqhkiG9w0BCQEWD3Rlc3RlckBv\n" + + "bmFwLm9yZzAeFw0yMDAyMTIwOTM0MjdaFw0yMTAyMTEwOTM0MjdaMIGXMQswCQYD\n" + + "VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuLUZyYW5j\n" + + "aXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwET05BUDER\n" + + "MA8GA1UEAwwIb25hcC5vcmcxHjAcBgkqhkiG9w0BCQEWD3Rlc3RlckBvbmFwLm9y\n" + + "ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCFrnO7/eT6V+7XkPPd\n" + + "eiL/6xXreuegvit/1/jTVjG+3AOVcmTn2WXwXXRcQLvkWQfJVPoltsY8E3FqFRti\n" + + "797XjY6cdQJFVDyzNU0+Fb4vJL9FK5wSvnS6EFjBEn3JvXRlENorDCs/mfjkjJoa\n" + + "Dl74gXQEJYcg4nsTeNIj7cm3Q7VK3mZt1t7LSJJ+czxv69UJDuNJpmQ/2WOKyLZA\n" + + "gTtBJ+Hyol45/OLsrqwq1dAn9ZRWIFPvRt/XQYH9bI/6MtqSreRVUrdYCiTe/XpP\n" + + "B/OM6NEi2+p5QLi3Yi70CEbqP3HqUVbkzF+r7bwIb6M5/HxfqzLmGwLvD+6rYnUn\n" + + "Bm8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAhXoO65DXth2X/zFRNsCNpLwmDy7r\n" + + "PxT9ZAIZAzSxx3/aCYiuTrKP1JnqjkO+F2IbikrI4n6sKO49SKnRf9SWTFhd+5dX\n" + + "vxq5y7MaqxHAY9J7+Qzq33+COVFQnaF7ddel2NbyUVb2b9ZINNsaZkkPXui6DtQ7\n" + + "/Fb/1tmAGWd3hMp75G2thBSzs816JMKKa9WD+4VGATEs6OSll4sv2fOZEn+0mAD3\n" + + "9q9c+WtLGIudOwcHwzPb2njtNntQSCK/tVOqbY+vzhMY3JW+p9oSrLDSdGC+pAKK\n" + + "m/wB+2VPIYcsPMtIhHC4tgoSaiCqjXYptaOh4b8ye8CPBUCpX/AYYkN0Ow==\n" + + "-----END CERTIFICATE-----"; + + + @BeforeAll + static void setUpSecurity() { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + + + @Test + void returnListOfCertificationWhenGivenCaCertInCaPubsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + PKIMessage respPkiMessage = mockExtraCerts(null); + + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {caCmpCertificate}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityCertificate(certs, TEST_1LAYER_ENTITY_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList(certs, caCmpCertificate); + } + + @Test + void returnListOfCertificationWhenGivenCaCertInExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] extraCmpCertificates = {caCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CertRepMessage certRepMessage = mockCaPubs(null); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityCertificate(certs, TEST_1LAYER_ENTITY_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList(certs, caCmpCertificate); + } + + @Test + void returnListOfCertificationWhenGivenCaCertInExtraCertsAndExtraTrustAnchorInCaPubsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] extraCmpCertificates = {caCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CMPCertificate extraTrustAnchor = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {extraTrustAnchor}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityCertificate(certs, TEST_1LAYER_ENTITY_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate, extraTrustAnchor + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertInExtraCertsAndExtraTrustAnchorInExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate trustedCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] extraCmpCertificates = {caCmpCertificate, trustedCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CertRepMessage certRepMessage = mockCaPubs(null); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityCertificate(certs, TEST_1LAYER_ENTITY_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate, trustedCmpCertificate + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertAndIntermediateCertInExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate intermediateCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_INTERMEDIATE_CERT); + CMPCertificate[] extraCmpCertificates = {caCmpCertificate, intermediateCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CertRepMessage certRepMessage = mockCaPubs(null); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityAndIntermediateCertificate(certs, TEST_2LAYER_ENTITY_CERT, TEST_2LAYER_INTERMEDIATE_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertAndIntermediateCertInCmpCertificatesAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + PKIMessage respPkiMessage = mockExtraCerts(null); + + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate intermediateCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_INTERMEDIATE_CERT); + CMPCertificate[] cmpCertificates = {caCmpCertificate, intermediateCmpCertificate}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityAndIntermediateCertificate(certs, TEST_2LAYER_ENTITY_CERT, TEST_2LAYER_INTERMEDIATE_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertInCaPubsAndIntermediateCertInExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate intermediateCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_INTERMEDIATE_CERT); + CMPCertificate[] extraCmpCertificates = {intermediateCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {caCmpCertificate}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityAndIntermediateCertificate(certs, TEST_2LAYER_ENTITY_CERT, TEST_2LAYER_INTERMEDIATE_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertInCaPubsAndExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] extraCmpCertificates = {caCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + CMPCertificate[] cmpCertificates = {mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT)}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityCertificate(certs, TEST_1LAYER_ENTITY_CERT); + assertThatRootCaAndTrustedCaAreInSecondList(certs, mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT)); + + } + + @Test + void returnListOfCertificationWhenGivenCaCertAndIntermediateCertInExtraCertsAndIntermediateCertInCaPubsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate intermediateCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_INTERMEDIATE_CERT); + CMPCertificate[] extraCmpCertificates = {caCmpCertificate, intermediateCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + CMPCertificate[] cmpCertificates = {intermediateCmpCertificate}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityAndIntermediateCertificate(certs, TEST_2LAYER_ENTITY_CERT, TEST_2LAYER_INTERMEDIATE_CERT); + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertAndExtraTrustAnchorInCaPubsAndIntermediateCertInExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate intermediateCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_INTERMEDIATE_CERT); + CMPCertificate[] extraCmpCertificates = {intermediateCmpCertificate}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate extraTrustAnchor = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {caCmpCertificate, extraTrustAnchor}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityAndIntermediateCertificate(certs, TEST_2LAYER_ENTITY_CERT, TEST_2LAYER_INTERMEDIATE_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate, extraTrustAnchor + ); + } + + @Test + void returnListOfCertificationWhenGivenCaCertAndFirstExtraTrustAnchorInCaPubsAndIntermediateCertAndSecondExtraTrustAnchorInExtraCertsAndEntityCertInLeafCertificate() + throws CertificateException, CmpClientException, IOException, NoSuchProviderException { + // given + CMPCertificate intermediateCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_INTERMEDIATE_CERT); + CMPCertificate extraTrustAnchor01 = mockCmpCertificateFromPem(TEST_1LAYER_ENTITY_CERT); + CMPCertificate[] extraCmpCertificates = {intermediateCmpCertificate, extraTrustAnchor01}; + PKIMessage respPkiMessage = mockExtraCerts(extraCmpCertificates); + + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate extraTrustAnchor02 = mockCmpCertificateFromPem(TEST_1LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {caCmpCertificate, extraTrustAnchor02}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Cmpv2CertificationModel certs = CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate); + + // then + assertThatChainContainsEntityAndIntermediateCertificate(certs, TEST_2LAYER_ENTITY_CERT, TEST_2LAYER_INTERMEDIATE_CERT); + + assertThatRootCaAndTrustedCaAreInSecondList( + certs, + caCmpCertificate, extraTrustAnchor01, extraTrustAnchor02 + ); + } + + @Test + void throwsExceptionWhenNoCaCertForEntityCertIsGivenAndOnlyExtraTrustAnchorIsReturned() + throws CertificateException, IOException, NoSuchProviderException { + // given + + PKIMessage respPkiMessage = mockExtraCerts(null); + + CMPCertificate trustedCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {trustedCmpCertificate}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Exception exception = assertThrows( + CmpClientException.class, + () -> CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate + ) + ); + + String actualMessage = exception.getMessage(); + + // then + assertThat(actualMessage).isEqualTo(EXPECTED_ERROR_MESSAGE); + } + + @Test + void throwsExceptionWhenBothExtraCertsAndCaPubsAreEmpty() + throws CertificateException, IOException, NoSuchProviderException { + // given + + PKIMessage respPkiMessage = mockExtraCerts(null); + CertRepMessage certRepMessage = mockCaPubs(null); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_1LAYER_ENTITY_CERT); + + // when + Exception exception = assertThrows( + CmpClientException.class, + () -> CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate + ) + ); + + String actualMessage = exception.getMessage(); + + // then + assertThat(actualMessage).isEqualTo(EXPECTED_ERROR_MESSAGE); + } + + @Test + void throwsExceptionWhenNoIntermediateCertForEntityCertIsGiven() + throws CertificateException, IOException, NoSuchProviderException { + // given + + PKIMessage respPkiMessage = mockExtraCerts(null); + + CMPCertificate caCmpCertificate = mockCmpCertificateFromPem(TEST_2LAYER_CA_CERT); + CMPCertificate[] cmpCertificates = {caCmpCertificate}; + CertRepMessage certRepMessage = mockCaPubs(cmpCertificates); + + X509Certificate leafCertificate = getX509CertificateFromPem(TEST_2LAYER_ENTITY_CERT); + + // when + Exception exception = assertThrows( + CmpClientException.class, + () -> CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore( + respPkiMessage, certRepMessage, leafCertificate + ) + ); + + String actualMessage = exception.getMessage(); + + // then + assertThat(actualMessage).isEqualTo(EXPECTED_ERROR_MESSAGE); + } + + + private void assertThatRootCaAndTrustedCaAreInSecondList( + Cmpv2CertificationModel certs, CMPCertificate... rootAndTrustedCerts + ) throws IOException { + assertThat(certs.getTrustedCertificates().size()).isEqualTo(rootAndTrustedCerts.length); + for (CMPCertificate certificate : rootAndTrustedCerts) { + assertThat(certs.getTrustedCertificates()) + .extracting(Certificate::getEncoded) + .contains(certificate.getEncoded()); + } + } + + private void assertThatChainContainsEntityCertificate( + Cmpv2CertificationModel certs, String entityCertificate + ) throws CertificateEncodingException, IOException { + assertThat(certs.getCertificateChain().size()).isEqualTo(1); + assertThat(certs.getCertificateChain().get(0).getEncoded()).isEqualTo(createPemObject(entityCertificate).getContent()); + } + + private void assertThatChainContainsEntityAndIntermediateCertificate( + Cmpv2CertificationModel certs, String entityCertificate, String intermediateCertificate + ) throws CertificateEncodingException, IOException { + assertThat(certs.getCertificateChain().size()).isEqualTo(2); + assertThat(certs.getCertificateChain().get(0).getEncoded()).isEqualTo(createPemObject(entityCertificate).getContent()); + assertThat(certs.getCertificateChain().get(1).getEncoded()).isEqualTo(createPemObject(intermediateCertificate).getContent()); + } + + private X509Certificate getX509CertificateFromPem(String pem) throws CertificateException, NoSuchProviderException, IOException { + return (X509Certificate) + CertificateFactory.getInstance("X.509", "BC").generateCertificate( + new ByteArrayInputStream(createPemObject(pem).getContent()) + ); + } + + private PKIMessage mockExtraCerts(CMPCertificate[] cmpCertificates) { + PKIMessage respPkiMessage = mock(PKIMessage.class); + when(respPkiMessage.getExtraCerts()).thenReturn(cmpCertificates); + return respPkiMessage; + } + + private CertRepMessage mockCaPubs(CMPCertificate[] cmpCertificates) { + CertRepMessage certRepMessage = mock(CertRepMessage.class); + when(certRepMessage.getCaPubs()).thenReturn(cmpCertificates); + return certRepMessage; + } + + private CMPCertificate mockCmpCertificateFromPem(String pem) throws IOException { + return mockCmpCertificate(createPemObject(pem).getContent()); + } + + private CMPCertificate mockCmpCertificate(byte[] encodedCertificate) throws IOException { + CMPCertificate cmpCertificate01 = mock(CMPCertificate.class); + when(cmpCertificate01.getEncoded()).thenReturn(encodedCertificate); + return cmpCertificate01; + } + + private PemObject createPemObject(String pem) throws IOException { + try (StringReader stringReader = new StringReader(pem); + PemReader pemReader = new PemReader(stringReader)) { + return pemReader.readPemObject(); + } + } +} -- 2.16.6