package cn.report.pdf.sign;

import com.itextpdf.signatures.CRLVerifier;
import com.itextpdf.signatures.CertificateVerification;
import com.itextpdf.signatures.OCSPVerifier;
import com.itextpdf.signatures.PdfPKCS7;
import com.itextpdf.signatures.SignatureUtil;
import com.itextpdf.signatures.VerificationException;
import com.itextpdf.signatures.VerificationOK;

import cn.report.pdf.sign.base.SignatureIntegrity;

import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CRL;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class VerifyCertificate extends SignatureIntegrity 
{
	KeyStore ks;

	public PdfPKCS7 verifySignature(SignatureUtil signUtil, String name)throws GeneralSecurityException, IOException 
	{
		PdfPKCS7 pkcs7 = super.verifySignature(signUtil, name);
		
		Certificate[] certs = pkcs7.getSignCertificateChain();
		Calendar cal = pkcs7.getSignDate();
		List<VerificationException> errors = CertificateVerification.verifyCertificates(certs, ks, cal);
		if (errors.size() == 0)
			System.out.println("Certificates verified against the KeyStore");
		else
			System.out.println(errors);
		
		
		for (int i = 0; i < certs.length; i++) {
			X509Certificate cert = (X509Certificate) certs[i];
			System.out.println("=== Certificate " + i + " ===");
			showCertificateInfo(cert, cal.getTime());
		}
		X509Certificate signCert = (X509Certificate) certs[0];
		X509Certificate issuerCert = (certs.length > 1 ? (X509Certificate) certs[1] : null);
		System.out.println("=== Checking validity of the document at the time of signing ===");
		checkRevocation(pkcs7, signCert, issuerCert, cal.getTime());
		System.out.println("=== Checking validity of the document today ===");
		checkRevocation(pkcs7, signCert, issuerCert, new Date());
		return pkcs7;
	}

	public static void checkRevocation(PdfPKCS7 pkcs7, X509Certificate signCert, X509Certificate issuerCert, Date date) throws GeneralSecurityException, IOException 
	{
		List<BasicOCSPResp> ocsps = new ArrayList<BasicOCSPResp>();
		if (pkcs7.getOcsp() != null)
			ocsps.add(pkcs7.getOcsp());
		OCSPVerifier ocspVerifier = new OCSPVerifier(null, ocsps);
		List<VerificationOK> verification =
				ocspVerifier.verify(signCert, issuerCert, date);
		if (verification.size() == 0) {
			List<X509CRL> crls = new ArrayList<X509CRL>();
			if (pkcs7.getCRLs() != null) {
				for (CRL crl : pkcs7.getCRLs())
					crls.add((X509CRL) crl);
			}
			CRLVerifier crlVerifier = new CRLVerifier(null, crls);
			verification.addAll(crlVerifier.verify(signCert, issuerCert, date));
		}
		if (verification.size() == 0) {
			System.out.println("The signing certificate couldn't be verified");
		} else {
			for (VerificationOK v : verification)
				System.out.println(v);
		}
	}
	public void showCertificateInfo(X509Certificate cert, Date signDate) {
		System.out.println("Issuer: " + cert.getIssuerDN());
		System.out.println("Subject: " + cert.getSubjectDN());
		SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
		date_format.setTimeZone(TimeZone.getTimeZone("Universal"));
		System.out.println("Valid from: " + date_format.format(cert.getNotBefore()));
		System.out.println("Valid to: " + date_format.format(cert.getNotAfter()));
		try {
			cert.checkValidity(signDate);
			System.out.println("The certificate was valid at the time of signing.");
		} catch (CertificateExpiredException e) {
			System.out.println("The certificate was expired at the time of signing.");
		} catch (CertificateNotYetValidException e) {
			System.out.println("The certificate wasn't valid yet at the time of signing.");
		}
		try {
			cert.checkValidity();
			System.out.println("The certificate is still valid.");
		} catch (CertificateExpiredException e) {
			System.out.println("The certificate has expired.");
		} catch (CertificateNotYetValidException e) {
			System.out.println("The certificate isn't valid yet.");
		}
	}

	public static void main(String[] args) throws IOException, GeneralSecurityException 
	{
		String ADOBE = "D:/test/encryption/adobeRootCA.cer";
		String CACERT = "D:/test/encryption/CACertSigningAuthority.crt";
		String BRUNO = "D:/test/encryption/bruno.crt";
		// public static final String EXAMPLE1 = "results/chapter3/hello_cacert_ocsp_ts.pdf";
		//String EXAMPLE2 = "results/chapter3/hello_token.pdf";
		// public static final String EXAMPLE4 = "results/chapter4/hello_smartcard_Signature.pdf";


		BouncyCastleProvider provider = new BouncyCastleProvider();
		Security.addProvider(provider);
		VerifyCertificate app = new VerifyCertificate();
	
		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		ks.load(null, null);
		//从文件中读取证书   操作数字证书（x509） X.509 是密码学里公钥证书的格式标准
		CertificateFactory cf = CertificateFactory.getInstance("X.509");
		ks.setCertificateEntry("adobe", cf.generateCertificate(new FileInputStream(ADOBE)));
		ks.setCertificateEntry("cacert", cf.generateCertificate(new FileInputStream(CACERT)));
		ks.setCertificateEntry("bruno", cf.generateCertificate(new FileInputStream(BRUNO)));
	
		//从密钥库中直接读取证书
		String JKS = "D:/test/test.keystore";
		String EXAMPLE3 = "D:/test/target.pdf";
	    String pass = "123456";
	    FileInputStream in = new FileInputStream(JKS);
	    ks = KeyStore.getInstance("JKS");
	    ks.load(in,pass.toCharArray());
	    //java.security.cert.Certificate c=ks.getCertificate(alias);//alias为条目的别名
		
		app.setKeyStore(ks);

		 //app.verifySignatures(EXAMPLE1);
		// app.verifySignatures(EXAMPLE2);
		app.verifySignatures(EXAMPLE3);
		// app.verifySignatures(EXAMPLE4);
	}
	private void setKeyStore(KeyStore ks) {
		this.ks = ks;
	}
}
//https://blog.csdn.net/zhangrenhong/article/details/6178601