2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2020 Nordix Foundation.
4 * Copyright (C) 2021 Nokia.
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.oom.certservice.cmpv2client.impl;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.security.InvalidKeyException;
27 import java.security.KeyPair;
28 import java.security.NoSuchAlgorithmException;
29 import java.security.NoSuchProviderException;
30 import java.security.Signature;
31 import java.security.SignatureException;
32 import java.util.Date;
34 import org.bouncycastle.asn1.ASN1EncodableVector;
35 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
36 import org.bouncycastle.asn1.DERBitString;
37 import org.bouncycastle.asn1.DEROutputStream;
38 import org.bouncycastle.asn1.DERSequence;
39 import org.bouncycastle.asn1.DERTaggedObject;
40 import org.bouncycastle.asn1.crmf.CertRequest;
41 import org.bouncycastle.asn1.crmf.OptionalValidity;
42 import org.bouncycastle.asn1.crmf.POPOSigningKey;
43 import org.bouncycastle.asn1.crmf.ProofOfPossession;
44 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
45 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
46 import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
47 import org.bouncycastle.asn1.x509.Extension;
48 import org.bouncycastle.asn1.x509.Extensions;
49 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
50 import org.bouncycastle.asn1.x509.GeneralName;
51 import org.bouncycastle.asn1.x509.GeneralNames;
52 import org.bouncycastle.asn1.x509.KeyPurposeId;
53 import org.bouncycastle.asn1.x509.KeyUsage;
54 import org.bouncycastle.asn1.x509.Time;
55 import org.bouncycastle.jce.provider.BouncyCastleProvider;
56 import org.onap.oom.certservice.cmpv2client.exceptions.CmpClientException;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 public final class CmpMessageHelper {
62 private static final Logger LOG = LoggerFactory.getLogger(CmpMessageHelper.class);
63 private static final boolean CRITICAL_FALSE = false;
65 private CmpMessageHelper() {
69 * Creates an Optional Validity, which is used to specify how long the returned cert should be
72 * @param notBefore Date specifying certificate is not valid before this date.
73 * @param notAfter Date specifying certificate is not valid after this date.
74 * @return {@link OptionalValidity} that can be set for certificate on external CA.
76 public static OptionalValidity generateOptionalValidity(
77 final Date notBefore, final Date notAfter) {
78 LOG.debug("Generating Optional Validity from Date objects");
79 ASN1EncodableVector optionalValidityV = new ASN1EncodableVector();
80 if (notBefore != null) {
81 Time nb = new Time(notBefore);
82 optionalValidityV.add(new DERTaggedObject(true, 0, nb));
84 if (notAfter != null) {
85 Time na = new Time(notAfter);
86 optionalValidityV.add(new DERTaggedObject(true, 1, na));
88 return OptionalValidity.getInstance(new DERSequence(optionalValidityV));
92 * Create Extensions from Subject Alternative Names.
94 * @return {@link Extensions}.
96 public static Extensions generateExtension(final GeneralName[] sansArray)
97 throws CmpClientException {
98 LOG.debug("Generating Extensions from Subject Alternative Names");
99 final ExtensionsGenerator extGenerator = new ExtensionsGenerator();
101 extGenerator.addExtension(Extension.keyUsage, CRITICAL_FALSE, getKeyUsage());
102 extGenerator.addExtension(Extension.extendedKeyUsage, CRITICAL_FALSE, getExtendedKeyUsage());
103 extGenerator.addExtension(
104 Extension.subjectAlternativeName, CRITICAL_FALSE, new GeneralNames(sansArray));
105 } catch (IOException ioe) {
106 CmpClientException cmpClientException =
107 new CmpClientException(
108 "Exception occurred while creating extensions for PKIMessage", ioe);
109 LOG.error("Exception occurred while creating extensions for PKIMessage");
110 throw cmpClientException;
112 return extGenerator.generate();
116 * Method generates Proof-of-Possession (POP) of Private Key. To allow a CA/RA to properly
117 * validity binding between an End Entity and a Key Pair, the PKI Operations specified here make
118 * it possible for an End Entity to prove that it has possession of the Private Key corresponding
119 * to the Public Key for which a Certificate is requested.
121 * @param certRequest Certificate request that requires proof of possession
122 * @param keypair keypair associated with the subject sending the certificate request
123 * @return {@link ProofOfPossession}.
124 * @throws CmpClientException A general-purpose Cmp client exception.
126 public static ProofOfPossession generateProofOfPossession(
127 final CertRequest certRequest, final KeyPair keypair) throws CmpClientException {
128 ProofOfPossession proofOfPossession;
129 try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
130 final DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
131 derOutputStream.writeObject(certRequest);
133 byte[] popoProtectionBytes = byteArrayOutputStream.toByteArray();
134 final String sigalg = PKCSObjectIdentifiers.sha256WithRSAEncryption.getId();
135 final Signature signature = Signature.getInstance(sigalg, BouncyCastleProvider.PROVIDER_NAME);
136 signature.initSign(keypair.getPrivate());
137 signature.update(popoProtectionBytes);
138 DERBitString bs = new DERBitString(signature.sign());
141 new ProofOfPossession(
143 null, new AlgorithmIdentifier(new ASN1ObjectIdentifier(sigalg)), bs));
145 | NoSuchProviderException
146 | NoSuchAlgorithmException
147 | InvalidKeyException
148 | SignatureException ex) {
149 CmpClientException cmpClientException =
150 new CmpClientException(
151 "Exception occurred while creating proof of possession for PKIMessage", ex);
152 LOG.error("Exception occurred while creating proof of possession for PKIMessage");
153 throw cmpClientException;
155 return proofOfPossession;
158 private static KeyUsage getKeyUsage() {
160 KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.nonRepudiation);
163 private static ExtendedKeyUsage getExtendedKeyUsage() {
164 return new ExtendedKeyUsage(
165 new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth});