<commit_before>/*
 *   Copyright (c) 2012 <NAME>.  All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
package cloudeventbus.pki;

import cloudeventbus.CloudEventBusException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author <NAME> <<EMAIL>>
 */
public class Certificate {

	public enum Type {
		AUTHORITY,
		CLIENT,
		SERVER
	}

	static final int KEY_LENGTH = 294;
	static final int MAX_STRING_LENGTH = 16384;
	static final int SIGNATURE_LENGTH = 256;

	private final Type type;
	private final long serialNumber;
	private final long issuer;
	private final long expirationDate;
	private final PublicKey publicKey;
	private final List<String> subscribePermissions;
	private final List<String> publishPermissions;
	private final String comment;
	private final byte[] signature;

	private volatile byte[] hash;

	public Certificate(Type type, long serialNumber, long issuer, long expirationDate, PublicKey publicKey, List<String> subscribePermissions, List<String> publishPermissions, String comment, byte[] signature) {
		if (type == null) {
			throw new IllegalArgumentException("type cannot be null");
		}
		this.type = type;
		this.serialNumber = serialNumber;
		this.issuer = issuer;
		this.expirationDate = expirationDate;
		if (publicKey == null) {
			throw new IllegalArgumentException("public key cannot be null");
		}
		this.publicKey = publicKey;
		this.subscribePermissions = copyListUnmodifably(subscribePermissions);
		this.publishPermissions = copyListUnmodifably(publishPermissions);
		this.comment = comment;
		if (signature != null && signature.length != SIGNATURE_LENGTH) {
			throw new InvalidCertificateException("Invalid signature, signature must be " + SIGNATURE_LENGTH + " bytes long.");
		}
		this.signature = signature;
	}

	public Certificate(InputStream in) throws IOException {
		final DataInputStream data = new DataInputStream(in);
		try {
			this.type = Type.values()[data.read()];
			serialNumber = data.readLong();
			issuer = data.readLong();
			expirationDate = data.readLong();
			// Read and decode public key
			final byte[] key = new byte[KEY_LENGTH];
			data.readFully(key);
			final KeyFactory keyFactory;
			keyFactory = KeyFactory.getInstance("RSA");
			final X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
			publicKey = keyFactory.generatePublic(publicKeySpec);

			final String subscribe = readString(data);
			subscribePermissions = commaSeperateStringToList(subscribe);
			final String publish = readString(data);
			publishPermissions = commaSeperateStringToList(publish);
			final String commentString = readString(data);
			comment = commentString.length() == 0 ? null : commentString;

			final byte[] signature = new byte[SIGNATURE_LENGTH];
			final int readBytes = data.read(signature);
			if (readBytes <= 0) {
				this.signature = null;
			} else if (readBytes != SIGNATURE_LENGTH) {
				throw new EOFException("Premature end of certificate");
			} else {
				this.signature = signature;
			}

		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw new CloudEventBusException("Error decoding simple certificate", e);
		}
	}

	public void store(OutputStream out) throws IOException {
		store(out, true);
	}

	private void store(OutputStream out, boolean writeSignature) throws IOException {
		final DataOutputStream data = new DataOutputStream(out);
		data.write(type.ordinal());
		data.writeLong(serialNumber);
		data.writeLong(issuer);
		data.writeLong(expirationDate);
		data.write(publicKey.getEncoded());
		data.write(listToCommaSeparatedString(subscribePermissions).getBytes());
		data.write(0);
		data.write(listToCommaSeparatedString(publishPermissions).getBytes());
		data.write(0);
		data.write((comment == null ? "" : comment).getBytes());
		data.write(0);
		if (writeSignature && signature != null) {
			data.write(signature);
		}
	}

	public byte[] hash() {
		if (hash != null) {
			return hash.clone();
		}
		try {
			final ByteArrayOutputStream out = new ByteArrayOutputStream();
			store(out, false);
			final MessageDigest digest = MessageDigest.getInstance("SHA-1");
			hash = digest.digest(out.toByteArray());
			return hash.clone();
		} catch (IOException | NoSuchAlgorithmException e) {
			throw new CloudEventBusException("Error calculating hash of certificate", e);
		}
	}

	private String listToCommaSeparatedString(List<String> list) {
		final StringBuilder buff = new StringBuilder();
		String sep = "";
		for (String s : list) {
		    buff.append(sep);
		    buff.append(s);
		    sep = ",";
		}
		return buff.toString();<commit_msg>Added a flush after writing the certificate.<commit_after>/*
 *   Copyright (c) 2012 <NAME>.  All rights reserved.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */
package cloudeventbus.pki;

import cloudeventbus.CloudEventBusException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author <NAME> <<EMAIL>>
 */
public class Certificate {

	public enum Type {
		AUTHORITY,
		CLIENT,
		SERVER
	}

	static final int KEY_LENGTH = 294;
	static final int MAX_STRING_LENGTH = 16384;
	static final int SIGNATURE_LENGTH = 256;

	private final Type type;
	private final long serialNumber;
	private final long issuer;
	private final long expirationDate;
	private final PublicKey publicKey;
	private final List<String> subscribePermissions;
	private final List<String> publishPermissions;
	private final String comment;
	private final byte[] signature;

	private volatile byte[] hash;

	public Certificate(Type type, long serialNumber, long issuer, long expirationDate, PublicKey publicKey, List<String> subscribePermissions, List<String> publishPermissions, String comment, byte[] signature) {
		if (type == null) {
			throw new IllegalArgumentException("type cannot be null");
		}
		this.type = type;
		this.serialNumber = serialNumber;
		this.issuer = issuer;
		this.expirationDate = expirationDate;
		if (publicKey == null) {
			throw new IllegalArgumentException("public key cannot be null");
		}
		this.publicKey = publicKey;
		this.subscribePermissions = copyListUnmodifably(subscribePermissions);
		this.publishPermissions = copyListUnmodifably(publishPermissions);
		this.comment = comment;
		if (signature != null && signature.length != SIGNATURE_LENGTH) {
			throw new InvalidCertificateException("Invalid signature, signature must be " + SIGNATURE_LENGTH + " bytes long.");
		}
		this.signature = signature;
	}

	public Certificate(InputStream in) throws IOException {
		final DataInputStream data = new DataInputStream(in);
		try {
			this.type = Type.values()[data.read()];
			serialNumber = data.readLong();
			issuer = data.readLong();
			expirationDate = data.readLong();
			// Read and decode public key
			final byte[] key = new byte[KEY_LENGTH];
			data.readFully(key);
			final KeyFactory keyFactory;
			keyFactory = KeyFactory.getInstance("RSA");
			final X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
			publicKey = keyFactory.generatePublic(publicKeySpec);

			final String subscribe = readString(data);
			subscribePermissions = commaSeperateStringToList(subscribe);
			final String publish = readString(data);
			publishPermissions = commaSeperateStringToList(publish);
			final String commentString = readString(data);
			comment = commentString.length() == 0 ? null : commentString;

			final byte[] signature = new byte[SIGNATURE_LENGTH];
			final int readBytes = data.read(signature);
			if (readBytes <= 0) {
				this.signature = null;
			} else if (readBytes != SIGNATURE_LENGTH) {
				throw new EOFException("Premature end of certificate");
			} else {
				this.signature = signature;
			}

		} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
			throw new CloudEventBusException("Error decoding simple certificate", e);
		}
	}

	public void store(OutputStream out) throws IOException {
		store(out, true);
	}

	private void store(OutputStream out, boolean writeSignature) throws IOException {
		final DataOutputStream data = new DataOutputStream(out);
		data.write(type.ordinal());
		data.writeLong(serialNumber);
		data.writeLong(issuer);
		data.writeLong(expirationDate);
		data.write(publicKey.getEncoded());
		data.write(listToCommaSeparatedString(subscribePermissions).getBytes());
		data.write(0);
		data.write(listToCommaSeparatedString(publishPermissions).getBytes());
		data.write(0);
		data.write((comment == null ? "" : comment).getBytes());
		data.write(0);
		if (writeSignature && signature != null) {
			data.write(signature);
		}
		data.flush();
	}

	public byte[] hash() {
		if (hash != null) {
			return hash.clone();
		}
		try {
			final ByteArrayOutputStream out = new ByteArrayOutputStream();
			store(out, false);
			final MessageDigest digest = MessageDigest.getInstance("SHA-1");
			hash = digest.digest(out.toByteArray());
			return hash.clone();
		} catch (IOException | NoSuchAlgorithmException e) {
			throw new CloudEventBusException("Error calculating hash of certificate", e);
		}
	}

	private String listToCommaSeparatedString(List<String> list) {
		final StringBuilder buff = new StringBuilder();
		String sep = "";
		for (String s : list) {
		    buff.append(sep);
		    buff.append(s);
		    sep = ",";
		}
		return buff.toString();
{'fraction_non_alphanumeric': 0.1009050156474668, 'fraction_numerical': 0.005751501311003975, 'mean_word_length': 4.7342386032977695, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '252330', 'n_tokens_mistral': 3634, 'n_tokens_neox': 3290, 'n_words': 950}