1 /*============LICENSE_START=======================================================
2 * aaf-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
10 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.aaf.certservice.client.certification;
22 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
23 import org.bouncycastle.asn1.x509.Extension;
24 import org.bouncycastle.asn1.x509.Extensions;
25 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
26 import org.bouncycastle.asn1.x509.GeneralName;
27 import org.bouncycastle.asn1.x509.GeneralNames;
28 import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
29 import org.bouncycastle.operator.ContentSigner;
30 import org.bouncycastle.operator.OperatorCreationException;
31 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
32 import org.bouncycastle.pkcs.PKCS10CertificationRequest;
33 import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
35 import org.onap.aaf.certservice.client.certification.exception.CsrGenerationException;
36 import org.onap.aaf.certservice.client.configuration.model.CsrConfiguration;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import javax.security.auth.x500.X500Principal;
41 import java.io.IOException;
42 import java.io.StringWriter;
43 import java.nio.charset.StandardCharsets;
44 import java.security.KeyPair;
45 import java.util.Base64;
46 import java.util.Optional;
48 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.COMMON_NAME;
49 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.COUNTRY;
50 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.LOCATION;
51 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.ORGANIZATION;
52 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.ORGANIZATION_UNIT;
53 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.SIGN_ALGORITHM;
54 import static org.onap.aaf.certservice.client.certification.EncryptionAlgorithmConstants.STATE;
57 public class CsrFactory {
59 private final Logger LOGGER = LoggerFactory.getLogger(CsrFactory.class);
60 private static final String SANS_DELIMITER = ":";
61 private final CsrConfiguration configuration;
64 public CsrFactory(CsrConfiguration configuration) {
65 this.configuration = configuration;
69 public String createCsrInPem(KeyPair keyPair) throws CsrGenerationException {
70 PKCS10CertificationRequest request;
71 String csrParameters = getMandatoryParameters().append(getOptionalParameters()).toString();
72 X500Principal subject = new X500Principal(csrParameters);
73 request = createPKCS10Csr(subject, keyPair);
74 return convertPKC10CsrToPem(request);
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()));
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();
97 private PKCS10CertificationRequest createPKCS10Csr(X500Principal subject, KeyPair keyPair) throws CsrGenerationException {
98 JcaPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic());
100 if (isParameterPresent(configuration.getSans())) {
101 builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, generateSansExtension());
104 return builder.build(getContentSigner(keyPair));
107 private ContentSigner getContentSigner(KeyPair keyPair) throws CsrGenerationException {
108 ContentSigner contentSigner;
110 contentSigner = new JcaContentSignerBuilder(SIGN_ALGORITHM).build(keyPair.getPrivate());
111 } catch (OperatorCreationException e) {
112 LOGGER.error("Creation of PKCS10Csr failed, exception message: {}", e.getMessage());
113 throw new CsrGenerationException(e);
116 return contentSigner;
119 private String convertPKC10CsrToPem(PKCS10CertificationRequest request) throws CsrGenerationException {
120 final StringWriter stringWriter = new StringWriter();
121 try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
122 pemWriter.writeObject(request);
123 } catch (IOException e) {
124 LOGGER.error("Conversion to PEM failed, exception message: {}", e.getMessage());
125 throw new CsrGenerationException(e);
127 return stringWriter.toString();
130 private Extensions generateSansExtension() throws CsrGenerationException {
131 ExtensionsGenerator generator = new ExtensionsGenerator();
133 generator.addExtension(Extension.subjectAlternativeName, false, createGeneralNames());
134 } catch (IOException e) {
135 LOGGER.error("Generation of SANs parameter failed, exception message: {}", e.getMessage());
136 throw new CsrGenerationException(e);
138 return generator.generate();
141 private GeneralNames createGeneralNames() {
142 String[] sansTable = this.configuration.getSans().split(SANS_DELIMITER);
143 int length = sansTable.length;
144 GeneralName[] generalNames = new GeneralName[length];
145 for (int i = 0; i < length; i++) {
146 generalNames[i] = new GeneralName(GeneralName.dNSName, sansTable[i]);
148 return new GeneralNames(generalNames);
151 private static Boolean isParameterPresent(String parameter) {
152 return parameter != null && !"".equals(parameter);