Remove CSRMeta class dependency
[oom/platform/cert-service.git] / certService / src / main / java / org / onap / aaf / certservice / cmpv2client / impl / CmpClientImpl.java
1 /*-
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aaf.certservice.cmpv2client.impl;
22
23 import java.security.KeyPair;
24 import java.security.PublicKey;
25
26 import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.checkIfCmpResponseContainsError;
27 import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.getCertfromByteArray;
28 import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseHelper.verifyAndReturnCertChainAndTrustSTore;
29 import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseValidationHelper.checkImplicitConfirm;
30 import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifyPasswordBasedProtection;
31 import static org.onap.aaf.certservice.cmpv2client.impl.CmpResponseValidationHelper.verifySignature;
32
33 import java.io.IOException;
34 import java.security.cert.CertificateParsingException;
35 import java.security.cert.X509Certificate;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Date;
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.Optional;
42
43 import org.apache.http.impl.client.CloseableHttpClient;
44 import org.bouncycastle.asn1.cmp.CMPCertificate;
45 import org.bouncycastle.asn1.cmp.CertRepMessage;
46 import org.bouncycastle.asn1.cmp.CertResponse;
47 import org.bouncycastle.asn1.cmp.PKIBody;
48 import org.bouncycastle.asn1.cmp.PKIHeader;
49 import org.bouncycastle.asn1.cmp.PKIMessage;
50 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
51 import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server;
52 import org.onap.aaf.certservice.certification.model.CsrModel;
53 import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException;
54 import org.onap.aaf.certservice.cmpv2client.api.CmpClient;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * Implementation of the CmpClient Interface conforming to RFC4210 (Certificate Management Protocol
60  * (CMP)) and RFC4211 (Certificate Request Message Format (CRMF)) standards.
61  */
62 public class CmpClientImpl implements CmpClient {
63
64     private static final Logger LOG = LoggerFactory.getLogger(CmpClientImpl.class);
65     private final CloseableHttpClient httpClient;
66
67     private static final String DEFAULT_PROFILE = "RA";
68     private static final String DEFAULT_CA_NAME = "Certification Authority";
69
70     public CmpClientImpl(CloseableHttpClient httpClient) {
71         this.httpClient = httpClient;
72     }
73
74     @Override
75     public List<List<X509Certificate>> createCertificate(
76             String caName,
77             String profile,
78             CsrModel csrModel,
79             Cmpv2Server server,
80             X509Certificate cert,
81             Date notBefore,
82             Date notAfter)
83             throws CmpClientException {
84
85         validate(csrModel, server, cert, caName, profile, httpClient, notBefore, notAfter);
86         KeyPair keyPair = new KeyPair(csrModel.getPublicKey(), csrModel.getPrivateKey());
87
88         final CreateCertRequest certRequest =
89                 CmpMessageBuilder.of(CreateCertRequest::new)
90                         .with(CreateCertRequest::setIssuerDn, server.getIssuerDN())
91                         .with(CreateCertRequest::setSubjectDn, csrModel.getSubjectData())
92                         .with(CreateCertRequest::setSansList, csrModel.getSans())
93                         .with(CreateCertRequest::setSubjectKeyPair, keyPair)
94                         .with(CreateCertRequest::setNotBefore, notBefore)
95                         .with(CreateCertRequest::setNotAfter, notAfter)
96                         .with(CreateCertRequest::setInitAuthPassword, server.getAuthentication().getIak())
97                         .with(CreateCertRequest::setSenderKid, server.getAuthentication().getRv())
98                         .build();
99
100         final PKIMessage pkiMessage = certRequest.generateCertReq();
101         Cmpv2HttpClient cmpv2HttpClient = new Cmpv2HttpClient(httpClient);
102         return retrieveCertificates(caName, csrModel, server, pkiMessage, cmpv2HttpClient);
103     }
104
105     @Override
106     public List<List<X509Certificate>> createCertificate(
107             String caName, String profile, CsrModel csrModel, Cmpv2Server server, X509Certificate csr)
108             throws CmpClientException {
109         return createCertificate(caName, profile, csrModel, server, csr, null, null);
110     }
111
112     private void checkCmpResponse(
113             final PKIMessage respPkiMessage, final PublicKey publicKey, final String initAuthPassword)
114             throws CmpClientException {
115         final PKIHeader header = respPkiMessage.getHeader();
116         final AlgorithmIdentifier protectionAlgo = header.getProtectionAlg();
117         verifySignatureWithPublicKey(respPkiMessage, publicKey);
118         verifyProtectionWithProtectionAlgo(respPkiMessage, initAuthPassword, header, protectionAlgo);
119     }
120
121     private void verifySignatureWithPublicKey(PKIMessage respPkiMessage, PublicKey publicKey)
122             throws CmpClientException {
123         if (Objects.nonNull(publicKey)) {
124             LOG.debug("Verifying signature of the response.");
125             verifySignature(respPkiMessage, publicKey);
126         } else {
127             LOG.error("Public Key is not available, therefore cannot verify signature");
128             throw new CmpClientException(
129                     "Public Key is not available, therefore cannot verify signature");
130         }
131     }
132
133     private void verifyProtectionWithProtectionAlgo(
134             PKIMessage respPkiMessage,
135             String initAuthPassword,
136             PKIHeader header,
137             AlgorithmIdentifier protectionAlgo)
138             throws CmpClientException {
139         if (Objects.nonNull(protectionAlgo)) {
140             LOG.debug("Verifying PasswordBased Protection of the Response.");
141             verifyPasswordBasedProtection(respPkiMessage, initAuthPassword, protectionAlgo);
142             checkImplicitConfirm(header);
143         } else {
144             LOG.error(
145                     "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm");
146             throw new CmpClientException(
147                     "Protection Algorithm is not available when expecting PBE protected response containing protection algorithm");
148         }
149     }
150
151     private List<List<X509Certificate>> checkCmpCertRepMessage(final PKIMessage respPkiMessage)
152             throws CmpClientException {
153         final PKIBody pkiBody = respPkiMessage.getBody();
154         if (Objects.nonNull(pkiBody) && pkiBody.getContent() instanceof CertRepMessage) {
155             final CertRepMessage certRepMessage = (CertRepMessage) pkiBody.getContent();
156             if (Objects.nonNull(certRepMessage)) {
157                 final CertResponse certResponse =
158                         getCertificateResponseContainingNewCertificate(certRepMessage);
159                 try {
160                     return verifyReturnCertChainAndTrustStore(respPkiMessage, certRepMessage, certResponse);
161                 } catch (IOException | CertificateParsingException ex) {
162                     CmpClientException cmpClientException =
163                             new CmpClientException(
164                                     "Exception occurred while retrieving Certificates from response", ex);
165                     LOG.error("Exception occurred while retrieving Certificates from response", ex);
166                     throw cmpClientException;
167                 }
168             } else {
169                 return new ArrayList<>(Collections.emptyList());
170             }
171         }
172         return new ArrayList<>(Collections.emptyList());
173     }
174
175     private List<List<X509Certificate>> verifyReturnCertChainAndTrustStore(
176             PKIMessage respPkiMessage, CertRepMessage certRepMessage, CertResponse certResponse)
177             throws CertificateParsingException, CmpClientException, IOException {
178         LOG.info("Verifying certificates returned as part of CertResponse.");
179         final CMPCertificate cmpCertificate =
180                 certResponse.getCertifiedKeyPair().getCertOrEncCert().getCertificate();
181         final Optional<X509Certificate> leafCertificate =
182                 getCertfromByteArray(cmpCertificate.getEncoded(), X509Certificate.class);
183         if (leafCertificate.isPresent()) {
184             return verifyAndReturnCertChainAndTrustSTore(
185                     respPkiMessage, certRepMessage, leafCertificate.get());
186         }
187         return Collections.emptyList();
188     }
189
190     private CertResponse getCertificateResponseContainingNewCertificate(
191             CertRepMessage certRepMessage) {
192         return certRepMessage.getResponse()[0];
193     }
194
195     /**
196      * Validate inputs for Certificate Creation.
197      *
198      * @param csrModel        Certificate Signing Request model. Must not be {@code null}.
199      * @param server          CMPv2 Server. Must not be {@code null}.
200      * @param cert            Certificate object needed to validate response from CA server.
201      * @param incomingCaName  Date specifying certificate is not valid before this date.
202      * @param incomingProfile Date specifying certificate is not valid after this date.
203      * @throws IllegalArgumentException if Before Date is set after the After Date.
204      */
205     private static void validate(
206             final CsrModel csrModel,
207             final Cmpv2Server server,
208             final X509Certificate cert,
209             final String incomingCaName,
210             final String incomingProfile,
211             final CloseableHttpClient httpClient,
212             final Date notBefore,
213             final Date notAfter) {
214
215         String caName = CmpUtil.isNullOrEmpty(incomingCaName) ? incomingCaName : DEFAULT_CA_NAME;
216         String caProfile = CmpUtil.isNullOrEmpty(incomingProfile) ? incomingProfile : DEFAULT_PROFILE;
217         LOG.info(
218                 "Validate before creating Certificate Request for CA :{} in Mode {} ", caName, caProfile);
219
220         CmpUtil.notNull(csrModel, "CsrModel Instance");
221         CmpUtil.notNull(csrModel.getSubjectData(), "Subject DN");
222         CmpUtil.notNull(csrModel.getPrivateKey(), "Subject private key");
223         CmpUtil.notNull(csrModel.getPublicKey(), "Subject public key");
224         CmpUtil.notNull(server.getIssuerDN(), "Issuer DN");
225         CmpUtil.notNull(server.getUrl(), "External CA URL");
226         CmpUtil.notNull(server.getAuthentication().getIak(), "IAK/RV Password");
227         CmpUtil.notNull(cert, "Certificate Signing Request (CSR)");
228         CmpUtil.notNull(httpClient, "Closeable Http Client");
229
230         if (notBefore != null && notAfter != null && notBefore.compareTo(notAfter) > 0) {
231             throw new IllegalArgumentException("Before Date is set after the After Date");
232         }
233     }
234
235     private List<List<X509Certificate>> retrieveCertificates(
236             String caName, CsrModel csrModel, Cmpv2Server server, PKIMessage pkiMessage, Cmpv2HttpClient cmpv2HttpClient)
237             throws CmpClientException {
238         final byte[] respBytes = cmpv2HttpClient.postRequest(pkiMessage, server.getUrl(), caName);
239         try {
240             final PKIMessage respPkiMessage = PKIMessage.getInstance(respBytes);
241             LOG.info("Received response from Server");
242             checkIfCmpResponseContainsError(respPkiMessage);
243             checkCmpResponse(respPkiMessage, csrModel.getPublicKey(), server.getAuthentication().getIak());
244             return checkCmpCertRepMessage(respPkiMessage);
245         } catch (IllegalArgumentException iae) {
246             CmpClientException cmpClientException =
247                     new CmpClientException(
248                             "Error encountered while processing response from CA server ", iae);
249             LOG.error("Error encountered while processing response from CA server ", iae);
250             throw cmpClientException;
251         }
252     }
253 }