diff options
Diffstat (limited to 'utils-test/src/main/java/org/onap/policy/common/utils/security')
-rw-r--r-- | utils-test/src/main/java/org/onap/policy/common/utils/security/SelfSignedKeyStore.java | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/security/SelfSignedKeyStore.java b/utils-test/src/main/java/org/onap/policy/common/utils/security/SelfSignedKeyStore.java new file mode 100644 index 00000000..0787872c --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/security/SelfSignedKeyStore.java @@ -0,0 +1,161 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2021 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.security; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import lombok.Getter; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.onap.policy.common.utils.resources.ResourceUtils; + +/** + * Keystore, containing a self-signed certificate, valid for one day (see the argument to + * the "-valid" flag below). For use in junit tests. + */ +@Getter +public class SelfSignedKeyStore { + public static final String KEYSTORE_PASSWORD = "Pol1cy_0nap"; + public static final String PRIVATE_KEY_PASSWORD = KEYSTORE_PASSWORD; + public static final String RELATIVE_PATH = "target/test-classes/policy-keystore"; + + /** + * File containing subject-alternative names (i.e., list of servers that may use this + * keystore). + */ + private static final String KEYSTORE_SAN = "keystore_san.txt"; + + private final String keystoreName; + + + /** + * Generates the keystore, if it does not exist or if it's more than a few hours old. + * + * @throws IOException if an I/O error occurs + * @throws InterruptedException if an interrupt occurs + */ + public SelfSignedKeyStore() throws IOException, InterruptedException { + this(RELATIVE_PATH); + } + + /** + * Generates the keystore, if it does not exist or if it's more than a few hours old. + * + * @param relativePath path to the keystore, relative to the "user.dir" system + * property + * @throws IOException if an I/O error occurs + * @throws InterruptedException if an interrupt occurs + */ + public SelfSignedKeyStore(String relativePath) throws IOException, InterruptedException { + keystoreName = System.getProperty("user.dir") + "/" + relativePath; + + // use existing file if it isn't too old + var keystoreFile = new File(keystoreName); + if (keystoreFile.exists()) { + if (System.currentTimeMillis() < keystoreFile.lastModified() + + TimeUnit.MILLISECONDS.convert(5, TimeUnit.HOURS)) { + return; + } + + Files.delete(keystoreFile.toPath()); + } + + /* + * Read the list of subject-alternative names, joining the lines with commas, and + * dropping the trailing comma. + */ + String sanFileName = getKeystoreSanName(); + var sanString = ResourceUtils.getResourceAsString(sanFileName); + if (sanString == null) { + throw new FileNotFoundException(sanFileName); + } + + var sanArray = sanString.replace("DNS:", "").replace("\r", "").split("\n"); + GeneralName[] nameArray = Arrays.stream(sanArray).map(name -> new GeneralName(GeneralName.dNSName, name)) + .toList().toArray(new GeneralName[0]); + final var names = new GeneralNames(nameArray); + + try (var ostr = new FileOutputStream(keystoreFile)) { + var keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + final var keyPair = keyPairGenerator.generateKeyPair(); + + final long tcur = System.currentTimeMillis(); + + final var dn = new X500Name("C=US, O=ONAP, OU=OSAAF, OU=policy@policy.onap.org:DEV, CN=policy"); + final var serial = BigInteger.valueOf(new SecureRandom().nextInt()); + final var notBefore = new Date(tcur); + final var notAfter = new Date(tcur + TimeUnit.MILLISECONDS.convert(365, TimeUnit.DAYS)); + final var pubKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); + + ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA") + .setProvider(new BouncyCastleFipsProvider()).build(keyPair.getPrivate()); + + X509CertificateHolder holder = new X509v3CertificateBuilder(dn, serial, notBefore, notAfter, dn, pubKeyInfo) + .addExtension(Extension.subjectAlternativeName, false, names).build(signer); + + var cert = new JcaX509CertificateConverter().setProvider(new BouncyCastleFipsProvider()) + .getCertificate(holder); + final Certificate[] chain = {cert}; + + var keystore = KeyStore.getInstance("PKCS12"); + keystore.load(null, null); + keystore.setKeyEntry("policy@policy.onap.org", keyPair.getPrivate(), PRIVATE_KEY_PASSWORD.toCharArray(), + chain); + + keystore.store(ostr, KEYSTORE_PASSWORD.toCharArray()); + + } catch (NoSuchAlgorithmException | OperatorCreationException | CertificateException | KeyStoreException e) { + throw new IOException("cannot create certificate", e); + } + } + + // may be overridden by junit tests + + protected String getKeystoreSanName() { + return KEYSTORE_SAN; + } +} |