[OOM-K8S-CERT-EXTERNAL-PROVIDER] Add send update request functionality 06/122606/5
authorTomasz Wrobel <tomasz.wrobel@nokia.com>
Tue, 13 Jul 2021 14:47:08 +0000 (16:47 +0200)
committerTomasz Wrobel <tomasz.wrobel@nokia.com>
Thu, 15 Jul 2021 10:15:13 +0000 (12:15 +0200)
Issue-ID: OOM-2753
Signed-off-by: Tomasz Wrobel <tomasz.wrobel@nokia.com>
Change-Id: I0637ded5c870cc66c7dc27dff269dab0f7d9015b

13 files changed:
certServiceK8sExternalProvider/src/certserviceclient/cert_service_client.go
certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_factory.go
certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_factory_test.go
certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_mock.go
certServiceK8sExternalProvider/src/certserviceclient/cert_service_client_test.go
certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_schema.go
certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go
certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go
certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go
certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_mock.go
certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_test.go
certServiceK8sExternalProvider/src/model/sign_certificate_model.go [new file with mode: 0644]
certServiceK8sExternalProvider/src/testdata/constants.go

index 4806c4a..f4cc999 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * 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.
@@ -25,21 +25,27 @@ import (
        "encoding/json"
        "fmt"
        "net/http"
+
+       "onap.org/oom-certservice/k8s-external-provider/src/model"
 )
 
 const (
-       CsrHeaderName = "CSR"
-       PkHeaderName  = "PK"
+       CsrHeaderName            = "CSR"
+       PkHeaderName             = "PK"
+       OldPkHeaderName          = "OLD_PK"
+       OldCertificateHeaderName = "OLD_CERT"
 )
 
 type CertServiceClient interface {
        GetCertificates(csr []byte, key []byte) (*CertificatesResponse, error)
        CheckHealth() error
+       UpdateCertificate(csr []byte, key []byte, signCertificateModel model.SignCertificateModel) (*CertificatesResponse, error)
 }
 
 type CertServiceClientImpl struct {
        healthUrl        string
        certificationUrl string
+       updateUrl        string
        httpClient       HTTPClient
 }
 
@@ -83,6 +89,25 @@ func (client *CertServiceClientImpl) GetCertificates(csr []byte, key []byte) (*C
 
        request.Header.Add(CsrHeaderName, base64.StdEncoding.EncodeToString(csr))
        request.Header.Add(PkHeaderName, base64.StdEncoding.EncodeToString(key))
+
+       return client.executeRequest(request)
+}
+
+func (client *CertServiceClientImpl) UpdateCertificate(csr []byte, key []byte, signCertificateModel model.SignCertificateModel) (*CertificatesResponse, error) {
+       request, err := http.NewRequest("GET", client.updateUrl, nil)
+       if err != nil {
+               return nil, err
+       }
+
+       request.Header.Add(CsrHeaderName, base64.StdEncoding.EncodeToString(csr))
+       request.Header.Add(PkHeaderName, base64.StdEncoding.EncodeToString(key))
+       request.Header.Add(OldPkHeaderName, signCertificateModel.OldPrivateKey)
+       request.Header.Add(OldCertificateHeaderName, signCertificateModel.OldCertificate)
+
+       return client.executeRequest(request)
+}
+
+func (client *CertServiceClientImpl) executeRequest(request *http.Request) (*CertificatesResponse, error) {
        response, err := client.httpClient.Do(request)
        if err != nil {
                return nil, err
index 0fa1d16..380cbcf 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * Copyright (C) 2020 Nokia. All rights reserved.
+ * Copyright (C) 2020-2021 Nokia. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ import (
        "path"
 )
 
-func CreateCertServiceClient(baseUrl string, healthEndpoint string, certEndpoint string, caName string,
+func CreateCertServiceClient(baseUrl string, healthEndpoint string, certEndpoint string, updateEndpoint string, caName string,
        keyPemBase64 []byte, certPemBase64 []byte, cacertPemBase64 []byte) (*CertServiceClientImpl, error) {
        cert, err := tls.X509KeyPair(certPemBase64, keyPemBase64)
        if err != nil {
@@ -49,31 +49,34 @@ func CreateCertServiceClient(baseUrl string, healthEndpoint string, certEndpoint
                        },
                },
        }
-       healthUrl, certificationUrl, err := validateAndParseUrls(baseUrl, healthEndpoint, certEndpoint, caName)
+       healthUrl, certificationUrl, updateUrl, err := validateAndParseUrls(baseUrl, healthEndpoint, certEndpoint, updateEndpoint, caName)
        if err != nil {
                return nil, err
        }
        client := CertServiceClientImpl{
                healthUrl:        healthUrl,
                certificationUrl: certificationUrl,
+               updateUrl:        updateUrl,
                httpClient:       httpClient,
        }
 
        return &client, nil
 }
 
-func validateAndParseUrls(baseUrl string, healthEndpoint string, certEndpoint string, caName string) (string, string, error) {
+func validateAndParseUrls(baseUrl string, healthEndpoint string, certEndpoint string, updateEndpoint string, caName string) (string, string, string, error) {
        if err := validateUrls(baseUrl, healthEndpoint, certEndpoint, caName); err != nil {
-               return "", "", err
+               return "", "", "", err
        }
 
        certUrl, _ := url.Parse(baseUrl)
        healthUrl, _ := url.Parse(baseUrl)
+       updateUrl, _ := url.Parse(baseUrl)
 
        certUrl.Path = path.Join(certEndpoint, caName)
        healthUrl.Path = path.Join(healthEndpoint)
+       updateUrl.Path = path.Join(updateEndpoint, caName)
 
-       return healthUrl.String(), certUrl.String(), nil
+       return healthUrl.String(), certUrl.String(), updateUrl.String(), nil
 }
 
 func validateUrls(baseUrl string, healthEndpoint string, certEndpoint string, caName string) error {
index 5d255a6..a844e53 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * Copyright (C) 2020 Nokia. All rights reserved.
+ * Copyright (C) 2020-2021 Nokia. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,17 +29,19 @@ import (
 )
 
 const (
-       validUrl                 = "https://oom-cert-service:8443/"
-       validUrl2                = "https://oom-cert-service:8443"
-       invalidUrl               = "https://oom-cert  service:8443/"
-       healthEndpoint           = "actuator/health"
-       healthEndpointInvalid    = ":/actuator/health"
-       certEndpoint             = "v1/certificate"
-       certEndpointInvalid      = ":/v1/certificate"
-       caName                   = "RA"
-       caNameInvalid            = ":/RA"
-       expectedCertificationUrl = "https://oom-cert-service:8443/v1/certificate/RA"
-       expectedHealthCheckUrl   = "https://oom-cert-service:8443/actuator/health"
+       validUrl                  = "https://oom-cert-service:8443/"
+       validUrl2                 = "https://oom-cert-service:8443"
+       invalidUrl                = "https://oom-cert  service:8443/"
+       healthEndpoint            = "actuator/health"
+       healthEndpointInvalid     = ":/actuator/health"
+       certEndpoint              = "v1/certificate"
+       updateEndpoint            = "v1/certificate-update"
+       certEndpointInvalid       = ":/v1/certificate"
+       certUpdateEndpointInvalid = ":/v1/certificate-update"
+       caName                    = "RA"
+       caNameInvalid             = ":/RA"
+       expectedCertificationUrl  = "https://oom-cert-service:8443/v1/certificate/RA"
+       expectedHealthCheckUrl    = "https://oom-cert-service:8443/actuator/health"
 )
 
 func Test_shouldCreateCertServiceClient(t *testing.T) {
@@ -48,7 +50,7 @@ func Test_shouldCreateCertServiceClient(t *testing.T) {
 }
 
 func shouldCreateCertServiceClientWithExpectedUrl(t *testing.T, baseUrl string) {
-       client, err := CreateCertServiceClient(baseUrl, healthEndpoint, certEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(baseUrl, healthEndpoint, certEndpoint, updateEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.NotNil(t, client)
        assert.Nil(t, err)
@@ -57,42 +59,49 @@ func shouldCreateCertServiceClientWithExpectedUrl(t *testing.T, baseUrl string)
 }
 
 func Test_shouldReturnError_whenCaNameInvalid(t *testing.T) {
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, caNameInvalid, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, updateEndpoint, caNameInvalid, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
 }
 
 func Test_shouldReturnError_whenHealthEndpointInvalid(t *testing.T) {
-       client, err := CreateCertServiceClient(validUrl, healthEndpointInvalid, certEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpointInvalid, certEndpoint, updateEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
 }
 
 func Test_shouldReturnError_whenCertEndpointInvalid(t *testing.T) {
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpointInvalid, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpointInvalid, updateEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+
+       assert.Nil(t, client)
+       assert.Error(t, err)
+}
+
+func Test_shouldReturnError_whenUpdateCertificateEndpointInvalid(t *testing.T) {
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, certUpdateEndpointInvalid, caName, testdata.KeyBytes, testdata.CertBytes, testdata.KeyBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
 }
 
 func Test_shouldReturnError_whenUrlInvalid(t *testing.T) {
-       client, err := CreateCertServiceClient(invalidUrl, healthEndpoint, certEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(invalidUrl, healthEndpoint, certEndpoint, updateEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
 }
 
 func Test_shouldReturnError_whenCanameEmpty(t *testing.T) {
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, "", testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, updateEndpoint, "", testdata.KeyBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
 }
 
 func Test_shouldReturnError_whenKeyNotMatchingCert(t *testing.T) {
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, caName, testdata.NotMatchingKeyBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, updateEndpoint, caName, testdata.NotMatchingKeyBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
@@ -100,7 +109,7 @@ func Test_shouldReturnError_whenKeyNotMatchingCert(t *testing.T) {
 
 func Test_shouldReturnError_whenKeyInvalid(t *testing.T) {
        //Cert used as key
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, caName, testdata.CertBytes, testdata.CertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, updateEndpoint, caName, testdata.CertBytes, testdata.CertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
@@ -108,7 +117,7 @@ func Test_shouldReturnError_whenKeyInvalid(t *testing.T) {
 
 func Test_shouldReturnError_whenCertInvalid(t *testing.T) {
        //Cacert used as cert
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, caName, testdata.KeyBytes, testdata.CacertBytes, testdata.CacertBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, updateEndpoint, caName, testdata.KeyBytes, testdata.CacertBytes, testdata.CacertBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
@@ -116,7 +125,7 @@ func Test_shouldReturnError_whenCertInvalid(t *testing.T) {
 
 func Test_shouldReturnError_whenCacertInvalid(t *testing.T) {
        //Key used as cacert
-       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.KeyBytes)
+       client, err := CreateCertServiceClient(validUrl, healthEndpoint, certEndpoint, updateEndpoint, caName, testdata.KeyBytes, testdata.CertBytes, testdata.KeyBytes)
 
        assert.Nil(t, client)
        assert.Error(t, err)
index d060a98..a6fec1f 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * 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 certserviceclient
 
+import "onap.org/oom-certservice/k8s-external-provider/src/model"
+
 type CertServiceClientMock struct {
-       GetCertificatesFunc func(csr []byte, key []byte) (*CertificatesResponse, error)
+       GetCertificatesFunc   func(csr []byte, key []byte) (*CertificatesResponse, error)
+       UpdateCertificateFunc func(csr []byte, key []byte, signCertificateModel model.SignCertificateModel) (*CertificatesResponse, error)
+}
+
+func (client *CertServiceClientMock) UpdateCertificate(csr []byte, key []byte, signCertificateModel model.SignCertificateModel) (*CertificatesResponse, error) {
+       return client.UpdateCertificateFunc(csr, key, signCertificateModel)
 }
 
 func (client *CertServiceClientMock) GetCertificates(csr []byte, key []byte) (*CertificatesResponse, error) {
index 5e80f7f..e1c6bb9 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * 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.
@@ -30,11 +30,13 @@ import (
 
        "github.com/stretchr/testify/assert"
 
+       "onap.org/oom-certservice/k8s-external-provider/src/model"
        "onap.org/oom-certservice/k8s-external-provider/src/testdata"
 )
 
 const (
-       certificationUrl = "https://oom-cert-service:8443/v1/certificate/RA"
+       certificationUrl     = "https://oom-cert-service:8443/v1/certificate/RA"
+       certificateUpdateUrl = "https://oom-cert-service:8443/v1/certificate-update/RA"
 )
 
 func Test_GetCertificates_shouldParseCertificateResponseCorrectly(t *testing.T) {
@@ -97,6 +99,48 @@ func Test_GetCertificates_shouldReturnError_whenResponseOtherThan200(t *testing.
        assert.Error(t, err)
 }
 
+func Test_UpdateCertificates_shouldParseCertificateResponseCorrectly(t *testing.T) {
+       responseJson := `{"certificateChain": ["cert-0", "cert-1"], "trustedCertificates": ["trusted-cert-0", "trusted-cert-1"]}`
+       responseJsonReader := ioutil.NopCloser(bytes.NewReader([]byte(responseJson)))
+       client := CertServiceClientImpl{
+               updateUrl:  certificateUpdateUrl,
+               httpClient: getMockedClient(responseJsonReader, http.StatusOK),
+       }
+
+       response, _ := client.UpdateCertificate(testdata.CsrBytes, testdata.PkBytes, getTestSignCertificateModel())
+       assert.ElementsMatch(t, []string{"cert-0", "cert-1"}, response.CertificateChain)
+       assert.ElementsMatch(t, []string{"trusted-cert-0", "trusted-cert-1"}, response.TrustedCertificates)
+}
+
+
+func Test_UpdateCertificates_shouldReturnError_whenHttpClientReturnsError(t *testing.T) {
+       client := CertServiceClientImpl{
+               updateUrl: certificateUpdateUrl,
+               httpClient: &httpClientMock{
+                       DoFunc: func(req *http.Request) (response *http.Response, err error) {
+                               return nil, fmt.Errorf("mock error")
+                       },
+               },
+       }
+       response, err := client.UpdateCertificate(testdata.CsrBytes, testdata.PkBytes, getTestSignCertificateModel())
+
+       assert.Nil(t, response)
+       assert.Error(t, err)
+}
+
+func Test_UpdateCertificates_shouldReturnError_whenResponseOtherThan200(t *testing.T) {
+       responseJson := `{"errorMessage": "CertService API error"}`
+       responseJsonReader := ioutil.NopCloser(bytes.NewReader([]byte(responseJson)))
+       client := CertServiceClientImpl{
+               updateUrl:  updateEndpoint,
+               httpClient: getMockedClient(responseJsonReader, http.StatusNotFound),
+       }
+       response, err := client.UpdateCertificate(testdata.CsrBytes, testdata.PkBytes, getTestSignCertificateModel())
+
+       assert.Nil(t, response)
+       assert.Error(t, err)
+}
+
 func Test_CheckHealth_shouldReturnNil_whenHttpClientReturnsStatusCode200(t *testing.T) {
        client := CertServiceClientImpl{
                certificationUrl: certificationUrl,
@@ -168,3 +212,11 @@ type httpClientMock struct {
 func (client httpClientMock) Do(req *http.Request) (*http.Response, error) {
        return client.DoFunc(req)
 }
+
+func getTestSignCertificateModel() model.SignCertificateModel {
+       testSignCertificateModel := model.SignCertificateModel{
+               OldCertificate: testdata.OldCertificateEncoded,
+               OldPrivateKey:  testdata.OldPrivateKeyEncoded,
+       }
+       return testSignCertificateModel
+}
index 7339206..9c2d3e1 100644 (file)
@@ -3,7 +3,7 @@
  * oom-certservice-k8s-external-provider
  * ================================================================================
  * Copyright (c) 2019 Smallstep Labs, Inc.
- * Modifications copyright (C) 2020 Nokia. All rights reserved.
+ * Copyright (C) 2021 Nokia. All rights reserved.
  * ================================================================================
  * This source code was copied from the following git repository:
  * https://github.com/smallstep/step-issuer
@@ -41,6 +41,8 @@ type CMPv2IssuerSpec struct {
        HealthEndpoint string `json:"healthEndpoint"`
        // Path to certificate signing endpoint.
        CertEndpoint string `json:"certEndpoint"`
+       // Path to certificate update endpoint.
+       UpdateEndpoint string `json:"updateEndpoint"`
        // CaName is the name of the external CA server
        CaName string `json:"caName"`
        // KeyRef is a reference to a Secret containing the provisioner
index 1032ee0..9d26685 100644 (file)
@@ -3,7 +3,7 @@
  * oom-certservice-k8s-external-provider
  * ================================================================================
  * Copyright 2019 The cert-manager authors.
- * Modifications copyright (C) 2020-2021 Nokia. All rights reserved.
+ * Copyright (C) 2020-2021 Nokia. All rights reserved.
  * ================================================================================
  * This source code was copied from the following git repository:
  * https://github.com/smallstep/step-issuer
@@ -43,6 +43,7 @@ import (
        "onap.org/oom-certservice/k8s-external-provider/src/cmpv2controller/util"
        provisioners "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner"
        "onap.org/oom-certservice/k8s-external-provider/src/leveledlogger"
+       "onap.org/oom-certservice/k8s-external-provider/src/model"
        x509utils "onap.org/oom-certservice/k8s-external-provider/src/x509"
 )
 
@@ -142,12 +143,18 @@ func (controller *CertificateRequestController) Reconcile(k8sRequest ctrl.Reques
        isUpdateRevision, oldCertificate, oldPrivateKey := util.CheckIfCertificateUpdateAndRetrieveOldCertificateAndPk(
                controller.Client, certificateRequest, ctx)
        if isUpdateRevision {
-               log.Debug("Certificate will be updated.", "old-certificate", oldCertificate,
-                       "old-private-key", oldPrivateKey) //TODO: remove private key from logger
+               log.Info("Update revision detected")
+       }
+       signCertificateModel := model.SignCertificateModel{
+               CertificateRequest: certificateRequest,
+               PrivateKeyBytes:    privateKeyBytes,
+               IsUpdateRevision:   isUpdateRevision,
+               OldCertificate:     oldCertificate,
+               OldPrivateKey:      oldPrivateKey,
        }
 
        // 11. Sign CertificateRequest
-       signedPEM, trustedCAs, err := provisioner.Sign(ctx, certificateRequest, privateKeyBytes)
+       signedPEM, trustedCAs, err := provisioner.Sign(ctx, signCertificateModel)
        if err != nil {
                controller.handleErrorFailedToSignCertificate(certUpdater, log, err)
                return ctrl.Result{}, nil
index ee65b3c..dc2824c 100644 (file)
@@ -3,7 +3,7 @@
  * oom-certservice-k8s-external-provider
  * ================================================================================
  * Copyright (c) 2019 Smallstep Labs, Inc.
- * Modifications copyright (C) 2020 Nokia. All rights reserved.
+ * Copyright (C) 2020-2021 Nokia. All rights reserved.
  * ================================================================================
  * This source code was copied from the following git repository:
  * https://github.com/smallstep/step-issuer
@@ -29,13 +29,13 @@ import (
        "context"
        "sync"
 
-       certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
        "k8s.io/apimachinery/pkg/types"
 
        "onap.org/oom-certservice/k8s-external-provider/src/certserviceclient"
        "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api"
        "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner/csr"
        "onap.org/oom-certservice/k8s-external-provider/src/leveledlogger"
+       "onap.org/oom-certservice/k8s-external-provider/src/model"
 )
 
 var collection = new(sync.Map)
@@ -86,10 +86,17 @@ func Store(namespacedName types.NamespacedName, provisioner *CertServiceCA) {
 
 func (ca *CertServiceCA) Sign(
        ctx context.Context,
-       certificateRequest *certmanager.CertificateRequest,
-       privateKeyBytes []byte,
+       signCertificateModel model.SignCertificateModel,
 ) (signedCertificateChain []byte, trustedCertificates []byte, err error) {
        log := leveledlogger.GetLoggerWithName("certservice-provisioner")
+
+       if signCertificateModel.IsUpdateRevision {
+               log.Debug("Certificate will be updated.", "old-certificate", signCertificateModel.OldCertificate,
+                       "old-private-key", signCertificateModel.OldPrivateKey)
+       }
+
+       certificateRequest := signCertificateModel.CertificateRequest
+       privateKeyBytes := signCertificateModel.PrivateKeyBytes
        log.Info("Signing certificate: ", "cert-name", certificateRequest.Name)
 
        log.Info("CA: ", "name", ca.name, "url", ca.url)
@@ -103,9 +110,19 @@ func (ca *CertServiceCA) Sign(
        }
        log.Debug("Filtered out CSR PEM: ", "bytes", filteredCsrBytes)
 
-       response, err := ca.certServiceClient.GetCertificates(filteredCsrBytes, privateKeyBytes)
-       if err != nil {
-               return nil, nil, err
+       var response *certserviceclient.CertificatesResponse
+       var errAPI error
+
+       if signCertificateModel.IsUpdateRevision {
+               log.Info("Attempt to send certificate update request")
+               response, errAPI = ca.certServiceClient.UpdateCertificate(filteredCsrBytes, privateKeyBytes, signCertificateModel)
+       } else {
+               log.Info("Attempt to send certificate request")
+               response, errAPI = ca.certServiceClient.GetCertificates(filteredCsrBytes, privateKeyBytes)
+       }
+
+       if errAPI != nil {
+               return nil, nil, errAPI
        }
        log.Info("Successfully received response from CertService API")
        log.Debug("Certificate Chain", "cert-chain", response.CertificateChain)
index cf55266..ee06be3 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * 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.
@@ -51,7 +51,7 @@ func (f *ProvisionerFactoryImpl) CreateProvisioner(issuer *cmpv2api.CMPv2Issuer,
                return nil, err
        }
 
-       certServiceClient, err := certserviceclient.CreateCertServiceClient(issuer.Spec.URL, issuer.Spec.HealthEndpoint, issuer.Spec.CertEndpoint,
+       certServiceClient, err := certserviceclient.CreateCertServiceClient(issuer.Spec.URL, issuer.Spec.HealthEndpoint, issuer.Spec.CertEndpoint, issuer.Spec.UpdateEndpoint,
                issuer.Spec.CaName, keyBase64, certBase64, cacertBase64)
        if err != nil {
                return nil, err
index f2ffa86..cb3b8c6 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * 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.
@@ -26,6 +26,7 @@ import (
        "onap.org/oom-certservice/k8s-external-provider/src/certserviceclient"
        "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api"
        "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner/testdata"
+       "onap.org/oom-certservice/k8s-external-provider/src/model"
 )
 
 type ProvisionerFactoryMock struct {
@@ -37,6 +38,9 @@ func (f *ProvisionerFactoryMock) CreateProvisioner(issuer *cmpv2api.CMPv2Issuer,
                GetCertificatesFunc: func(csr []byte, pk []byte) (response *certserviceclient.CertificatesResponse, e error) {
                        return &testdata.SampleCertServiceResponse, nil
                },
+               UpdateCertificateFunc: func(csr []byte, key []byte, signCertificateModel model.SignCertificateModel) (*certserviceclient.CertificatesResponse, error) {
+                       return &testdata.SampleCertServiceResponse, nil
+               },
        })
 
        return provisioner, err
index cfafe95..1a06665 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * oom-certservice-k8s-external-provider
  * ================================================================================
- * 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.
@@ -32,6 +32,7 @@ import (
 
        "onap.org/oom-certservice/k8s-external-provider/src/certserviceclient"
        "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api"
+       "onap.org/oom-certservice/k8s-external-provider/src/model"
        "onap.org/oom-certservice/k8s-external-provider/src/testdata"
 )
 
@@ -64,7 +65,7 @@ func Test_shouldSuccessfullyLoadPreviouslyStoredProvisioner(t *testing.T) {
        assert.Equal(t, provisioner.url, issuer.Spec.URL, "Unexpected provisioner url.")
 }
 
-func Test_shouldReturnCorrectSignedPemsWhenParametersAreCorrect(t *testing.T) {
+func Test_shouldReturnCorrectSignedPemsWhenParametersAreCorrectForCertificateRequest(t *testing.T) {
        issuer := createIssuerAndCerts(ISSUER_NAME, ISSUER_URL)
        provisionerFactory := ProvisionerFactoryMock{}
        provisioner, err := provisionerFactory.CreateProvisioner(&issuer, apiv1.Secret{})
@@ -80,7 +81,46 @@ func Test_shouldReturnCorrectSignedPemsWhenParametersAreCorrect(t *testing.T) {
        request := createCertificateRequest()
        privateKeyBytes := getPrivateKeyBytes()
 
-       signedPEM, trustedCAs, err := provisioner.Sign(ctx, request, privateKeyBytes)
+       signCertificateModel := model.SignCertificateModel{
+               CertificateRequest: request,
+               PrivateKeyBytes:    privateKeyBytes,
+               IsUpdateRevision:   false,
+               OldCertificate:     "",
+               OldPrivateKey:      "",
+       }
+
+       signedPEM, trustedCAs, err := provisioner.Sign(ctx, signCertificateModel)
+
+       assert.Nil(t, err)
+
+       testdata.VerifyCertsAreEqualToExpected(t, signedPEM, trustedCAs)
+}
+
+func Test_shouldReturnCorrectSignedPemsWhenParametersAreCorrectForUpdateCertificateRequest(t *testing.T) {
+       issuer := createIssuerAndCerts(ISSUER_NAME, ISSUER_URL)
+       provisionerFactory := ProvisionerFactoryMock{}
+       provisioner, err := provisionerFactory.CreateProvisioner(&issuer, apiv1.Secret{})
+
+       issuerNamespaceName := testdata.CreateIssuerNamespaceName(ISSUER_NAMESPACE, ISSUER_NAME)
+       Store(issuerNamespaceName, provisioner)
+
+       provisioner, ok := Load(issuerNamespaceName)
+
+       testdata.VerifyThatConditionIsTrue(ok, "Provisioner could not be loaded", t)
+
+       ctx := context.Background()
+       request := createCertificateRequest()
+       privateKeyBytes := getPrivateKeyBytes()
+
+       signCertificateModel := model.SignCertificateModel{
+               CertificateRequest: request,
+               PrivateKeyBytes:    privateKeyBytes,
+               IsUpdateRevision:   true,
+               OldCertificate:     testdata.OldCertificateEncoded,
+               OldPrivateKey:      testdata.OldPrivateKeyEncoded,
+       }
+
+       signedPEM, trustedCAs, err := provisioner.Sign(ctx, signCertificateModel)
 
        assert.Nil(t, err)
 
diff --git a/certServiceK8sExternalProvider/src/model/sign_certificate_model.go b/certServiceK8sExternalProvider/src/model/sign_certificate_model.go
new file mode 100644 (file)
index 0000000..40dca1a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ * oom-certservice-k8s-external-provider
+ * ================================================================================
+ * 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 model
+
+import cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
+
+type SignCertificateModel struct {
+       CertificateRequest *cmapi.CertificateRequest
+       PrivateKeyBytes    []byte
+       IsUpdateRevision   bool
+       OldCertificate     string
+       OldPrivateKey      string
+}
index d2097ba..c1e8614 100644 (file)
@@ -1,3 +1,23 @@
+/*
+ * ============LICENSE_START=======================================================
+ * oom-certservice-k8s-external-provider
+ * ================================================================================
+ * 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 testdata
 
 import "encoding/base64"
@@ -10,3 +30,6 @@ var (
        CsrBytes, _            = base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQy9EQ0NBZVFDQVFBd2JqRUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdUQ2tOaGJHbG1iM0p1YVdFeApDekFKQmdOVkJBY1RBbFZUTVEwd0N3WURWUVFLRXdSdmJtRndNUkF3RGdZRFZRUUxFd2R2Ym1Gd0xXOTFNUnd3CkdnWURWUVFERXhOalpYSjBhWE56ZFdWeUxtOXVZWEF1YjNKbk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0MKQVE4QU1JSUJDZ0tDQVFFQTJSZDFCV3JIRm5wUGdkdy9VaFBOYWZPWjB1S3lGZytuWHNNQUJEbFB6YzBxaWRWWQp4RTVmU0E0dUFXUmpvV1FLQ1dxQWxGZS9LYjBSL3ZlTTN6K0hUMUQrRXBsNjNZWUF5dVYyNFdaa2JEeDhGdGV6CjBqd2l1R21Zb0lld1JXMmZXY25RcTV6WDZ4LzEyalJ3eThtQVpJaFRtUXloTjRQWGpjd09ZcVJrVTBQeGsrQnAKMmt4RWpTQi9SSlJGKzE5YWh6K2IreStLQ0dJRVdiSStXb0RpMzFNYmh0aVVrMnd2MXdzUk8vbmRnM1RxT2Y0Twp5QjNtVGlYMkIyL0t1ZXpLbFlseENobUdjS1UxdTUwU0pYT3JZYU9KNTZJemdDTU1FVk1YaEpzYlgweFlnMkZMCjZsSkxXcjlma3pxeFRmenMxYUdVWXdDcG9rWHROa1UybXBPT1lRSURBUUFCb0Vrd1J3WUpLb1pJaHZjTkFRa08KTVRvd09EQXBCZ05WSFJFRUlqQWdnZ2xzYjJOaGJHaHZjM1NDRTJObGNuUnBjM04xWlhJdWIyNWhjQzV2Y21jdwpDd1lEVlIwUEJBUURBZ1dnTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDNThJWkt5dlFwdUFuVHR3NUd1eVh2ClQralNML240Kzg1b1dwamwxYVZnQTVRcWVHWU8wRzVQbzNMbGVLeTlCaEJIclBmY201eEFudFUvM0VKWmJBSFQKai9WWkRzdmVaR3JEc2hqRWI4dmNuSTRROXVpY0dNYnlUbktFcVpzSm5EMlpqN1RiWFBocXQrV1Z5S0RJc0ZLdAprNHVSWGpaRTI0VVh6ZFhiWnNUeWlscFl0RzJkR3RTTXVSdll6NlR1eUlWRVZMNVBXRWRGak5VUVJSK2czTVpDCmtrc2pKSzkvSC9OZVl1TS9QN1BUWjZkRWFUY3c5UmtSdEVGazBPQlRxWUlyaUhmczJUMUlIdzdMaVl6NFhyUEQKVFBncHppM1IyZVFINDhzSDVMS1UvblQxUWtNbHZiS3RpcWdoZkx6eGQ0MXEzRTZxNkRZdGJybHN6eTVpeTFqOAotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K")
        PkBytes, _             = base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRFpGM1VGYXNjV2VrK0IKM0Q5U0U4MXA4NW5TNHJJV0Q2ZGV3d0FFT1UvTnpTcUoxVmpFVGw5SURpNEJaR09oWkFvSmFvQ1VWNzhwdlJIKwo5NHpmUDRkUFVQNFNtWHJkaGdESzVYYmhabVJzUEh3VzE3UFNQQ0s0YVppZ2g3QkZiWjlaeWRDcm5OZnJIL1hhCk5IREx5WUJraUZPWkRLRTNnOWVOekE1aXBHUlRRL0dUNEduYVRFU05JSDlFbEVYN1gxcUhQNXY3TDRvSVlnUloKc2o1YWdPTGZVeHVHMkpTVGJDL1hDeEU3K2QyRGRPbzUvZzdJSGVaT0pmWUhiOHE1N01xVmlYRUtHWVp3cFRXNwpuUklsYzZ0aG80bm5vak9BSXd3UlV4ZUVteHRmVEZpRFlVdnFVa3RhdjErVE9yRk4vT3pWb1pSakFLbWlSZTAyClJUYWFrNDVoQWdNQkFBRUNnZ0VBWk04NHZ5QTdmUnVsQ2hlZHE5NllOOGd3T1RhZUhoSjgxVXRXR2FBSGgvanEKOVFDR2JQbzcwcmtLOGdpTkgyZldKVk00akNwSEVmbkRmcE96N2dPUk1PcmFZUWEyZ0dIMndrRldPQXNWUFJIRgpTZEkycGJ6WkhxdWlmWUVsQU1pTUErVHNxcFIxeTdDV3VSSTdBdGI2Y1RUQkpVUXhKUmRySkdTS2xaSGpLS3A4Clo4V0xkNllqTnVzSlY0c1ZNb0pTR3RPc0tkcUx0RytlYnhPK2RCdWNnbm95S01KMi9LREl2bmxHWE1CME5IOUYKaXVOSVFYNnlReHI0VlNGSVZndldwZ0dPYTI0QWtxSlFGRm80UVdlc1JVa01EaFU5aTJPYWc4ekNxL1U4VmFpMQo2eTBTa2FucTBxbVFoN1c5UFV5RXlKdGhhbk5RczYzNDZXWkZmNWY0bFFLQmdRRHc1MzRLZFNiYkJjQTBZVmpaCjI1NDRLbE1MMzBSc04vN2JIdEt0djVVKzIyYjBXekRycHVVbFpVUmVFZGducGlBQVg3RUpLdFRDUVVCektuTmQKU1g4WTE4WTB6cEkyZnhJb2pjN0YwVy9aMzB0bkM1NjNKLzV0WTk4eVJzQ3lISXpRTXQvOTMzUVRLU2pGaktBcwp1ZCtKbDR6Q25yWUpnVitrTGV6V1R4c1ptd0tCZ1FEbXNmaGZPOUF6V3diQS9IS09aL01ZaTRjcWE5disyN2xSCktmYStZQ3ZMRUE0UWhJRTZXVFA2ZGFtTnpwZytQMW9QcmdmSVBpNVJOelRmdlFoSzlMaW56dDdxVTJYbWJYVXUKNDJpR2p3UklwY1hLaGxYeFNCTTFQVGZ5dW5GaXlORW1qVlR2SWdudW9vNmdJNUp0RVNLQ2hGZy95YWIwRm9ONQpVRnV1MGp2bHN3S0JnRUpKWUZ3ZVNqZkFDRmdoWlNKbEZNOGRqa1paQStuSEtxQStoZmY3SEdUMFdBcnF3TFpHCjhReHVKZmJBY0RyUXNrT0lFUjJWcEg5akZ3blpaMjhHMXlzTnpHTWhhQWdJeFFWVnA4eTB5Vk1vNXdXT28vaC8KejdsbjNyVmwxSVh0NXkwdW9vV25vN2ZWL25zRks5bkN0MmlUdzg2VmZ6OTBVczNKT1Q3cSsyajdBb0dBUVFpTQp0dlFhcGsrVDROV0p5Y0ZlRTE1S0ZWaGdwVUQxeGY2cGMxT1RKT1I2d29kSUV0WFF4RnRsRi9mVWpUKzR1TkRiCm1zU0V0QnAzQ2xlMHZjU3RSWWtZNkQvb2F3UVNVOHlCeStVSFZSOStXYkJ6QzlqQXFYSi9raXFqQ2pFSVhQRGMKcjZrTjJicnpzQXMzSFE0R2gzcWRraVhicmRXbTdJME51NFBDcE9jQ2dZRUFzNUh1aFZ3Q0lrL0IrY0I0Um5RbwpXWTJOQjVPd1FpbmVmd3RVVlJJTWxGTkhWeWljTmhyME5wQkJ0TGF0RFRZOTlYRmx3eHh3LzMrM0hBbUdxTjVvCmNvVTVkQ3dNRWo0RmZ6V3ZIUTBWa0VsRVRkZ3ZnVFluejFYU015alJXZjZweTRaTXpBZ0xJL2pDQXlGMnQvMVMKZ1ZIR01LRFV0YjdNRHIzamg3ZmxoUG89Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K")
 )
+
+const OldPrivateKeyEncoded = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRHBTaUdjSGdxaEJPYUgKVzZ4NFBZaWdnVUhBYW5nVXVNNEkxWlFFZkZleDZCMXMvYWVzUUdVckJMNGNlRnBBd0JMWTY4d2IwR1JsQTFCZgo2eVVpc2FhRXAxL0J4OVR2VUVhd2E1TlprV0pvVVV5TTM2MXBlMXF5bExJcVd0aEdGa3Q1dStGRGJjVFFHcjV1CjBQYzVDMEN4V28rSCsxVU5IdW9sZEo4Zk5adHZLMEpFcm4zWmFsRjgwVVRzbjdpVW1SeWFlQVplTGpiOHZWcWwKSlU2NVpnVzZldThKWk1yUnA4T283T0drZnFrR2lLdWtSVWtValZNNG91eUNERkprYVRJUzB1RnJ6N1JYSlRUbQo0eXBaQnNKeXp5OHhsRHZtbFMwZ3phSHdqOHVPK2pLa3d0bjd1TDk2am1JaVFtbFhkK3h1M0NoeFpJWEpmaTgzCjZpdFc1VjdaQWdNQkFBRUNnZ0VCQUpNcXg3OFRtSUx4YzNnS1ZlZlljWnRIVHpKU09BUmlmTjlIMU1OcnFXcTYKMGUxU3F2YlgvTHBCbUtpZko5aFhFc3l6RzZTa051RWVVUkxoWlNEWXp3STZFQWRQeDcxY2QrdU5RWHdzWWRjTApDbTZJSUg0OWFmN2tIT3pwT0N3bW5tQmlMSDM3L2orRno0SmE0c3FpbGFJTVRpVnJZTUVSTW9hRVpta3F5UzQ3CitHb2FhcDFGMkFoTjVaVTRKeFlMV0ZqejhBWVJSSGRnWDZJTk1paWxmTDJNT0Rld3V5TEpaaXFScTByMFVGcUIKdElia25JcE02azgrczd2VkVXVXY1cVlibkU3TjQzSWIxd2lYbnZQY2orMzR5ZEhadkpWcVprWXdsQ2xWWEVOQgpyeVpkMjBySTc2aTgxTk9Wd1RGaUNRazNZcjNJTWhWTmtRSTF4Nzdvc0lFQ2dZRUEvRVFyZXh4emh5QTVlbkRRCmVIaEh2T042WU54Y1h6VHFhVk16dHRxa0JwbEZ3aENIdzA2c3hZSmJUN3g5UnB3MzZYWS9FaFBwdG9lSVhUWU0Kc1ZaMkljUVorQXN6ejYzK2JYVmJ2VXJjUGRpb0U3bEJadGpTVHlUMnVlYm1Zck52N3ZNNEJOb0tzOUczd2U1bAo2d1BHR0tkdmJuWDhUcVdDNE54LzFUM2w2VEVDZ1lFQTdMNFBKdTA4Ry9FUzROTHlTaVdVSGJsSHRkcXVSQmE3CkhZcWIwejJ3NnBpei8xNHNNOTVmWGZSODZmai92RVVjNndUb2FuRFplc1UvSDg0SEZ0VTFuSGpLTW9qVFQ1ckMKWDQvblJTZ2FBdGwrVmpLNGphb0Jyb1NFeEJsdUI2VGJwUW9LTG0rdUtxakJQYkNjSllORitIMkxUYWdMRVliUwpkNTNPOGlKaDVpa0NnWUJJZ2Z1UmJqTVNrc09TbXR5QTArbWl0Q3VYclo3clNwVlo0cTFKa0h4MjNSVTgyMjE2ClNLSEdQMXFwaDM3bWpiNVFYMGx2azhPb1VEcDB1RFZidjROQzMwK3JpT1RDZTd0V2tOWG1pWjdXTS9EVGducjIKNmJsQlFGbWVRMnpTejhxTGZ1TUtHZlhiaTVycXBmQXJaYkZKb3M2WGpGZ1I3dWE0WlFobExWNW84UUtCZ1FEUgpMRWlVKzAxOTNxM3dhVkhjZzRGd0ZkR3ZjeTFBU2RsQUM4VU1pdGh6SDBNQ29nRnFQdE9DWDArekp1ZEdRTWFCClBNL2hwQjN6NUsyV2UySTJJV0lDQTVPYnZOci8zZHhadFBzQlZxSk0zRUJOQnZtYmFaZWN5OGZHd0RWQW1iL2IKL1pmcldZL0liMXgyRmtLUXZvRW5RajIvK25iMUlHdDdkcnB2cEVOZHFRS0JnUUNLSUFweVRLTXVWNjlPc3U2NAoranRXNG51RkYreDRlQjU1Wk1CYUxGY3ZpWElIUjVWUzlnOUlTTEROZHdJT2V5THlhcDE4UkZWY0xVT1IzOUFICmZodWJXSjhBSjJ4cUFJajBiNmYzeUVBRklHdWE2UnRKcXpUeElFVlFPMFdBS1VuUElBR1UxdkhNSDFGRDZsc3MKRkU4Q1o4enN1dlBRaXRqd0Z2NFJNV0JCMnc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg=="
+const OldCertificateEncoded = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVyRENDQXhTZ0F3SUJBZ0lVUFdUaGxyU1IyRXFwemRpVjZJUU1sOEo1ZVpBd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1V6RVZNQk1HQ2dtU0pvbVQ4aXhrQVFFTUJURXlNelExTVJVd0V3WURWUVFEREF4TllXNWhaMlZ0Wlc1MApRMEV4SXpBaEJnTlZCQW9NR2tWS1FrTkJJRU52Ym5SaGFXNWxjaUJSZFdsamEzTjBZWEowTUI0WERUSXhNRGN3Ck56RXlNRGMxTVZvWERUSXpNRGN3TnpFeU1EYzFNRm93ZHpFUk1BOEdBMVVFQXd3SWIyNWhjQzV2Y21jeERUQUwKQmdOVkJBc01CRTlPUVZBeEdUQVhCZ05WQkFvTUVFeHBiblY0TFVadmRXNWtZWFJwYjI0eEZqQVVCZ05WQkFjTQpEVk5oYmkxR2NtRnVZMmx6WTI4eEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhDekFKQmdOVkJBWVRBbFZUCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBNDJ2QmZpeCtCb3NwblRXVTN0REQKY0FHSjZGU010YVJyS3pPTllQWGhyTE9TNU9BZmpNYkNLbGxrcmErWVlMSGZPTzdTajcwNWVEUi8zVVlPWEtiSQo3eERLL0JvamZwVG1YbWVrcmVONXFmazNmV2hOdUEvS054anJybHpIbDdza0t3UW55QVBWVG03ekh2a01sZEFlCjIrTVp1RHNDcXA0NVpEa3RER241NTBpNFQyRlAwdnVoRDVzWFZ5dUkzQVZxbVkySGJYMmE3MVgxT1lZSEthcE4KWE50RVpIYXN2K0w1ZWw2NUM3Qk5BMEpiNGdRK2kzRnJuMFJoNXFuWVZ3QS85MkIxa2FwMC9FQS95dEl1aGdwMAo5R29rNlhGQkc0TGNzbmlHR29WU2dxUHpmd0w0Tm90Mk5FSDlmWC9vSC9LbnEyRjMxTGpodGI0T0p2VG5KTENYCnB3SURBUUFCbzRIVE1JSFFNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVUveDhMN2tXWGhDcG8KOVZrSGVaYnlna04wbzJ3d1J3WURWUjBSQkVBd1BvRU5kR1Z6ZEVCdmJtRndMbTl5WjRJSWIyNWhjQzV2Y21lQwpEWFJsYzNRdWIyNWhjQzV2Y21lR0RtWjBjRG92TDNSbGMzUXViM0puaHdSL0FBQUJNQ2NHQTFVZEpRUWdNQjRHCkNDc0dBUVVGQndNQ0JnZ3JCZ0VGQlFjREJBWUlLd1lCQlFVSEF3RXdIUVlEVlIwT0JCWUVGS3ZUR2Rtc3JCUmUKSTlzcFIrYlExWGdVVlBSSE1BNEdBMVVkRHdFQi93UUVBd0lGNERBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQQpoWE9HWU1OUjJzcTNreUVhWG9KL3BYWlJiRW1jWlNYZ2p1dXVES0U4bjh3WlArY2Izd0FDZWU3MmFtZ2dJODR6CmtJeGlkU2ZNZWgvdURZTVBGR2pBQUFyQ1kydTdxcnZsMzBmaVU1OG9qZmhsbUwzbHYyalZBSnRyWksvVnVrWnUKajB6ZG5TU2d6ZyszN0NhL1BHeE1nY3pDaHdhZVEvU0ZpZHhuYWczdHhmUFZjYUdXa25pTkljVER5ZlRUQ3J0YQpjU0JPQ3B1S1doOWRCZk15dTg3VjhNc2N2dGh0WDNIWFhEQStVSXN4VzJlekxOS25UYmM1SURHL3NqOGVteXNmCnA1aDE3alQxblZ1eEY3QWluWC96Um84YzBBK20zVUdLQVdxM1NKU3k1RDdDRkVzTWtRRUhiNWQ3WGtZR3pRNWkKNmZPZURuZlpZZjV0R2tYSTdaZmZXZjduRkprVEhmU25xNkxUaE9SbDAyRWliMXJoZXFCR2xSR0hhTFNIWTh4OApZb0trK2dZbkI3a2MzSzRuV3NMNGpTWW5oUlBaMmpJMTM1RWxwckFtaGlBQ2E3Zk5wMThLbitsQzZvWGZ6b1FqCmJPMlFhUENCdU1NMFFhLzZzb2NwN1lnZkRXZjdUR3llYi9rV2pIWEpXaUUzSk5FcjBhN28ycGVFUHQvSEY4WTAKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoK"