// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.handler.ssl.util;

import java.security.NoSuchAlgorithmException;
import java.security.KeyPairGenerator;
import io.netty.util.internal.ObjectUtil;
import java.security.KeyPair;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import io.netty.buffer.ByteBuf;
import java.io.FileOutputStream;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.CharsetUtil;
import io.netty.handler.codec.base64.Base64;
import io.netty.buffer.Unpooled;
import java.security.cert.CertificateEncodingException;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.io.FileInputStream;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.io.File;
import java.util.Date;
import io.netty.util.internal.logging.InternalLogger;

@Deprecated
public final class SelfSignedCertificate
{
    private static final InternalLogger logger;
    private static final Date DEFAULT_NOT_BEFORE;
    private static final Date DEFAULT_NOT_AFTER;
    private static final int DEFAULT_KEY_LENGTH_BITS;
    private final File certificate;
    private final File privateKey;
    private final X509Certificate cert;
    private final PrivateKey key;
    
    public SelfSignedCertificate() throws CertificateException {
        this(new Builder());
    }
    
    public SelfSignedCertificate(final Date notBefore, final Date notAfter) throws CertificateException {
        this(new Builder().notBefore(notBefore).notAfter(notAfter));
    }
    
    public SelfSignedCertificate(final Date notBefore, final Date notAfter, final String algorithm, final int bits) throws CertificateException {
        this(new Builder().notBefore(notBefore).notAfter(notAfter).algorithm(algorithm).bits(bits));
    }
    
    public SelfSignedCertificate(final String fqdn) throws CertificateException {
        this(new Builder().fqdn(fqdn));
    }
    
    public SelfSignedCertificate(final String fqdn, final String algorithm, final int bits) throws CertificateException {
        this(new Builder().fqdn(fqdn).algorithm(algorithm).bits(bits));
    }
    
    public SelfSignedCertificate(final String fqdn, final Date notBefore, final Date notAfter) throws CertificateException {
        this(new Builder().fqdn(fqdn).notBefore(notBefore).notAfter(notAfter));
    }
    
    public SelfSignedCertificate(final String fqdn, final Date notBefore, final Date notAfter, final String algorithm, final int bits) throws CertificateException {
        this(new Builder().fqdn(fqdn).notBefore(notBefore).notAfter(notAfter).algorithm(algorithm).bits(bits));
    }
    
    public SelfSignedCertificate(final String fqdn, final SecureRandom random, final int bits) throws CertificateException {
        this(new Builder().fqdn(fqdn).random(random).bits(bits));
    }
    
    public SelfSignedCertificate(final String fqdn, final SecureRandom random, final String algorithm, final int bits) throws CertificateException {
        this(new Builder().fqdn(fqdn).random(random).algorithm(algorithm).bits(bits));
    }
    
    public SelfSignedCertificate(final String fqdn, final SecureRandom random, final int bits, final Date notBefore, final Date notAfter) throws CertificateException {
        this(new Builder().fqdn(fqdn).notBefore(notBefore).notAfter(notAfter).random(random).bits(bits));
    }
    
    public SelfSignedCertificate(final String fqdn, final SecureRandom random, final int bits, final Date notBefore, final Date notAfter, final String algorithm) throws CertificateException {
        this(new Builder().fqdn(fqdn).random(random).algorithm(algorithm).bits(bits).notBefore(notBefore).notAfter(notAfter));
    }
    
    private SelfSignedCertificate(final Builder builder) throws CertificateException {
        if (!builder.generateCertificateBuilder() && !builder.generateBc() && !builder.generateKeytool() && !builder.generateSunMiscSecurity()) {
            throw (CertificateException)builder.failure;
        }
        this.certificate = new File(builder.paths[0]);
        this.privateKey = new File(builder.paths[1]);
        this.key = builder.privateKey;
        try (final FileInputStream certificateInput = new FileInputStream(this.certificate)) {
            this.cert = (X509Certificate)CertificateFactory.getInstance("X509").generateCertificate(certificateInput);
        }
        catch (final Exception e) {
            throw new CertificateEncodingException(e);
        }
    }
    
    public static Builder builder() {
        return new Builder();
    }
    
    public File certificate() {
        return this.certificate;
    }
    
    public File privateKey() {
        return this.privateKey;
    }
    
    public X509Certificate cert() {
        return this.cert;
    }
    
    public PrivateKey key() {
        return this.key;
    }
    
    public void delete() {
        safeDelete(this.certificate);
        safeDelete(this.privateKey);
    }
    
    static String[] newSelfSignedCertificate(String fqdn, final PrivateKey key, final X509Certificate cert) throws IOException, CertificateEncodingException {
        ByteBuf wrappedBuf = Unpooled.wrappedBuffer(key.getEncoded());
        String keyText;
        try {
            final ByteBuf encodedBuf = Base64.encode(wrappedBuf, true);
            try {
                keyText = "-----BEGIN PRIVATE KEY-----\n" + encodedBuf.toString(CharsetUtil.US_ASCII) + "\n-----END PRIVATE KEY-----\n";
            }
            finally {
                encodedBuf.release();
            }
        }
        finally {
            wrappedBuf.release();
        }
        fqdn = fqdn.replaceAll("[^\\w.-]", "x");
        final File keyFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".key", null);
        keyFile.deleteOnExit();
        OutputStream keyOut = new FileOutputStream(keyFile);
        try {
            keyOut.write(keyText.getBytes(CharsetUtil.US_ASCII));
            keyOut.close();
            keyOut = null;
        }
        finally {
            if (keyOut != null) {
                safeClose(keyFile, keyOut);
                safeDelete(keyFile);
            }
        }
        wrappedBuf = Unpooled.wrappedBuffer(cert.getEncoded());
        String certText;
        try {
            final ByteBuf encodedBuf = Base64.encode(wrappedBuf, true);
            try {
                certText = "-----BEGIN CERTIFICATE-----\n" + encodedBuf.toString(CharsetUtil.US_ASCII) + "\n-----END CERTIFICATE-----\n";
            }
            finally {
                encodedBuf.release();
            }
        }
        finally {
            wrappedBuf.release();
        }
        final File certFile = PlatformDependent.createTempFile("keyutil_" + fqdn + '_', ".crt", null);
        certFile.deleteOnExit();
        OutputStream certOut = new FileOutputStream(certFile);
        try {
            certOut.write(certText.getBytes(CharsetUtil.US_ASCII));
            certOut.close();
            certOut = null;
        }
        finally {
            if (certOut != null) {
                safeClose(certFile, certOut);
                safeDelete(certFile);
                safeDelete(keyFile);
            }
        }
        return new String[] { certFile.getPath(), keyFile.getPath() };
    }
    
    private static void safeDelete(final File certFile) {
        if (!certFile.delete() && SelfSignedCertificate.logger.isWarnEnabled()) {
            SelfSignedCertificate.logger.warn("Failed to delete a file: " + certFile);
        }
    }
    
    private static void safeClose(final File keyFile, final OutputStream keyOut) {
        try {
            keyOut.close();
        }
        catch (final IOException e) {
            if (SelfSignedCertificate.logger.isWarnEnabled()) {
                SelfSignedCertificate.logger.warn("Failed to close a file: " + keyFile, e);
            }
        }
    }
    
    private static boolean isBouncyCastleAvailable() {
        try {
            Class.forName("org.bouncycastle.cert.X509v3CertificateBuilder");
            return true;
        }
        catch (final ClassNotFoundException e) {
            return false;
        }
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(SelfSignedCertificate.class);
        DEFAULT_NOT_BEFORE = new Date(SystemPropertyUtil.getLong("io.netty.selfSignedCertificate.defaultNotBefore", System.currentTimeMillis() - 31536000000L));
        DEFAULT_NOT_AFTER = new Date(SystemPropertyUtil.getLong("io.netty.selfSignedCertificate.defaultNotAfter", 253402300799000L));
        DEFAULT_KEY_LENGTH_BITS = SystemPropertyUtil.getInt("io.netty.handler.ssl.util.selfSignedKeyStrength", 2048);
    }
    
    public static final class Builder
    {
        String fqdn;
        SecureRandom random;
        int bits;
        Date notBefore;
        Date notAfter;
        String algorithm;
        Throwable failure;
        KeyPair keypair;
        PrivateKey privateKey;
        String[] paths;
        
        private Builder() {
            this.fqdn = "localhost";
            this.bits = SelfSignedCertificate.DEFAULT_KEY_LENGTH_BITS;
            this.notBefore = SelfSignedCertificate.DEFAULT_NOT_BEFORE;
            this.notAfter = SelfSignedCertificate.DEFAULT_NOT_AFTER;
            this.algorithm = "RSA";
        }
        
        public Builder fqdn(final String fqdn) {
            this.fqdn = ObjectUtil.checkNotNullWithIAE(fqdn, "fqdn");
            return this;
        }
        
        public Builder random(final SecureRandom random) {
            this.random = random;
            return this;
        }
        
        public Builder bits(final int bits) {
            this.bits = bits;
            return this;
        }
        
        public Builder notBefore(final Date notBefore) {
            this.notBefore = ObjectUtil.checkNotNullWithIAE(notBefore, "notBefore");
            return this;
        }
        
        public Builder notAfter(final Date notAfter) {
            this.notAfter = ObjectUtil.checkNotNullWithIAE(notAfter, "notAfter");
            return this;
        }
        
        public Builder algorithm(final String algorithm) {
            if ("EC".equalsIgnoreCase(algorithm)) {
                this.algorithm = "EC";
            }
            else {
                if (!"RSA".equalsIgnoreCase(algorithm)) {
                    throw new IllegalArgumentException("Algorithm not valid: " + algorithm);
                }
                this.algorithm = "RSA";
            }
            return this;
        }
        
        private SecureRandom randomOrDefault() {
            return (this.random == null) ? ThreadLocalInsecureRandom.current() : this.random;
        }
        
        private void generateKeyPairLocally() {
            if (this.keypair != null) {
                return;
            }
            try {
                final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(this.algorithm);
                keyGen.initialize(this.bits, this.randomOrDefault());
                this.keypair = keyGen.generateKeyPair();
            }
            catch (final NoSuchAlgorithmException e) {
                throw new IllegalStateException(e);
            }
            this.privateKey = this.keypair.getPrivate();
        }
        
        private void addFailure(final Throwable t) {
            if (this.failure != null) {
                t.addSuppressed(this.failure);
            }
            this.failure = t;
        }
        
        boolean generateBc() {
            if (!isBouncyCastleAvailable()) {
                SelfSignedCertificate.logger.debug("Failed to generate a self-signed X.509 certificate because BouncyCastle PKIX is not available in classpath");
                return false;
            }
            this.generateKeyPairLocally();
            try {
                this.paths = BouncyCastleSelfSignedCertGenerator.generate(this.fqdn, this.keypair, this.randomOrDefault(), this.notBefore, this.notAfter, this.algorithm);
                return true;
            }
            catch (final Throwable t) {
                SelfSignedCertificate.logger.debug("Failed to generate a self-signed X.509 certificate using Bouncy Castle:", t);
                this.addFailure(t);
                return false;
            }
        }
        
        boolean generateKeytool() {
            if (!KeytoolSelfSignedCertGenerator.isAvailable()) {
                SelfSignedCertificate.logger.debug("Not attempting to generate certificate with keytool because keytool is missing");
                return false;
            }
            if (this.random != null) {
                SelfSignedCertificate.logger.debug("Not attempting to generate certificate with keytool because of explicitly set SecureRandom");
                return false;
            }
            try {
                KeytoolSelfSignedCertGenerator.generate(this);
                return true;
            }
            catch (final Throwable t) {
                SelfSignedCertificate.logger.debug("Failed to generate a self-signed X.509 certificate using keytool:", t);
                this.addFailure(t);
                return false;
            }
        }
        
        boolean generateCertificateBuilder() {
            if (!CertificateBuilderCertGenerator.isAvailable()) {
                SelfSignedCertificate.logger.debug("Not attempting to generate a certificate with CertificateBuilder because it's not available on the classpath");
                return false;
            }
            try {
                CertificateBuilderCertGenerator.generate(this);
                return true;
            }
            catch (final CertificateException ce) {
                SelfSignedCertificate.logger.debug(ce);
                this.addFailure(ce);
            }
            catch (final Exception e) {
                final String msg = "Failed to generate a self-signed X.509 certificate using CertificateBuilder:";
                SelfSignedCertificate.logger.debug(msg, e);
                this.addFailure(new CertificateException(msg, e));
            }
            return false;
        }
        
        boolean generateSunMiscSecurity() {
            this.generateKeyPairLocally();
            try {
                this.paths = OpenJdkSelfSignedCertGenerator.generate(this.fqdn, this.keypair, this.randomOrDefault(), this.notBefore, this.notAfter, this.algorithm);
                return true;
            }
            catch (final Throwable t2) {
                SelfSignedCertificate.logger.debug("Failed to generate a self-signed X.509 certificate using sun.security.x509:", t2);
                final CertificateException certificateException = new CertificateException("No provider succeeded to generate a self-signed certificate. See debug log for the root cause.", t2);
                this.addFailure(certificateException);
                return false;
            }
        }
        
        public SelfSignedCertificate build() throws CertificateException {
            return new SelfSignedCertificate(this, null);
        }
    }
}
