[OOM-CERT-SERVICE] Add Key Update Request functionality 75/122375/7
authorTomasz Wrobel <tomasz.wrobel@nokia.com>
Wed, 30 Jun 2021 14:14:25 +0000 (16:14 +0200)
committerJoanna Jeremicz <joanna.jeremicz@nokia.com>
Mon, 5 Jul 2021 11:11:04 +0000 (13:11 +0200)
Issue-ID: OOM-2753
Signed-off-by: Tomasz Wrobel <tomasz.wrobel@nokia.com>
Change-Id: Icecef30b830c38606e17fbc2c502208543d048d2

13 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/CertificationProvider.java
certService/src/main/java/org/onap/oom/certservice/certification/model/CertificateUpdateModel.java
certService/src/main/java/org/onap/oom/certservice/cmpv2client/api/CmpClient.java
certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CmpClientImpl.java
certService/src/main/java/org/onap/oom/certservice/cmpv2client/impl/CreateCertRequest.java
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/CertificationProviderTest.java
certService/src/test/java/org/onap/oom/certservice/certification/TestData.java
certService/src/test/java/org/onap/oom/certservice/cmpv2client/ClientTestData.java [new file with mode: 0644]
certService/src/test/java/org/onap/oom/certservice/cmpv2client/Cmpv2ClientTest.java

index d21b1eb..9f87778 100644 (file)
@@ -112,7 +112,7 @@ public class CertificationController {
             @RequestHeader("PK") String encodedPrivateKey,
             @RequestHeader("OLD_CERT") String encodedOldCert,
             @RequestHeader("OLD_PK") String encodedOldPrivateKey
-    ) throws DecryptionException, CertificateDecryptionException {
+    ) throws DecryptionException, CmpClientException, CertificateDecryptionException {
         caName = replaceWhiteSpaceChars(caName);
         LOGGER.info("Received certificate update request for CA named: {}", caName);
         CertificateUpdateModel certificateUpdateModel = new CertificateUpdateModel.CertificateUpdateModelBuilder()
index d5d9d9b..a5076a3 100644 (file)
@@ -76,7 +76,7 @@ public class CertificationModelFactory {
     }
 
     public CertificationModel createCertificationModel(CertificateUpdateModel certificateUpdateModel)
-        throws DecryptionException, CertificateDecryptionException {
+        throws DecryptionException, CmpClientException, CertificateDecryptionException {
         LOGGER.info("CSR: " + certificateUpdateModel.getEncodedCsr() +
                 ", old cert: " + certificateUpdateModel.getEncodedOldCert() +
                 ", CA: " + certificateUpdateModel.getCaName());
@@ -87,10 +87,15 @@ public class CertificationModelFactory {
         final X509CertificateModel certificateModel = x509CertificateModelFactory.createCertificateModel(
             new StringBase64(certificateUpdateModel.getEncodedOldCert()));
 
+        Cmpv2Server cmpv2Server = cmpv2ServerProvider.getCmpv2Server(certificateUpdateModel.getCaName());
+        LOGGER.debug("Found server for given CA name: \n{}", cmpv2Server);
+        LOGGER.info("Sending update request for certification model for CA named: {}, and certificate update request:\n{}",
+            certificateUpdateModel.getCaName(), csrModel);
+
         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");
+            return certificationProvider.updateCertificate(csrModel, cmpv2Server, certificateUpdateModel);
         } else {
             LOGGER.info(
                 "Certificate Signing Request and Old Certificate have different parameters. Preparing Certification Request");
index 91148a2..bfa8310 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * 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.
@@ -24,6 +24,7 @@ import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
 import org.bouncycastle.util.io.pem.PemObjectGenerator;
 import org.bouncycastle.util.io.pem.PemWriter;
 import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+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.cmpv2client.api.CmpClient;
@@ -59,6 +60,13 @@ public class CertificationProvider {
                 convertFromX509CertificateListToPemList(certificates.getTrustedCertificates()));
     }
 
+    public CertificationModel updateCertificate(CsrModel csrModel, Cmpv2Server cmpv2Server,
+        CertificateUpdateModel certificateUpdateModel) throws CmpClientException {
+        Cmpv2CertificationModel certificates = cmpClient.updateCertificate(csrModel, cmpv2Server, certificateUpdateModel);
+        return new CertificationModel(convertFromX509CertificateListToPemList(certificates.getCertificateChain()),
+            convertFromX509CertificateListToPemList(certificates.getTrustedCertificates()));
+    }
+
     private static List<String> convertFromX509CertificateListToPemList(List<X509Certificate> certificates) {
         return certificates.stream().map(CertificationProvider::convertFromX509CertificateToPem).filter(cert -> !cert.isEmpty())
                 .collect(Collectors.toList());
@@ -74,5 +82,4 @@ public class CertificationProvider {
         }
         return sw.toString();
     }
-
 }
index 699ffe7..9423af5 100644 (file)
 
 package org.onap.oom.certservice.certification.model;
 
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.Objects;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.onap.oom.certservice.certification.PemObjectFactory;
+import org.onap.oom.certservice.certification.StringBase64;
+import org.onap.oom.certservice.certification.exception.KeyDecryptionException;
 
 public final class CertificateUpdateModel {
 
@@ -29,6 +38,7 @@ public final class CertificateUpdateModel {
     private final String encodedOldCert;
     private final String encodedOldPrivateKey;
     private final String caName;
+    private static final PemObjectFactory PEM_OBJECT_FACTORY = new PemObjectFactory();
 
     private CertificateUpdateModel(String encodedCsr, String encodedPrivateKey, String encodedOldCert,
                                    String encodedOldPrivateKey, String caName) {
@@ -59,6 +69,20 @@ public final class CertificateUpdateModel {
         return caName;
     }
 
+    public PrivateKey getOldPrivateKeyObject()
+        throws KeyDecryptionException, InvalidKeySpecException, NoSuchAlgorithmException {
+
+        StringBase64 stringBase64 = new StringBase64(encodedOldPrivateKey);
+        PemObject pemObject = stringBase64.asString()
+            .flatMap(PEM_OBJECT_FACTORY::createPemObject)
+            .orElseThrow(
+                () -> new KeyDecryptionException("Incorrect Key, decryption failed")
+            );
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pemObject.getContent());
+        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+        return keyFactory.generatePrivate(keySpec);
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
index d525c6e..5ded305 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Nordix Foundation.
+ *  Copyright (C) 2021 Nokia.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +24,7 @@ package org.onap.oom.certservice.cmpv2client.api;
 import java.util.Date;
 
 import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.model.CertificateUpdateModel;
 import org.onap.oom.certservice.certification.model.CsrModel;
 import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
 import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
@@ -71,4 +73,19 @@ public interface CmpClient {
       CsrModel csrModel,
       Cmpv2Server server)
       throws CmpClientException;
+
+  /**
+   * Requests for a External Root CA Certificate to be updated for the passed keyPair wrapped
+   * in a CSRMeta with common details. Authentication using End Entity Certificate. Old certificate and old privateKey
+   * are wrapped in CertificateUpdateModel.class
+   * Exception thrown if verification fails or issue encountered in fetching certificate from CA.
+   *
+   * @param csrModel  Certificate Signing Request Model. Must not be {@code null}.
+   * @param cmpv2Server    CMPv2 server. Must not be {@code null}.
+   * @param certificateUpdateModel    Model with key update parameters {@code null}.
+   * @return model for certification containing certificate chain and trusted certificates
+   * @throws CmpClientException if client error occurs.
+   */
+  Cmpv2CertificationModel updateCertificate(CsrModel csrModel, Cmpv2Server cmpv2Server,
+      CertificateUpdateModel certificateUpdateModel) throws CmpClientException;
 }
index 06e785a..270b599 100644 (file)
@@ -29,15 +29,21 @@ import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHel
 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifyPasswordBasedProtection;
 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifySignature;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Objects;
 import java.util.Optional;
+import org.apache.commons.codec.binary.Base64;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cmp.CMPCertificate;
@@ -47,8 +53,12 @@ import org.bouncycastle.asn1.cmp.PKIBody;
 import org.bouncycastle.asn1.cmp.PKIHeader;
 import org.bouncycastle.asn1.cmp.PKIMessage;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.util.io.pem.PemReader;
 import org.onap.oom.certservice.certification.configuration.model.CaMode;
 import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
+import org.onap.oom.certservice.certification.exception.KeyDecryptionException;
+import org.onap.oom.certservice.certification.model.CertificateUpdateModel;
 import org.onap.oom.certservice.certification.model.CsrModel;
 import org.onap.oom.certservice.cmpv2client.api.CmpClient;
 import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
@@ -83,25 +93,19 @@ public class CmpClientImpl implements CmpClient {
             throws CmpClientException {
 
         validate(csrModel, server, httpClient, notBefore, notAfter);
-        KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
 
         final String iak = server.getAuthentication().getIak();
         final PkiMessageProtection pkiMessageProtection = new PasswordBasedProtection(iak);
         final CreateCertRequest certRequest =
-                CmpMessageBuilder.of(CreateCertRequest::new)
-                        .with(CreateCertRequest::setIssuerDn, server.getIssuerDN())
-                        .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
-                        .with(CreateCertRequest::setSansArray, csrModel.getSans())
-                        .with(CreateCertRequest::setSubjectKeyPair, keyPair)
+                getCmpMessageBuilderWithCommonRequestValues(csrModel, server)
                         .with(CreateCertRequest::setNotBefore, notBefore)
                         .with(CreateCertRequest::setNotAfter, notAfter)
                         .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv())
+                        .with(CreateCertRequest::setCmpRequestType, PKIBody.TYPE_INIT_REQ)
                         .with(CreateCertRequest::setProtection, pkiMessageProtection)
                         .build();
 
-        final PKIMessage pkiMessage = certRequest.generateCertReq();
-        Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
-        return retrieveCertificates(csrModel, server, pkiMessage, cmpv2HttpClient);
+        return executeCmpRequest(csrModel, server, certRequest);
     }
 
     @Override
@@ -110,6 +114,66 @@ public class CmpClientImpl implements CmpClient {
         return createCertificate(csrModel, server, null, null);
     }
 
+    @Override
+    public Cmpv2CertificationModel updateCertificate(CsrModel csrModel, Cmpv2Server cmpv2Server,
+        CertificateUpdateModel certificateUpdateModel) throws CmpClientException {
+        validate(csrModel, cmpv2Server, httpClient, null, null);
+
+        final PkiMessageProtection pkiMessageProtection = getSignatureProtection(certificateUpdateModel);
+        final CreateCertRequest certRequest =
+            getCmpMessageBuilderWithCommonRequestValues(csrModel, cmpv2Server)
+                .with(CreateCertRequest::setCmpRequestType, PKIBody.TYPE_KEY_UPDATE_REQ)
+                .with(CreateCertRequest::setExtraCerts, getCMPCertificateFromPem(certificateUpdateModel.getEncodedOldCert()))
+                .with(CreateCertRequest::setProtection, pkiMessageProtection)
+                .build();
+
+        return executeCmpRequest(csrModel, cmpv2Server, certRequest);
+
+    }
+
+    private Cmpv2CertificationModel executeCmpRequest(CsrModel csrModel, Cmpv2Server cmpv2Server,
+        CreateCertRequest certRequest) throws CmpClientException {
+        final PKIMessage pkiMessage = certRequest.generateCertReq();
+        Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
+        return retrieveCertificates(csrModel, cmpv2Server, pkiMessage, cmpv2HttpClient);
+    }
+
+    private CmpMessageBuilder<CreateCertRequest> getCmpMessageBuilderWithCommonRequestValues(CsrModel csrModel,
+        Cmpv2Server cmpv2Server) {
+        KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
+        return CmpMessageBuilder.of(CreateCertRequest::new)
+            .with(CreateCertRequest::setIssuerDn, cmpv2Server.getIssuerDN())
+            .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
+            .with(CreateCertRequest::setSansArray, csrModel.getSans())
+            .with(CreateCertRequest::setSubjectKeyPair, keyPair);
+    }
+
+    private SignatureProtection getSignatureProtection(CertificateUpdateModel certificateUpdateModel)
+        throws CmpClientException {
+        try {
+            PrivateKey oldPrivateKey = certificateUpdateModel.getOldPrivateKeyObject();
+            return new SignatureProtection(oldPrivateKey);
+        } catch (NoSuchAlgorithmException | KeyDecryptionException | InvalidKeySpecException e) {
+            throw new CmpClientException("Cannot parse old private key ", e);
+        }
+
+    }
+
+    private CMPCertificate[] getCMPCertificateFromPem(String encodedCertPem) throws CmpClientException {
+        try {
+            Certificate certificate = Certificate.getInstance(
+                new PemReader(
+                    new InputStreamReader(
+                        new ByteArrayInputStream(
+                            Base64.decodeBase64(encodedCertPem))))
+                .readPemObject().getContent());
+            CMPCertificate cert = new CMPCertificate(certificate);
+            return new CMPCertificate[]{cert};
+        } catch (IOException | NullPointerException e ) {
+            throw new CmpClientException("Cannot parse old certificate", e);
+        }
+    }
+
     private void checkCmpResponse(
             final PKIMessage respPkiMessage, final PublicKey publicKey, final String initAuthPassword)
             throws CmpClientException {
index 0ed493b..c328304 100644 (file)
@@ -29,6 +29,7 @@ import java.util.Date;
 
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.cmp.CMPCertificate;
 import org.bouncycastle.asn1.cmp.PKIBody;
 import org.bouncycastle.asn1.cmp.PKIHeader;
 import org.bouncycastle.asn1.cmp.PKIMessage;
@@ -58,6 +59,8 @@ class CreateCertRequest {
     private Date notBefore;
     private Date notAfter;
     private String senderKid;
+    private int cmpRequestType;
+    private CMPCertificate[] extraCerts;
 
     private final int certReqId = createRandomInt(Integer.MAX_VALUE);
     private final AlgorithmIdentifier signingAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder()
@@ -95,6 +98,14 @@ class CreateCertRequest {
         this.senderKid = senderKid;
     }
 
+    public void setCmpRequestType(int requestType) {
+        this.cmpRequestType = requestType;
+    }
+
+    public void setExtraCerts(CMPCertificate[] extraCert) {
+        this.extraCerts = extraCert;
+    }
+
     /**
      * Method to create {@link PKIMessage} from {@link CertRequest},{@link ProofOfPossession}, {@link
      * CertReqMsg}, {@link CertReqMessages}, {@link PKIHeader} and {@link PKIBody}.
@@ -127,9 +138,9 @@ class CreateCertRequest {
                         issuerDn,
                         pkiMessageProtection.getAlgorithmIdentifier(),
                         senderKid);
-        final PKIBody pkiBody = new PKIBody(PKIBody.TYPE_INIT_REQ, certReqMessages);
+        final PKIBody pkiBody = new PKIBody(cmpRequestType, certReqMessages);
 
         final DERBitString messageProtection = this.pkiMessageProtection.generatePkiMessageProtection(pkiHeader, pkiBody);
-        return new PKIMessage(pkiHeader, pkiBody, messageProtection);
+        return new PKIMessage(pkiHeader, pkiBody, messageProtection, extraCerts);
     }
 }
index f1d5baa..4ac0b50 100644 (file)
@@ -33,7 +33,6 @@ 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;
@@ -159,7 +158,7 @@ class CertificationControllerTest {
 
     @Test
     void shouldUpdateEndpointReturnDataAboutCsrBaseOnEncodedParameters()
-        throws DecryptionException, CertificateDecryptionException {
+        throws DecryptionException, CmpClientException, CertificateDecryptionException {
         // Given
         CertificationModel testCertificationModel = new CertificationModel(
                 Arrays.asList("ENTITY_CERT", "INTERMEDIATE_CERT"),
@@ -179,7 +178,7 @@ class CertificationControllerTest {
 
     @Test
     void shouldThrowCertificateDecryptionExceptionWhenCreatingPemModelFails()
-        throws DecryptionException, CertificateDecryptionException {
+        throws DecryptionException, CertificateDecryptionException, CmpClientException {
         // Given
         String expectedMessage = "Incorrect certificate, decryption failed";
         when(certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL))
index c898b68..1233168 100644 (file)
@@ -26,6 +26,8 @@ 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.times;
+import static org.mockito.Mockito.verify;
 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;
@@ -47,7 +49,6 @@ 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;
@@ -61,12 +62,12 @@ import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
 @ExtendWith(MockitoExtension.class)
 class CertificationModelFactoryTest {
 
-    private static final String TEST_CA = "testCA";
+    private static final String TEST_CA_NAME = "TestCA";
     private static final String ENCODED_CSR = getEncodedString(TEST_CSR);
     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";
@@ -105,7 +106,7 @@ class CertificationModelFactoryTest {
 
     @Test
     void shouldCreateProperCertificationModelWhenGivenProperCsrModelAndCaName()
-        throws CmpClientException, DecryptionException, Cmpv2ClientAdapterException {
+        throws CmpClientException, DecryptionException {
 
         // Given
         CsrModel csrModel = mockCsrFactoryModelCreation();
@@ -114,7 +115,7 @@ class CertificationModelFactoryTest {
 
         // When
         CertificationModel certificationModel =
-            certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA);
+            certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA_NAME);
 
         // Then
         assertEquals(2, certificationModel.getCertificateChain().size());
@@ -140,7 +141,7 @@ class CertificationModelFactoryTest {
         // When
         Exception exception = assertThrows(
             DecryptionException.class, () ->
-                certificationModelFactory.createCertificationModel(ENCODED_WRONG_CSR, ENCODED_WRONG_PK, TEST_CA)
+                certificationModelFactory.createCertificationModel(ENCODED_WRONG_CSR, ENCODED_WRONG_PK, TEST_CA_NAME)
         );
 
         // Then
@@ -154,7 +155,7 @@ class CertificationModelFactoryTest {
         String expectedMessage = "CA not found";
         mockCsrFactoryModelCreation();
         when(
-            cmpv2ServerProvider.getCmpv2Server(TEST_CA)
+            cmpv2ServerProvider.getCmpv2Server(TEST_CA_NAME)
         ).thenThrow(
             new Cmpv2ServerNotFoundException(expectedMessage)
         );
@@ -162,7 +163,7 @@ class CertificationModelFactoryTest {
         // When
         Exception exception = assertThrows(
             Cmpv2ServerNotFoundException.class, () ->
-                certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA)
+                certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA_NAME)
         );
 
         // Then
@@ -171,7 +172,7 @@ class CertificationModelFactoryTest {
 
     @Test
     void shouldThrowCmpClientExceptionWhenSigningCsrFailed()
-        throws DecryptionException, CmpClientException, Cmpv2ClientAdapterException {
+        throws DecryptionException, CmpClientException {
         // Given
         String expectedMessage = "failed to sign certificate";
         CsrModel csrModel = mockCsrFactoryModelCreation();
@@ -185,7 +186,7 @@ class CertificationModelFactoryTest {
         // When
         Exception exception = assertThrows(
             CmpClientException.class, () ->
-                certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA)
+                certificationModelFactory.createCertificationModel(ENCODED_CSR, ENCODED_PK, TEST_CA_NAME)
         );
 
         // Then
@@ -193,17 +194,51 @@ class CertificationModelFactoryTest {
     }
 
     @Test
-    void shouldPerformKurWhenCsrAndOldCertDataMatch() throws CertificateDecryptionException, DecryptionException {
+    void shouldPerformKurWhenCsrAndOldCertDataMatch()
+        throws CertificateDecryptionException, DecryptionException, CmpClientException {
         //given
-        mockCsrFactoryModelCreation();
+        CsrModel csrModel = mockCsrFactoryModelCreation();
+        Cmpv2Server testServer = mockCmpv2ProviderServerSelection();
+        mockCertificateProviderCertificateUpdate(csrModel, testServer);
         mockCertificateFactoryModelCreation();
         when(updateRequestTypeDetector.isKur(any(), any())).thenReturn(true);
         //when, then
+
+        CertificationModel certificationModel = certificationModelFactory
+            .createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL);
+
+        // Then
+        assertEquals(2, certificationModel.getCertificateChain().size());
+        assertThat(certificationModel.getCertificateChain()).contains(INTERMEDIATE_CERT, ENTITY_CERT);
+        assertEquals(2, certificationModel.getTrustedCertificates().size());
+        assertThat(certificationModel.getTrustedCertificates()).contains(CA_CERT, EXTRA_CA_CERT);
+
+        verify(certificationProvider, times(1))
+            .updateCertificate(csrModel, testServer, TEST_CERTIFICATE_UPDATE_MODEL);
+    }
+
+    @Test
+    void shouldThrowCmpClientExceptionWhenUpdateRequestFailed()
+        throws DecryptionException, CmpClientException, CertificateDecryptionException {
+
+        // Given
+        String expectedMessage = "Exception occurred while send request to CMPv2 Server";
+        CsrModel csrModel = mockCsrFactoryModelCreation();
+        Cmpv2Server testServer = mockCmpv2ProviderServerSelection();
+        mockCertificateFactoryModelCreation();
+
+        when(certificationProvider.updateCertificate(csrModel, testServer, TEST_CERTIFICATE_UPDATE_MODEL))
+            .thenThrow(new CmpClientException(expectedMessage));
+        when(updateRequestTypeDetector.isKur(any(), any())).thenReturn(true);
+
+        // When
         Exception exception = assertThrows(
-            UnsupportedOperationException.class, () ->
+            CmpClientException.class, () ->
                 certificationModelFactory.createCertificationModel(TEST_CERTIFICATE_UPDATE_MODEL)
         );
-        assertEquals(exception.getMessage(), "TODO: implement KUR in separate MR");
+
+        // Then
+        assertTrue(exception.getMessage().contains(expectedMessage));
     }
 
     @Test
@@ -233,8 +268,16 @@ class CertificationModelFactoryTest {
         );
     }
 
+    private void mockCertificateProviderCertificateUpdate(CsrModel csrModel, Cmpv2Server testServer)
+        throws CmpClientException {
+        CertificationModel expectedCertificationModel = getCertificationModel();
+        when(
+            certificationProvider.updateCertificate(csrModel, testServer, TEST_CERTIFICATE_UPDATE_MODEL)
+        ).thenReturn(expectedCertificationModel);
+    }
+
     private void mockCertificateProviderCertificateSigning(CsrModel csrModel, Cmpv2Server testServer)
-        throws CmpClientException, Cmpv2ClientAdapterException {
+        throws CmpClientException {
         CertificationModel expectedCertificationModel = getCertificationModel();
         when(
             certificationProvider.signCsr(csrModel, testServer)
@@ -244,7 +287,7 @@ class CertificationModelFactoryTest {
     private Cmpv2Server mockCmpv2ProviderServerSelection() {
         Cmpv2Server testServer = getCmpv2Server();
         when(
-            cmpv2ServerProvider.getCmpv2Server(TEST_CA)
+            cmpv2ServerProvider.getCmpv2Server(TEST_CA_NAME)
         ).thenReturn(testServer);
         return testServer;
     }
index 54744ba..4e7908f 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * OOM Certification 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.io.StringReader;
+import java.util.List;
 import org.apache.commons.io.IOUtils;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
 import org.junit.jupiter.api.BeforeEach;
 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.configuration.model.Cmpv2Server;
+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.cmpv2client.api.CmpClient;
@@ -46,10 +54,13 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
+import static org.onap.oom.certservice.certification.TestData.TEST_CMPv2_KEYSTORE;
+import static org.onap.oom.certservice.certification.TestData.TEST_CMPv2_TRUSTSTORE;
 
 @ExtendWith(MockitoExtension.class)
 class CertificationProviderTest {
 
+    private static final int EXPECTED_SIZE_ONE = 1;
     @Mock
     private CsrModel csrModel;
     @Mock
@@ -63,13 +74,23 @@ class CertificationProviderTest {
 
     private CertificationProvider certificationProvider;
 
+    private static final CertificateUpdateModel TEST_CERTIFICATE_UPDATE_MODEL = new CertificateUpdateModelBuilder()
+        .setEncodedCsr("encodedCSR")
+        .setEncodedPrivateKey("encodedPK")
+        .setEncodedOldCert("encodedOldCert")
+        .setEncodedOldPrivateKey("encodedOldPK")
+        .setCaName("TestCA")
+        .build();
+    private static final String EXPECTED_BEGIN_OF_CERTIFICATE = "-----BEGIN CERTIFICATE-----\n";
+    private static final String EXPECTED_END_OF_CERTIFICATE = "-----END CERTIFICATE-----\n";
+
     @BeforeEach
     public void init() {
         certificationProvider = new CertificationProvider(cmpClient);
     }
 
     @Test
-    void shouldConvertToCertificationModel()
+    void shouldConvertToCertificationModelForSignCsr()
             throws CertificateException, NoSuchProviderException, IOException, CmpClientException {
         // When
         when(
@@ -94,8 +115,9 @@ class CertificationProviderTest {
     }
 
 
+
     @Test
-    void certificationProviderThrowCmpClientWhenCallingClientFails()
+    void certificationProviderThrowCmpClientWhenCallingClientFailsForSignCsr()
             throws CmpClientException {
         // Given
         String expectedErrorMessage = "connecting to CMP client failed";
@@ -114,6 +136,50 @@ class CertificationProviderTest {
         assertThat(exception.getMessage()).isEqualTo(expectedErrorMessage);
     }
 
+    @Test
+    void shouldCorrectConvertToCertificationModelForUpdateRequest()
+        throws IOException, CertificateException, CmpClientException {
+
+        // When
+        when(
+            cmpClient.updateCertificate(any(CsrModel.class), any(Cmpv2Server.class), any(CertificateUpdateModel.class))
+        ).thenReturn(getCMPv2CertificationModel());
+
+        CertificationModel certificationModel = certificationProvider
+            .updateCertificate(csrModel, server, TEST_CERTIFICATE_UPDATE_MODEL);
+        List<String> certificateChain = certificationModel.getCertificateChain();
+        List<String> trustedCertificates = certificationModel.getTrustedCertificates();
+
+        assertThat(certificateChain.size()).isEqualTo(EXPECTED_SIZE_ONE);
+        assertThat(certificateChain.get(0)).startsWith(EXPECTED_BEGIN_OF_CERTIFICATE);
+        assertThat(certificateChain.get(0)).endsWith(EXPECTED_END_OF_CERTIFICATE);
+
+        assertThat(trustedCertificates.size()).isEqualTo(EXPECTED_SIZE_ONE);
+        assertThat(trustedCertificates.get(0)).startsWith(EXPECTED_BEGIN_OF_CERTIFICATE);
+        assertThat(trustedCertificates.get(0)).endsWith(EXPECTED_END_OF_CERTIFICATE);
+    }
+
+    @Test
+    void certificationProviderThrowCmpClientWhenCallingClientFailsForUpdateCertificate()
+        throws CmpClientException {
+        // Given
+        String expectedErrorMessage = "Exception occurred while send request to CMPv2 Server";
+
+        when(
+            cmpClient.updateCertificate(any(CsrModel.class), any(Cmpv2Server.class), any(CertificateUpdateModel.class))
+        ).thenThrow(new CmpClientException(expectedErrorMessage));
+
+        // When
+        Exception exception = assertThrows(
+            CmpClientException.class, () ->
+                certificationProvider.updateCertificate(testCsrModel, testServer, TEST_CERTIFICATE_UPDATE_MODEL)
+        );
+
+        // Then
+        assertThat(exception.getMessage()).isEqualTo(expectedErrorMessage);
+    }
+
+
     private Cmpv2CertificationModel createCorrectClientResponse()
             throws CertificateException, NoSuchProviderException {
         InputStream certificateChain = getClass().getClassLoader().getResourceAsStream("certificateChain.first");
@@ -129,4 +195,20 @@ class CertificationProviderTest {
     private String removeLineEndings(String string) {
         return string.replace("\n", "").replace("\r", "");
     }
+
+    private Cmpv2CertificationModel getCMPv2CertificationModel() throws IOException, CertificateException {
+        List<X509Certificate> certificateChain = getX509CertificateFromPem(TEST_CMPv2_KEYSTORE);
+        List<X509Certificate> trustedCertificates = getX509CertificateFromPem(TEST_CMPv2_TRUSTSTORE);
+        return new Cmpv2CertificationModel(certificateChain, trustedCertificates);
+    }
+
+
+    private List<X509Certificate> getX509CertificateFromPem(String pemString) throws IOException, CertificateException {
+        PEMParser pemParser = new PEMParser(new StringReader(pemString));
+        X509CertificateHolder certHolder = (X509CertificateHolder) pemParser.readObject();
+        X509Certificate x509Certificate = new JcaX509CertificateConverter()
+            .setProvider(new BouncyCastleProvider())
+            .getCertificate(certHolder);
+        return List.of(x509Certificate);
+    }
 }
index 92b239f..3c47d86 100644 (file)
@@ -99,4 +99,61 @@ public final class TestData {
         + "MIIDIzCCAgsCAQAwgZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh"
         + "-----END WRONG REQUEST-----";
 
+
+    public static final String TEST_CMPv2_KEYSTORE = "-----BEGIN CERTIFICATE-----\n"
+        + "MIIEfTCCAuWgAwIBAgIUdF/Efkrll/wuwfT2w+RO9cJrLvQwDQYJKoZIhvcNAQEL\n"
+        + "BQAwUzEVMBMGCgmSJomT8ixkAQEMBTEyMzQ1MRUwEwYDVQQDDAxNYW5hZ2VtZW50\n"
+        + "Q0ExIzAhBgNVBAoMGkVKQkNBIENvbnRhaW5lciBRdWlja3N0YXJ0MB4XDTIxMDcw\n"
+        + "MTE0MjUxMloXDTIzMDcwMTE0MjUxMVowdzERMA8GA1UEAwwIb25hcC5vcmcxGTAX\n"
+        + "BgNVBAsMEExpbnV4LUZvdW5kYXRpb24xDTALBgNVBAoMBE9OQVAxFjAUBgNVBAcM\n"
+        + "DVNhbi1GcmFuY2lzY28xEzARBgNVBAgMCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVT\n"
+        + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzEAteHOLPXl93XwT9yDa\n"
+        + "3SS1nOV1KN+wZApOqvzbA7NBf8Pw/72i6LXvAHABfzUWkUQnbWw9WygPiEbWfaGO\n"
+        + "AArlcVTtXnY5RkfOpt5UXBokZwn1PaT3a1hXrFHA2W2jwD7q2Ft3gRNNFsxenJYi\n"
+        + "BcsnJOBkS3hc+zAG7mYw5gYdnPX69D+/+4G0N1k1bBA5rsaK7F3h54NJfPxILpJB\n"
+        + "0yuVBE0QBEaHVoqDyq6AwmiHpk2Nt5h4TMDiINwwkoxvNG+ZCaM2CSFbmUyq3j1c\n"
+        + "xZ/ZrhLIZ1rRlMTPz+lA1wo1yBB2AoDtoPsAlQG5nIWfr1d5ToUtLOxZ+dVTQkJi\n"
+        + "VQIDAQABo4GkMIGhMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU0pApkFDmdpd/\n"
+        + "O4a/byDxTEP2UvAwGAYDVR0RBBEwD4INdGVzdC5vbmFwLm9yZzAnBgNVHSUEIDAe\n"
+        + "BggrBgEFBQcDAgYIKwYBBQUHAwQGCCsGAQUFBwMBMB0GA1UdDgQWBBS3Wd6mIs8L\n"
+        + "ZxAfYyvM/U2g/XM2wjAOBgNVHQ8BAf8EBAMCBeAwDQYJKoZIhvcNAQELBQADggGB\n"
+        + "AF9ecmK2jpi4u8NERcx/HGXGxZDgj8EpRMxzHAPMa8gJRwm87O4tn4QZHFSnDYdl\n"
+        + "ZmDWhA1iEvOOrTNDimRslAOoOE3bRAiA5c3cYVhancZq0OqS8dUOyOxSwLoXtnFC\n"
+        + "RGnHMmABq3OpdWpeRTv3iLzPDeybP+hJn3WrlX9v4kjwgO5mbwQTG+MCzTWgNsyy\n"
+        + "xeEF0pH6JYDyIoRNWwrrRG7zWzjIaFuMtOfbN1lVaaycMsRw+IxvojsDmXK2PWOA\n"
+        + "HRu5k2xpQfHF/waN4F0vzKxmHyCVnwbwx6by6G2FJo8CYQeDwgyRARm3xywu1Wwt\n"
+        + "CIbRhIekMFY7RLPs5vkTPxs65numYZlbI+z+EdYofQBbJFeyqpzkoIQqWyfkwtki\n"
+        + "x7sJ9B6sUPIibxQnMI+tdX+wz5p5Ift+nnSUbx8N+FVvc9kEbvvPpzJRpAyQBVIq\n"
+        + "CIvPjAmyZNIztpxLi0bh7voZqH5ZwHdWWEaDrwg5cMjFZMRUGRPUQ8NN04KGiAww\n"
+        + "hQ==\n"
+        + "-----END CERTIFICATE-----";
+
+    public static final String TEST_CMPv2_TRUSTSTORE = "-----BEGIN CERTIFICATE-----\n"
+        + "MIIElzCCAv+gAwIBAgIUcwn218DR9QI1ORrGSb2exaf2tp0wDQYJKoZIhvcNAQEL\n"
+        + "BQAwUzEVMBMGCgmSJomT8ixkAQEMBTEyMzQ1MRUwEwYDVQQDDAxNYW5hZ2VtZW50\n"
+        + "Q0ExIzAhBgNVBAoMGkVKQkNBIENvbnRhaW5lciBRdWlja3N0YXJ0MB4XDTIxMDcw\n"
+        + "MTEzNTIwMloXDTMxMDcwMTEzNTIwMVowUzEVMBMGCgmSJomT8ixkAQEMBTEyMzQ1\n"
+        + "MRUwEwYDVQQDDAxNYW5hZ2VtZW50Q0ExIzAhBgNVBAoMGkVKQkNBIENvbnRhaW5l\n"
+        + "ciBRdWlja3N0YXJ0MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEApU2B\n"
+        + "yG6Y5D72dC7jL7kW7HxMDL93sKGo/9GriPL/re+ogipg9SezZoX0UbYH6ZCuPlPn\n"
+        + "GKXaLDdrwX/oAaYnq1h4hA75Fy2P7Im3rcuw01F7kBk3bSbFSU0RLbr0POuP+rNr\n"
+        + "IXNpFS1tonTpLgS0l7vfMkAfbBBlFLuSZQUD6oq0OHB60uBe8vOF7olq6bewduUz\n"
+        + "jNgqjoUz5cR9cuWCXMZUA71+ZEzSNDJtDeFpkd00wklB9Q86JQ3/5poe4ALKuyRj\n"
+        + "+6ltNdRtybHY+gT9iltDqJ7d9JWwG5EIBjYWlqLAHV1YP3XnonLiyCfArvnp1fLZ\n"
+        + "vOdyxk0cA21EFF6b6MUGI3bMJPdwYjWEKkVFETqsbpxRyFNjqJ1EUibLttRnOKeS\n"
+        + "COw8KdP+1QTvygA7lb75lCnVpn8JKDy2FbpuoEnSkA6o/25tlM5BjzFrKeCCdO8t\n"
+        + "lcUFlQsxniSXaZF4i46U1BI1x5onY82hpLB3kErPCCA7Y4wu6fpmuOLxvvR3AgMB\n"
+        + "AAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU0pApkFDmdpd/O4a/\n"
+        + "byDxTEP2UvAwHQYDVR0OBBYEFNKQKZBQ5naXfzuGv28g8UxD9lLwMA4GA1UdDwEB\n"
+        + "/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAYEAf6cQKAUp1oz/wNu+wGr0ihCtYoTv\n"
+        + "0E8Sj6NaVg25iZWwE+eH9iJ5tP84ofVVBSKbnbcWcKOiBWdwMHK926yWhDq1Okj3\n"
+        + "cKn4PEo/Tp7DWgSxaBFWvafIMYgD08AeCSBykdwodNxhPWIMIe0Wv+timjacYUFV\n"
+        + "Aq8IjFfEwpznlbJWNoctne5YFcbGbo+Z3cMaFG0eWtjpVg5pF3p+D35FBCeNzIx7\n"
+        + "PjI4blyKAAIVBppYY7mTC9iEvu1Djt9653LOj2ZalC8mj6ZnvSxEhcQ3PJle8VvT\n"
+        + "jq1pCYNegPATlT3eRKXlQOUQQtkw2NK9jQCCbwHnQHreL76iGhoIyFfR8P/EA2KU\n"
+        + "CCMFg1yrToWJxDhkuBHu7vqbceC4YNGU1wl7nGJA18PDpglm0WI1mWzh7s+oeNNr\n"
+        + "vvPaTimegOkO/u2vbl9McNdSu5Chj+gUpz6w6a3DiBeROYAVQaw44LncJtaGKojU\n"
+        + "Q81F/bSyUp6jkdo5Dx2pFQDLDdhmMF4txiqG\n"
+        + "-----END CERTIFICATE-----";
+
 }
diff --git a/certService/src/test/java/org/onap/oom/certservice/cmpv2client/ClientTestData.java b/certService/src/test/java/org/onap/oom/certservice/cmpv2client/ClientTestData.java
new file mode 100644 (file)
index 0000000..5a9a683
--- /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.cmpv2client;
+
+import org.onap.oom.certservice.certification.model.CertificateUpdateModel;
+import org.onap.oom.certservice.certification.model.CertificateUpdateModel.CertificateUpdateModelBuilder;
+
+public final class ClientTestData {
+
+    static final String KUR_CORRECT_SERVER_RESPONSE_ENCODED = "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";
+
+    private static final String TEST_CA = "TestCA";
+    private static final String WRONG_OLD_CERT = "wrong old cert";
+    private static final String WRONG_OLD_PRIVATE_KEY = "wrong old private key";
+
+    private static final String TEST_ENCODED_CSR = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQzV6Q0NBYzhDQVFBd2R6RUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeApGakFVQmdOVkJBY01EVk5oYmkxR2NtRnVZMmx6WTI4eERUQUxCZ05WQkFzTUJFOU9RVkF4R1RBWEJnTlZCQW9NCkVFeHBiblY0TFVadmRXNWtZWFJwYjI0eEVUQVBCZ05WQkFNTUNHOXVZWEF1YjNKbk1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXdVZmxSZ3k4d2tRajFib1hucGY5Q2lmakN6Y1lWSnJnTlZnVQplVnlOWjltczZIYVBuRDQ3M1M0b09kN3JXTnY3WEloWk9FU3lUM1FsVlB6eFBBNjdYMFR3dERqc3JUUjBxZGhtClBQS1crdHozM1pCRGttVnhLa0hmVmsrVUVmbi9rOHE3L0RGRmExWnV3NFdzT3R0ZDV0MHNIWU01eDFBY0dKVGgKdW9hcUh1WHpXK3BhcHlqYmpZbkJjUHB2bEsxbTdtRmVIWXNtMktBNk9yRHdxaE1wSGVSQkNldjNwMWlhSUdvUQpTTVAwTmhLMVFmaDFjazZ5Zmhid1lRUGZJaklMTWx6Z2J4QkszOXI0M2xSU0NsalkzdCtHSllPM1NKQURtS0YvCkFxMjJTbHg4ZWdCSDFmdHpzWGdOTnl3Y25tNUpGOFZXd0ZTamsydndPeDJjVEpIeDZRSURBUUFCb0Nzd0tRWUoKS29aSWh2Y05BUWtPTVJ3d0dqQVlCZ05WSFJFRUVUQVBnZzEwWlhOMExtOXVZWEF1YjNKbk1BMEdDU3FHU0liMwpEUUVCQ3dVQUE0SUJBUUFSVGo4T2FJUnM4WVI1QmFrRDhFYTRPOEdZZUJmd3pCdWRoU0x4UVIvM01xWHpQMGYzCkpHRVN6TCtVeWduQjE4dG9GdG9qdk5sR25TYVFOcnI4K2lwQkpiLzRVUTdydFJwNThaa0p1Nk9lZGNwSGd1emIKRUw1NnBGNlBnRk5tcFlGbnZ4MDc1UzhxZ2w0eWtzK09DK0hSK1dwaVZuOGQyMlEraVMwL3NZb0VRWkRRTVUvaQplWDRMVDlqSjNWM2lKTFh6OUZmRlhzY1VxeDZ6RGt5VUZJQms0aUZHWE9RLzQ1MmRla0ZPaGlKQ0x1VlRHdTVpCk5NODdZRVptWnVLbXNtd2x4WDU2UjRrd0Z6azZLcWFyMlhNTU5MV0U3VUZ4SDROUlFIeUUxOEVTNU5adDMwMnAKYzl4YkRyekEvbVNSdDVlTTZTYlVqNStlU0tyUDQxdlk4S3hPCi0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQo=";
+    private static final String TEST_ENCODED_PRIVATE_KEY = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRRFNOS0pCUU5XRmF0ajUKUmRhTmQyMGdnTnBOVUVaYzgxU1Y1d0hLeHEwUy9rT0llTG8rekI5c3lkUUJLWm9JRXJPL3JIekwzb0VCNW5YNQp4NVBkeTViYUdYTzRQZDVCUStrSXRHckFnNzVrRldmT3VHNU9GWUxpWldMUEcra3FBRXJKNTdzQlB5TzNKNjVxCkEzN0gxNnBiZVhRL2VzR2RNaVZsenM4dE9UOEtaT1lDQUpGQXg1ZEk4OEowaEt4ZThONFdrZEJLSStXVndKMUsKR05pZmgwc2Jjam9rOG1Gbll4Nzcwd3BuZU5nbkZUbU9MWXFIUTRuSklUODdza1BYSUt1RHNMRUJDL01kVGQ0QwpPTTZJekZYZmQxNzNDc1p5UnR3V0F2ZXNDcWJGYkVhcDBiWHlCRFN1R0w3NlhGdzRTdjZiZkoyano2SjIyRU9SCmVPOGpVdWhKQWdNQkFBRUNnZ0VBUWJyZHBjUHRRSnZwbndEY2x6M3A3TWo5K2tFSXo1WHpORENaR2R4SVVIRWIKa3ZnVlhQK2RML3BvaGJpSmhzNjZVRXhTZGJsczQ3ZzUyZEl6aFo1YzNIUXJBRWl3VC80NVIxU0xNUW5CSmpDZgpWai9MbGpVWnlVdGt1MWlCNzNWSjdacTltaVV4T050NnFZSFFTaE5CSFB0OGcwRVNlK0lyV1l0eXN6UjhadllXCjlqWm9xb0pOTW5ySVkyNmdtdFRCRURpTmVmaEhBMGVoVHkwYzNBQ1lDTUY3aWlNenplMWhkUjZvTDhuTEZscmQKVGJZRGdCUzBueEpvRVpxQnZBZWViZFVBaXc1UCtqZ1NXcXhnUkhpWGk2Rk0xWXVnMGF5Mm9GNEl1alV0ek5kNwplbnNqeTVTTGFGcVp5dy81bkdlWDJMTXYvbFovQUtWYlZ6NnNBa3RVdFFLQmdRRHBBT3BBUVorNWRheGxyQk5oClFoYy9ndnRPekJpRTA0YU5EdDVLMllEVU80dHdFRmYxTVdXSkNrV3Z1czNOSUphdkJ1K25GYzdEREphUEFxbk4KZnQrUGw3NTJ4UUlJRk1GdUt5QTdKL1hSZzFjVUIzNEFrZWtZeTZvRlYwa2FlWmZvYXBRbGdDWnFWVkd5L2FCdAprSHBndDJnckpZZG82OE11bFQ0ZWplbGE4d0tCZ1FEbTg3UWE4YzFYRTNuTGFQcGJIeTU5N0N5S0ZKTzBRdC9tCm1RT1FNaEJCOTJGU0JpRE05ZHFkbUU2d3JVU2NFYVo3aDlaZ1kwQUdxVVFobzE3d3oyL1BxaGhaUFRiOU0rVTgKWUVaWTdnWnNoYkJ1MDgvTkJLTDNGTitGd216VG8xN1d1SlNyQWFWV3dra1RMOWVSbkI2cUFTeHBMaDFKQ0J4cQpQSE9Kd1FmRzB3S0JnUUNSTHlUSGpSeDliemxRMFB2eWFrQWFMdjl3aGZQeEwreHpFSVNxbHdTVE9kY1VxTnBsCnliVyt3a3ZSeDlCY3RLV3Z3ZDZxZWdndnVUUkhRQjJXRWl3elNSWkE0MWowdUJvZkQzZ3g1Q0Jqd0RjT0grei8KWmV1Y3E2cnhVUVlZSFJQdW1ocGRrNUJjU1hWeTFsNlVacVlhaGEyKzFNK2ZMT2lkcWhqZTZRWXl5UUtCZ1FDbwpTclhYWEpRUSs3UW9zVnFkdzk4UkMyUjVTZjFId2VOK0djb3E3UkJEd1l3OVJSSHB5TTJCUVZjMkQweUxuYUQvCkswRGdBL0xINTlncDJ1NTM4L0M2Rm15ZnVxZXpZbm1Nd1dzQnFwRXJ5MCtCc3Y4ZG1sOVdSUE9NZU56c2E0UFUKVzdTWjJCMHZWMndBZTBCT2JzRTVpSmxnRzZaamJYR25TRjI0NTl4TzJRS0JnRER1cXJBcThQMXpXU28wcWZ0QgpkTS9Xc3p6U3VZRHdjemhvajNKek5VK3lvQ3g4ejNzY0NML240eXFUT1RiWVhLTXFHbUVrSW01eXJ1SWlJeHBRCmNJM1pDUlVZbHZDY0FaeCtiVU1QSXNkek1TeGJMaHNqSU5Oc3F4dDJlMlQvd2dJWXpWenVERExpZ1drN1lDZkEKNDJ4YXVldHQ0M21qM25wYUFvcURIVG92Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K";
+    private static final String TEST_ENCODED_OLD_PRIVATE_KEY = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRHVTZ0FtVWovWEo5NlQKOGkzREZxWE1QOUpaa3RkeEtYZUV2NDZhcGFVTmJyNGNoSi9EM2pLdTFBRFdWVEx4YlNyOEpuT0dDSUFzenZjaQozQmxLb1VUaHVvaUl0NXBnKy9kaFc2SDRnazdtdTRVdzAxdEFVQjZqM2ZubVhJTDZaMTlmS2F0bXlRTmVMSzRQCnlNNTFMTG03ZjgyNjZsR0NrektmdWx0NVAvc21Ya0lxL0FGN1d6Q3gzclNXOW0yZkdKaEFGcjAyL09pV1JvOWIKd0VHSjFkUnJhaTc5ZGtpMEN4eFlDMXYvb3FXVTJLTmpSZDF5RXNXb0ZxTC9pd1NoWCtwazl5cmtSMzQ0aEZLZwpRVFl1bGg3Z0NKQmwyOFhGM2kraDNiVHZJL3VMRk5PLzdvZ29PTjlDM04wdTRiTzhlNUNnUERHZWI5eG9VQklrClVnM0hOSkczQWdNQkFBRUNnZ0VCQU1qOTFEaCtvZWlxY1h5YkJ1eUtPdGtZY0NZcnpOdGZuYmQwR0NYcldGZ0gKTkFZNys4S3J0bFp1N2pIYmRYZmNuQ2hKaXFIZ283U244aDhPUmFzRWNtUndBV0JJZGNnZVgrQlgrVHZ6TmZnNgo3YkpzWklqUHk3aHVzSzRWRkVtQVRocW52REtibE9Lbmp6NHpJNm9FU3JtVHFJVmp4ZEw4cy9PMHJobU0xUnZiClB0QXRCeDNMTEtBUFZWTlN6a05JeHFIY1cwaFJ1bFlqOU81S3hmZDJJdDlrKzVzY0RpQU5qcU9YTVRIMmxrdzcKcE41ZFRtNU1EQXcrMGpaUVNpUFRia0hFQWdPZElxNmxuYStSN1lBRkFNc0ZVKzlmbnNEZVR2M2VHT2h6ZVhCSApJRlRkTStIOVU3Tm8xOW91d0NHU05JTEJESnE1V2JVd3VLQWRTUDBVaGZFQ2dZRUEvNElFa1g2djJVSUpqbldwCjBnbzBTSFJFamQ1Vmk0T2dnQW00L0hDbWx6Q3ljZzljRUhXeXFsd3RNN24xVkw5WDdJaytxMFc2UjBYRXlJbHMKTWlPSEF0cFhxbDB0RnRNQVd5SnBLY05vaktyRjlMR3g2VDVKQk5HTFo1b2JCaXRLNTZtTVZta0RTTzljc3hRYgpURXhQY1JGZmpQdkpJTlBRNDRZYWNRR0padThDZ1lFQTdyOStJN2svdzg2YVNWWkJObTh4NURrZVd4anRBSmRnCndFeXpEWklxYXEwU3IvQ0dxZnRSTFFrNnF4RDQxVWh2d1QrVHVMd2xuWWJMWmRzQmJVVW11cHNBZWpMbnY0RGIKR1RiTHpiNEM1c2FONW13dEJPeU5vaGNoUUVHVUF0RzdzN3BCYStsOE9MSmVzS1FsMWJBc0JXRC9leG01bzZyYwo0T0NYQkNpaHdia0NnWUFjU2ZEbml2YzlQcXFBTTFiU0FuODNabWdRclFVYnBUOG43ZXVsUjNPcVdhSG9MdnNxCmQxMklyeHZ5Rml5cmJXUDJ0RnRUNnl4c3A3VFozeDB6ait0cXpYSFhVdW1qRlVsOHpacUhIVE4rSDRvN1JWRkYKV2JnTDZJZGV1UmswM2FZMWIvZ3h1UDY4SElSTzczTDJSNXlrRUNCY0k2UnBGZ3FTcGs1WEpLeHAwUUtCZ1FEWAp5VmhYTFg1R21odTFJVEs3NG5Dem1EU3BuYlBJandteGhTRm9xSzJSMFhCTWVSY2QxN3FjKy9SODNWQXFaZGdzClVDeFNFaXZsWHduRHU5aGtUTlllWHk1bFJGRldNejdVWVVSL1pyZjBvWTFyc0daWVJ2NFVmTmRlM21iS3pZbmIKZmdMWGFDY1FqNWNxREpMdHV0ZHUzU2JNdW9taE5qT0JSVHo1VTBnd2NRS0JnUURyRy9wZDN6aUw1dFZFSk1KOApUQmdodko1NTZNMjFXaXFRNG1WUGcwbGNub1RXVmdCV0hqS2k2MzNhZVVRRlYrQTN2d3ljS1h4YVdsUzBYWmZVCmRobTJaUnVwYzhaeVA2ZGNaR3VLTWxHR21kSGtGaExIcUNhZXViS25ZUEkyVTFrZklNVGlWR0Jubmk2Y3dkRGIKUzczSG12YVpwU0xsbDlhbXkvZEx1N0QxdGc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==";
+    private static final String TEST_ENCODED_OLD_CERT = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVmVENDQXVXZ0F3SUJBZ0lVRncwd1VZc2wxUkF2bHB1bXpqcFRrNjZzNGlzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1V6RVZNQk1HQ2dtU0pvbVQ4aXhrQVFFTUJURXlNelExTVJVd0V3WURWUVFEREF4TllXNWhaMlZ0Wlc1MApRMEV4SXpBaEJnTlZCQW9NR2tWS1FrTkJJRU52Ym5SaGFXNWxjaUJSZFdsamEzTjBZWEowTUI0WERUSXhNRGN3Ck1qQTNNVE0xTlZvWERUSXpNRGN3TWpBM01EWTFNMW93ZHpFUk1BOEdBMVVFQXd3SWIyNWhjQzV2Y21jeEdUQVgKQmdOVkJBc01FRXhwYm5WNExVWnZkVzVrWVhScGIyNHhEVEFMQmdOVkJBb01CRTlPUVZBeEZqQVVCZ05WQkFjTQpEVk5oYmkxR2NtRnVZMmx6WTI4eEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhDekFKQmdOVkJBWVRBbFZUCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBN2tvQUpsSS8xeWZlay9JdHd4YWwKekQvU1daTFhjU2wzaEwrT21xV2xEVzYrSElTZnc5NHlydFFBMWxVeThXMHEvQ1p6aGdpQUxNNzNJdHdaU3FGRQo0YnFJaUxlYVlQdjNZVnVoK0lKTzVydUZNTk5iUUZBZW85MzU1bHlDK21kZlh5bXJac2tEWGl5dUQ4ak9kU3k1CnUzL051dXBSZ3BNeW43cGJlVC83Smw1Q0t2d0JlMXN3c2Q2MGx2WnRueGlZUUJhOU52em9sa2FQVzhCQmlkWFUKYTJvdS9YWkl0QXNjV0F0Yi82S2xsTmlqWTBYZGNoTEZxQmFpLzRzRW9WL3FaUGNxNUVkK09JUlNvRUUyTHBZZQo0QWlRWmR2RnhkNHZvZDIwN3lQN2l4VFR2KzZJS0RqZlF0emRMdUd6dkh1UW9Ed3hubS9jYUZBU0pGSU54elNSCnR3SURBUUFCbzRHa01JR2hNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVzWW1NQytnWTE4WWMKdDVMejJheDlTRjhzaHlNd0dBWURWUjBSQkJFd0Q0SU5kR1Z6ZEM1dmJtRndMbTl5WnpBbkJnTlZIU1VFSURBZQpCZ2dyQmdFRkJRY0RBZ1lJS3dZQkJRVUhBd1FHQ0NzR0FRVUZCd01CTUIwR0ExVWREZ1FXQkJTdDVzYTFCc1VNCjJrTHFpdXNGWkxCWGlFMStJREFPQmdOVkhROEJBZjhFQkFNQ0JlQXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnR0IKQUkrYW94LzJPUFdQZC9IanB0b1VhVlRhNkFnZldMMHoyR3NkOS9oVDBPVTdGSWloTEwrUGx4Y3Z2VWVQWGZlRQpHaEtJb3FJb3pERndMeWs1OUVWNVMxSEdsYVI3QnlUZkVJcVl5T0I5YkNPMmlPWjdIcW9vNmxoN2hoUTZXUENRCmtpY3VRMXJRWDJFSHlOVW05aFovU2dWamFYQlpZY0l0cFNsN1lWSVFGUElXY2VYYmtFaG8rSG5HczExTDI0V0QKUWhCNkpWWWJzME9JQzVaNDNablBKaHdHYlVyOCs5Q2IwR0J1dzNITUVMN05mNmFQSWVjOG8ydXphVHU3WXlOegpjYlN4WmMyUzRpeVpSbjdkTldQSmtFUVFGd1dPNlBOYzRwd0xSeC9zR3pHdlY0cFZGdlRuOGE3c0FiVXpwcnZBCmRDNFdjYnJhNE1wTFFKczZqNWJoUFJ1QlRsSXBnZEVhdkVSM1J5bUVGUTBSRHdER2thSndNbkdVVTlqSWdFN3AKR282RG5aV2RJZEFoRlN2Q3RybERLL1NGYmVKM3RTNlMrSUxXUDgydWU3Q0UwSnYrUEVoUnc5aWVreE5hRElNbwpzeXcvM2tnSUNnckV3RjJzUHY5UnVLQ212V3NrMkdKaHRRcXZSK3FFRE5FQW15aXJodFh6L214QUZ0dy9ualpVCjBBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoK";
+
+
+    public static final CertificateUpdateModel TEST_CERTIFICATE_UPDATE_MODEL_WITH_WRONG_OLD_CERT =
+        new CertificateUpdateModelBuilder()
+        .setEncodedCsr(TEST_ENCODED_CSR)
+        .setEncodedPrivateKey(TEST_ENCODED_PRIVATE_KEY)
+        .setEncodedOldCert(WRONG_OLD_CERT)
+        .setEncodedOldPrivateKey(TEST_ENCODED_OLD_PRIVATE_KEY)
+        .setCaName(TEST_CA)
+        .build();
+
+    public static final CertificateUpdateModel TEST_CERTIFICATE_UPDATE_MODEL_WITH_WRONG_PRIVATE_KEY =
+        new CertificateUpdateModelBuilder()
+        .setEncodedCsr(TEST_ENCODED_CSR)
+        .setEncodedPrivateKey(TEST_ENCODED_PRIVATE_KEY)
+        .setEncodedOldCert(TEST_ENCODED_OLD_CERT)
+        .setEncodedOldPrivateKey(WRONG_OLD_PRIVATE_KEY)
+        .setCaName(TEST_CA)
+        .build();
+
+    static final CertificateUpdateModel TEST_CERTIFICATE_UPDATE_MODEL = new CertificateUpdateModelBuilder()
+        .setEncodedCsr(TEST_ENCODED_CSR)
+        .setEncodedPrivateKey(TEST_ENCODED_PRIVATE_KEY)
+        .setEncodedOldCert(TEST_ENCODED_OLD_CERT)
+        .setEncodedOldPrivateKey(TEST_ENCODED_OLD_PRIVATE_KEY)
+        .setCaName(TEST_CA)
+        .build();
+
+    private ClientTestData() {
+    }
+
+}
index 337ed8c..7ae42b3 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.onap.oom.certservice.cmpv2client;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
@@ -44,6 +45,8 @@ import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.Base64.Decoder;
 import java.util.Date;
 
 import org.apache.commons.io.IOUtils;
@@ -103,6 +106,8 @@ class Cmpv2ClientTest {
 
     private static KeyPair keyPair;
 
+    private final static Decoder BASE64_DECODER = Base64.getDecoder();
+
     @BeforeEach
     void setUp()
             throws NoSuchProviderException, NoSuchAlgorithmException, IOException,
@@ -135,6 +140,60 @@ class Cmpv2ClientTest {
         return new KeyPair(publicKey, privateKey);
     }
 
+    @Test
+    void shouldReturnCorrectCmpCertificateForCorrectKeyUpdateResponse() throws CmpClientException, IOException {
+
+        // given
+        setCsrModelAndServerTestDefaultValues();
+        when(httpClient.execute(any())).thenReturn(httpResponse);
+        when(httpResponse.getEntity()).thenReturn(httpEntity);
+
+        doAnswer(
+            invocation -> {
+                OutputStream os = invocation.getArgument(0);
+                os.write(BASE64_DECODER.decode(ClientTestData.KUR_CORRECT_SERVER_RESPONSE_ENCODED.getBytes()));
+                return null;
+            })
+            .when(httpEntity)
+            .writeTo(any(OutputStream.class));
+        CmpClientImpl cmpClient = new CmpClientImpl(httpClient);
+
+        // when
+        Cmpv2CertificationModel cmpClientResult =
+            cmpClient.updateCertificate(csrModel, server, ClientTestData.TEST_CERTIFICATE_UPDATE_MODEL);
+
+        // then
+        assertNotNull(cmpClientResult);
+        assertThat(cmpClientResult.getCertificateChain()).isNotEmpty();
+        assertThat(cmpClientResult.getCertificateChain()).isNotEmpty();
+
+    }
+
+    @Test
+    void shouldThrowCmpClientExceptionWhenCannotParseOldPrivateKey() {
+        setCsrModelAndServerTestDefaultValues();
+
+        CmpClientImpl cmpClient = new CmpClientImpl(httpClient);
+        assertThatExceptionOfType(CmpClientException.class)
+            .isThrownBy(() -> cmpClient.updateCertificate(csrModel, server, ClientTestData.TEST_CERTIFICATE_UPDATE_MODEL_WITH_WRONG_PRIVATE_KEY))
+            .withMessageContaining("Cannot parse old private key");
+
+    }
+
+
+    @Test
+    void shouldThrowCMPClientExceptionWhenCannotParseOldCertificate() {
+        setCsrModelAndServerTestDefaultValues();
+
+        CmpClientImpl cmpClient = new CmpClientImpl(httpClient);
+
+        // When // Then
+        assertThatExceptionOfType(CmpClientException.class)
+            .isThrownBy(() -> cmpClient.updateCertificate(csrModel, server, ClientTestData.TEST_CERTIFICATE_UPDATE_MODEL_WITH_WRONG_OLD_CERT))
+            .withMessageContaining("Cannot parse old certificate");
+    }
+
+
     @Test
     void shouldReturnValidPkiMessageWhenCreateCertificateRequestMessageMethodCalledWithValidCsr()
             throws Exception {
@@ -340,6 +399,18 @@ class Cmpv2ClientTest {
         this.notAfter = notAfter;
     }
 
+    private void setCsrModelAndServerTestDefaultValues() {
+        csrModel = new CsrModel(null, dn, keyPair.getPrivate(), keyPair.getPublic(), new GeneralName[0]);
+
+        Authentication authentication = new Authentication();
+        authentication.setIak("mypassword");
+        authentication.setRv("senderKID");
+        server = new Cmpv2Server();
+        server.setAuthentication(authentication);
+        server.setUrl("http://127.0.0.1/ejbca/publicweb/cmp/cmp");
+        server.setIssuerDN(dn);
+    }
+
     private PKIMessage preparePKIMessageWithoutProtectionAlgorithm() {
 
         CertTemplateBuilder certTemplateBuilder = new CertTemplateBuilder();