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 static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.checkIfCmpResponseContainsError;
26 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.getCertFromByteArray;
27 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore;
29 import java.io.IOException;
30 import java.security.KeyPair;
31 import java.security.Security;
32 import java.security.cert.CertificateParsingException;
33 import java.security.cert.X509Certificate;
34 import java.util.Collections;
35 import java.util.Date;
36 import java.util.Objects;
37 import java.util.Optional;
38 import org.apache.http.impl.client.CloseableHttpClient;
39 import org.bouncycastle.asn1.cmp.CMPCertificate;
40 import org.bouncycastle.asn1.cmp.CertRepMessage;
41 import org.bouncycastle.asn1.cmp.CertResponse;
42 import org.bouncycastle.asn1.cmp.PKIBody;
43 import org.bouncycastle.asn1.cmp.PKIMessage;
44 import org.bouncycastle.asn1.x509.Certificate;
45 import org.bouncycastle.jce.provider.BouncyCastleProvider;
46 import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
47 import org.onap.oom.certservice.certification.model.CsrModel;
48 import org.onap.oom.certservice.certification.model.OldCertificateModel;
49 import org.onap.oom.certservice.cmpv2client.api.CmpClient;
50 import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
51 import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
52 import org.onap.oom.certservice.cmpv2client.validation.CmpCertificationValidator;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Implementation of the CmpClient Interface conforming to RFC4210 (Certificate Management Protocol
58 * (CMP)) and RFC4211 (Certificate Request Message Format (CRMF)) standards.
60 public class CmpClientImpl implements CmpClient {
62 private static final Logger LOG = LoggerFactory.getLogger(CmpClientImpl.class);
63 private final CloseableHttpClient httpClient;
64 private final CmpCertificationValidator validator;
66 public CmpClientImpl(CloseableHttpClient httpClient) {
67 this.httpClient = httpClient;
68 this.validator = new CmpCertificationValidator();
72 Security.addProvider(new BouncyCastleProvider());
76 public Cmpv2CertificationModel executeInitializationRequest(
81 throws CmpClientException {
83 validator.validate(csrModel, server, httpClient, notBefore, notAfter);
84 final CreateCertRequest certRequest = getIakRvRequest(csrModel, server, notBefore, notAfter, PKIBody.TYPE_INIT_REQ);
85 return executeCmpRequest(csrModel, server, certRequest);
89 public Cmpv2CertificationModel executeInitializationRequest(CsrModel csrModel, Cmpv2Server server)
90 throws CmpClientException {
91 return executeInitializationRequest(csrModel, server, null, null);
95 public Cmpv2CertificationModel executeKeyUpdateRequest(CsrModel csrModel, Cmpv2Server cmpv2Server,
96 OldCertificateModel oldCertificateModel) throws CmpClientException {
97 validator.validate(csrModel, cmpv2Server, httpClient, null, null);
99 final PkiMessageProtection pkiMessageProtection = getSignatureProtection(oldCertificateModel);
100 final CreateCertRequest certRequest =
101 getCmpMessageBuilderWithCommonRequestValues(csrModel, cmpv2Server)
102 .with(CreateCertRequest::setCmpRequestType, PKIBody.TYPE_KEY_UPDATE_REQ)
103 .with(CreateCertRequest::setExtraCerts, getCmpCertificate(oldCertificateModel.getOldCertificate()))
104 .with(CreateCertRequest::setProtection, pkiMessageProtection)
107 return executeCmpRequest(csrModel, cmpv2Server, certRequest);
111 public Cmpv2CertificationModel executeCertificationRequest(CsrModel csrModel, Cmpv2Server cmpv2Server) throws CmpClientException {
113 validator.validate(csrModel, cmpv2Server, httpClient, null, null);
114 final CreateCertRequest certRequest = getIakRvRequest(csrModel, cmpv2Server, null, null, PKIBody.TYPE_CERT_REQ);
115 return executeCmpRequest(csrModel, cmpv2Server, certRequest);
118 private CreateCertRequest getIakRvRequest(
125 final String iak = server.getAuthentication().getIak();
126 final PkiMessageProtection pkiMessageProtection = new PasswordBasedProtection(iak);
127 return getCmpMessageBuilderWithCommonRequestValues(csrModel, server)
128 .with(CreateCertRequest::setNotBefore, notBefore)
129 .with(CreateCertRequest::setNotAfter, notAfter)
130 .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv())
131 .with(CreateCertRequest::setCmpRequestType, requestType)
132 .with(CreateCertRequest::setProtection, pkiMessageProtection)
136 private Cmpv2CertificationModel executeCmpRequest(CsrModel csrModel, Cmpv2Server cmpv2Server,
137 CreateCertRequest certRequest) throws CmpClientException {
138 final PKIMessage pkiMessage = certRequest.generateCertReq();
139 Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
140 return retrieveCertificates(csrModel, cmpv2Server, pkiMessage, cmpv2HttpClient);
143 private CmpMessageBuilder<CreateCertRequest> getCmpMessageBuilderWithCommonRequestValues(CsrModel csrModel,
144 Cmpv2Server cmpv2Server) {
145 KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
146 return CmpMessageBuilder.of(CreateCertRequest::new)
147 .with(CreateCertRequest::setIssuerDn, cmpv2Server.getIssuerDN())
148 .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
149 .with(CreateCertRequest::setSansArray, csrModel.getSans())
150 .with(CreateCertRequest::setSubjectKeyPair, keyPair);
153 private SignatureProtection getSignatureProtection(OldCertificateModel oldCertificateModel) {
154 return new SignatureProtection(oldCertificateModel.getOldPrivateKey());
157 private CMPCertificate[] getCmpCertificate(Certificate oldCertificate) {
158 CMPCertificate cert = new CMPCertificate(oldCertificate);
159 return new CMPCertificate[]{cert};
162 private Cmpv2CertificationModel retrieveCertificates(
163 CsrModel csrModel, Cmpv2Server server, PKIMessage pkiMessage, Cmpv2HttpClient cmpv2HttpClient)
164 throws CmpClientException {
165 final byte[] respBytes = cmpv2HttpClient.postRequest(pkiMessage, server.getUrl(), server.getCaName());
167 final PKIMessage respPkiMessage = PKIMessage.getInstance(respBytes);
168 LOG.info("Received response from Server");
169 checkIfCmpResponseContainsError(respPkiMessage);
170 validator.checkCmpResponse(respPkiMessage, csrModel.getPublicKey(), server.getAuthentication().getIak());
171 return checkCmpCertRepMessage(respPkiMessage);
172 } catch (IllegalArgumentException iae) {
173 CmpClientException cmpClientException =
174 new CmpClientException(
175 "Error encountered while processing response from CA server ", iae);
176 LOG.error("Error encountered while processing response from CA server ", iae);
177 throw cmpClientException;
181 private Cmpv2CertificationModel checkCmpCertRepMessage(final PKIMessage respPkiMessage)
182 throws CmpClientException {
183 final PKIBody pkiBody = respPkiMessage.getBody();
184 if (Objects.nonNull(pkiBody) && pkiBody.getContent() instanceof CertRepMessage) {
185 final CertRepMessage certRepMessage = (CertRepMessage) pkiBody.getContent();
186 if (Objects.nonNull(certRepMessage)) {
188 CertResponse certResponse = getCertificateResponseContainingNewCertificate(certRepMessage);
189 validator.checkServerResponse(certResponse);
190 return verifyReturnCertChainAndTrustStore(respPkiMessage, certRepMessage, certResponse);
191 } catch (IOException | CertificateParsingException ex) {
192 CmpClientException cmpClientException =
193 new CmpClientException(
194 "Exception occurred while retrieving Certificates from response", ex);
195 LOG.error("Exception occurred while retrieving Certificates from response", ex);
196 throw cmpClientException;
199 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
202 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
205 private Cmpv2CertificationModel verifyReturnCertChainAndTrustStore(
206 PKIMessage respPkiMessage, CertRepMessage certRepMessage, CertResponse certResponse)
207 throws CertificateParsingException, CmpClientException, IOException {
208 LOG.info("Verifying certificates returned as part of CertResponse.");
209 final CMPCertificate cmpCertificate =
210 certResponse.getCertifiedKeyPair().getCertOrEncCert().getCertificate();
211 final Optional<X509Certificate> leafCertificate =
212 getCertFromByteArray(cmpCertificate.getEncoded(), X509Certificate.class);
213 if (leafCertificate.isPresent()) {
214 return verifyAndReturnCertChainAndTrustSTore(
215 respPkiMessage, certRepMessage, leafCertificate.get());
217 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
220 private CertResponse getCertificateResponseContainingNewCertificate(
221 CertRepMessage certRepMessage) {
222 return certRepMessage.getResponse()[0];