All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.nagyesta.filebarj.job.util.KeyStoreUtil Maven / Gradle / Ivy

package com.github.nagyesta.filebarj.job.util;


import com.github.nagyesta.filebarj.core.backup.ArchivalException;
import lombok.experimental.UtilityClass;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.jetbrains.annotations.NotNull;

import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.Date;

/**
 * Utility class for key store operations.
 */
@UtilityClass
public final class KeyStoreUtil {

    private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
    private static final SecureRandom RANDOM = new SecureRandom();
    private static final int NUMBER_OF_BITS_SERIAL = 160;
    private static final long ONE_HUNDRED_YEARS_IN_MILLIS = 3153600000000L;
    private static final String PKCS_12 = "PKCS12";

    /**
     * Opens a PKCS12 key store and returns the private key.
     *
     * @param source    the path of the key store
     * @param alias     the alias of the key
     * @param storePass the password protecting the key store
     * @param keyPass   the password protecting the key
     * @return the private key
     */
    public static PrivateKey readPrivateKey(
            @NotNull final Path source,
            @NotNull final String alias,
            final char @NotNull [] storePass,
            final char @NotNull [] keyPass) {
        try {
            return (PrivateKey) KeyStore.getInstance(source.toFile(), storePass)
                    .getKey(alias, keyPass);
        } catch (final Exception e) {
            throw new ArchivalException("Failed to read private key from key store: " + source, e);
        }
    }

    /**
     * Opens a PKCS12 key store and returns the public key.
     *
     * @param source    the path of the key store
     * @param alias     the alias of the key
     * @param storePass the password protecting the key store
     * @return the public key
     */
    public static PublicKey readPublicKey(
            @NotNull final Path source,
            @NotNull final String alias,
            final char @NotNull [] storePass) {
        try {
            return KeyStore.getInstance(source.toFile(), storePass)
                    .getCertificateChain(alias)[0].getPublicKey();
        } catch (final Exception e) {
            throw new ArchivalException("Failed to read public key from key store: " + source, e);
        }
    }

    /**
     * Creates and writes a PKCS12 key store and stores the key pair in it.
     *
     * @param target    the path of the key store
     * @param alias     the alias of the key
     * @param keyPair   the key pair
     * @param storePass the password protecting the key store
     * @param keyPass   the password protecting the key
     */
    public static void writeKey(
            @NotNull final Path target,
            @NotNull final String alias,
            @NotNull final KeyPair keyPair,
            final char @NotNull [] storePass,
            final char @NotNull [] keyPass) {
        try (var stream = Files.newOutputStream(target)) {
            final var keyStore = newKeyStore(storePass);
            final var chain = createChainFor(keyPair);
            keyStore.setKeyEntry(alias, keyPair.getPrivate(), keyPass, chain);
            keyStore.store(stream, storePass);
        } catch (final Exception e) {
            throw new ArchivalException("Failed to write key to key store: " + target, e);
        }
    }

    @NotNull
    private static Certificate[] createChainFor(
            @NotNull final KeyPair keyPair) throws Exception {
        final var subject = new X500Name("CN=Ignore");
        final var now = new Date(Instant.now().toEpochMilli());
        final var future = new Date(Instant.now().toEpochMilli() + ONE_HUNDRED_YEARS_IN_MILLIS);
        final var serial = new BigInteger(NUMBER_OF_BITS_SERIAL, RANDOM);
        final var signer = new JcaContentSignerBuilder("SHA256withRSA")
                .setProvider(BOUNCY_CASTLE_PROVIDER)
                .build(keyPair.getPrivate());
        final var publicKey = keyPair.getPublic();
        final var holder = new JcaX509v3CertificateBuilder(subject, serial, now, future, subject, publicKey)
                .build(signer);
        final var converter = new JcaX509CertificateConverter();
        converter.setProvider(BOUNCY_CASTLE_PROVIDER);
        return new X509Certificate[]{converter.getCertificate(holder)};
    }

    @NotNull
    private static KeyStore newKeyStore(
            final char @NotNull [] password) throws Exception {
        final var store = KeyStore.getInstance(PKCS_12);
        store.load(null, password);
        return store;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy