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

org.graylog2.shared.security.tls.SelfSignedCertificate Maven / Gradle / Ivy

There is a newer version: 6.0.2
Show newest version
/**
 * This file is part of Graylog.
 *
 * Graylog is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Graylog is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Graylog.  If not, see .
 */
package org.graylog2.shared.security.tls;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Date;

import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Objects.requireNonNull;

/**
 * Generates a temporary self-signed certificate for testing purposes.
 * 

* NOTE: * Never use the certificate and private key generated by this class in production. * It is purely for testing purposes, and thus it is very insecure. * It even uses an insecure pseudo-random generator for faster generation internally. *

*/ public final class SelfSignedCertificate { private static final Logger LOG = LoggerFactory.getLogger(SelfSignedCertificate.class); /** * Current time minus 1 year, just in case software clock goes back due to time synchronization */ static final Date NOT_BEFORE = new Date(System.currentTimeMillis() - 86400000L * 365); /** * The maximum possible value in X.509 specification: 9999-12-31 23:59:59 */ static final Date NOT_AFTER = new Date(253402300799000L); private final Certificate certificate; private final PrivateKey privateKey; private final KeyStore keyStore; private SelfSignedCertificate(PrivateKey privateKey, Certificate certificate, KeyStore keyStore) throws CertificateException { this.privateKey = requireNonNull(privateKey); this.certificate = requireNonNull(certificate); this.keyStore = requireNonNull(keyStore); } /** * Returns the generated X.509 certificate. */ public Certificate certificate() { return certificate; } /** * Returns the generated RSA private key. */ public PrivateKey privateKey() { return privateKey; } /** * Returns the key store with the generated RSA private key and the X.509 certificate. */ public KeyStore keyStore() { return keyStore; } /** * Creates a new instance with FQDN "example.com". */ public static SelfSignedCertificate create() throws GeneralSecurityException, IOException { return create("example.com", "password"); } /** * Creates a new instance with a key length of 1024 bits. * * Bypass entropy collection by using insecure random generator. * We just want to generate it without any delay because it's for testing purposes only. * * @param fqdn a fully qualified domain name * @param password the password for the generated key store */ public static SelfSignedCertificate create(String fqdn, String password) throws GeneralSecurityException, IOException { return create(fqdn, ThreadLocalInsecureRandom.current(), 1024, password); } /** * Creates a new instance. * * @param fqdn a fully qualified domain name * @param random the {@link SecureRandom} to use * @param bits the number of bits of the generated private key * @param password the password for the generated key store */ public static SelfSignedCertificate create(String fqdn, SecureRandom random, int bits, String password) throws GeneralSecurityException, IOException { if (isNullOrEmpty(fqdn)) { throw new IllegalArgumentException("FQDN must not be empty"); } if (isNullOrEmpty(password)) { throw new IllegalArgumentException("Key store password must not be empty"); } final KeyPair keypair = generateKeyPair(random, bits); final PrivateKey privateKey = keypair.getPrivate(); final X509Certificate certificate; try { certificate = generateCertificate(fqdn, keypair, random); } catch (Throwable t) { LOG.debug("Failed to generate a self-signed X.509 certificate using sun.security.x509:", t); throw new CertificateException("No provider succeeded to generate a self-signed certificate. See debug log for the root cause."); } final KeyStore keyStore = generateKeyStore(fqdn, privateKey, certificate, password); return new SelfSignedCertificate(privateKey, certificate, keyStore); } private static KeyPair generateKeyPair(SecureRandom random, int bits) { final KeyPair keypair; try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(bits, random); keypair = keyGen.generateKeyPair(); } catch (NoSuchAlgorithmException e) { // Should not reach here because every Java implementation must have RSA key pair generator. throw new Error(e); } return keypair; } private static X509Certificate generateCertificate(String fqdn, KeyPair keypair, SecureRandom random) throws Exception { final PrivateKey key = keypair.getPrivate(); // Prepare the information required for generating an X.509 certificate. final X509CertInfo info = new X509CertInfo(); final X500Name owner = new X500Name("CN=" + fqdn); info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(new BigInteger(64, random))); try { info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); } catch (CertificateException ignore) { info.set(X509CertInfo.SUBJECT, owner); } try { info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); } catch (CertificateException ignore) { info.set(X509CertInfo.ISSUER, owner); } info.set(X509CertInfo.VALIDITY, new CertificateValidity(NOT_BEFORE, NOT_AFTER)); info.set(X509CertInfo.KEY, new CertificateX509Key(keypair.getPublic())); info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid))); // Sign the cert to identify the algorithm that's used. X509CertImpl cert = new X509CertImpl(info); cert.sign(key, "SHA1withRSA"); // Update the algorithm and sign again. info.set(CertificateAlgorithmId.NAME + '.' + CertificateAlgorithmId.ALGORITHM, cert.get(X509CertImpl.SIG_ALG)); cert = new X509CertImpl(info); cert.sign(key, "SHA1withRSA"); cert.verify(keypair.getPublic()); return cert; } private static KeyStore generateKeyStore(String fqdn, PrivateKey privateKey, Certificate certificate, String password) throws GeneralSecurityException, IOException { final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // Initialize key store keyStore.load(null, password.toCharArray()); keyStore.setKeyEntry(fqdn, privateKey, password.toCharArray(), new Certificate[]{certificate}); return keyStore; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy