Add decoding CSR received from client.
authorBartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Tue, 11 Feb 2020 12:27:08 +0000 (13:27 +0100)
committerBartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Tue, 11 Feb 2020 12:29:13 +0000 (13:29 +0100)
Issue-ID: AAF-995
Signed-off-by: Bartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Change-Id: I7c868f4835397f58ae4e6fad0e764e21d886d3d3

15 files changed:
.gitignore
certService/README.md
certService/pom.xml
certService/src/main/java/org/onap/aaf/certservice/api/CertificationService.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/certification/CsrModelFactory.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/certification/PemObjectFactory.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/certification/exceptions/CsrDecryptionException.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/certification/model/CsrModel.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/rest/CertificationService.java [deleted file]
certService/src/test/java/org/onap/aaf/certservice/api/CertificationServiceTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/aaf/certservice/certification/CsrModelFactoryTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/aaf/certservice/certification/PemObjectFactoryTest.java [new file with mode: 0644]
certService/src/test/java/org/onap/aaf/certservice/certification/TestData.java [new file with mode: 0644]
certService/src/test/java/org/onap/aaf/certservice/certification/TestUtils.java [new file with mode: 0644]
certService/src/test/java/org/onap/aaf/certservice/certification/model/CsrModelTest.java [new file with mode: 0644]

index 89d4012..9575602 100644 (file)
@@ -1,7 +1,7 @@
 target/
 !**/src/main/**
 !**/src/test/**
-*/var
+**/var
 
 ### STS ###
 .apt_generated
index 25bb672..db96fa9 100644 (file)
@@ -95,4 +95,18 @@ audit.log  error.log  trace.log
 ### Sonar results
     ```     
       https://sonarcloud.io/dashboard?id=onap_aaf-certservice
-    ```
\ No newline at end of file
+    ```
+
+### RestAPI
+API is described by Swagger ( OpenAPI 3.0 ) on endpoint /docs 
+( endpoint is defined in properties as springdoc.swagger-ui.path )
+  
+    ```
+    http://localchost:8080/docs
+    
+    ```
+
+### Sonar results
+    ```     
+      https://sonarcloud.io/dashboard?id=onap_aaf-certservice
+    ```
index 8402d74..38c356c 100644 (file)
@@ -20,7 +20,6 @@
         <artifactId>aaf-certservice-parent</artifactId>
         <version>0.0.1-SNAPSHOT</version>
     </parent>
-    <groupId>org.onap.aaf</groupId>
     <artifactId>cert-service</artifactId>
     <version>0.0.1-SNAPSHOT</version>
     <name>cert-service</name>
@@ -38,6 +37,7 @@
         <spring-boot-starter-actuator.version>2.2.4.RELEASE</spring-boot-starter-actuator.version>
         <spring-boot-starter-log4j2.version>2.1.5.RELEASE</spring-boot-starter-log4j2.version>
         <springdoc-openapi-ui.version>1.2.21</springdoc-openapi-ui.version>
+        <bouncycastle.version>1.60</bouncycastle.version>
         <docker-maven-plugin.version>0.33.0</docker-maven-plugin.version>
     </properties>
 
             <artifactId>springdoc-openapi-ui</artifactId>
             <version>${springdoc-openapi-ui.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>${bouncycastle.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>${bouncycastle.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/certService/src/main/java/org/onap/aaf/certservice/api/CertificationService.java b/certService/src/main/java/org/onap/aaf/certservice/api/CertificationService.java
new file mode 100644 (file)
index 0000000..a46e07f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.api;
+
+import org.onap.aaf.certservice.certification.CsrModelFactory;
+import org.onap.aaf.certservice.certification.CsrModelFactory.StringBase64;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+import org.onap.aaf.certservice.certification.model.CsrModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+public class CertificationService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(CertificationService.class);
+
+    private final CsrModelFactory csrModelFactory;
+
+    @Autowired
+    CertificationService(CsrModelFactory csrModelFactory) {
+        this.csrModelFactory = csrModelFactory;
+    }
+
+    /**
+     * Request for signing certificate by given CA.
+     *
+     *
+     * @param caName the name of Certification Authority that will sign root certificate
+     * @param encodedCsr Certificate Sign Request encoded in Base64 form
+     * @param encodedPrivateKey Private key for CSR, needed for PoP, encoded in Base64 form
+     * @return JSON containing trusted certificates and certificate chain
+     */
+    @GetMapping("v1/certificate/{caName}")
+    public ResponseEntity<String> signCertificate(
+            @PathVariable String caName,
+            @RequestHeader("CSR") String encodedCsr,
+            @RequestHeader("PK") String encodedPrivateKey
+    ) {
+        caName = caName.replaceAll("[\n|\r|\t]", "_");
+        LOGGER.info("Received certificate signing request for CA named: {}", caName);
+
+        try {
+            CsrModel csrModel = csrModelFactory.createCsrModel(
+                    new StringBase64(encodedCsr),
+                    new StringBase64(encodedPrivateKey)
+            );
+            LOGGER.debug("Received CSR meta data: \n{}", csrModel);
+            return new ResponseEntity<>(csrModel.toString(), HttpStatus.OK);
+        } catch (CsrDecryptionException e) {
+            LOGGER.error("Exception occurred during certificate signing:", e);
+            return new ResponseEntity<>("Wrong certificate signing request (CSR) format", HttpStatus.BAD_REQUEST);
+        }
+    }
+
+
+}
diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/CsrModelFactory.java b/certService/src/main/java/org/onap/aaf/certservice/certification/CsrModelFactory.java
new file mode 100644 (file)
index 0000000..80858f4
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification;
+
+import java.io.IOException;
+import java.util.Base64;
+
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+import org.onap.aaf.certservice.certification.model.CsrModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+
+@Service
+public class CsrModelFactory {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(CsrModelFactory.class);
+    private final PemObjectFactory pemObjectFactory = new PemObjectFactory();
+
+    public CsrModel createCsrModel(StringBase64 csr, StringBase64 privateKey) throws CsrDecryptionException {
+        LOGGER.debug("Decoded CSR: \n{}", csr.asString());
+
+        try {
+            PemObject pemObject = pemObjectFactory.createPmObject(csr.asString());
+            PKCS10CertificationRequest decodedCsr = new PKCS10CertificationRequest(
+                    pemObject.getContent()
+            );
+            PemObject decodedPrivateKey = pemObjectFactory.createPmObject(privateKey.asString());
+            return new CsrModel(decodedCsr, decodedPrivateKey);
+        } catch (IOException e) {
+            throw new CsrDecryptionException("Incorrect CSR, decryption failed", e);
+        }
+    }
+
+    public static class StringBase64 {
+        private final String value;
+        private final Base64.Decoder decoder = Base64.getDecoder();
+
+        public StringBase64(String value) {
+            this.value = value;
+        }
+
+        public String asString() {
+            return new String(decoder.decode(value));
+        }
+    }
+}
+
+
diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/PemObjectFactory.java b/certService/src/main/java/org/onap/aaf/certservice/certification/PemObjectFactory.java
new file mode 100644 (file)
index 0000000..e3339cc
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemReader;
+
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+
+
+public class PemObjectFactory {
+
+    public PemObject createPmObject(String pem) throws CsrDecryptionException {
+
+        try (StringReader stringReader = new StringReader(pem);
+             PemReader pemReader = new PemReader(stringReader)) {
+            return pemReader.readPemObject();
+        } catch (IOException e) {
+            throw new CsrDecryptionException("Unable to create PEM", e);
+        }
+    }
+
+}
diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/exceptions/CsrDecryptionException.java b/certService/src/main/java/org/onap/aaf/certservice/certification/exceptions/CsrDecryptionException.java
new file mode 100644 (file)
index 0000000..fb16ad9
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification.exceptions;
+
+public class CsrDecryptionException extends Exception {
+    public CsrDecryptionException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/certService/src/main/java/org/onap/aaf/certservice/certification/model/CsrModel.java b/certService/src/main/java/org/onap/aaf/certservice/certification/model/CsrModel.java
new file mode 100644 (file)
index 0000000..ef76144
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification.model;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+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.aaf.certservice.certification.exceptions.CsrDecryptionException;
+
+
+public class CsrModel {
+
+
+    private final PKCS10CertificationRequest csr;
+    private final PemObject privateKey;
+
+    public CsrModel(PKCS10CertificationRequest csr, PemObject privateKey) {
+        this.csr = csr;
+        this.privateKey = privateKey;
+    }
+
+    public PemObject getPublicKey() throws CsrDecryptionException {
+        try {
+            return new PemObject("PUBLIC KEY", csr.getSubjectPublicKeyInfo().getEncoded());
+        } catch (IOException e) {
+            throw new CsrDecryptionException("Reading Public Key from CSR failed", e.getCause());
+        }
+    }
+
+    public PemObject getPrivateKey() {
+        return privateKey;
+    }
+
+    public X500Name getSubjectData() {
+        return csr.getSubject();
+    }
+
+    public List<String> getSansData() {
+        Extensions extensions =
+                Extensions.getInstance(csr.getAttributes()[0].getAttrValues().getObjectAt(0));
+        GeneralName[] arrayOfAlternativeNames =
+                GeneralNames.fromExtensions(extensions, Extension.subjectAlternativeName).getNames();
+
+        return Arrays.stream(arrayOfAlternativeNames)
+                .map(GeneralName::getName)
+                .map(Objects::toString)
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public String toString() {
+        return "Subject: { " + getSubjectData().toString()
+                + " ,SANs: " + getSansData().toString() + " }";
+    }
+
+}
diff --git a/certService/src/main/java/org/onap/aaf/certservice/rest/CertificationService.java b/certService/src/main/java/org/onap/aaf/certservice/rest/CertificationService.java
deleted file mode 100644 (file)
index 7a93bff..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * ============LICENSE_START=======================================================
- * PROJECT
- * ================================================================================
- * Copyright (C) 2020 Nokia. All rights reserved.
- * ================================================================================
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ============LICENSE_END=========================================================
- */
-
-package org.onap.aaf.certservice.rest;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestHeader;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.Base64;
-
-@RestController
-public class CertificationService {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(CertificationService.class);
-    private static final Base64.Decoder DECODER = Base64.getDecoder();
-
-    @GetMapping("/csr/{caName}")
-    public ResponseEntity<String> getEncodesCSR(
-            @PathVariable String caName,
-            @RequestHeader("CSR") String encodedCSR,
-            @RequestHeader("PK") String encodedPrivateKey
-    ) {
-
-        String csr = decode(encodedCSR);
-        String privateKey = decode(encodedPrivateKey);
-
-        LOGGER.info("Received CSR for CA named: {}",caName);
-        LOGGER.debug("Decoded received CSR: \n{}", csr);
-
-        return new ResponseEntity<>(csr, HttpStatus.OK);
-
-    }
-
-    private String decode(String encodedData) {
-        return new String(DECODER.decode(encodedData));
-    }
-
-}
diff --git a/certService/src/test/java/org/onap/aaf/certservice/api/CertificationServiceTest.java b/certService/src/test/java/org/onap/aaf/certservice/api/CertificationServiceTest.java
new file mode 100644 (file)
index 0000000..99ca09b
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.api;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.aaf.certservice.certification.CsrModelFactory;
+import org.onap.aaf.certservice.certification.CsrModelFactory.StringBase64;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+import org.onap.aaf.certservice.certification.model.CsrModel;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import java.io.IOException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+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;
+
+
+class CertificationServiceTest {
+
+    private CertificationService certificationService;
+
+    @Mock
+    private CsrModelFactory csrModelFactory;
+
+    @BeforeEach
+    void serUp() {
+        MockitoAnnotations.initMocks(this);
+        certificationService = new CertificationService(csrModelFactory);
+    }
+
+    @Test
+    void shouldReturnDataAboutCsrBaseOnEncodedParameters() throws CsrDecryptionException {
+        // given
+        final String testStringCsr = "testData";
+        CsrModel mockedCsrModel = mock(CsrModel.class);
+        when(mockedCsrModel.toString()).thenReturn(testStringCsr);
+        when(csrModelFactory.createCsrModel(any(StringBase64.class), any(StringBase64.class)))
+                .thenReturn(mockedCsrModel);
+
+        // when
+        ResponseEntity<String> testResponse =
+                certificationService.signCertificate("TestCa", "encryptedCSR", "encryptedPK");
+
+        // then
+        assertEquals(testResponse.getStatusCode(), HttpStatus.OK);
+        assertTrue(
+                testResponse.toString().contains(testStringCsr)
+        );
+    }
+
+    @Test
+    void shouldReturnBadRequestWhenCreatingCsrModelFails() throws CsrDecryptionException {
+        // given
+        when(csrModelFactory.createCsrModel(any(StringBase64.class), any(StringBase64.class)))
+                .thenThrow(new CsrDecryptionException("creation fail",new IOException()));
+
+        // when
+        ResponseEntity<String> testResponse =
+                certificationService.signCertificate("TestCa", "encryptedCSR", "encryptedPK");
+
+        String expectedMessage = "Wrong certificate signing request (CSR) format";
+
+        // then
+        assertEquals(HttpStatus.BAD_REQUEST, testResponse.getStatusCode());
+        assertTrue(
+                testResponse.toString().contains(expectedMessage)
+        );
+
+    }
+
+}
diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/CsrModelFactoryTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/CsrModelFactoryTest.java
new file mode 100644 (file)
index 0000000..8b5f5dc
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification;
+
+import org.bouncycastle.util.encoders.Base64;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.aaf.certservice.certification.CsrModelFactory.StringBase64;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+import org.onap.aaf.certservice.certification.model.CsrModel;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.onap.aaf.certservice.certification.TestData.TEST_CSR;
+import static org.onap.aaf.certservice.certification.TestData.TEST_PK;
+import static org.onap.aaf.certservice.certification.TestData.TEST_WRONG_CSR;
+
+
+class CsrModelFactoryTest {
+
+    private CsrModelFactory csrModelFactory;
+
+    @BeforeEach
+    void setUp() {
+        csrModelFactory = new CsrModelFactory();
+    }
+
+    @Test
+    void shouldDecryptCsrAndReturnStringWithDataAboutIt() throws CsrDecryptionException {
+        // given
+        String encoderCsr = new String(Base64.encode(TEST_CSR.getBytes()));
+        String encoderPK = new String(Base64.encode(TEST_PK.getBytes()));
+
+        // when
+        CsrModel decryptedCsr = csrModelFactory
+                .createCsrModel(new StringBase64(encoderCsr), new StringBase64(encoderPK));
+
+        // then
+        assertTrue(
+                decryptedCsr.toString()
+                        .contains(
+                                "C=US,ST=California,L=San-Francisco,O=Linux-Foundation,"
+                                        + "OU=ONAP,CN=onap.org,E=tester@onap.org")
+                        &&
+                        decryptedCsr.toString()
+                                .contains("SANs: [gerrit.onap.org, test.onap.org, onap.com]")
+        );
+    }
+
+
+    @Test
+    void shouldThrowCsrDecryptionExceptionWhenCsrAreIncorrect() {
+        // given
+        String encoderPK = new String(Base64.encode(TEST_PK.getBytes()));
+        String wrongCsr = new String(Base64.encode(TEST_WRONG_CSR.getBytes()));
+
+        // when
+        Exception exception = assertThrows(
+                CsrDecryptionException.class, () -> csrModelFactory
+                        .createCsrModel(new StringBase64(wrongCsr), new StringBase64(encoderPK))
+        );
+
+        String expectedMessage = "Incorrect CSR, decryption failed";
+        String actualMessage = exception.getMessage();
+
+        // then
+        assertTrue(actualMessage.contains(expectedMessage));
+    }
+
+}
diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/PemObjectFactoryTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/PemObjectFactoryTest.java
new file mode 100644 (file)
index 0000000..67d7f1d
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification;
+
+import org.bouncycastle.util.io.pem.PemObject;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+
+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.onap.aaf.certservice.certification.TestData.TEST_PEM;
+import static org.onap.aaf.certservice.certification.TestData.TEST_WRONG_PEM;
+import static org.onap.aaf.certservice.certification.TestUtils.pemObjectToString;
+
+
+class PemObjectFactoryTest {
+
+
+    private PemObjectFactory pemObjectFactory;
+
+    @BeforeEach
+    void setUp() {
+        pemObjectFactory = new PemObjectFactory();
+    }
+
+    @Test
+    void shouldTransformStringInToPemObjectAndBackToString() throws CsrDecryptionException {
+        // when
+        PemObject pemObject = pemObjectFactory.createPmObject(TEST_PEM);
+        String parsedPemObject = pemObjectToString(pemObject);
+
+        // then
+        assertEquals(TEST_PEM, parsedPemObject);
+    }
+
+    @Test
+    void shouldThrowExceptionWhenParsingPemFailed() {
+        // when
+        Exception exception = assertThrows(
+                CsrDecryptionException.class, () -> pemObjectFactory.createPmObject(TEST_WRONG_PEM)
+        );
+
+        String expectedMessage = "Unable to create PEM";
+        String actualMessage = exception.getMessage();
+
+        // then
+        assertTrue(actualMessage.contains(expectedMessage));
+    }
+
+}
diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/TestData.java b/certService/src/test/java/org/onap/aaf/certservice/certification/TestData.java
new file mode 100644 (file)
index 0000000..6fea5b5
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification;
+
+public final class TestData {
+
+    private TestData() {
+    }
+
+    public static final String TEST_CSR = ""
+            + "-----BEGIN CERTIFICATE REQUEST-----\n"
+            + "MIIDIzCCAgsCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh\n"
+            + "MRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkwFwYDVQQKDBBMaW51eC1Gb3VuZGF0\n"
+            + "aW9uMQ0wCwYDVQQLDARPTkFQMREwDwYDVQQDDAhvbmFwLm9yZzEeMBwGCSqGSIb3\n"
+            + "DQEJARYPdGVzdGVyQG9uYXAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
+            + "CgKCAQEA13K1LrQ1L6eL7B8K4kucNct0sSjZe7Ww91V40s6mjcWajeFJk+pObZKz\n"
+            + "BfnImkVJwxdNMDD6tX16wykbGfQPyh4BBiAjLVk9XSeoPHFRBQ4LKTuyPtXhEXyr\n"
+            + "qwatYXGWZE554qq64pbReddOUJHgMc38SrOk/eMAKxB0uRrXpA0mPH7zwIZ4X8g2\n"
+            + "PoxJKI1BSYc8kOvvujsGSMw3e5nS8A+doFUwVi3jJMnaVCoZrvJbtREfXHZqBLQ5\n"
+            + "XQ8mNpIFfmGYF/tvW/O6LBdlZkuAQ9i4FBgf5+HdIVZOXrn09ksIZxW6vxIvAVi0\n"
+            + "5AOSgXictyphcNP2i/erBeCQCVB7MwIDAQABoEYwRAYJKoZIhvcNAQkOMTcwNTAz\n"
+            + "BgNVHREELDAqgg9nZXJyaXQub25hcC5vcmeCDXRlc3Qub25hcC5vcmeCCG9uYXAu\n"
+            + "Y29tMA0GCSqGSIb3DQEBCwUAA4IBAQBXH2nRwodQRJTuyrLe/VSg3PUdcPyAx2Ew\n"
+            + "63tWiGO+qWo8rK2a9Rr/t/zkQe2lx6NHqcMc2Rt6NeKGbrAvHGxTiYM35gktBdxG\n"
+            + "UaQS1ymrBWHAwbC+kv78r+5lCfafNm/EVdhUZbEw+crsw2wx4iKEW0byS4Ln0o5g\n"
+            + "aXVUW3i4G5FaYiYBUIDsujDdnH1IoxunEA6pDzDv1h6R9/TYu6Se8HToREIjOPBZ\n"
+            + "pDI5lDRu0YmI8r+TmAU3tTT1sY2WVxYDnhJut9ofegfMPQV4FIohxtPcCfoLSWti\n"
+            + "ml6jbcFqDvlzq3B3CXH9HU3jdJt33iSjCQGsSqy6bmCOdMS6XTPU\n"
+            + "-----END CERTIFICATE REQUEST-----\n";
+
+    public static final String TEST_WRONG_CSR = ""
+            + "-----BEGIN CERTIFICATE REQUEST-----\n"
+            + "MIIDIzCCAgsCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh\n"
+            + "MRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkwFwYDVQQKDBBMaW51eC1Gb3VuZGF0\n"
+            + "aW9uMQ0wCwYDVQQLDARPTkFQMREwDwYDVQQDDAhvbmFwLm9yZzEeMBwGCSqGSIb3\n"
+            + "-----END CERTIFICATE REQUEST-----\n";
+
+    public static final String TEST_PK = "-----BEGIN PRIVATE KEY-----\n"
+            + "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDXcrUutDUvp4vs\n"
+            + "HwriS5w1y3SxKNl7tbD3VXjSzqaNxZqN4UmT6k5tkrMF+ciaRUnDF00wMPq1fXrD\n"
+            + "KRsZ9A/KHgEGICMtWT1dJ6g8cVEFDgspO7I+1eERfKurBq1hcZZkTnniqrriltF5\n"
+            + "105QkeAxzfxKs6T94wArEHS5GtekDSY8fvPAhnhfyDY+jEkojUFJhzyQ6++6OwZI\n"
+            + "zDd7mdLwD52gVTBWLeMkydpUKhmu8lu1ER9cdmoEtDldDyY2kgV+YZgX+29b87os\n"
+            + "F2VmS4BD2LgUGB/n4d0hVk5eufT2SwhnFbq/Ei8BWLTkA5KBeJy3KmFw0/aL96sF\n"
+            + "4JAJUHszAgMBAAECggEAJ1StdsU3IGf5xzUzi3Q6JCfsOZs3eLoGgGB+Gh3XkfIM\n"
+            + "8PG7uOEBSEeLnv+me2NCv/a1BKMsYY1yp8YNSIOhjkhD75ZWVaUA6syejcox/DZA\n"
+            + "G1rmg0oQOF0GCcbCSBOwXMdmwNZiH5Ng0llX1qWKxAzSjeCVsjOKiFIMvO4Fh9D4\n"
+            + "9Io6/dRRNCxB6MEs1GT5IDfCV2PGDIalJ3znFqDnfdu9RDEDfNVHSUr6Jdu3Hrf5\n"
+            + "3qCcSEkMGuXYLotCNtTP1x0H0wW5gVpcbQEb29qdmHL1qkp3UiA3afsHnO/3k0gv\n"
+            + "gV5FxaldugyZAjqUGERdKaY6BMDJkDuu0qD0tPQK4QKBgQDuP5X5BcQ4iHNej+il\n"
+            + "xxT8QaEcZj0YEzcXzfm3ztZP7g+Jc1MbQXh6BuHLkXG5LeCwdnmk+LUD0MLoUSm3\n"
+            + "N2ZdtVuOHX7VEBrhrTwK/kMDpC7ganQzfvgOr9WQGmgGMRiUYAyK1J/x78yX967Z\n"
+            + "IAzdVZ/JSDdsyA983JckLL7CPQKBgQDngDkEJKYGfDt2mfItD8c8nhczGbDdoyYh\n"
+            + "s93ppTtgzFoNgFL4y/DOvisWMGgoeeYXSgH5uoPv6yY7IIkQzYySY6qQ3gmk1/X+\n"
+            + "bO+IsKVtlHBzqqojFteg3MfVojisMoAx6y5aBw1BXE2nAU8yWBTtuk+3KgGn9Oxk\n"
+            + "+Z4rdP06LwKBgA4b09zIW6NhaTubWBKhJHv/wvO0lj+bu7J8LyKUbBqVpXPlUXGW\n"
+            + "wfSv/aUZetuVfO3WRkPfupB8R16Ml+TSsgwwljhnRMCHUKA2qwyXnA5WJbSCeVkn\n"
+            + "Vrc/8Gy1M53SQHtg6L079DDWm44QS9ltzXU6Adlgnm+htVEWmxi4UZ+dAoGAfr6z\n"
+            + "+LG7+GcCA2AruEIgOe7wErkpHV+am+8nOymMxeV8FFJCmxbFQ9vYKTDdhfOfZvbM\n"
+            + "+BYG8E8VQmAAyyNOqENK+j+mlgrrEp4/0t2r5L/VhW5V8hoqelcGTc+gKZ8IkswJ\n"
+            + "N58Owc8wcJQF8TFKXBGaXVTxTSyKVIpZ778AeV8CgYAAvuicDkdwWv5EhDFf3aTI\n"
+            + "wfRFYflA6oiygnI63HzVyY4a+SyZs+nQpB5HBDo+Lyz8RaVRC5E7jQ8kiXJpxAu7\n"
+            + "1wnspz+pa3q61yR32N+zGuub71FXdLWSOlys6rzJqvqYihKxY22C2TyDyBCR2tMj\n"
+            + "mdnshXNAJfKkfghkJhFHrg==\n"
+            + "-----END PRIVATE KEY-----";
+
+    public static final String TEST_PEM = ""
+            + "-----BEGIN CERTIFICATE REQUEST-----\n"
+            + "MIIDIzCCAgsCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh\n"
+            + "-----END CERTIFICATE REQUEST-----\n";
+
+    public static final String TEST_WRONG_PEM = ""
+            + "-----BEGIN WRONG REQUEST-----"
+            + "MIIDIzCCAgsCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh"
+            + "-----END WRONG REQUEST-----";
+
+}
diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/TestUtils.java b/certService/src/test/java/org/onap/aaf/certservice/certification/TestUtils.java
new file mode 100644 (file)
index 0000000..156cf8b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification;
+
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemWriter;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+
+public final class TestUtils {
+
+    private TestUtils() {
+    }
+
+    public static String pemObjectToString(PemObject pemObject) throws CsrDecryptionException {
+        try (StringWriter output = new StringWriter()) {
+            PemWriter pemWriter = new PemWriter(output);
+            pemWriter.writeObject(pemObject);
+            pemWriter.close();
+            return output.getBuffer().toString();
+
+        } catch (IOException e) {
+            throw new CsrDecryptionException("Writing PAM Object to string failed", e);
+        }
+    }
+}
diff --git a/certService/src/test/java/org/onap/aaf/certservice/certification/model/CsrModelTest.java b/certService/src/test/java/org/onap/aaf/certservice/certification/model/CsrModelTest.java
new file mode 100644 (file)
index 0000000..ffce61d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.certification.model;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.junit.jupiter.api.Test;
+import org.onap.aaf.certservice.certification.PemObjectFactory;
+import org.onap.aaf.certservice.certification.exceptions.CsrDecryptionException;
+
+import java.io.IOException;
+
+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.aaf.certservice.certification.TestData.TEST_CSR;
+import static org.onap.aaf.certservice.certification.TestData.TEST_PK;
+import static org.onap.aaf.certservice.certification.TestUtils.pemObjectToString;
+
+
+class CsrModelTest {
+
+
+    @Test
+    void shouldByConstructedAndReturnProperFields() throws CsrDecryptionException, IOException {
+        // given
+        PemObject testPublicKey = generateTestPublicKey();
+
+        // when
+        CsrModel csrModel = generateTestCsrModel();
+
+
+        // then
+        assertEquals(
+                pemObjectToString(csrModel.getPrivateKey()).trim(),
+                TEST_PK.trim());
+        assertEquals(
+                pemObjectToString(csrModel.getPublicKey()).trim(),
+                pemObjectToString((testPublicKey)).trim());
+        assertThat(csrModel.getSansData())
+                .contains(
+                        "gerrit.onap.org", "test.onap.org", "onap.com");
+        assertThat(csrModel.getSubjectData().toString())
+                .contains(
+                        "C=US,ST=California,L=San-Francisco,O=Linux-Foundation,OU=ONAP,CN=onap.org,E=tester@onap.org");
+    }
+
+    @Test
+    void shouldThrowExceptionWhenKeyIsNotCorrect() throws IOException, CsrDecryptionException {
+        // given
+        PemObjectFactory pemObjectFactory = new PemObjectFactory();
+        PKCS10CertificationRequest testCsr = mock(PKCS10CertificationRequest.class);
+        SubjectPublicKeyInfo wrongKryInfo = mock(SubjectPublicKeyInfo.class);
+        when(testCsr.getSubjectPublicKeyInfo())
+                .thenReturn(wrongKryInfo);
+        when(wrongKryInfo.getEncoded())
+                .thenThrow(new IOException());
+        PemObject testPrivateKey = pemObjectFactory.createPmObject(TEST_PK);
+        CsrModel csrModel = new CsrModel(testCsr, testPrivateKey);
+
+        // when
+        Exception exception = assertThrows(
+                CsrDecryptionException.class,
+                csrModel::getPublicKey
+        );
+
+        String expectedMessage = "Reading Public Key from CSR failed";
+        String actualMessage = exception.getMessage();
+
+        // then
+        assertTrue(actualMessage.contains(expectedMessage));
+    }
+
+    private CsrModel generateTestCsrModel() throws CsrDecryptionException, IOException {
+        PemObjectFactory pemObjectFactory = new PemObjectFactory();
+        PKCS10CertificationRequest testCsr = new PKCS10CertificationRequest(
+                pemObjectFactory.createPmObject(TEST_CSR).getContent()
+        );
+        PemObject testPrivateKey = pemObjectFactory.createPmObject(TEST_PK);
+        return new CsrModel(testCsr, testPrivateKey);
+    }
+
+    private PemObject generateTestPublicKey() throws CsrDecryptionException, IOException {
+        PemObjectFactory pemObjectFactory = new PemObjectFactory();
+        PKCS10CertificationRequest testCsr = new PKCS10CertificationRequest(
+                pemObjectFactory.createPmObject(TEST_CSR).getContent()
+        );
+        return new PemObject("PUBLIC KEY", testCsr.getSubjectPublicKeyInfo().getEncoded());
+    }
+}