package org.jboss.resteasy.security;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Date;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;

/**
 * Class provides utility functions for generation of V1
 * {@link java.security.cert.X509Certificate}
 *
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 2 $
 */
public class KeyTools {
    static {
        BouncyIntegration.init();
    }

    /**
     * Generate version 1 self signed {@link java.security.cert.X509Certificate}
     *
     * @param pair the CA key pair
     * @return the x509 certificate
     * @throws Exception the exception
     */
    public static X509Certificate generateTestCertificate(KeyPair pair) {
        return generateV1SelfSignedCertificate(pair, "Test", null);
    }

    /**
     * Generate version 1 self signed {@link java.security.cert.X509Certificate}
     *
     * @param subject the subject name
     * @param issuer  the issuer name
     * @param pair    the CA key pair
     * @return the x509 certificate
     * @throws Exception the exception
     */
    public static X509Certificate generateTestCertificate(String subject,
            String issuer, KeyPair pair) {
        return generateV1SelfSignedCertificate(pair, subject, issuer);
    }

    /**
     * Generate version 1 self signed {@link java.security.cert.X509Certificate}
     *
     * @param caKeyPair the CA key pair
     * @param subject   the subject name
     * @param issuer    the issuer name
     * @return the x509 certificate
     * @throws Exception the exception
     */
    private static X509Certificate generateV1SelfSignedCertificate(KeyPair caKeyPair,
            String subject, String issuer) {
        try {
            X500Name subjectDN = new X500Name("CN=" + subject);
            X500Name issuerDN;
            if (issuer == null) {
                issuerDN = subjectDN;
            } else {
                issuerDN = new X500Name("CN=" + issuer);
            }

            Date validityStartDate = new Date(System.currentTimeMillis() - 10000);
            Date validityEndDate = new Date(System.currentTimeMillis() + 10000);
            SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo
                    .getInstance(caKeyPair.getPublic().getEncoded());

            X509v1CertificateBuilder certGen = new X509v1CertificateBuilder(
                    issuerDN, BigInteger.valueOf(System.currentTimeMillis()),
                    validityStartDate, validityEndDate, subjectDN, subPubKeyInfo);

            X509CertificateHolder holder = certGen.build(createSigner(caKeyPair.getPrivate()));

            return new JcaX509CertificateConverter().getCertificate(holder);

        } catch (Exception e) {
            throw new RuntimeException("Error creating X509v1Certificate.", e);
        }
    }

    /**
     * Creates the content signer for generation of Version 1
     * {@link java.security.cert.X509Certificate}.
     *
     * @param privateKey the private key
     * @return the content signer
     */
    private static ContentSigner createSigner(PrivateKey privateKey) {
        try {
            AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
                    .find("SHA256WithRSAEncryption");
            AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
                    .find(sigAlgId);

            return new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
                    .build(PrivateKeyFactory.createKey(privateKey.getEncoded()));
        } catch (Exception e) {
            throw new RuntimeException("Could not create content signer.", e);
        }
    }
}
