2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2020 Nordix Foundation.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.oom.certservice.cmpv2client.impl;
23 import java.security.KeyPair;
24 import java.security.PublicKey;
26 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.checkIfCmpResponseContainsError;
27 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.getCertFromByteArray;
28 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore;
29 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.checkImplicitConfirm;
30 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifyPasswordBasedProtection;
31 import static org.onap.oom.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifySignature;
33 import java.io.IOException;
34 import java.security.cert.CertificateParsingException;
35 import java.security.cert.X509Certificate;
36 import java.util.Collections;
37 import java.util.Date;
38 import java.util.Objects;
39 import java.util.Optional;
41 import org.apache.http.impl.client.CloseableHttpClient;
42 import org.bouncycastle.asn1.cmp.CMPCertificate;
43 import org.bouncycastle.asn1.cmp.CertRepMessage;
44 import org.bouncycastle.asn1.cmp.CertResponse;
45 import org.bouncycastle.asn1.cmp.PKIBody;
46 import org.bouncycastle.asn1.cmp.PKIHeader;
47 import org.bouncycastle.asn1.cmp.PKIMessage;
48 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
49 import org.onap.oom.certservice.certification.configuration.model.CaMode;
50 import org.onap.oom.certservice.certification.configuration.model.Cmpv2Server;
51 import org.onap.oom.certservice.certification.model.CsrModel;
52 import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
53 import org.onap.oom.certservice.cmpv2client.api.CmpClient;
54 import org.onap.oom.certservice.cmpv2client.model.Cmpv2CertificationModel;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
59 * Implementation of the CmpClient Interface conforming to RFC4210 (Certificate Management Protocol
60 * (CMP)) and RFC4211 (Certificate Request Message Format (CRMF)) standards.
62 public class CmpClientImpl implements CmpClient {
64 private static final Logger LOG = LoggerFactory.getLogger(CmpClientImpl.class);
65 private final CloseableHttpClient httpClient;
67 private static final String DEFAULT_CA_NAME = "Certification Authority";
68 private static final String DEFAULT_PROFILE = CaMode.RA.getProfile();
70 public CmpClientImpl(CloseableHttpClient httpClient) {
71 this.httpClient = httpClient;
75 public Cmpv2CertificationModel createCertificate(
80 throws CmpClientException {
82 validate(csrModel, server, httpClient, notBefore, notAfter);
83 KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
85 final CreateCertRequest certRequest =
86 CmpMessageBuilder.of(CreateCertRequest::new)
87 .with(CreateCertRequest::setIssuerDn, server.getIssuerDN())
88 .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
89 .with(CreateCertRequest::setSansList, csrModel.getSans())
90 .with(CreateCertRequest::setSubjectKeyPair, keyPair)
91 .with(CreateCertRequest::setNotBefore, notBefore)
92 .with(CreateCertRequest::setNotAfter, notAfter)
93 .with(CreateCertRequest::setInitAuthPassword, server.getAuthentication().getIak())
94 .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv())
97 final PKIMessage pkiMessage = certRequest.generateCertReq();
98 Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
99 return retrieveCertificates(csrModel, server, pkiMessage, cmpv2HttpClient);
103 public Cmpv2CertificationModel createCertificate(CsrModel csrModel, Cmpv2Server server)
104 throws CmpClientException {
105 return createCertificate(csrModel, server, null, null);
108 private void checkCmpResponse(
109 final PKIMessage respPkiMessage, final PublicKey publicKey, final String initAuthPassword)
110 throws CmpClientException {
111 final PKIHeader header = respPkiMessage.getHeader();
112 final AlgorithmIdentifier protectionAlgo = header.getProtectionAlg();
113 verifySignatureWithPublicKey(respPkiMessage, publicKey);
114 verifyProtectionWithProtectionAlgo(respPkiMessage, initAuthPassword, header, protectionAlgo);
117 private void verifySignatureWithPublicKey(PKIMessage respPkiMessage, PublicKey publicKey)
118 throws CmpClientException {
119 if (Objects.nonNull(publicKey)) {
120 LOG.debug("Verifying signature of the response.");
121 verifySignature(respPkiMessage, publicKey);
123 LOG.error("Public Key is not available, therefore cannot verify signature");
124 throw new CmpClientException(
125 "Public Key is not available, therefore cannot verify signature");
129 private void verifyProtectionWithProtectionAlgo(
130 PKIMessage respPkiMessage,
131 String initAuthPassword,
133 AlgorithmIdentifier protectionAlgo)
134 throws CmpClientException {
135 if (Objects.nonNull(protectionAlgo)) {
136 LOG.debug("Verifying PasswordBased Protection of the Response.");
137 verifyPasswordBasedProtection(respPkiMessage, initAuthPassword, protectionAlgo);
138 checkImplicitConfirm(header);
141 "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm");
142 throw new CmpClientException(
143 "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm");
147 private Cmpv2CertificationModel checkCmpCertRepMessage(final PKIMessage respPkiMessage)
148 throws CmpClientException {
149 final PKIBody pkiBody = respPkiMessage.getBody();
150 if (Objects.nonNull(pkiBody) && pkiBody.getContent() instanceof CertRepMessage) {
151 final CertRepMessage certRepMessage = (CertRepMessage) pkiBody.getContent();
152 if (Objects.nonNull(certRepMessage)) {
153 final CertResponse certResponse =
154 getCertificateResponseContainingNewCertificate(certRepMessage);
156 return verifyReturnCertChainAndTrustStore(respPkiMessage, certRepMessage, certResponse);
157 } catch (IOException | CertificateParsingException ex) {
158 CmpClientException cmpClientException =
159 new CmpClientException(
160 "Exception occurred while retrieving Certificates from response", ex);
161 LOG.error("Exception occurred while retrieving Certificates from response", ex);
162 throw cmpClientException;
165 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
168 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
171 private Cmpv2CertificationModel verifyReturnCertChainAndTrustStore(
172 PKIMessage respPkiMessage, CertRepMessage certRepMessage, CertResponse certResponse)
173 throws CertificateParsingException, CmpClientException, IOException {
174 LOG.info("Verifying certificates returned as part of CertResponse.");
175 final CMPCertificate cmpCertificate =
176 certResponse.getCertifiedKeyPair().getCertOrEncCert().getCertificate();
177 final Optional<X509Certificate> leafCertificate =
178 getCertFromByteArray(cmpCertificate.getEncoded(), X509Certificate.class);
179 if (leafCertificate.isPresent()) {
180 return verifyAndReturnCertChainAndTrustSTore(
181 respPkiMessage, certRepMessage, leafCertificate.get());
183 return new Cmpv2CertificationModel(Collections.emptyList(), Collections.emptyList());
186 private CertResponse getCertificateResponseContainingNewCertificate(
187 CertRepMessage certRepMessage) {
188 return certRepMessage.getResponse()[0];
192 * Validate inputs for Certificate Creation.
194 * @param csrModel Certificate Signing Request model. Must not be {@code null}.
195 * @param server CMPv2 Server. Must not be {@code null}.
196 * @throws IllegalArgumentException if Before Date is set after the After Date.
198 private static void validate(
199 final CsrModel csrModel,
200 final Cmpv2Server server,
201 final CloseableHttpClient httpClient,
202 final Date notBefore,
203 final Date notAfter) {
205 String caName = CmpUtil.isNullOrEmpty(server.getCaName()) ? server.getCaName() : DEFAULT_CA_NAME;
206 String profile = server.getCaMode() != null ? server.getCaMode().getProfile() : DEFAULT_PROFILE;
208 "Validate before creating Certificate Request for CA :{} in Mode {} ", caName, profile);
210 CmpUtil.notNull(csrModel, "CsrModel Instance");
211 CmpUtil.notNull(csrModel.getSubjectData(), "Subject DN");
212 CmpUtil.notNull(csrModel.getPrivateKey(), "Subject private key");
213 CmpUtil.notNull(csrModel.getPublicKey(), "Subject public key");
214 CmpUtil.notNull(server.getIssuerDN(), "Issuer DN");
215 CmpUtil.notNull(server.getUrl(), "External CA URL");
216 CmpUtil.notNull(server.getAuthentication().getIak(), "IAK/RV Password");
217 CmpUtil.notNull(httpClient, "Closeable Http Client");
219 if (notBefore != null && notAfter != null && notBefore.compareTo(notAfter) > 0) {
220 throw new IllegalArgumentException("Before Date is set after the After Date");
224 private Cmpv2CertificationModel retrieveCertificates(
225 CsrModel csrModel, Cmpv2Server server, PKIMessage pkiMessage, Cmpv2HttpClient cmpv2HttpClient)
226 throws CmpClientException {
227 final byte[] respBytes = cmpv2HttpClient.postRequest(pkiMessage, server.getUrl(), server.getCaName());
229 final PKIMessage respPkiMessage = PKIMessage.getInstance(respBytes);
230 LOG.info("Received response from Server");
231 checkIfCmpResponseContainsError(respPkiMessage);
232 checkCmpResponse(respPkiMessage, csrModel.getPublicKey(), server.getAuthentication().getIak());
233 return checkCmpCertRepMessage(respPkiMessage);
234 } catch (IllegalArgumentException iae) {
235 CmpClientException cmpClientException =
236 new CmpClientException(
237 "Error encountered while processing response from CA server ", iae);
238 LOG.error("Error encountered while processing response from CA server ", iae);
239 throw cmpClientException;