package org.bouncycastle.ocsp.test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Date;

import javax.crypto.KeyGenerator;

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERInputStream;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.X509V3CertificateGenerator;

public class OCSPTestUtil
{
	
	public static SecureRandom     rand;
	public static KeyPairGenerator kpg;
	public static KeyGenerator     desede128kg;
	public static KeyGenerator     desede192kg;
	public static KeyGenerator     rc240kg;
	public static KeyGenerator     rc264kg;
	public static KeyGenerator     rc2128kg;
	public static BigInteger       serialNumber;
	
	public static final boolean DEBUG = true;
	
	static {
		try {
			rand = new SecureRandom();

			kpg  = KeyPairGenerator.getInstance("RSA", "BC");
			kpg.initialize(1024, rand);

			serialNumber = new BigInteger("1");
		}
		catch(Exception ex) {
			log(ex);
		}
	}
	
	public static KeyPair makeKeyPair() {
		return kpg.generateKeyPair();
	}
	
	public static X509Certificate makeCertificate(KeyPair _subKP, String _subDN, KeyPair _issKP, String _issDN) 
		throws GeneralSecurityException, IOException {
		
		return makeCertificate(_subKP, _subDN, _issKP, _issDN, false);
	}
	
	public static X509Certificate makeCACertificate(KeyPair _subKP, String _subDN, KeyPair _issKP, String _issDN) 
		throws GeneralSecurityException, IOException {
		
		return makeCertificate(_subKP, _subDN, _issKP, _issDN, true);
	}
	
	
	public static X509Certificate makeCertificate(KeyPair _subKP, String _subDN, KeyPair _issKP, String _issDN, boolean _ca) 
		throws GeneralSecurityException, IOException {

		PublicKey  _subPub  = _subKP.getPublic();
		PrivateKey _issPriv = _issKP.getPrivate();
		PublicKey  _issPub  = _issKP.getPublic();
		
		X509V3CertificateGenerator _v3CertGen = new X509V3CertificateGenerator();
		
		_v3CertGen.reset();
		_v3CertGen.setSerialNumber(allocateSerialNumber());
		_v3CertGen.setIssuerDN(new X509Name(_issDN));
		_v3CertGen.setNotBefore(new Date(System.currentTimeMillis()));
		_v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)));
		_v3CertGen.setSubjectDN(new X509Name(_subDN));
		_v3CertGen.setPublicKey(_subPub);
		_v3CertGen.setSignatureAlgorithm("MD5WithRSAEncryption");

		_v3CertGen.addExtension(
			X509Extensions.SubjectKeyIdentifier,
			false,
			createSubjectKeyId(_subPub));

		_v3CertGen.addExtension(
			X509Extensions.AuthorityKeyIdentifier,
			false,
			createAuthorityKeyId(_issPub));

		_v3CertGen.addExtension(
			X509Extensions.BasicConstraints,
			false,
			new BasicConstraints(_ca));

		X509Certificate _cert = _v3CertGen.generateX509Certificate(_issPriv);

		_cert.checkValidity(new Date());
		_cert.verify(_issPub);

		return _cert;
	}
	


	/*  
	 *  
	 *  INTERNAL METHODS
	 *  
	 */ 
	
	private static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey _pubKey) 
		throws IOException {
		
		ByteArrayInputStream _bais = new ByteArrayInputStream(_pubKey.getEncoded());
		SubjectPublicKeyInfo _info = new SubjectPublicKeyInfo(
			(ASN1Sequence)new DERInputStream(_bais).readObject());

		return new AuthorityKeyIdentifier(_info);
	}

	private static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey _pubKey, X509Name _name, int _sNumber) 
		throws IOException {
		
		ByteArrayInputStream _bais = new ByteArrayInputStream(_pubKey.getEncoded());
		SubjectPublicKeyInfo _info = new SubjectPublicKeyInfo(
			(ASN1Sequence)new DERInputStream(_bais).readObject());

		GeneralName             _genName = new GeneralName(_name);

		return new AuthorityKeyIdentifier(
			_info, new GeneralNames(new DERSequence(_genName)), BigInteger.valueOf(_sNumber));

	}
	
	private static SubjectKeyIdentifier createSubjectKeyId(PublicKey _pubKey) 
		throws IOException {
		
		ByteArrayInputStream _bais = new ByteArrayInputStream(_pubKey.getEncoded());
		SubjectPublicKeyInfo _info = new SubjectPublicKeyInfo(
			(ASN1Sequence)new DERInputStream(_bais).readObject());
		return new SubjectKeyIdentifier(_info);
	}

	private static BigInteger allocateSerialNumber() {
		BigInteger _tmp = serialNumber;
		serialNumber = serialNumber.add(BigInteger.ONE);
		return _tmp;
	}
	
	
	public static void log(Exception _ex) {
		if(DEBUG) {
			_ex.printStackTrace();
		}
	}

	public static void log(String _msg) {
		if(DEBUG) {
			System.out.println(_msg);
		}
	}
}
