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

org.cesecore.certificates.certificatetransparency.CTLogInfo Maven / Gradle / Ivy

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/
package org.cesecore.certificates.certificatetransparency;

import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.util.Random;

import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.cesecore.keys.util.KeyTools;

/**
 * Represents a Certificate Transparency log
 *
 * @version $Id: CTLogInfo.java 27471 2017-12-07 15:13:58Z bastianf $
 */
public final class CTLogInfo implements Serializable {

    private static final Logger log = Logger.getLogger(CTLogInfo.class);
    private static final long serialVersionUID = 1L;

    private final int logId;
    private byte[] publicKeyBytes;
    private String url; // base URL, without "add-chain" or "add-pre-chain"
    private int timeout = 5000; // milliseconds
    private String label;
    @Deprecated
    private boolean isMandatory;
    private Integer expirationYearRequired;

    private transient PublicKey publicKey;

    private static final Random random = new Random();

    /**
     * Creates a CT log info object, but does not parse the public key yet
     * (so it can be created from static blocks etc.)
     *
     * @param url  Base URL to the log. The CT log library will automatically append
     *        the strings "add-chain" or "add-pre-chain" depending on whether
     *        EJBCA is submitting a pre-certificate or a regular certificate.
     * @param publicKeyBytes  The ASN1 encoded public key of the log.
     * @param label to place CT log under.
     * @param timeout of SCT response in ms.
     */
    public CTLogInfo(final String url, final byte[] publicKeyBytes, final String label, final int timeout) {
        if (!url.endsWith("/")) {
            log.error("CT Log URL must end with a slash. URL: "+url); // EJBCA 6.4 didn't enforce this due to a regression
        }
        if (!url.endsWith("/ct/v1/")) {
            log.warn("CT Log URL should end with /ct/v1/. URL: "+url);
        }
        this.logId = random.nextInt();
        this.url = url;
        if (publicKeyBytes == null) {
            throw new IllegalArgumentException("publicKeyBytes is null");
        }
        this.publicKeyBytes = publicKeyBytes.clone();
        if (label != null && label.isEmpty()) {
            this.label = "Unlabeled";
        } else {
            this.label = label;
        }
        this.timeout = timeout;
    }

    private void ensureParsed() {
        if (publicKey == null) {
            publicKey = KeyTools.getPublicKeyFromBytes(publicKeyBytes);
            if (publicKey == null) {
                throw new IllegalStateException("Failed to parse key");
            }
        }
    }

    /** @return Internal Id consisting of the hashcode of the URL */
    public int getLogId() {
        return logId;
    }

    public PublicKey getLogPublicKey() {
        ensureParsed();
        return publicKey;
    }

    public byte[] getPublicKeyBytes() {
        return publicKeyBytes;
    }

    public void setLogPublicKey(final byte[] publicKeyBytes) {
        this.publicKey = null;
        this.publicKeyBytes = publicKeyBytes;
    }

    /** @return Log Key ID as specified by the RFC, in human-readable format */
    public String getLogKeyIdString() {
        try {
            ensureParsed();
            final MessageDigest md = MessageDigest.getInstance("SHA256");
            final byte[] keyId = md.digest(publicKey.getEncoded());
            return Base64.toBase64String(keyId);
        } catch (NoSuchAlgorithmException e) {
            // Should not happen, but not critical.
            return "";
        } catch (Exception e) {
            return e.getLocalizedMessage();
        }
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(final String url) {
        this.url = url;
    }

    public int getTimeout() {
        return timeout;
    }

    /**
     * Determine whether this certificate transparency log belongs to the group of certificate
     * transparency logs to which it is mandatory to publish.
     * @return true if this is a mandatory log, or false otherwise
     */
    public boolean isMandatory() {
        return isMandatory;
    }

    /** Sets the timeout in milliseconds when sending a request to the log server */
    public void setTimeout(final int timeout) {
        if (timeout < 0) {
            throw new IllegalArgumentException("Timeout value is negative");
        }
        this.timeout = timeout;
    }

    /** Makes sure that a URL ends with /ct/v1/ */
    public static String fixUrl(final String urlToFix) {
        String url = (urlToFix.endsWith("/") ? urlToFix : urlToFix + "/");
        if (!url.endsWith("/ct/v1/")) {
            if (!url.endsWith("/ct/")) {
                url = url + "ct/v1/";
            } else {
                url = url + "v1/";
            }
        }
        return url;
    }

    public String getLabel() {
        return label == null ? "Unlabeled" : label;
    }

    public void setLabel(final String label) {
        this.label = label;
    }

    /**
     * Returns the expiration year which certificates published to this CT log must
     * have in order to be accepted, or null if there is no such requirement. For
     * example, if this method returns "2019" then you should only try to publish
     * certificates to this CT log expiring in 2019, since all other certificates
     * will be rejected.
     * @return the expiration year required for all certificates being published to
     * this log or null if there is no such requirement
     */
    public Integer getExpirationYearRequired() {
        return expirationYearRequired;
    }

    /**
     * Returns the expiration year which certificates published to this CT log must
     * have in order to be accepted, or null if there is no such requirement. See
     * {@link #getExpirationYearRequired()}.
     * @param expirationYearRequired the expiration year required for new certificates
     * being published to this CT log, or null if no such requirement
     */
    public void setExpirationYearRequired(final Integer expirationYearRequired) {
        this.expirationYearRequired = expirationYearRequired;
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || o.getClass() != CTLogInfo.class) {
            return false;
        }

        final CTLogInfo ctLogInfo = (CTLogInfo) o;
        return logId == ctLogInfo.getLogId() &&
                url.equals(ctLogInfo.getUrl());
    }

    @Override
    public int hashCode() {
        return logId + (url.hashCode() * 4711);
    }

    @Override
    public String toString() {
        return getUrl();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy