[OOM CERT-SERVICE-CLIENT] Fix null pointer when sans empty
[oom/platform/cert-service.git] / certServiceClient / src / main / java / org / onap / oom / certservice / client / certification / CsrFactory.java
1 /*============LICENSE_START=======================================================
2  * oom-certservice-client
3  * ================================================================================
4  * Copyright (C) 2020 Nokia. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  * ============LICENSE_END=========================================================
18  */
19
20 package org.onap.oom.certservice.client.certification;
21
22 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.COMMON_NAME;
23 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.COUNTRY;
24 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.LOCATION;
25 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.ORGANIZATION;
26 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.ORGANIZATION_UNIT;
27 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.SIGN_ALGORITHM;
28 import static org.onap.oom.certservice.client.certification.EncryptionAlgorithmConstants.STATE;
29
30 import java.io.IOException;
31 import java.io.StringWriter;
32 import java.security.KeyPair;
33 import java.util.List;
34 import java.util.Optional;
35 import java.util.stream.Collectors;
36 import javax.security.auth.x500.X500Principal;
37 import org.apache.commons.collections.CollectionUtils;
38 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
39 import org.bouncycastle.asn1.x509.Extension;
40 import org.bouncycastle.asn1.x509.Extensions;
41 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
42 import org.bouncycastle.asn1.x509.GeneralName;
43 import org.bouncycastle.asn1.x509.GeneralNames;
44 import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
45 import org.bouncycastle.operator.ContentSigner;
46 import org.bouncycastle.operator.OperatorCreationException;
47 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
48 import org.bouncycastle.pkcs.PKCS10CertificationRequest;
49 import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
50 import org.onap.oom.certservice.client.certification.exception.CsrGenerationException;
51 import org.onap.oom.certservice.client.configuration.model.CsrConfiguration;
52 import org.onap.oom.certservice.client.configuration.model.San;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56
57 public class CsrFactory {
58
59     private static final Logger LOGGER = LoggerFactory.getLogger(CsrFactory.class);
60     private final CsrConfiguration configuration;
61
62
63     public CsrFactory(CsrConfiguration configuration) {
64         this.configuration = configuration;
65     }
66
67
68     public String createCsrInPem(KeyPair keyPair) throws CsrGenerationException {
69         LOGGER.info("Creation of CSR has been started with following parameters: {}", configuration.toString());
70         String csrParameters = getMandatoryParameters().append(getOptionalParameters()).toString();
71         X500Principal subject = new X500Principal(csrParameters);
72         PKCS10CertificationRequest request = createPkcs10Csr(subject, keyPair);
73
74         LOGGER.info("Creation of CSR has been completed successfully");
75         return convertPkcs10CsrToPem(request);
76     }
77
78     private StringBuilder getMandatoryParameters() {
79         return new StringBuilder(String.format("%s=%s, %s=%s, %s=%s, %s=%s",
80             COMMON_NAME, configuration.getCommonName(),
81             COUNTRY, configuration.getCountry(),
82             STATE, configuration.getState(),
83             ORGANIZATION, configuration.getOrganization()));
84     }
85
86     private String getOptionalParameters() {
87         StringBuilder optionalParameters = new StringBuilder();
88         Optional.ofNullable(configuration.getOrganizationUnit())
89             .filter(CsrFactory::isParameterPresent)
90             .map(unit -> optionalParameters.append(String.format(", %s=%s", ORGANIZATION_UNIT, unit)));
91         Optional.ofNullable(configuration.getLocation())
92             .filter(CsrFactory::isParameterPresent)
93             .map(location -> optionalParameters.append(String.format(", %s=%s", LOCATION, location)));
94         return optionalParameters.toString();
95     }
96
97     private PKCS10CertificationRequest createPkcs10Csr(X500Principal subject, KeyPair keyPair)
98         throws CsrGenerationException {
99         JcaPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject,
100             keyPair.getPublic());
101
102         if (!CollectionUtils.isEmpty(configuration.getSans())) {
103             builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, generateSansExtension());
104         }
105
106         return builder.build(getContentSigner(keyPair));
107     }
108
109     private ContentSigner getContentSigner(KeyPair keyPair) throws CsrGenerationException {
110         ContentSigner contentSigner;
111         try {
112             contentSigner = new JcaContentSignerBuilder(SIGN_ALGORITHM).build(keyPair.getPrivate());
113         } catch (OperatorCreationException e) {
114             LOGGER.error("Creation of PKCS10Csr failed, exception message: {}", e.getMessage());
115             throw new CsrGenerationException(e);
116
117         }
118         return contentSigner;
119     }
120
121     private String convertPkcs10CsrToPem(PKCS10CertificationRequest request) throws CsrGenerationException {
122         final StringWriter stringWriter = new StringWriter();
123         try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
124             LOGGER.info("Conversion of CSR to PEM has been started");
125             pemWriter.writeObject(request);
126         } catch (IOException e) {
127             LOGGER.error("Conversion to PEM failed, exception message: {}", e.getMessage());
128             throw new CsrGenerationException(e);
129         }
130         return stringWriter.toString();
131     }
132
133     private Extensions generateSansExtension() throws CsrGenerationException {
134         ExtensionsGenerator generator = new ExtensionsGenerator();
135         try {
136             generator.addExtension(Extension.subjectAlternativeName, false, createGeneralNames());
137         } catch (IOException e) {
138             LOGGER.error("Generation of SANs parameter failed, exception message: {}", e.getMessage());
139             throw new CsrGenerationException(e);
140         }
141         return generator.generate();
142     }
143
144     private GeneralNames createGeneralNames() {
145         List<San> sans = this.configuration.getSans();
146         GeneralName[] generalNames = sans.stream()
147             .map(san -> new GeneralName(san.getType(), san.getValue()))
148             .collect(Collectors.toList())
149             .toArray(GeneralName[]::new);
150         return new GeneralNames(generalNames);
151     }
152
153     private static Boolean isParameterPresent(String parameter) {
154         return parameter != null && !"".equals(parameter);
155     }
156 }