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

io.r2.simplepemkeystore.spi.PemCertKey Maven / Gradle / Ivy

There is a newer version: 0.3
Show newest version
package io.r2.simplepemkeystore.spi;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * A certificate chain and private key read from PEM
 * Note: intentionally defined as package access only
 */
class PemCertKey {

    public static String META_KEY_ALIAS = "alias";
    public static String META_KEY_CREATIONDATE = "creationdate";

    protected String alias;
    protected Date creationDate;
    protected Map metaData;
    protected Key privateKey;
    protected List certificateChain;
    protected Certificate[] certificateChainPacked;

    /**
     * Create an empty object, for adding fields later
     */
    public PemCertKey() {
        alias = "server"; // default alias
        creationDate = new Date();
        privateKey = null;
        certificateChain = new ArrayList<>();
        metaData = new HashMap<>();
    }

    /**
     * Finish construction of this object
     * @return object for chaining
     */
    public PemCertKey build() {
        // put to packed structure
        certificateChainPacked = certificateChain.toArray(new Certificate[0]);
        return this;
    }

    /**
     * Sets metadata and parses alias/creationDate, if available
     *
     * @param metaData input unparsed metadata
     * @throws CertificateException if creationDate exists but invalid format
     */
    public void setMetaData(Map metaData) throws CertificateException {
        this.metaData = metaData;
        if (metaData.containsKey(META_KEY_ALIAS)) {
            alias = metaData.get(META_KEY_ALIAS);
        }
        if (metaData.containsKey(META_KEY_CREATIONDATE)) {
            Instant t;
            try {
                t = Instant.parse(metaData.get(META_KEY_CREATIONDATE));
            }
            catch (DateTimeParseException e) {
                throw new CertificateException(e);
            }
            creationDate = Date.from(t);
        }
    }

    /**
     * Method used during parsing : sets the private key in this entry
     *
     * @param key the chunk containing certificate
     * @throws CertificateException if key already exists
     */
    public void setPrivateKey(List key) throws CertificateException, NoSuchAlgorithmException {
        if (privateKey != null) throw new CertificateException("More than one private key in PEM input");

        String b64key = String.join("", key.subList(1, key.size() - 1));
        byte[] binKey = Base64.getDecoder().decode(b64key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(binKey);

        KeyFactory kf = KeyFactory.getInstance("RSA");
        try {
            privateKey = kf.generatePrivate(keySpec);
        }
        catch (InvalidKeySpecException e) {
            throw new CertificateException(e);
        }
    }

    /**
     * Add a new certificate to the chain
     * @param chunk the chunk containing certificate
     */
    public void addCertificate(List chunk) throws CertificateException {
        InputStream is = new ByteArrayInputStream(
                String.join("\n", chunk).getBytes(StandardCharsets.UTF_8)
        );
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        certificateChain.add(cf.generateCertificate(is));
    }

    /**
     * Gets the alias of this certificate
     * @return alias of certificate
     */
    public String getAlias() {
        return alias;
    }

    /**
     * Change alias of certificate
     * @param alias new alias
     */
    public void setAlias(String alias) {
        this.alias = alias;
        metaData.put(META_KEY_ALIAS, alias);
    }

    /**
     * Creation date is unknown in this store, so return object creation date
     * @return creation date
     */
    public Date getCreationDate() {
        return creationDate;
    }

    /**
     * Change creation date of certificate
     * @param creationDate new alias
     */
    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
        metaData.put(META_KEY_CREATIONDATE, creationDate.toInstant().toString());
    }

    /**
     * Returns the optional metadata fields in this certificate (including alias and creationDate)
     * @return map of metadata keys and values
     */
    public Map getMetaData() {
        return metaData;
    }

    /**
     * Gets the private key - private keys are not password protected
     *
     * @return the private key
     * @throws UnrecoverableKeyException if password is incorrect
     */
    public Key getPrivateKey() throws UnrecoverableKeyException {
        return privateKey;
    }

    /**
     * @return certificate chain
     */
    public Certificate[] getCertificateChain() {
        return certificateChainPacked;
    }

    /**
     * @return the certificate or null if not found in input
     */
    public Certificate getCertificate() {
        return certificateChainPacked.length > 0 ? certificateChainPacked[0] : null;
    }

    /**
     * @return true if input has a key
     */
    public boolean hasKey() {
        return privateKey != null;
    }

    /**
     * @return true if input has a certificate
     */
    public boolean hasCertificate() {
        return certificateChainPacked.length > 0;
    }

    /**
     * @return true if parameter certificate matches this one
     */
    public boolean matchesCertificate(Certificate other) {
        if (certificateChainPacked.length == 0) return false;
        return certificateChainPacked[0].equals(other);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy