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