[OOM-CERT-SERVICE] Add logic for KUR/CR detection 67/122367/4
authorRemigiusz Janeczek <remigiusz.janeczek@nokia.com>
Wed, 30 Jun 2021 10:44:29 +0000 (12:44 +0200)
committerRemigiusz Janeczek <remigiusz.janeczek@nokia.com>
Thu, 1 Jul 2021 08:05:01 +0000 (10:05 +0200)
Issue-ID: OOM-2753
Signed-off-by: Remigiusz Janeczek <remigiusz.janeczek@nokia.com>
Change-Id: I571ad3914a870dde83929cb6121c2c63a5df3ae4

23 files changed:
certService/src/main/java/org/onap/oom/certservice/api/CertificationController.java
certService/src/main/java/org/onap/oom/certservice/certification/CertificationModelFactory.java
certService/src/main/java/org/onap/oom/certservice/certification/CsrModelFactory.java
certService/src/main/java/org/onap/oom/certservice/certification/PemStringToCertificateConverter.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/StringBase64.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetector.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateModelFactory.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/X509CertificateParser.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/exception/CertificateDecryptionException.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/exception/StringToCertificateConversionException.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateData.java [new file with mode: 0644]
certService/src/main/java/org/onap/oom/certservice/certification/model/CsrModel.java
certService/src/main/java/org/onap/oom/certservice/certification/model/X509CertificateModel.java [new file with mode: 0644]
certService/src/test/java/org/onap/oom/certservice/api/CertificationControllerTest.java
certService/src/test/java/org/onap/oom/certservice/certification/CertificationModelFactoryTest.java
certService/src/test/java/org/onap/oom/certservice/certification/CsrModelFactoryTest.java
certService/src/test/java/org/onap/oom/certservice/certification/PemStringToCertificateConverterTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/oom/certservice/certification/TestData.java
certService/src/test/java/org/onap/oom/certservice/certification/TestUtils.java
certService/src/test/java/org/onap/oom/certservice/certification/UpdateRequestTypeDetectorTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateModelFactoryTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/oom/certservice/certification/X509CertificateParserTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/oom/certservice/certification/model/CertificateDataTest.java [new file with mode: 0644]

index a0972d5..d21b1eb 100644 (file)
@@ -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()
index 94332f2..d5d9d9b 100644 (file)
@@ -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");
+        }
     }
 }
index 758427f..789773d 100644 (file)
@@ -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.
 
 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<String> 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 (file)
index 0000000..7126cd4
--- /dev/null
@@ -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 (file)
index 0000000..9929e52
--- /dev/null
@@ -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<String> 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 (file)
index 0000000..bef2fd5
--- /dev/null
@@ -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 (file)
index 0000000..17367ce
--- /dev/null
@@ -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 (file)
index 0000000..67b1279
--- /dev/null
@@ -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<List<?>> sans = certificate.getSubjectAlternativeNames();
+        if (sans == null) {
+            return new GeneralName[0];
+        }
+        final ArrayList<GeneralName> 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 (file)
index 0000000..16fdb44
--- /dev/null
@@ -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 (file)
index 0000000..087172d
--- /dev/null
@@ -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 (file)
index 0000000..3a00c91
--- /dev/null
@@ -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<GeneralName> 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<GeneralName> getSortedSansList(GeneralName[] sans) {
+        return Arrays.stream(sans).sorted(Comparator.comparing(GeneralName::toString))
+            .collect(Collectors.toUnmodifiableList());
+    }
+
+}
index 03d1a9d..9675583 100644 (file)
@@ -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 (file)
index 0000000..f45fb7d
--- /dev/null
@@ -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;
+    }
+}
index 0bf3790..f1d5baa 100644 (file)
@@ -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<CertificationModel> 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);
+    }
+
 }
index 705ae00..c898b68 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ============LICENSE_START=======================================================
- * PROJECT
+ * Cert Service
  * ================================================================================
  * Copyright (C) 2020-2021 Nokia. All rights reserved.
  * ================================================================================
 
 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();
     }
index 88cc6fb..eb6a055 100644 (file)
@@ -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 (file)
index 0000000..ecdb1a2
--- /dev/null
@@ -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<? extends Serializable> 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);
+    }
+
+}
index 1c883f8..92b239f 100644 (file)
@@ -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"
index 7c69bd5..0dceda1 100644 (file)
@@ -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 (file)
index 0000000..ee07853
--- /dev/null
@@ -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<Arguments> 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<Arguments> 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 (file)
index 0000000..bad4887
--- /dev/null
@@ -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 (file)
index 0000000..4629234
--- /dev/null
@@ -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 (file)
index 0000000..e32ade8
--- /dev/null
@@ -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<Arguments> 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<Arguments> 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);
+    }
+}