From: Remigiusz Janeczek Date: Wed, 30 Jun 2021 10:44:29 +0000 (+0200) Subject: [OOM-CERT-SERVICE] Add logic for KUR/CR detection X-Git-Tag: 2.4.0~21^2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=92ec2f3a119d6253da3c8aebefe3b0b9829f7645;p=oom%2Fplatform%2Fcert-service.git [OOM-CERT-SERVICE] Add logic for KUR/CR detection Issue-ID: OOM-2753 Signed-off-by: Remigiusz Janeczek Change-Id: I571ad3914a870dde83929cb6121c2c63a5df3ae4 --- diff --git a/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java b/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java index a0972d59..d21b1eb5 100644 --- a/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java +++ b/certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ * Copyright (C) 2020-2021 Nokia. All rights reserved. * ================================================================================ @@ -27,10 +27,11 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.CertificationModelFactory; +import org.onap.oom.certservice.certification.exception.CertificateDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; import org.onap.oom.certservice.certification.exception.ErrorResponseModel; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.model.CertificationModel; import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException; import org.slf4j.Logger; @@ -111,7 +112,7 @@ public class CertificationController { @RequestHeader("PK") String encodedPrivateKey, @RequestHeader("OLD_CERT") String encodedOldCert, @RequestHeader("OLD_PK") String encodedOldPrivateKey - ) { + ) throws DecryptionException, CertificateDecryptionException { caName = replaceWhiteSpaceChars(caName); LOGGER.info("Received certificate update request for CA named: {}", caName); CertificateUpdateModel certificateUpdateModel = new CertificateUpdateModel.CertificateUpdateModelBuilder() diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java index 94332f2d..d5d9d9b8 100644 --- a/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java +++ b/certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ * Copyright (C) 2020 Nokia. All rights reserved. * ================================================================================ @@ -22,10 +22,12 @@ package org.onap.oom.certservice.certification; import org.onap.oom.certservice.certification.configuration.Cmpv2ServerProvider; import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server; +import org.onap.oom.certservice.certification.exception.CertificateDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.model.CertificationModel; import org.onap.oom.certservice.certification.model.CsrModel; +import org.onap.oom.certservice.certification.model.X509CertificateModel; import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,23 +42,28 @@ public class CertificationModelFactory { private final CsrModelFactory csrModelFactory; private final Cmpv2ServerProvider cmpv2ServerProvider; private final CertificationProvider certificationProvider; + private final X509CertificateModelFactory x509CertificateModelFactory; + private final UpdateRequestTypeDetector updateRequestTypeDetector; @Autowired CertificationModelFactory( CsrModelFactory csrModelFactory, Cmpv2ServerProvider cmpv2ServerProvider, - CertificationProvider certificationProvider - ) { + CertificationProvider certificationProvider, + X509CertificateModelFactory x509CertificateModelFactory, + UpdateRequestTypeDetector updateRequestTypeDetector) { this.cmpv2ServerProvider = cmpv2ServerProvider; this.csrModelFactory = csrModelFactory; this.certificationProvider = certificationProvider; + this.x509CertificateModelFactory = x509CertificateModelFactory; + this.updateRequestTypeDetector = updateRequestTypeDetector; } public CertificationModel createCertificationModel(String encodedCsr, String encodedPrivateKey, String caName) throws DecryptionException, CmpClientException { CsrModel csrModel = csrModelFactory.createCsrModel( - new CsrModelFactory.StringBase64(encodedCsr), - new CsrModelFactory.StringBase64(encodedPrivateKey) + new StringBase64(encodedCsr), + new StringBase64(encodedPrivateKey) ); LOGGER.debug("Received CSR meta data: \n{}", csrModel); @@ -68,10 +75,26 @@ public class CertificationModelFactory { return certificationProvider.signCsr(csrModel, cmpv2Server); } - public CertificationModel createCertificationModel(CertificateUpdateModel certificateUpdateModel) { + public CertificationModel createCertificationModel(CertificateUpdateModel certificateUpdateModel) + throws DecryptionException, CertificateDecryptionException { LOGGER.info("CSR: " + certificateUpdateModel.getEncodedCsr() + ", old cert: " + certificateUpdateModel.getEncodedOldCert() + ", CA: " + certificateUpdateModel.getCaName()); - throw new UnsupportedOperationException("TODO: it will be delivered in the next MR"); + final CsrModel csrModel = csrModelFactory.createCsrModel( + new StringBase64(certificateUpdateModel.getEncodedCsr()), + new StringBase64(certificateUpdateModel.getEncodedPrivateKey()) + ); + final X509CertificateModel certificateModel = x509CertificateModelFactory.createCertificateModel( + new StringBase64(certificateUpdateModel.getEncodedOldCert())); + + if (updateRequestTypeDetector.isKur(csrModel.getCertificateData(), certificateModel.getCertificateData())) { + LOGGER.info( + "Certificate Signing Request and Old Certificate have the same parameters. Preparing Key Update Request"); + throw new UnsupportedOperationException("TODO: implement KUR in separate MR"); + } else { + LOGGER.info( + "Certificate Signing Request and Old Certificate have different parameters. Preparing Certification Request"); + throw new UnsupportedOperationException("TODO: implement CR in separate MR"); + } } } diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java index 758427f6..789773d6 100644 --- a/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java +++ b/certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java @@ -1,8 +1,8 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ - * Copyright (C) 2020 Nokia. All rights reserved. + * Copyright (C) 2020-2021 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. @@ -20,18 +20,12 @@ package org.onap.oom.certservice.certification; -import java.util.Base64; -import java.util.Objects; -import java.util.Optional; - import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.util.io.pem.PemObject; import org.onap.oom.certservice.certification.exception.CsrDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; import org.onap.oom.certservice.certification.exception.KeyDecryptionException; import org.onap.oom.certservice.certification.model.CsrModel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -71,43 +65,6 @@ public class CsrModelFactory { ); } - public static class StringBase64 { - private final String value; - private final Base64.Decoder decoder = Base64.getDecoder(); - private static final Logger LOGGER = LoggerFactory.getLogger(StringBase64.class); - - public StringBase64(String value) { - this.value = value; - } - - public Optional asString() { - try { - String decodedString = new String(decoder.decode(value)); - return Optional.of(decodedString); - } catch (RuntimeException e) { - LOGGER.error("Exception occurred during decoding:", e); - return Optional.empty(); - } - } - - @Override - public boolean equals(Object otherObject) { - if (this == otherObject) { - return true; - } - if (otherObject == null || getClass() != otherObject.getClass()) { - return false; - } - StringBase64 that = (StringBase64) otherObject; - return Objects.equals(value, that.value); - } - - @Override - public int hashCode() { - return value.hashCode(); - } - } - } diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/PemStringToCertificateConverter.java b/certService/src/main/java/org/onap/oom/certservice/certification/PemStringToCertificateConverter.java new file mode 100644 index 00000000..7126cd4a --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/PemStringToCertificateConverter.java @@ -0,0 +1,50 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import java.io.IOException; +import java.io.StringReader; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Optional; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMParser; +import org.onap.oom.certservice.certification.exception.StringToCertificateConversionException; +import org.springframework.stereotype.Service; + +@Service +public class PemStringToCertificateConverter { + + public X509Certificate convert(String certificatePemString) throws StringToCertificateConversionException { + try (PEMParser pemParser = new PEMParser(new StringReader(certificatePemString))) { + X509CertificateHolder certHolder = Optional.ofNullable((X509CertificateHolder) pemParser.readObject()) + .orElseThrow( + () -> new StringToCertificateConversionException("The certificate could not be converted correctly.")); + return new JcaX509CertificateConverter() + .setProvider(new BouncyCastleProvider()) + .getCertificate(certHolder); + } catch (IOException | CertificateException e) { + throw new StringToCertificateConversionException("Exception occurred during certificate conversion.", e); + } + } +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/StringBase64.java b/certService/src/main/java/org/onap/oom/certservice/certification/StringBase64.java new file mode 100644 index 00000000..9929e52c --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/StringBase64.java @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2020-2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import java.util.Base64; +import java.util.Objects; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StringBase64 { + + private final String value; + private final Base64.Decoder decoder = Base64.getDecoder(); + private static final Logger LOGGER = LoggerFactory.getLogger(StringBase64.class); + + public StringBase64(String value) { + this.value = value; + } + + public Optional asString() { + try { + String decodedString = new String(decoder.decode(value)); + return Optional.of(decodedString); + } catch (RuntimeException e) { + LOGGER.error("Exception occurred during decoding:", e); + return Optional.empty(); + } + } + + @Override + public boolean equals(Object otherObject) { + if (this == otherObject) { + return true; + } + if (otherObject == null || getClass() != otherObject.getClass()) { + return false; + } + StringBase64 that = (StringBase64) otherObject; + return Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetector.java b/certService/src/main/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetector.java new file mode 100644 index 00000000..bef2fd5f --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetector.java @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import org.onap.oom.certservice.certification.model.CertificateData; +import org.springframework.stereotype.Service; + +@Service +public class UpdateRequestTypeDetector { + + public boolean isKur(CertificateData certificateData1, CertificateData certificateData2) { + return certificateData1.equals(certificateData2); + } + +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateModelFactory.java b/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateModelFactory.java new file mode 100644 index 00000000..17367ce7 --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateModelFactory.java @@ -0,0 +1,77 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.onap.oom.certservice.certification.exception.CertificateDecryptionException; +import org.onap.oom.certservice.certification.exception.StringToCertificateConversionException; +import org.onap.oom.certservice.certification.model.X509CertificateModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class X509CertificateModelFactory { + + private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----\n"; + private static final String END_CERTIFICATE = "-----END CERTIFICATE-----\n"; + + private final PemStringToCertificateConverter pemStringToCertificateConverter; + private final X509CertificateParser x509CertificateParser; + + @Autowired + public X509CertificateModelFactory(PemStringToCertificateConverter pemStringToCertificateConverter, + X509CertificateParser x509CertificateParser) { + this.pemStringToCertificateConverter = pemStringToCertificateConverter; + this.x509CertificateParser = x509CertificateParser; + } + + public X509CertificateModel createCertificateModel(StringBase64 base64EncodedCertificate) + throws CertificateDecryptionException { + final String certificateString = base64EncodedCertificate.asString() + .map(this::getFirstCertificateFromCertificateChain) + .orElseThrow(() -> new CertificateDecryptionException("Incorrect certificate, decryption failed")); + try { + final X509Certificate certificate = pemStringToCertificateConverter.convert(certificateString); + final X500Name subjectData = x509CertificateParser.getSubject(certificate); + final GeneralName[] sans = x509CertificateParser.getSans(certificate); + return new X509CertificateModel(certificate, subjectData, sans); + } catch (StringToCertificateConversionException e) { + throw new CertificateDecryptionException("Cannot convert certificate", e); + + } catch (CertificateParsingException e) { + throw new CertificateDecryptionException("Cannot read Subject Alternative Names from certificate"); + } + } + + private String getFirstCertificateFromCertificateChain(String certificateChain) { + if (doesNotContainCertificates(certificateChain)) { + return null; + } + return certificateChain.split(END_CERTIFICATE)[0] + END_CERTIFICATE; + } + + private boolean doesNotContainCertificates(String certificateChain) { + return !(certificateChain.contains(BEGIN_CERTIFICATE) && certificateChain.contains(END_CERTIFICATE)); + } +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateParser.java b/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateParser.java new file mode 100644 index 00000000..67b12796 --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateParser.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.springframework.stereotype.Service; + +@Service +public class X509CertificateParser { + + public X500Name getSubject(X509Certificate certificate) { + final X500Principal subjectX500Principal = certificate.getSubjectX500Principal(); + return new X500Name(subjectX500Principal.getName()); + } + + public GeneralName[] getSans(X509Certificate certificate) throws CertificateParsingException { + final Collection> sans = certificate.getSubjectAlternativeNames(); + if (sans == null) { + return new GeneralName[0]; + } + final ArrayList generalNames = new ArrayList<>(); + for (List san : sans) { + GeneralName sanGn = new GeneralName((Integer) san.get(0), san.get(1).toString()); + generalNames.add(sanGn); + } + return generalNames.toArray(new GeneralName[0]); + } +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/CertificateDecryptionException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/CertificateDecryptionException.java new file mode 100644 index 00000000..16fdb44b --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/CertificateDecryptionException.java @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification.exception; + +public class CertificateDecryptionException extends Exception { + + public CertificateDecryptionException(String message, Throwable cause) { + super(message, cause); + } + + public CertificateDecryptionException(String message) { + super(message); + } + +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/exception/StringToCertificateConversionException.java b/certService/src/main/java/org/onap/oom/certservice/certification/exception/StringToCertificateConversionException.java new file mode 100644 index 00000000..087172d7 --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/exception/StringToCertificateConversionException.java @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification.exception; + +public class StringToCertificateConversionException extends Exception { + + public StringToCertificateConversionException(String message, Throwable cause) { + super(message, cause); + } + + public StringToCertificateConversionException(String message) { + super(message); + } + +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateData.java b/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateData.java new file mode 100644 index 00000000..3a00c915 --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateData.java @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification.model; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class CertificateData { + + private final X500Name subject; + private final List sortedSans; + + public CertificateData(X500Name subject, GeneralName[] sans) { + this.subject = subject; + this.sortedSans = sans != null ? getSortedSansList(sans) : Collections.emptyList(); + } + + public X500Name getSubject() { + return subject; + } + + public GeneralName[] getSortedSans() { + return sortedSans.toArray(new GeneralName[0]); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CertificateData that = (CertificateData) o; + return Objects.equals(subject, that.subject) && Objects.equals(sortedSans, that.sortedSans); + } + + @Override + public int hashCode() { + return Objects.hash(subject, sortedSans); + } + + private List getSortedSansList(GeneralName[] sans) { + return Arrays.stream(sans).sorted(Comparator.comparing(GeneralName::toString)) + .collect(Collectors.toUnmodifiableList()); + } + +} diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java b/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java index 03d1a9d2..96755832 100644 --- a/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java +++ b/certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java @@ -1,8 +1,8 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ - * Copyright (C) 2020 Nokia. All rights reserved. + * Copyright (C) 2020-2021 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. @@ -29,7 +29,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; - import java.util.stream.Collectors; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.Extension; @@ -38,7 +37,6 @@ import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.util.io.pem.PemObject; - import org.onap.oom.certservice.certification.exception.CsrDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; import org.onap.oom.certservice.certification.exception.KeyDecryptionException; @@ -47,18 +45,16 @@ import org.onap.oom.certservice.certification.exception.KeyDecryptionException; public class CsrModel { private final PKCS10CertificationRequest csr; - private final X500Name subjectData; private final PrivateKey privateKey; private final PublicKey publicKey; - private final GeneralName[] sans; + private final CertificateData certificateData; public CsrModel(PKCS10CertificationRequest csr, X500Name subjectData, PrivateKey privateKey, PublicKey publicKey, GeneralName[] sans) { this.csr = csr; - this.subjectData = subjectData; this.privateKey = privateKey; this.publicKey = publicKey; - this.sans = sans; + this.certificateData = new CertificateData(subjectData, sans); } public PKCS10CertificationRequest getCsr() { @@ -66,7 +62,7 @@ public class CsrModel { } public X500Name getSubjectData() { - return subjectData; + return certificateData.getSubject(); } public PrivateKey getPrivateKey() { @@ -78,16 +74,20 @@ public class CsrModel { } public GeneralName[] getSans() { - return sans; + return certificateData.getSortedSans(); + } + + public CertificateData getCertificateData() { + return certificateData; } @Override public String toString() { - return "CSR: { Subject: { " + subjectData + " }, SANs: [" + getSansInReadableFormat() + "] }"; + return "CSR: { Subject: { " + certificateData.getSubject() + " }, SANs: [" + getSansInReadableFormat() + "] }"; } private String getSansInReadableFormat() { - return Arrays.stream(this.sans) + return Arrays.stream(this.certificateData.getSortedSans()) .map(generalName -> generalName.getName().toString()) .collect(Collectors.joining(", ")); } diff --git a/certService/src/main/java/org/onap/oom/certservice/certification/model/X509CertificateModel.java b/certService/src/main/java/org/onap/oom/certservice/certification/model/X509CertificateModel.java new file mode 100644 index 00000000..f45fb7d3 --- /dev/null +++ b/certService/src/main/java/org/onap/oom/certservice/certification/model/X509CertificateModel.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification.model; + +import java.security.cert.X509Certificate; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; + +public class X509CertificateModel { + + private final X509Certificate certificate; + private final CertificateData certificateData; + + public X509CertificateModel(X509Certificate certificate, X500Name subjectData, + GeneralName[] sans) { + this.certificate = certificate; + this.certificateData = new CertificateData(subjectData, sans); + } + + public X509Certificate getCertificate() { + return certificate; + } + + public X500Name getSubjectData() { + return certificateData.getSubject(); + } + + public GeneralName[] getSans() { + return certificateData.getSortedSans(); + } + + public CertificateData getCertificateData() { + return certificateData; + } +} diff --git a/certService/src/test/java/org/onap/oom/certservice/api/CertificationControllerTest.java b/certService/src/test/java/org/onap/oom/certservice/api/CertificationControllerTest.java index 0bf37901..f1d5baaf 100644 --- a/certService/src/test/java/org/onap/oom/certservice/api/CertificationControllerTest.java +++ b/certService/src/test/java/org/onap/oom/certservice/api/CertificationControllerTest.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ * Copyright (C) 2020-2021 Nokia. All rights reserved. * ================================================================================ @@ -32,12 +32,15 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.oom.certservice.certification.exception.CertificateDecryptionException; +import org.onap.oom.certservice.certification.exception.StringToCertificateConversionException; import org.onap.oom.certservice.certification.model.CertificateUpdateModel; import org.onap.oom.certservice.certification.CertificationModelFactory; import org.onap.oom.certservice.certification.exception.Cmpv2ServerNotFoundException; import org.onap.oom.certservice.certification.exception.CsrDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; import org.onap.oom.certservice.certification.exception.KeyDecryptionException; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel.CertificateUpdateModelBuilder; import org.onap.oom.certservice.certification.model.CertificationModel; import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException; import org.springframework.http.HttpStatus; @@ -54,6 +57,13 @@ class CertificationControllerTest { private static final String TEST_WRONG_CA_NAME = "wrongTestCa"; private static final String TEST_ENCODED_OLD_PK = "encodedOldPK"; private static final String TEST_ENCODED_OLD_CERT = "encodedOldCert"; + private static final CertificateUpdateModel TEST_CERTIFICATE_UPDATE_MODEL = new CertificateUpdateModelBuilder() + .setEncodedCsr(TEST_ENCODED_CSR) + .setEncodedPrivateKey(TEST_ENCODED_PK) + .setEncodedOldCert(TEST_ENCODED_OLD_CERT) + .setEncodedOldPrivateKey(TEST_ENCODED_OLD_PK) + .setCaName(TEST_CA_NAME) + .build(); private CertificationController certificationController; @@ -148,20 +158,14 @@ class CertificationControllerTest { } @Test - void shouldUpdateEndpointReturnDataAboutCsrBaseOnEncodedParameters() { + void shouldUpdateEndpointReturnDataAboutCsrBaseOnEncodedParameters() + throws DecryptionException, CertificateDecryptionException { // Given CertificationModel testCertificationModel = new CertificationModel( Arrays.asList("ENTITY_CERT", "INTERMEDIATE_CERT"), Arrays.asList("CA_CERT", "EXTRA_CA_CERT") ); - CertificateUpdateModel certificateUpdateModel = new CertificateUpdateModel.CertificateUpdateModelBuilder() - .setEncodedCsr(TEST_ENCODED_CSR) - .setEncodedPrivateKey(TEST_ENCODED_PK) - .setEncodedOldCert(TEST_ENCODED_OLD_CERT) - .setEncodedOldPrivateKey(TEST_ENCODED_OLD_PK) - .setCaName(TEST_CA_NAME) - .build(); - when(certificationModelFactory.createCertificationModel(certificateUpdateModel)).thenReturn(testCertificationModel); + when(certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL)).thenReturn(testCertificationModel); // When ResponseEntity responseCertificationModel = @@ -173,4 +177,25 @@ class CertificationControllerTest { assertThat(responseCertificationModel.getBody()).isEqualToComparingFieldByField(testCertificationModel); } + @Test + void shouldThrowCertificateDecryptionExceptionWhenCreatingPemModelFails() + throws DecryptionException, CertificateDecryptionException { + // Given + String expectedMessage = "Incorrect certificate, decryption failed"; + when(certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL)) + .thenThrow(new CertificateDecryptionException(expectedMessage)); + + // When + Exception exception = assertThrows( + CertificateDecryptionException.class, () -> + certificationController.updateCertificate(TEST_CA_NAME, TEST_ENCODED_CSR, + TEST_ENCODED_PK, TEST_ENCODED_OLD_CERT, TEST_ENCODED_OLD_PK) + ); + + String actualMessage = exception.getMessage(); + + // Then + assertEquals(expectedMessage, actualMessage); + } + } diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/CertificationModelFactoryTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/CertificationModelFactoryTest.java index 705ae004..c898b687 100644 --- a/certService/src/test/java/org/onap/oom/certservice/certification/CertificationModelFactoryTest.java +++ b/certService/src/test/java/org/onap/oom/certservice/certification/CertificationModelFactoryTest.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ * Copyright (C) 2020-2021 Nokia. All rights reserved. * ================================================================================ @@ -20,6 +20,25 @@ package org.onap.oom.certservice.certification; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.onap.oom.certservice.certification.CertificationData.CA_CERT; +import static org.onap.oom.certservice.certification.CertificationData.ENTITY_CERT; +import static org.onap.oom.certservice.certification.CertificationData.EXTRA_CA_CERT; +import static org.onap.oom.certservice.certification.CertificationData.INTERMEDIATE_CERT; +import static org.onap.oom.certservice.certification.TestData.TEST_CSR; +import static org.onap.oom.certservice.certification.TestData.TEST_PK; +import static org.onap.oom.certservice.certification.TestData.TEST_WRONG_CSR; +import static org.onap.oom.certservice.certification.TestData.TEST_WRONG_PEM; + +import java.util.Arrays; +import java.util.Base64; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,33 +46,18 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.onap.oom.certservice.certification.configuration.Cmpv2ServerProvider; import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server; +import org.onap.oom.certservice.certification.exception.CertificateDecryptionException; import org.onap.oom.certservice.certification.exception.Cmpv2ClientAdapterException; import org.onap.oom.certservice.certification.exception.Cmpv2ServerNotFoundException; import org.onap.oom.certservice.certification.exception.CsrDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel; +import org.onap.oom.certservice.certification.model.CertificateUpdateModel.CertificateUpdateModelBuilder; import org.onap.oom.certservice.certification.model.CertificationModel; import org.onap.oom.certservice.certification.model.CsrModel; +import org.onap.oom.certservice.certification.model.X509CertificateModel; import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException; -import java.util.Arrays; -import java.util.Base64; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.onap.oom.certservice.certification.CertificationData.CA_CERT; -import static org.onap.oom.certservice.certification.CertificationData.ENTITY_CERT; -import static org.onap.oom.certservice.certification.CertificationData.INTERMEDIATE_CERT; -import static org.onap.oom.certservice.certification.CertificationData.EXTRA_CA_CERT; -import static org.onap.oom.certservice.certification.TestData.TEST_CSR; -import static org.onap.oom.certservice.certification.TestData.TEST_PK; -import static org.onap.oom.certservice.certification.TestData.TEST_WRONG_CSR; -import static org.onap.oom.certservice.certification.TestData.TEST_WRONG_PEM; - @ExtendWith(MockitoExtension.class) class CertificationModelFactoryTest { @@ -62,6 +66,18 @@ class CertificationModelFactoryTest { private static final String ENCODED_PK = getEncodedString(TEST_PK); private static final String ENCODED_WRONG_CSR = getEncodedString(TEST_WRONG_CSR); private static final String ENCODED_WRONG_PK = getEncodedString(TEST_WRONG_PEM); + private static final String TEST_CA_NAME = "TestCa"; + private static final String TEST_ENCODED_CSR = "encodedCSR"; + private static final String TEST_ENCODED_PK = "encodedPK"; + private static final String TEST_ENCODED_OLD_PK = "encodedOldPK"; + private static final String TEST_ENCODED_OLD_CERT = "encodedOldCert"; + private static final CertificateUpdateModel TEST_CERTIFICATE_UPDATE_MODEL = new CertificateUpdateModelBuilder() + .setEncodedCsr(TEST_ENCODED_CSR) + .setEncodedPrivateKey(TEST_ENCODED_PK) + .setEncodedOldCert(TEST_ENCODED_OLD_CERT) + .setEncodedOldPrivateKey(TEST_ENCODED_OLD_PK) + .setCaName(TEST_CA_NAME) + .build(); private CertificationModelFactory certificationModelFactory; @@ -71,7 +87,10 @@ class CertificationModelFactoryTest { private CsrModelFactory csrModelFactory; @Mock private CertificationProvider certificationProvider; - + @Mock + private X509CertificateModelFactory x509CertificateModelFactory; + @Mock + private UpdateRequestTypeDetector updateRequestTypeDetector; private static String getEncodedString(String testCsr) { return Base64.getEncoder().encodeToString(testCsr.getBytes()); @@ -80,12 +99,13 @@ class CertificationModelFactoryTest { @BeforeEach void setUp() { certificationModelFactory = - new CertificationModelFactory(csrModelFactory, cmpv2ServerProvider, certificationProvider); + new CertificationModelFactory(csrModelFactory, cmpv2ServerProvider, certificationProvider, + x509CertificateModelFactory, updateRequestTypeDetector); } @Test void shouldCreateProperCertificationModelWhenGivenProperCsrModelAndCaName() - throws CmpClientException, DecryptionException, Cmpv2ClientAdapterException { + throws CmpClientException, DecryptionException, Cmpv2ClientAdapterException { // Given CsrModel csrModel = mockCsrFactoryModelCreation(); @@ -94,7 +114,7 @@ class CertificationModelFactoryTest { // When CertificationModel certificationModel = - certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA); + certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA); // Then assertEquals(2, certificationModel.getCertificateChain().size()); @@ -105,22 +125,22 @@ class CertificationModelFactoryTest { @Test void shouldThrowDecryptionExceptionWhenGivenWrongEncodedCsr() - throws DecryptionException { + throws DecryptionException { // Given String expectedMessage = "Incorrect CSR, decryption failed"; when( - csrModelFactory.createCsrModel( - new CsrModelFactory.StringBase64(ENCODED_WRONG_CSR), - new CsrModelFactory.StringBase64(ENCODED_WRONG_PK) - ) + csrModelFactory.createCsrModel( + new StringBase64(ENCODED_WRONG_CSR), + new StringBase64(ENCODED_WRONG_PK) + ) ).thenThrow( - new CsrDecryptionException(expectedMessage) + new CsrDecryptionException(expectedMessage) ); // When Exception exception = assertThrows( - DecryptionException.class, () -> - certificationModelFactory.createCertificationModel(ENCODED_WRONG_CSR, ENCODED_WRONG_PK, TEST_CA) + DecryptionException.class, () -> + certificationModelFactory.createCertificationModel(ENCODED_WRONG_CSR, ENCODED_WRONG_PK, TEST_CA) ); // Then @@ -129,20 +149,20 @@ class CertificationModelFactoryTest { @Test void shouldThrowCmpv2ServerNotFoundExceptionWhenGivenWrongCaName() - throws DecryptionException { + throws DecryptionException { // Given String expectedMessage = "CA not found"; mockCsrFactoryModelCreation(); when( - cmpv2ServerProvider.getCmpv2Server(TEST_CA) + cmpv2ServerProvider.getCmpv2Server(TEST_CA) ).thenThrow( - new Cmpv2ServerNotFoundException(expectedMessage) + new Cmpv2ServerNotFoundException(expectedMessage) ); // When Exception exception = assertThrows( - Cmpv2ServerNotFoundException.class, () -> - certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA) + Cmpv2ServerNotFoundException.class, () -> + certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA) ); // Then @@ -151,56 +171,98 @@ class CertificationModelFactoryTest { @Test void shouldThrowCmpClientExceptionWhenSigningCsrFailed() - throws DecryptionException, CmpClientException, Cmpv2ClientAdapterException { + throws DecryptionException, CmpClientException, Cmpv2ClientAdapterException { // Given String expectedMessage = "failed to sign certificate"; CsrModel csrModel = mockCsrFactoryModelCreation(); Cmpv2Server testServer = mockCmpv2ProviderServerSelection(); when( - certificationProvider.signCsr(csrModel, testServer) + certificationProvider.signCsr(csrModel, testServer) ).thenThrow( - new CmpClientException(expectedMessage) + new CmpClientException(expectedMessage) ); // When Exception exception = assertThrows( - CmpClientException.class, () -> - certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA) + CmpClientException.class, () -> + certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA) ); // Then assertTrue(exception.getMessage().contains(expectedMessage)); } + @Test + void shouldPerformKurWhenCsrAndOldCertDataMatch() throws CertificateDecryptionException, DecryptionException { + //given + mockCsrFactoryModelCreation(); + mockCertificateFactoryModelCreation(); + when(updateRequestTypeDetector.isKur(any(), any())).thenReturn(true); + //when, then + Exception exception = assertThrows( + UnsupportedOperationException.class, () -> + certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL) + ); + assertEquals(exception.getMessage(), "TODO: implement KUR in separate MR"); + } + + @Test + void shouldPerformCrWhenCsrAndOldCertDataMatch() throws CertificateDecryptionException, DecryptionException { + //given + mockCsrFactoryModelCreation(); + mockCertificateFactoryModelCreation(); + when(updateRequestTypeDetector.isKur(any(), any())).thenReturn(false); + //when, then + Exception exception = assertThrows( + UnsupportedOperationException.class, () -> + certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL) + ); + assertEquals(exception.getMessage(), "TODO: implement CR in separate MR"); + } + + @Test + void shouldThrowCertificateDecryptionExceptionWhenOldCertificateInvalid() + throws CertificateDecryptionException { + //given + when(x509CertificateModelFactory.createCertificateModel(any())) + .thenThrow(new CertificateDecryptionException("Incorrect certificate, decryption failed")); + //when, then + assertThrows( + CertificateDecryptionException.class, () -> + certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL) + ); + } private void mockCertificateProviderCertificateSigning(CsrModel csrModel, Cmpv2Server testServer) - throws CmpClientException, Cmpv2ClientAdapterException { + throws CmpClientException, Cmpv2ClientAdapterException { CertificationModel expectedCertificationModel = getCertificationModel(); when( - certificationProvider.signCsr(csrModel, testServer) + certificationProvider.signCsr(csrModel, testServer) ).thenReturn(expectedCertificationModel); } private Cmpv2Server mockCmpv2ProviderServerSelection() { Cmpv2Server testServer = getCmpv2Server(); when( - cmpv2ServerProvider.getCmpv2Server(TEST_CA) + cmpv2ServerProvider.getCmpv2Server(TEST_CA) ).thenReturn(testServer); return testServer; } private CsrModel mockCsrFactoryModelCreation() - throws DecryptionException { + throws DecryptionException { CsrModel csrModel = getCsrModel(); - when( - csrModelFactory.createCsrModel( - new CsrModelFactory.StringBase64(ENCODED_CSR), - new CsrModelFactory.StringBase64(ENCODED_PK) - ) - ).thenReturn(csrModel); + when(csrModelFactory.createCsrModel(any(), any())).thenReturn(csrModel); return csrModel; } + private X509CertificateModel mockCertificateFactoryModelCreation() + throws CertificateDecryptionException { + final X509CertificateModel certificateModel = mock(X509CertificateModel.class); + when(x509CertificateModelFactory.createCertificateModel(any())).thenReturn(certificateModel); + return certificateModel; + } + private Cmpv2Server getCmpv2Server() { return new Cmpv2Server(); } diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/CsrModelFactoryTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/CsrModelFactoryTest.java index 88cc6fb8..eb6a0550 100644 --- a/certService/src/test/java/org/onap/oom/certservice/certification/CsrModelFactoryTest.java +++ b/certService/src/test/java/org/onap/oom/certservice/certification/CsrModelFactoryTest.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ * Copyright (C) 2020 Nokia. All rights reserved. * ================================================================================ @@ -23,7 +23,6 @@ package org.onap.oom.certservice.certification; import org.bouncycastle.util.encoders.Base64; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.onap.oom.certservice.certification.CsrModelFactory.StringBase64; import org.onap.oom.certservice.certification.exception.CsrDecryptionException; import org.onap.oom.certservice.certification.exception.DecryptionException; import org.onap.oom.certservice.certification.exception.KeyDecryptionException; @@ -58,6 +57,7 @@ class CsrModelFactoryTest { assertTrue(decryptedCsr.toString() .contains(TestData.EXPECTED_CERT_SUBJECT)); + System.out.println(decryptedCsr.toString()); assertTrue(decryptedCsr.toString() .contains(TestData.EXPECTED_CERT_SANS)); } diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/PemStringToCertificateConverterTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/PemStringToCertificateConverterTest.java new file mode 100644 index 00000000..ecdb1a2e --- /dev/null +++ b/certService/src/test/java/org/onap/oom/certservice/certification/PemStringToCertificateConverterTest.java @@ -0,0 +1,90 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.io.Serializable; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.List; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.jupiter.api.Test; +import org.onap.oom.certservice.certification.exception.StringToCertificateConversionException; + +class PemStringToCertificateConverterTest { + + private static final String CERTIFICATE_PEM_STRING = + "-----BEGIN CERTIFICATE-----\n" + + "MIIEizCCAvOgAwIBAgIUGEp2GZ6Y8nzDA9CKl5nURI7CUN8wDQYJKoZIhvcNAQEL\n" + + "BQAwYTEjMCEGCgmSJomT8ixkAQEME2MtMGpiZnE4cWExZm8wd2ttbnkxFTATBgNV\n" + + "BAMMDE1hbmFnZW1lbnRDQTEjMCEGA1UECgwaRUpCQ0EgQ29udGFpbmVyIFF1aWNr\n" + + "c3RhcnQwHhcNMjEwNjI5MDY1MDI1WhcNMjMwNjI5MDY1MDI0WjB3MREwDwYDVQQD\n" + + "DAhvbmFwLm9yZzEZMBcGA1UECwwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECgwE\n" + + "T05BUDEWMBQGA1UEBwwNU2FuLUZyYW5jaXNjbzETMBEGA1UECAwKQ2FsaWZvcm5p\n" + + "YTELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB\n" + + "zvbyrrEiaoBj8kma2QmC+VLmmtWFWyAJgSrYA4kxuyrjQCW4JyFGvmzbYoUFFLOF\n" + + "hfq18VjTs0cbTysX8cFSfkV2EKGERBaZnZRQso6SIJNGa3xMe5FHjREy34Np04IH\n" + + "jSQ42eIBgcNiIada4jgEnIQQYQJSPQtHkfOMC6O+3RpT/eHtvo5urR16MFY1K6so\n" + + "nWYitI5TpEKRozav6zqU/otHgn5jRLryj0IZy0ijlBfSKTxrFfZr3KoMDudVEfUM\n" + + "tsqER3poc1gFkqmCRK38BPNiY7v5KATRB+fWN+P75mw43drx9rImp+JtpOUsDK8r\n" + + "IBd8o4afNvr/WrygBB8rAgMBAAGjgaQwgaEwDAYDVR0TAQH/BAIwADAfBgNVHSME\n" + + "GDAWgBR9M1qUnQM0CpHUz34F5sVUUcIDtjAYBgNVHREEETAPgg10ZXN0Lm9uYXAu\n" + + "b3JnMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwEwHQYD\n" + + "VR0OBBYEFAfqcU6xGi8jzjlRuKQBtqRcXi+uMA4GA1UdDwEB/wQEAwIF4DANBgkq\n" + + "hkiG9w0BAQsFAAOCAYEAAdw77q7shKtS8lDGcJ/Y8dEjjNSlRtU14EMC59kmKeft\n" + + "Ri7d0oCQXtdRCut3ymizLVqQkmX6SrGshpWUsNzTdITjQ6JB2KOaiIWIF60NSleW\n" + + "0vLm36EmY1ErK+zKe7tvGWZhTNVzBXtnq+AMfJc41u2uelkx0LNczsX9aCajLH1v\n" + + "4z4XsUnm9qiXpnEm632emuJyj6Nt0JWVuNTJTPRnqVZf6KKR83v8JuV0EefWd5WV\n" + + "cFspL0H3MKJV7uf7hfllnIcRtzXa5pctBCboFShVkRNaAMPpJf0DBLQxm7dAWj5A\n" + + "hG1rwmTmzM6NpwGHW/I1SFMmtQiF0PACz1Un6nDW/Rf1iHEoGf8YBLP332LJSDug\n" + + "RKn0cM3QTcyUEzCZxSwKJ2ngC9eG9C2d3YhB6Zxtl+gUIa3AwwPbqr7YR9QkD2Eo\n" + + "d4LqEH9znyBfi7k2YCwP6rtfSi6ClybXe8eBcuMES4TAQfFK6FVf58tGQIx06I0O\n" + + "34nemZwkLoOBzZkepaQv\n" + + "-----END CERTIFICATE-----\n"; + + private static final List EXPECTED_SUBJECT_ALTERNATIVE_NAME = List + .of(GeneralName.dNSName, "test.onap.org"); + + private static final String EXPECTED_SUBJECT = "CN=onap.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + + private final PemStringToCertificateConverter converter = new PemStringToCertificateConverter(); + + @Test + void shouldConvertStringToCertificate() throws CertificateParsingException, StringToCertificateConversionException { + //given, when + X509Certificate certificate = converter.convert(CERTIFICATE_PEM_STRING); + //then + assertThat(certificate).isNotNull(); + assertThat(certificate.getSubjectDN()) + .hasToString(EXPECTED_SUBJECT); + assertThat(certificate.getSubjectAlternativeNames()) + .containsExactly(EXPECTED_SUBJECT_ALTERNATIVE_NAME); + } + + @Test + void shouldThrowExceptionWhenCertificateStringInvalid() { + assertThatThrownBy(() -> converter.convert("")) + .isInstanceOf(StringToCertificateConversionException.class); + } + +} diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/TestData.java b/certService/src/test/java/org/onap/oom/certservice/certification/TestData.java index 1c883f8e..92b239f1 100644 --- a/certService/src/test/java/org/onap/oom/certservice/certification/TestData.java +++ b/certService/src/test/java/org/onap/oom/certservice/certification/TestData.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * PROJECT * ================================================================================ - * Copyright (C) 2020 Nokia. All rights reserved. + * Copyright (C) 2020-2021 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. @@ -29,7 +29,7 @@ public final class TestData { public static final String EXPECTED_CERT_SUBJECT = "C=US,ST=California,L=San-Francisco,O=Linux-Foundation,OU=ONAP,CN=onap.org"; public static final String EXPECTED_CERT_SANS = - "SANs: [localhost, onap.org, test.onap.org, onap@onap.org, " + LOCALHOST_IP_IN_HEX + ", onap://cluster.local/]"; + "SANs: [onap@onap.org, localhost, onap.org, test.onap.org, onap://cluster.local/, " + LOCALHOST_IP_IN_HEX +"]"; public static final String TEST_CSR = "-----BEGIN CERTIFICATE REQUEST-----\n" diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/TestUtils.java b/certService/src/test/java/org/onap/oom/certservice/certification/TestUtils.java index 7c69bd52..0dceda19 100644 --- a/certService/src/test/java/org/onap/oom/certservice/certification/TestUtils.java +++ b/certService/src/test/java/org/onap/oom/certservice/certification/TestUtils.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * PROJECT + * Cert Service * ================================================================================ * Copyright (C) 2020 Nokia. All rights reserved. * ================================================================================ @@ -56,6 +56,6 @@ public final class TestUtils { String encoderCsr = new String(Base64.encode(TEST_CSR.getBytes())); String encoderPK = new String(Base64.encode(TEST_PK.getBytes())); return csrModelFactory - .createCsrModel(new CsrModelFactory.StringBase64(encoderCsr), new CsrModelFactory.StringBase64(encoderPK)); + .createCsrModel(new StringBase64(encoderCsr), new StringBase64(encoderPK)); } } diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetectorTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetectorTest.java new file mode 100644 index 00000000..ee078534 --- /dev/null +++ b/certService/src/test/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetectorTest.java @@ -0,0 +1,120 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.onap.oom.certservice.certification.model.CertificateData; + +class UpdateRequestTypeDetectorTest { + + private static final String SUBJECT = "CN=onap.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + private static final String SUBJECT_CHANGED_ORDER = "ST=California,C=US,OU=Linux-Foundation,CN=onap.org,O=ONAP,L=San-Francisco"; + private static final String OTHER_SUBJECT = "CN=onap1.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + private static final String DNS_NAME = "test.onap.org"; + private static final String OTHER_DNS_NAME = "test1.onap.org"; + + private final UpdateRequestTypeDetector updateRequestTypeDetector = new UpdateRequestTypeDetector(); + + private static Stream equalSubjectParameters() { + return Stream.of( + Arguments.of(SUBJECT, SUBJECT), + Arguments.of(SUBJECT_CHANGED_ORDER, SUBJECT_CHANGED_ORDER), + Arguments.of(SUBJECT, SUBJECT_CHANGED_ORDER), + Arguments.of(SUBJECT_CHANGED_ORDER, SUBJECT) + ); + } + + private static Stream notEqualSubjectParameters() { + return Stream.of( + Arguments.of(SUBJECT, OTHER_SUBJECT), + Arguments.of(OTHER_SUBJECT, SUBJECT), + Arguments.of(SUBJECT_CHANGED_ORDER, OTHER_SUBJECT), + Arguments.of(OTHER_SUBJECT, SUBJECT_CHANGED_ORDER) + ); + } + + @ParameterizedTest + @MethodSource("equalSubjectParameters") + void shouldBeKurWhenSameSubjectData(String subject1, String subject2) { + //given + final CertificateData certificateData1 = new CertificateData(new X500Name(subject1), null); + final CertificateData certificateData2 = new CertificateData(new X500Name(subject2), null); + //when, then + assertThat(updateRequestTypeDetector.isKur(certificateData1, certificateData2)).isTrue(); + } + + @ParameterizedTest + @MethodSource("notEqualSubjectParameters") + void shouldNotBeKurDifferentSubjectData(String subject1, String subject2) { + //given + final CertificateData certificateData1 = new CertificateData(new X500Name(subject1), null); + final CertificateData certificateData2 = new CertificateData(new X500Name(subject2), null); + //when, then + assertThat(updateRequestTypeDetector.isKur(certificateData1, certificateData2)).isFalse(); + } + + @Test + void shouldBeKurWhenEqualSans() { + //given + final GeneralName[] sans1 = new GeneralName[]{new GeneralName(GeneralName.dNSName, DNS_NAME)}; + final GeneralName[] sans2 = new GeneralName[]{new GeneralName(GeneralName.dNSName, DNS_NAME)}; + final CertificateData certificateData1 = new CertificateData(new X500Name(SUBJECT), sans1); + final CertificateData certificateData2 = new CertificateData(new X500Name(SUBJECT), sans2); + //when, then + assertThat(updateRequestTypeDetector.isKur(certificateData1, certificateData2)).isTrue(); + } + + @Test + void shouldNotBeKurWhenNotEqualSans() { + //given + final GeneralName[] sans1 = new GeneralName[]{new GeneralName(GeneralName.dNSName, DNS_NAME)}; + final GeneralName[] sans2 = new GeneralName[]{new GeneralName(GeneralName.dNSName, OTHER_DNS_NAME)}; + final CertificateData certificateData1 = new CertificateData(new X500Name(SUBJECT), sans1); + final CertificateData certificateData2 = new CertificateData(new X500Name(SUBJECT), sans2); + //when, then + assertThat(updateRequestTypeDetector.isKur(certificateData1, certificateData2)).isFalse(); + } + + @Test + void shouldBeKurWhenEqualSansIgnoringOrder() { + //given + GeneralName[] sans1 = new GeneralName[]{ + new GeneralName(GeneralName.dNSName, DNS_NAME), + new GeneralName(GeneralName.dNSName, OTHER_DNS_NAME) + }; + GeneralName[] sans2 = new GeneralName[]{ + new GeneralName(GeneralName.dNSName, OTHER_DNS_NAME), + new GeneralName(GeneralName.dNSName, DNS_NAME) + }; + final CertificateData certificateData1 = new CertificateData(new X500Name(SUBJECT), sans1); + final CertificateData certificateData2 = new CertificateData(new X500Name(SUBJECT), sans2); + //when, then + assertThat(updateRequestTypeDetector.isKur(certificateData1, certificateData2)).isTrue(); + } +} diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateModelFactoryTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateModelFactoryTest.java new file mode 100644 index 00000000..bad4887b --- /dev/null +++ b/certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateModelFactoryTest.java @@ -0,0 +1,166 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.jupiter.api.Test; +import org.onap.oom.certservice.certification.exception.CertificateDecryptionException; +import org.onap.oom.certservice.certification.model.X509CertificateModel; + +class X509CertificateModelFactoryTest { + + private static final String ENCODED_CERTIFICATE_STRING = + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVpekNDQXZPZ0F3SUJBZ0lVR0VwMkdaNlk4" + + "bnpEQTlDS2w1blVSSTdDVU44d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1lURWpNQ0VHQ2dtU0pvbVQ4" + + "aXhrQVFFTUUyTXRNR3BpWm5FNGNXRXhabTh3ZDJ0dGJua3hGVEFUQmdOVgpCQU1NREUxaGJtRm5a" + + "VzFsYm5SRFFURWpNQ0VHQTFVRUNnd2FSVXBDUTBFZ1EyOXVkR0ZwYm1WeUlGRjFhV05yCmMzUmhj" + + "blF3SGhjTk1qRXdOakk1TURZMU1ESTFXaGNOTWpNd05qSTVNRFkxTURJMFdqQjNNUkV3RHdZRFZR" + + "UUQKREFodmJtRndMbTl5WnpFWk1CY0dBMVVFQ3d3UVRHbHVkWGd0Um05MWJtUmhkR2x2YmpFTk1B" + + "c0dBMVVFQ2d3RQpUMDVCVURFV01CUUdBMVVFQnd3TlUyRnVMVVp5WVc1amFYTmpiekVUTUJFR0Ex" + + "VUVDQXdLUTJGc2FXWnZjbTVwCllURUxNQWtHQTFVRUJoTUNWVk13Z2dFaU1BMEdDU3FHU0liM0RR" + + "RUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRREIKenZieXJyRWlhb0JqOGttYTJRbUMrVkxtbXRXRld5" + + "QUpnU3JZQTRreHV5cmpRQ1c0SnlGR3ZtemJZb1VGRkxPRgpoZnExOFZqVHMwY2JUeXNYOGNGU2Zr" + + "VjJFS0dFUkJhWm5aUlFzbzZTSUpOR2EzeE1lNUZIalJFeTM0TnAwNElICmpTUTQyZUlCZ2NOaUlh" + + "ZGE0amdFbklRUVlRSlNQUXRIa2ZPTUM2TyszUnBUL2VIdHZvNXVyUjE2TUZZMUs2c28KbldZaXRJ" + + "NVRwRUtSb3phdjZ6cVUvb3RIZ241alJMcnlqMElaeTBpamxCZlNLVHhyRmZacjNLb01EdWRWRWZV" + + "TQp0c3FFUjNwb2MxZ0ZrcW1DUkszOEJQTmlZN3Y1S0FUUkIrZldOK1A3NW13NDNkcng5ckltcCtK" + + "dHBPVXNESzhyCklCZDhvNGFmTnZyL1dyeWdCQjhyQWdNQkFBR2pnYVF3Z2FFd0RBWURWUjBUQVFI" + + "L0JBSXdBREFmQmdOVkhTTUUKR0RBV2dCUjlNMXFVblFNMENwSFV6MzRGNXNWVVVjSUR0akFZQmdO" + + "VkhSRUVFVEFQZ2cxMFpYTjBMbTl1WVhBdQpiM0puTUNjR0ExVWRKUVFnTUI0R0NDc0dBUVVGQndN" + + "Q0JnZ3JCZ0VGQlFjREJBWUlLd1lCQlFVSEF3RXdIUVlEClZSME9CQllFRkFmcWNVNnhHaThqemps" + + "UnVLUUJ0cVJjWGkrdU1BNEdBMVVkRHdFQi93UUVBd0lGNERBTkJna3EKaGtpRzl3MEJBUXNGQUFP" + + "Q0FZRUFBZHc3N3E3c2hLdFM4bERHY0ovWThkRWpqTlNsUnRVMTRFTUM1OWttS2VmdApSaTdkMG9D" + + "UVh0ZFJDdXQzeW1pekxWcVFrbVg2U3JHc2hwV1VzTnpUZElUalE2SkIyS09haUlXSUY2ME5TbGVX" + + "CjB2TG0zNkVtWTFFcksrektlN3R2R1daaFROVnpCWHRucStBTWZKYzQxdTJ1ZWxreDBMTmN6c1g5" + + "YUNhakxIMXYKNHo0WHNVbm05cWlYcG5FbTYzMmVtdUp5ajZOdDBKV1Z1TlRKVFBSbnFWWmY2S0tS" + + "ODN2OEp1VjBFZWZXZDVXVgpjRnNwTDBIM01LSlY3dWY3aGZsbG5JY1J0elhhNXBjdEJDYm9GU2hW" + + "a1JOYUFNUHBKZjBEQkxReG03ZEFXajVBCmhHMXJ3bVRtek02TnB3R0hXL0kxU0ZNbXRRaUYwUEFD" + + "ejFVbjZuRFcvUmYxaUhFb0dmOFlCTFAzMzJMSlNEdWcKUktuMGNNM1FUY3lVRXpDWnhTd0tKMm5n" + + "QzllRzlDMmQzWWhCNlp4dGwrZ1VJYTNBd3dQYnFyN1lSOVFrRDJFbwpkNExxRUg5em55QmZpN2sy" + + "WUN3UDZydGZTaTZDbHliWGU4ZUJjdU1FUzRUQVFmRks2RlZmNTh0R1FJeDA2STBPCjM0bmVtWndr" + + "TG9PQnpaa2VwYVF2Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"; + + private static final String ENCODED_CERTIFICATE_CHAIN_STRING = + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVpekNDQXZPZ0F3SUJBZ0lVR0VwMkdaNlk4" + + "bnpEQTlDS2w1blVSSTdDVU44d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1lURWpNQ0VHQ2dtU0pvbVQ4" + + "aXhrQVFFTUUyTXRNR3BpWm5FNGNXRXhabTh3ZDJ0dGJua3hGVEFUQmdOVgpCQU1NREUxaGJtRm5a" + + "VzFsYm5SRFFURWpNQ0VHQTFVRUNnd2FSVXBDUTBFZ1EyOXVkR0ZwYm1WeUlGRjFhV05yCmMzUmhj" + + "blF3SGhjTk1qRXdOakk1TURZMU1ESTFXaGNOTWpNd05qSTVNRFkxTURJMFdqQjNNUkV3RHdZRFZR" + + "UUQKREFodmJtRndMbTl5WnpFWk1CY0dBMVVFQ3d3UVRHbHVkWGd0Um05MWJtUmhkR2x2YmpFTk1B" + + "c0dBMVVFQ2d3RQpUMDVCVURFV01CUUdBMVVFQnd3TlUyRnVMVVp5WVc1amFYTmpiekVUTUJFR0Ex" + + "VUVDQXdLUTJGc2FXWnZjbTVwCllURUxNQWtHQTFVRUJoTUNWVk13Z2dFaU1BMEdDU3FHU0liM0RR" + + "RUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRREIKenZieXJyRWlhb0JqOGttYTJRbUMrVkxtbXRXRld5" + + "QUpnU3JZQTRreHV5cmpRQ1c0SnlGR3ZtemJZb1VGRkxPRgpoZnExOFZqVHMwY2JUeXNYOGNGU2Zr" + + "VjJFS0dFUkJhWm5aUlFzbzZTSUpOR2EzeE1lNUZIalJFeTM0TnAwNElICmpTUTQyZUlCZ2NOaUlh" + + "ZGE0amdFbklRUVlRSlNQUXRIa2ZPTUM2TyszUnBUL2VIdHZvNXVyUjE2TUZZMUs2c28KbldZaXRJ" + + "NVRwRUtSb3phdjZ6cVUvb3RIZ241alJMcnlqMElaeTBpamxCZlNLVHhyRmZacjNLb01EdWRWRWZV" + + "TQp0c3FFUjNwb2MxZ0ZrcW1DUkszOEJQTmlZN3Y1S0FUUkIrZldOK1A3NW13NDNkcng5ckltcCtK" + + "dHBPVXNESzhyCklCZDhvNGFmTnZyL1dyeWdCQjhyQWdNQkFBR2pnYVF3Z2FFd0RBWURWUjBUQVFI" + + "L0JBSXdBREFmQmdOVkhTTUUKR0RBV2dCUjlNMXFVblFNMENwSFV6MzRGNXNWVVVjSUR0akFZQmdO" + + "VkhSRUVFVEFQZ2cxMFpYTjBMbTl1WVhBdQpiM0puTUNjR0ExVWRKUVFnTUI0R0NDc0dBUVVGQndN" + + "Q0JnZ3JCZ0VGQlFjREJBWUlLd1lCQlFVSEF3RXdIUVlEClZSME9CQllFRkFmcWNVNnhHaThqemps" + + "UnVLUUJ0cVJjWGkrdU1BNEdBMVVkRHdFQi93UUVBd0lGNERBTkJna3EKaGtpRzl3MEJBUXNGQUFP" + + "Q0FZRUFBZHc3N3E3c2hLdFM4bERHY0ovWThkRWpqTlNsUnRVMTRFTUM1OWttS2VmdApSaTdkMG9D" + + "UVh0ZFJDdXQzeW1pekxWcVFrbVg2U3JHc2hwV1VzTnpUZElUalE2SkIyS09haUlXSUY2ME5TbGVX" + + "CjB2TG0zNkVtWTFFcksrektlN3R2R1daaFROVnpCWHRucStBTWZKYzQxdTJ1ZWxreDBMTmN6c1g5" + + "YUNhakxIMXYKNHo0WHNVbm05cWlYcG5FbTYzMmVtdUp5ajZOdDBKV1Z1TlRKVFBSbnFWWmY2S0tS" + + "ODN2OEp1VjBFZWZXZDVXVgpjRnNwTDBIM01LSlY3dWY3aGZsbG5JY1J0elhhNXBjdEJDYm9GU2hW" + + "a1JOYUFNUHBKZjBEQkxReG03ZEFXajVBCmhHMXJ3bVRtek02TnB3R0hXL0kxU0ZNbXRRaUYwUEFD" + + "ejFVbjZuRFcvUmYxaUhFb0dmOFlCTFAzMzJMSlNEdWcKUktuMGNNM1FUY3lVRXpDWnhTd0tKMm5n" + + "QzllRzlDMmQzWWhCNlp4dGwrZ1VJYTNBd3dQYnFyN1lSOVFrRDJFbwpkNExxRUg5em55QmZpN2sy" + + "WUN3UDZydGZTaTZDbHliWGU4ZUJjdU1FUzRUQVFmRks2RlZmNTh0R1FJeDA2STBPCjM0bmVtWndr" + + "TG9PQnpaa2VwYVF2Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJ" + + "Q0FURS0tLS0tCk1JSUVzekNDQXh1Z0F3SUJBZ0lVTk1lYzl0ZlJEU3FPQlJGaTFkWDd2RCsyVG9z" + + "d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1lURWpNQ0VHQ2dtU0pvbVQ4aXhrQVFFTUUyTXRNR3BpWm5F" + + "NGNXRXhabTh3ZDJ0dGJua3hGVEFUQmdOVgpCQU1NREUxaGJtRm5aVzFsYm5SRFFURWpNQ0VHQTFV" + + "RUNnd2FSVXBDUTBFZ1EyOXVkR0ZwYm1WeUlGRjFhV05yCmMzUmhjblF3SGhjTk1qRXdOakk1TURZ" + + "ME9UQTFXaGNOTXpFd05qSTVNRFkwT1RBMFdqQmhNU013SVFZS0NaSW0KaVpQeUxHUUJBUXdUWXkw" + + "d2FtSm1jVGh4WVRGbWJ6QjNhMjF1ZVRFVk1CTUdBMVVFQXd3TVRXRnVZV2RsYldWdQpkRU5CTVNN" + + "d0lRWURWUVFLREJwRlNrSkRRU0JEYjI1MFlXbHVaWElnVVhWcFkydHpkR0Z5ZERDQ0FhSXdEUVlK" + + "CktvWklodmNOQVFFQkJRQURnZ0dQQURDQ0FZb0NnZ0dCQUtvRjduSzQzUDBaRi9tWUVCbU5GWENw" + + "MmZma2srcFoKS1hCdFRFY1UwZVZmQU5WMzRFNHZEcnFSc0k0U3RFdmt5NGxiWW1PUEx5SnVabnRk" + + "c1dyUC9rbi9kUENHWEEzQQovUE44WmNrUENBQVlwa0JuOFNsMVdxaW1UeTA3TStPM0UxOER0TnBO" + + "Sy9RNWVkWW0rRnFXU3BVWHY5NEMzbjZVCmVXKytWaWNlWWc0ZUFDMVBka01LZGV3VEdnWGYzK1lI" + + "Q1dySW9JeWozcDNCOUp4RDdXVVUzTEZ3UkdHWnljVU0KYnRla2FUZXhodDZ1N2JMeld0Ylg1ZHhw" + + "bTBuR1dZa05KVm1mN0pJOHErMGpZdUNDcXdjYXpTNHIyeko5K1BqVwoyclZ0TkdUdVE0RmQyMm1r" + + "c1RMK0FRbktJaW9jNEJadVcwTkM0emNPZ2Ztc2dRdnBsR2s1ZGlmY0RZeHU5c3hkClFGampMKzJ1" + + "UURaK0RMTWlkYXNHRGxEb3FSQXBnV0lnajFxZllOMVNNbXJFZkdKWjFud05WcTlmemswV2VHNEgK" + + "WmVBYjlnU3BxSk9LNlp6d0F0TTV5NlFxOXo4RC84U2hjS2JiN01waExiSnVGVFRyL1lWaE1QRVZE" + + "SHA5RGZMbApYbzhGbkJCRVplUE9FYmdPZk5XbzlhTkg4RlczcHc1Znd3SURBUUFCbzJNd1lUQVBC" + + "Z05WSFJNQkFmOEVCVEFECkFRSC9NQjhHQTFVZEl3UVlNQmFBRkgweldwU2RBelFLa2RUUGZnWG14" + + "VlJSd2dPMk1CMEdBMVVkRGdRV0JCUjkKTTFxVW5RTTBDcEhVejM0RjVzVlVVY0lEdGpBT0JnTlZI" + + "UThCQWY4RUJBTUNBWVl3RFFZSktvWklodmNOQVFFTApCUUFEZ2dHQkFLZUFSdHBweExodThZbms5" + + "RHJFWUFmZHVwb0pESWU0K0RHYlVyQ212LzlIcVozRWxSaHpYWWdqClJCSlgzSjR1eHdNcEZQZFdO" + + "Qk9nMWdzV2FQYkdYSGg4K1U0Y29HLzVjVm5abmYzRG1PelliVW5pYXU4eTFnclAKaWNyNTZLdDcr" + + "Q2tRMFIrWWxUdXBGM3M5WjlIZW5UaHA3YmQ5N3hZNXBFK2l3cG1hNjNmcHJhSFVBb01qQXZEYQpU" + + "S3ZRUDRZS3pJY1ZuamtEdzlqUUxyNGNxYTdaeU1EUVlNTTd5YUJZNkx0bDYxbU9xQ2l4Q2JMWStL" + + "MmlMV0lNCnRoT2grV3R0cXZRejFPc2x4VkV1cWZtak4zMHBhUy9xSWY4MjhLZHp4UEN6S0RnTVVP" + + "ZjdvTEZUa0JCcGVoWVkKYVd5SUFNd2hEa1grRXYzRXg4ZnRzRmJSUUdWOWMvcDJPUXd2TEdTOXdB" + + "cVUyM1pEckhaTTFla0J5RE5FZUVpUgpTSGRWOEtNSTJuQ0J0NlhUeFhTejkwME5EV0l4aXJET0Rp" + + "WmU5SGJJUm5hWlIzMEEyVCtTeElxYUpsNXZVQzYyCnlLbjNicnFUM1UyMlNlbmVpZ2w3bW81bWhL" + + "bE8wdHErc2lJK1Y0T3lORkhadnJHQUNaUTNxYUFUZlozYlN3RVcKYkg1QjRlbHRodz09Ci0tLS0t" + + "RU5EIENFUlRJRklDQVRFLS0tLS0K"; + + private static final String SUBJECT = "CN=onap.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + private static final GeneralName GENERAL_NAME = new GeneralName(GeneralName.dNSName, "test.onap.org"); + + private final X509CertificateModelFactory factory = + new X509CertificateModelFactory(new PemStringToCertificateConverter(), new X509CertificateParser()); + + @Test + void shouldCorrectlyParseX509CertificateFromCertificate() + throws CertificateDecryptionException { + //given + StringBase64 base64EncodedCertificate = new StringBase64(ENCODED_CERTIFICATE_STRING); + //when + final X509CertificateModel certificateModel = factory.createCertificateModel(base64EncodedCertificate); + //then + assertThat(certificateModel.getCertificate()).isNotNull(); + assertThat(certificateModel.getSubjectData()).isEqualTo(new X500Name(SUBJECT)); + assertThat(certificateModel.getSans()).containsExactly(GENERAL_NAME); + } + + @Test + void shouldCorrectlyParseX509CertificateFromCertificateChain() + throws CertificateDecryptionException { + //given + StringBase64 base64EncodedCertificate = new StringBase64(ENCODED_CERTIFICATE_CHAIN_STRING); + //when + final X509CertificateModel certificateModel = factory.createCertificateModel(base64EncodedCertificate); + //then + assertThat(certificateModel.getCertificate()).isNotNull(); + assertThat(certificateModel.getSubjectData()).isEqualTo(new X500Name(SUBJECT)); + assertThat(certificateModel.getSans()).containsExactly(GENERAL_NAME); + } + + @Test + void shouldThrowExceptionWhenCertificateChainHasNoCertificates() { + //given + StringBase64 base64EncodedCertificate = new StringBase64(""); + //when, then + assertThatThrownBy(() -> factory.createCertificateModel(base64EncodedCertificate)) + .isInstanceOf(CertificateDecryptionException.class); + } +} diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateParserTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateParserTest.java new file mode 100644 index 00000000..4629234e --- /dev/null +++ b/certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateParserTest.java @@ -0,0 +1,84 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.List; +import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.jupiter.api.Test; + +class X509CertificateParserTest { + + private static final String SUBJECT = "CN=onap.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + + private final X509CertificateParser parser = new X509CertificateParser(); + + @Test + void getSubject_shouldReturnCorrectSubject() { + //given + X509Certificate certificate = mock(X509Certificate.class); + when(certificate.getSubjectX500Principal()) + .thenReturn(new X500Principal(SUBJECT)); + //when + final X500Name subject = parser.getSubject(certificate); + //then + assertThat(subject).isEqualTo(new X500Name(SUBJECT)); + } + + @Test + void getSans_shouldReturnCorrectSansArray() throws CertificateParsingException { + //given + X509Certificate certificate = mock(X509Certificate.class); + when(certificate.getSubjectAlternativeNames()) + .thenReturn(List.of( + List.of(GeneralName.dNSName, "test.onap.org"), + List.of(GeneralName.dNSName, "test2.onap.org"), + List.of(GeneralName.iPAddress, "127.0.0.1") + )); + //when + final GeneralName[] sans = parser.getSans(certificate); + //then + assertThat(sans).containsExactlyInAnyOrder( + new GeneralName(GeneralName.dNSName, "test.onap.org"), + new GeneralName(GeneralName.dNSName, "test2.onap.org"), + new GeneralName(GeneralName.iPAddress, "127.0.0.1") + ); + } + + @Test + void getSans_shouldReturnEmptyArrayWhenNoSans() throws CertificateParsingException { + //given + X509Certificate certificate = mock(X509Certificate.class); + when(certificate.getSubjectAlternativeNames()).thenReturn(null); + //when + final GeneralName[] sans = parser.getSans(certificate); + //then + assertThat(sans).isEmpty(); + } + +} diff --git a/certService/src/test/java/org/onap/oom/certservice/certification/model/CertificateDataTest.java b/certService/src/test/java/org/onap/oom/certservice/certification/model/CertificateDataTest.java new file mode 100644 index 00000000..e32ade8d --- /dev/null +++ b/certService/src/test/java/org/onap/oom/certservice/certification/model/CertificateDataTest.java @@ -0,0 +1,117 @@ +/* + * ============LICENSE_START======================================================= + * Cert Service + * ================================================================================ + * Copyright (C) 2021 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.oom.certservice.certification.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.GeneralName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class CertificateDataTest { + + private static final String SUBJECT = "CN=onap.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + private static final String SUBJECT_CHANGED_ORDER = "ST=California,C=US,OU=Linux-Foundation,CN=onap.org,O=ONAP,L=San-Francisco"; + private static final String OTHER_SUBJECT = "CN=onap1.org,OU=Linux-Foundation,O=ONAP,L=San-Francisco,ST=California,C=US"; + private static final String DNS_NAME = "test.onap.org"; + private static final String OTHER_DNS_NAME = "test1.onap.org"; + + private static Stream equalSubjectParameters() { + return Stream.of( + Arguments.of(SUBJECT, SUBJECT), + Arguments.of(SUBJECT_CHANGED_ORDER, SUBJECT_CHANGED_ORDER), + Arguments.of(SUBJECT, SUBJECT_CHANGED_ORDER), + Arguments.of(SUBJECT_CHANGED_ORDER, SUBJECT) + ); + } + + private static Stream notEqualSubjectParameters() { + return Stream.of( + Arguments.of(SUBJECT, OTHER_SUBJECT), + Arguments.of(OTHER_SUBJECT, SUBJECT), + Arguments.of(SUBJECT_CHANGED_ORDER, OTHER_SUBJECT), + Arguments.of(OTHER_SUBJECT, SUBJECT_CHANGED_ORDER) + ); + } + + @ParameterizedTest + @MethodSource("equalSubjectParameters") + void shouldAcceptSameSubjectData(String subject1, String subject2) { + //given + final CertificateData certificateData1 = new CertificateData(new X500Name(subject1), null); + final CertificateData certificateData2 = new CertificateData(new X500Name(subject2), null); + //when, then + assertThat(certificateData1).isEqualTo(certificateData2); + } + + @ParameterizedTest + @MethodSource("notEqualSubjectParameters") + void shouldRejectDifferentSubjectData(String subject1, String subject2) { + //given + final CertificateData certificateData1 = new CertificateData(new X500Name(subject1), null); + final CertificateData certificateData2 = new CertificateData(new X500Name(subject2), null); + //when, then + assertThat(certificateData1).isNotEqualTo(certificateData2); + } + + @Test + void shouldAcceptEqualSans() { + //given + final GeneralName[] sans1 = new GeneralName[]{new GeneralName(GeneralName.dNSName, DNS_NAME)}; + final GeneralName[] sans2 = new GeneralName[]{new GeneralName(GeneralName.dNSName, DNS_NAME)}; + final CertificateData certificateData1 = new CertificateData(new X500Name(SUBJECT), sans1); + final CertificateData certificateData2 = new CertificateData(new X500Name(SUBJECT), sans2); + //when, then + assertThat(certificateData1).isEqualTo(certificateData2); + } + + @Test + void shouldRejectNotEqualSans() { + //given + final GeneralName[] sans1 = new GeneralName[]{new GeneralName(GeneralName.dNSName, DNS_NAME)}; + final GeneralName[] sans2 = new GeneralName[]{new GeneralName(GeneralName.dNSName, OTHER_DNS_NAME)}; + final CertificateData certificateData1 = new CertificateData(new X500Name(SUBJECT), sans1); + final CertificateData certificateData2 = new CertificateData(new X500Name(SUBJECT), sans2); + //when, then + assertThat(certificateData1).isNotEqualTo(certificateData2); + } + + @Test + void shouldAcceptEqualSansIgnoringOrder() { + //given + GeneralName[] sans1 = new GeneralName[]{ + new GeneralName(GeneralName.dNSName, DNS_NAME), + new GeneralName(GeneralName.dNSName, OTHER_DNS_NAME) + }; + GeneralName[] sans2 = new GeneralName[]{ + new GeneralName(GeneralName.dNSName, OTHER_DNS_NAME), + new GeneralName(GeneralName.dNSName, DNS_NAME) + }; + final CertificateData certificateData1 = new CertificateData(new X500Name(SUBJECT), sans1); + final CertificateData certificateData2 = new CertificateData(new X500Name(SUBJECT), sans2); + //when, then + assertThat(certificateData1).isEqualTo(certificateData2); + } +}