<commit_before>package uk.ac.cam.gpe21.droidssl.mitm.crypto;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.bc.BcX509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public final class CertificateGenerator {
	private static final TimeZone UTC = TimeZone.getTimeZone("Etc/UTC");

	private final X509CertificateHolder caCertificate;
	private final X509Certificate jcaCaCertificate;
	private final AsymmetricKeyParameter caPublicKey;
	private final AsymmetricKeyParameter caPrivateKey;
	private final AsymmetricCipherKeyPair keyPair;
	private BigInteger serial = BigInteger.ZERO;

	public CertificateGenerator(Path caCertificateFile, Path caKeyFile, AsymmetricCipherKeyPair keyPair) throws IOException, CertificateException {
		this.keyPair = keyPair;

		try (PEMParser parser = new PEMParser(Files.newBufferedReader(caCertificateFile, StandardCharsets.UTF_8))) {
			Object object = parser.readObject();
			if (!(object instanceof X509CertificateHolder))
				throw new IOException("Failed to read CA certificate file");

			caCertificate = (X509CertificateHolder) object;
			jcaCaCertificate = new JcaX509CertificateConverter().getCertificate(caCertificate);
		}

		try (PEMParser parser = new PEMParser(Files.newBufferedReader(caKeyFile, StandardCharsets.UTF_8))) {
			Object object = parser.readObject();
			if (!(object instanceof PEMKeyPair))
				throw new IOException("Failed to read CA key file");

			PEMKeyPair pair = (PEMKeyPair) object;
			caPublicKey = PublicKeyFactory.createKey(pair.getPublicKeyInfo());
			caPrivateKey = PrivateKeyFactory.createKey(pair.getPrivateKeyInfo());
		}
	}

	public X509Certificate getCaCertificate() {
		return jcaCaCertificate;
	}

	public X509Certificate generateJca(String cn, String[] sans) throws CertificateException {
		JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
		X509CertificateHolder certificate = generate(cn, sans);
		return converter.getCertificate(certificate);
	}

	public X509CertificateHolder generate(String cn, String[] sans) {
		try {
			/* basic certificate structure */
			//serial = serial.add(BigInteger.ONE);
			// TODO: temporary workaround as reusing serial numbers makes Firefox complain
			serial = new BigInteger(Long.toString(System.currentTimeMillis()));

			Calendar notBefore = new GregorianCalendar(UTC);
			notBefore.add(Calendar.HOUR, -1);

			Calendar notAfter = new GregorianCalendar(UTC);
			notAfter.add(Calendar.HOUR, 24);

			X500Name subject = new X500NameBuilder().addRDN(BCStyle.CN, cn).build();

			BcX509ExtensionUtils utils = new BcX509ExtensionUtils();
			X509v3CertificateBuilder builder = new BcX509v3CertificateBuilder(caCertificate, serial, notBefore.getTime(), notAfter.getTime(), subject, keyPair.getPublic());

			/* subjectAlernativeName extension */
			if (sans.length > 0) {
				GeneralName[] names = new GeneralName[sans.length];
				for (int i = 0; i < names.length; i++) {
					names[i] = new GeneralName(GeneralName.dNSName, sans[i]);
				}
				builder.addExtension(X509Extension.subjectAlternativeName, false, new GeneralNames(names));
			}
<commit_msg>Wrap exceptions produced by generateJca().<commit_after>package uk.ac.cam.gpe21.droidssl.mitm.crypto;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.cert.bc.BcX509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public final class CertificateGenerator {
	private static final TimeZone UTC = TimeZone.getTimeZone("Etc/UTC");

	private final X509CertificateHolder caCertificate;
	private final X509Certificate jcaCaCertificate;
	private final AsymmetricKeyParameter caPublicKey;
	private final AsymmetricKeyParameter caPrivateKey;
	private final AsymmetricCipherKeyPair keyPair;
	private BigInteger serial = BigInteger.ZERO;

	public CertificateGenerator(Path caCertificateFile, Path caKeyFile, AsymmetricCipherKeyPair keyPair) throws IOException, CertificateException {
		this.keyPair = keyPair;

		try (PEMParser parser = new PEMParser(Files.newBufferedReader(caCertificateFile, StandardCharsets.UTF_8))) {
			Object object = parser.readObject();
			if (!(object instanceof X509CertificateHolder))
				throw new IOException("Failed to read CA certificate file");

			caCertificate = (X509CertificateHolder) object;
			jcaCaCertificate = new JcaX509CertificateConverter().getCertificate(caCertificate);
		}

		try (PEMParser parser = new PEMParser(Files.newBufferedReader(caKeyFile, StandardCharsets.UTF_8))) {
			Object object = parser.readObject();
			if (!(object instanceof PEMKeyPair))
				throw new IOException("Failed to read CA key file");

			PEMKeyPair pair = (PEMKeyPair) object;
			caPublicKey = PublicKeyFactory.createKey(pair.getPublicKeyInfo());
			caPrivateKey = PrivateKeyFactory.createKey(pair.getPrivateKeyInfo());
		}
	}

	public X509Certificate getCaCertificate() {
		return jcaCaCertificate;
	}

	public X509Certificate generateJca(String cn, String[] sans) {
		try {
			JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
			X509CertificateHolder certificate = generate(cn, sans);
			return converter.getCertificate(certificate);
		} catch (CertificateException ex) {
			throw new CertificateGenerationException(ex);
		}
	}

	public X509CertificateHolder generate(String cn, String[] sans) {
		try {
			/* basic certificate structure */
			//serial = serial.add(BigInteger.ONE);
			// TODO: temporary workaround as reusing serial numbers makes Firefox complain
			serial = new BigInteger(Long.toString(System.currentTimeMillis()));

			Calendar notBefore = new GregorianCalendar(UTC);
			notBefore.add(Calendar.HOUR, -1);

			Calendar notAfter = new GregorianCalendar(UTC);
			notAfter.add(Calendar.HOUR, 24);

			X500Name subject = new X500NameBuilder().addRDN(BCStyle.CN, cn).build();

			BcX509ExtensionUtils utils = new BcX509ExtensionUtils();
			X509v3CertificateBuilder builder = new BcX509v3CertificateBuilder(caCertificate, serial, notBefore.getTime(), notAfter.getTime(), subject, keyPair.getPublic());

			/* subjectAlernativeName extension */
			if (sans.length > 0) {
				GeneralName[] names = new GeneralName[sans.length];
				for (int i = 0; i < names.length; i++) {
					names[i] = new GeneralName(GeneralName.dNSName, sans[i]);
				}
				builder.addExtension(X509Extension.subjectAlternativeName, false, new GeneralNames(names));
			}

{'fraction_non_alphanumeric': 0.09276852576282553, 'fraction_numerical': 0.023918631943668268, 'mean_word_length': 6.164131305044036, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3144178', 'n_tokens_mistral': 2998, 'n_tokens_neox': 2623, 'n_words': 541}