/**
 * Copyright (C) 2015 Zero11 S.r.l.
 *
 * 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 it.zero11.acme.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;

public class X509Utils {
	public static final String DEFAULT_ALGHORITM = "RSA";

	public static PKCS10CertificationRequest generateCSR(String[] commonNames, KeyPair pair) throws OperatorCreationException, IOException {
		X500NameBuilder namebuilder = new X500NameBuilder(X500Name.getDefaultStyle());
		namebuilder.addRDN(BCStyle.CN, commonNames[0]);
		
		List<GeneralName> subjectAltNames = new ArrayList<>(commonNames.length);
		for (String cn:commonNames)
			subjectAltNames.add(new GeneralName(GeneralName.dNSName, cn));
		GeneralNames subjectAltName = new GeneralNames(subjectAltNames.toArray(new GeneralName[0]));         
		
		ExtensionsGenerator extGen = new ExtensionsGenerator();
		extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltName.toASN1Primitive());
		
		PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(namebuilder.build(), pair.getPublic());
		p10Builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
		JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
		ContentSigner signer = csBuilder.build(pair.getPrivate());
		PKCS10CertificationRequest request = p10Builder.build(signer);
		return request;
	}

	public static KeyPair generateKeyPair(int size) throws NoSuchAlgorithmException {
		KeyPairGenerator keyGen = KeyPairGenerator.getInstance(DEFAULT_ALGHORITM);
		keyGen.initialize(size);
		return keyGen.generateKeyPair();
	}

	public static KeyPair loadPEMKeyPair(InputStream privateKeyInputStream) throws IOException {
		try(PEMParser pemParser = new PEMParser(new InputStreamReader(privateKeyInputStream))){
			PEMKeyPair keyPair = (PEMKeyPair) pemParser.readObject();
			return new JcaPEMKeyConverter().getKeyPair(keyPair);
		}
	}

	public static void savePEM(OutputStream outputStream, Object object) throws IOException {
		try(JcaPEMWriter writer = new JcaPEMWriter(new PrintWriter(outputStream))){
			writer.writeObject(object);
		}
	}

	public static String getCACertificateURL(X509Certificate certificate) throws IOException {
		byte[] bOctets = ((ASN1OctetString) ASN1Primitive.fromByteArray(certificate.getExtensionValue(Extension.authorityInfoAccess.getId()))).getOctets();
		AuthorityInformationAccess access = AuthorityInformationAccess.getInstance(ASN1Sequence.fromByteArray(bOctets));
		for (AccessDescription ad:access.getAccessDescriptions()){
			if (ad.getAccessMethod().equals(X509ObjectIdentifiers.id_ad_caIssuers)){
				return ad.getAccessLocation().getName().toString();
			}
		}
		return null;
	}
}