2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2020 Nordix Foundation.
4 * ================================================================================
5 * Modification copyright 2021 Nokia
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.oom.certservice.cmpv2client.impl;
25 import org.apache.http.impl.client.CloseableHttpClient;
26 import org.bouncycastle.asn1.cmp.CMPCertificate;
27 import org.bouncycastle.asn1.cmp.CertRepMessage;
28 import org.bouncycastle.asn1.cmp.CertResponse;
29 import org.bouncycastle.asn1.cmp.PKIBody;
30 import org.bouncycastle.asn1.cmp.PKIMessage;
31 import org.bouncycastle.asn1.x509.Certificate;
32 import org.bouncycastle.jce.provider.BouncyCastleProvider;
33 import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
34 import org.onap.oom.certservice.certification.model.CsrModel;
35 import org.onap.oom.certservice.certification.model.OldCertificateModel;
36 import org.onap.oom.certservice.cmpv2client.api.CmpClient;
37 import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
38 import org.onap.oom.certservice.cmpv2client.impl.protections.PasswordBasedProtection;
39 import org.onap.oom.certservice.cmpv2client.impl.protections.PkiMessageProtection;
40 import org.onap.oom.certservice.cmpv2client.impl.protections.SignatureProtection;
41 import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
42 import org.onap.oom.certservice.cmpv2client.validation.CmpCertificationValidator;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
46 import java.io.IOException;
47 import java.security.KeyPair;
48 import java.security.Security;
49 import java.security.cert.CertificateParsingException;
50 import java.security.cert.X509Certificate;
51 import java.util.Collections;
52 import java.util.Date;
53 import java.util.Objects;
54 import java.util.Optional;
56 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.checkIfCmpResponseContainsError;
57 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.getCertFromByteArray;
58 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore;
61 * Implementation of the CmpClient Interface conforming to RFC4210 (Certificate Management Protocol
62 * (CMP)) and RFC4211 (Certificate Request Message Format (CRMF)) standards.
64 public class CmpClientImpl implements CmpClient {
66 private static final Logger LOG = LoggerFactory.getLogger(CmpClientImpl.class);
67 private final CloseableHttpClient httpClient;
68 private final CmpCertificationValidator validator;
70 public CmpClientImpl(CloseableHttpClient httpClient) {
71 this.httpClient = httpClient;
72 this.validator = new CmpCertificationValidator();
76 Security.addProvider(new BouncyCastleProvider());
80 public Cmpv2CertificationModel executeInitializationRequest(
85 throws CmpClientException {
87 validator.validate(csrModel, server, httpClient, notBefore, notAfter);
88 final CreateCertRequest certRequest = getIakRvRequest(csrModel, server, notBefore, notAfter, PKIBody.TYPE_INIT_REQ);
89 return executeCmpRequest(csrModel, server, certRequest);
93 public Cmpv2CertificationModel executeInitializationRequest(CsrModel csrModel, Cmpv2Server server)
94 throws CmpClientException {
95 return executeInitializationRequest(csrModel, server, null, null);
99 public Cmpv2CertificationModel executeKeyUpdateRequest(CsrModel csrModel, Cmpv2Server cmpv2Server,
100 OldCertificateModel oldCertificateModel) throws CmpClientException {
101 validator.validate(csrModel, cmpv2Server, httpClient, null, null);
103 final PkiMessageProtection pkiMessageProtection = getSignatureProtection(oldCertificateModel);
104 final CreateCertRequest certRequest =
105 getCmpMessageBuilderWithCommonRequestValues(csrModel, cmpv2Server)
106 .with(CreateCertRequest::setCmpRequestType, PKIBody.TYPE_KEY_UPDATE_REQ)
107 .with(CreateCertRequest::setExtraCerts, getCMPCertificate(oldCertificateModel.getOldCertificate()))
108 .with(CreateCertRequest::setProtection, pkiMessageProtection)
111 return executeCmpRequest(csrModel, cmpv2Server, certRequest);
115 public Cmpv2CertificationModel executeCertificationRequest(CsrModel csrModel, Cmpv2Server cmpv2Server) throws CmpClientException {
117 validator.validate(csrModel, cmpv2Server, httpClient, null, null);
118 final CreateCertRequest certRequest = getIakRvRequest(csrModel, cmpv2Server, null, null, PKIBody.TYPE_CERT_REQ);
119 return executeCmpRequest(csrModel, cmpv2Server, certRequest);
122 private CreateCertRequest getIakRvRequest(
129 final String iak = server.getAuthentication().getIak();
130 final PkiMessageProtection pkiMessageProtection = new PasswordBasedProtection(iak);
131 return getCmpMessageBuilderWithCommonRequestValues(csrModel, server)
132 .with(CreateCertRequest::setNotBefore, notBefore)
133 .with(CreateCertRequest::setNotAfter, notAfter)
134 .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv())
135 .with(CreateCertRequest::setCmpRequestType, requestType)
136 .with(CreateCertRequest::setProtection, pkiMessageProtection)
140 private Cmpv2CertificationModel executeCmpRequest(CsrModel csrModel, Cmpv2Server cmpv2Server,
141 CreateCertRequest certRequest) throws CmpClientException {
142 final PKIMessage pkiMessage = certRequest.generateCertReq();
143 Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
144 return retrieveCertificates(csrModel, cmpv2Server, pkiMessage, cmpv2HttpClient);
147 private CmpMessageBuilder<CreateCertRequest> getCmpMessageBuilderWithCommonRequestValues(CsrModel csrModel,
148 Cmpv2Server cmpv2Server) {
149 KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
150 return CmpMessageBuilder.of(CreateCertRequest::new)
151 .with(CreateCertRequest::setIssuerDn, cmpv2Server.getIssuerDN())
152 .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
153 .with(CreateCertRequest::setSansArray, csrModel.getSans())
154 .with(CreateCertRequest::setSubjectKeyPair, keyPair);
157 private SignatureProtection getSignatureProtection(OldCertificateModel oldCertificateModel) {
158 return new SignatureProtection(oldCertificateModel.getOldPrivateKey());
161 private CMPCertificate[] getCMPCertificate(Certificate oldCertificate) {
162 CMPCertificate cert = new CMPCertificate(oldCertificate);
163 return new CMPCertificate[]{cert};
166 private Cmpv2CertificationModel retrieveCertificates(
167 CsrModel csrModel, Cmpv2Server server, PKIMessage pkiMessage, Cmpv2HttpClient cmpv2HttpClient)
168 throws CmpClientException {
169 final byte[] respBytes = cmpv2HttpClient.postRequest(pkiMessage, server.getUrl(), server.getCaName());
171 final PKIMessage respPkiMessage = PKIMessage.getInstance(respBytes);
172 LOG.info("Received response from Server");
173 checkIfCmpResponseContainsError(respPkiMessage);
174 validator.checkCmpResponse(respPkiMessage, csrModel.getPublicKey(), server.getAuthentication().getIak());
175 return checkCmpCertRepMessage(respPkiMessage);
176 } catch (IllegalArgumentException iae) {
177 CmpClientException cmpClientException =
178 new CmpClientException(
179 "Error encountered while processing response from CA server ", iae);
180 LOG.error("Error encountered while processing response from CA server ", iae);
181 throw cmpClientException;
185 private Cmpv2CertificationModel checkCmpCertRepMessage(final PKIMessage respPkiMessage)
186 throws CmpClientException {
187 final PKIBody pkiBody = respPkiMessage.getBody();
188 if (Objects.nonNull(pkiBody) && pkiBody.getContent() instanceof CertRepMessage) {
189 final CertRepMessage certRepMessage = (CertRepMessage) pkiBody.getContent();
190 if (Objects.nonNull(certRepMessage)) {
192 CertResponse certResponse = getCertificateResponseContainingNewCertificate(certRepMessage);
193 validator.checkServerResponse(certResponse);
194 return verifyReturnCertChainAndTrustStore(respPkiMessage, certRepMessage, certResponse);
195 } catch (IOException | CertificateParsingException ex) {
196 CmpClientException cmpClientException =
197 new CmpClientException(
198 "Exception occurred while retrieving Certificates from response", ex);
199 LOG.error("Exception occurred while retrieving Certificates from response", ex);
200 throw cmpClientException;
203 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
206 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
209 private Cmpv2CertificationModel verifyReturnCertChainAndTrustStore(
210 PKIMessage respPkiMessage, CertRepMessage certRepMessage, CertResponse certResponse)
211 throws CertificateParsingException, CmpClientException, IOException {
212 LOG.info("Verifying certificates returned as part of CertResponse.");
213 final CMPCertificate cmpCertificate =
214 certResponse.getCertifiedKeyPair().getCertOrEncCert().getCertificate();
215 final Optional<X509Certificate> leafCertificate =
216 getCertFromByteArray(cmpCertificate.getEncoded(), X509Certificate.class);
217 if (leafCertificate.isPresent()) {
218 return verifyAndReturnCertChainAndTrustSTore(
219 respPkiMessage, certRepMessage, leafCertificate.get());
221 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
224 private CertResponse getCertificateResponseContainingNewCertificate(
225 CertRepMessage certRepMessage) {
226 return certRepMessage.getResponse()[0];