Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2023 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package com.itextpdf.signatures;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.io.util.DateTimeUtil;
import com.itextpdf.io.util.MessageFormatUtil;
import com.itextpdf.kernel.counter.event.IMetaInfo;
import com.itextpdf.kernel.pdf.DocumentProperties;
import com.itextpdf.kernel.pdf.PdfArray;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfStream;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPException;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import static com.itextpdf.signatures.LtvVerification.CertificateOption;
/**
* Verifies the signatures in an LTV document.
*/
public class LtvVerifier extends RootStoreVerifier {
/** The Logger instance */
protected static final Logger LOGGER = LoggerFactory.getLogger(LtvVerifier.class);
/** Option to specify level of verification; signing certificate only or the entire chain. */
protected CertificateOption option = CertificateOption.SIGNING_CERTIFICATE;
/** Verify root. */
protected boolean verifyRootCertificate = true;
/** A document object for the revision that is being verified. */
protected PdfDocument document;
/** The fields in the revision that is being verified. */
protected PdfAcroForm acroForm;
/** The date the revision was signed, or null for the highest revision. */
protected Date signDate;
/** The signature that covers the revision. */
protected String signatureName;
/** The PdfPKCS7 object for the signature. */
protected PdfPKCS7 pkcs7;
/** Indicates if we're working with the latest revision. */
protected boolean latestRevision = true;
/** The document security store for the revision that is being verified */
protected PdfDictionary dss;
/** Security provider to use, use null for default*/
protected String securityProviderCode = null;
/** The meta info */
protected IMetaInfo metaInfo;
private SignatureUtil sgnUtil;
/**
* Creates a VerificationData object for a PdfReader
* @param document The document we want to verify.
* @throws GeneralSecurityException if some problem with signature or security are occurred
*/
public LtvVerifier(PdfDocument document) throws GeneralSecurityException {
super(null);
initLtvVerifier(document);
}
public LtvVerifier(PdfDocument document, String securityProviderCode) throws GeneralSecurityException {
super(null);
this.securityProviderCode = securityProviderCode;
initLtvVerifier(document);
}
/**
* Sets an extra verifier.
* @param verifier the verifier to set
*/
public void setVerifier(CertificateVerifier verifier) {
this.verifier = verifier;
}
/**
* Sets the certificate option.
* @param option Either CertificateOption.SIGNING_CERTIFICATE (default) or CertificateOption.WHOLE_CHAIN
*/
public void setCertificateOption(CertificateOption option) {
this.option = option;
}
/**
* Set the verifyRootCertificate to false if you can't verify the root certificate.
*
* @param verifyRootCertificate false if you can't verify the root certificate, otherwise true
*/
public void setVerifyRootCertificate(boolean verifyRootCertificate) {
this.verifyRootCertificate = verifyRootCertificate;
}
/**
* Sets the {@link IMetaInfo} that will be used during {@link PdfDocument} creation.
*
* @param metaInfo meta info to set
*/
public void setEventCountingMetaInfo(IMetaInfo metaInfo) {
this.metaInfo = metaInfo;
}
/**
* Verifies all the document-level timestamps and all the signatures in the document.
*
* @param result a list of {@link VerificationOK} objects
* @return a list of all {@link VerificationOK} objects after verification
* @throws IOException signals that an I/O exception has occurred
* @throws GeneralSecurityException if some problems with signature or security occurred
*/
public List verify(List result) throws IOException, GeneralSecurityException {
if (result == null)
result = new ArrayList<>();
while (pkcs7 != null) {
result.addAll(verifySignature());
}
return result;
}
/**
* Verifies a document level timestamp.
*
* @return a list of {@link VerificationOK} objects
* @throws GeneralSecurityException if some problems with signature or security occurred
* @throws IOException signals that an I/O exception has occurred
*/
public List verifySignature() throws GeneralSecurityException, IOException {
LOGGER.info("Verifying signature.");
List result = new ArrayList<>();
// Get the certificate chain
Certificate[] chain = pkcs7.getSignCertificateChain();
verifyChain(chain);
// how many certificates in the chain do we need to check?
int total = 1;
if (CertificateOption.WHOLE_CHAIN.equals(option)) {
total = chain.length;
}
// loop over the certificates
X509Certificate signCert;
X509Certificate issuerCert;
for (int i = 0; i < total; ) {
// the certificate to check
signCert = (X509Certificate) chain[i++];
// its issuer
issuerCert = (X509Certificate) null;
if (i < chain.length)
issuerCert = (X509Certificate) chain[i];
// now lets verify the certificate
LOGGER.info(signCert.getSubjectDN().getName());
List list = verify(signCert, issuerCert, signDate);
if (list.size() == 0) {
try {
signCert.verify(signCert.getPublicKey());
if (latestRevision && chain.length > 1) {
list.add(new VerificationOK(signCert, this.getClass(), "Root certificate in final revision"));
}
if (list.size() == 0 && verifyRootCertificate) {
throw new GeneralSecurityException();
}
else if (chain.length > 1)
list.add(new VerificationOK(signCert, this.getClass(), "Root certificate passed without checking"));
}
catch(GeneralSecurityException e) {
throw new VerificationException(signCert, "Couldn't verify with CRL or OCSP or trusted anchor");
}
}
result.addAll(list);
}
// go to the previous revision
switchToPreviousRevision();
return result;
}
/**
* Checks the certificates in a certificate chain:
* are they valid on a specific date, and
* do they chain up correctly?
* @param chain the certificate chain
* @throws GeneralSecurityException when requested cryptographic algorithm or security provider
* is not available, if the certificate is invalid on a specific date and if the certificates
* chained up incorrectly
*/
public void verifyChain(Certificate[] chain) throws GeneralSecurityException {
// Loop over the certificates in the chain
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = (X509Certificate) chain[i];
// check if the certificate was/is valid
cert.checkValidity(signDate);
// check if the previous certificate was issued by this certificate
if (i > 0)
chain[i-1].verify(chain[i].getPublicKey());
}
LOGGER.info("All certificates are valid on " + signDate.toString());
}
/**
* Verifies certificates against a list of CRLs and OCSP responses.
* @param signCert the signing certificate
* @param issuerCert the issuer's certificate
* @return a list of VerificationOK objects.
* The list will be empty if the certificate couldn't be verified.
* @throws GeneralSecurityException if some problems with signature or security occurred
* @throws IOException signals that an I/O exception has occurred
* @see com.itextpdf.signatures.RootStoreVerifier#verify(java.security.cert.X509Certificate, java.security.cert.X509Certificate, java.util.Date)
*/
public List verify(X509Certificate signCert, X509Certificate issuerCert, Date signDate) throws GeneralSecurityException, IOException {
// we'll verify agains the rootstore (if present)
RootStoreVerifier rootStoreVerifier = new RootStoreVerifier(verifier);
rootStoreVerifier.setRootStore(rootStore);
// We'll verify against a list of CRLs
CRLVerifier crlVerifier = new CRLVerifier(rootStoreVerifier, getCRLsFromDSS());
crlVerifier.setRootStore(rootStore);
crlVerifier.setOnlineCheckingAllowed(latestRevision || onlineCheckingAllowed);
// We'll verify against a list of OCSPs
OCSPVerifier ocspVerifier = new OCSPVerifier(crlVerifier, getOCSPResponsesFromDSS());
ocspVerifier.setRootStore(rootStore);
ocspVerifier.setOnlineCheckingAllowed(latestRevision || onlineCheckingAllowed);
// We verify the chain
return ocspVerifier.verify(signCert, issuerCert, signDate);
}
/**
* Switches to the previous revision.
* @throws IOException signals that an I/O exception has occurred
* @throws GeneralSecurityException if some problems with signature or security occurred
*/
public void switchToPreviousRevision() throws IOException, GeneralSecurityException {
LOGGER.info("Switching to previous revision.");
latestRevision = false;
dss = document.getCatalog().getPdfObject().getAsDictionary(PdfName.DSS);
Calendar cal = pkcs7.getTimeStampDate();
if (cal == TimestampConstants.UNDEFINED_TIMESTAMP_DATE) {
cal = pkcs7.getSignDate();
}
// TODO: get date from signature
signDate = cal.getTime();
List names = sgnUtil.getSignatureNames();
if (names.size() > 1) {
signatureName = names.get(names.size() - 2);
try (PdfReader readerTmp = new PdfReader(sgnUtil.extractRevision(signatureName))) {
document = new PdfDocument(readerTmp, new DocumentProperties().setEventCountingMetaInfo(metaInfo));
this.acroForm = PdfAcroForm.getAcroForm(document, true);
this.sgnUtil = new SignatureUtil(document);
names = sgnUtil.getSignatureNames();
signatureName = names.get(names.size() - 1);
pkcs7 = coversWholeDocument();
LOGGER.info(MessageFormatUtil.format("Checking {0}signature {1}", pkcs7.isTsp() ? "document-level timestamp " : "", signatureName));
}
}
else {
LOGGER.info("No signatures in revision");
pkcs7 = null;
}
}
/**
* Gets a list of X509CRL objects from a Document Security Store.
* @return a list of CRLs
* @throws GeneralSecurityException when requested cryptographic algorithm or security provider
* is not available
* @throws IOException signals that an I/O exception has occurred
*/
public List getCRLsFromDSS() throws GeneralSecurityException, IOException {
List crls = new ArrayList<>();
if (dss == null)
return crls;
PdfArray crlarray = dss.getAsArray(PdfName.CRLs);
if (crlarray == null)
return crls;
for (int i = 0; i < crlarray.size(); i++) {
PdfStream stream = crlarray.getAsStream(i);
crls.add((X509CRL) SignUtils.parseCrlFromStream(new ByteArrayInputStream(stream.getBytes())));
}
return crls;
}
/**
* Gets OCSP responses from the Document Security Store.
* @return a list of BasicOCSPResp objects
* @throws IOException signals that an I/O exception has occurred
* @throws GeneralSecurityException if OCSP response failed
*/
public List getOCSPResponsesFromDSS() throws IOException, GeneralSecurityException {
List ocsps = new ArrayList<>();
if (dss == null)
return ocsps;
PdfArray ocsparray = dss.getAsArray(PdfName.OCSPs);
if (ocsparray == null)
return ocsps;
for (int i = 0; i < ocsparray.size(); i++) {
PdfStream stream = ocsparray.getAsStream(i);
OCSPResp ocspResponse = new OCSPResp(stream.getBytes());
if (ocspResponse.getStatus() == 0)
try {
ocsps.add((BasicOCSPResp) ocspResponse.getResponseObject());
} catch (OCSPException e) {
throw new GeneralSecurityException(e.toString());
}
}
return ocsps;
}
protected void initLtvVerifier(PdfDocument document) throws GeneralSecurityException {
this.document = document;
this.acroForm = PdfAcroForm.getAcroForm(document, true);
this.sgnUtil = new SignatureUtil(document);
List names = sgnUtil.getSignatureNames();
signatureName = names.get(names.size() - 1);
this.signDate = DateTimeUtil.getCurrentTimeDate();
pkcs7 = coversWholeDocument();
LOGGER.info(MessageFormatUtil.format("Checking {0}signature {1}", pkcs7.isTsp() ? "document-level timestamp " : "", signatureName));
}
/**
* Checks if the signature covers the whole document
* and throws an exception if the document was altered
* @return a PdfPKCS7 object
* @throws GeneralSecurityException if some problems with signature or security occurred
*/
protected PdfPKCS7 coversWholeDocument() throws GeneralSecurityException {
PdfPKCS7 pkcs7 = sgnUtil.readSignatureData(signatureName, securityProviderCode);
if (sgnUtil.signatureCoversWholeDocument(signatureName)) {
LOGGER.info("The timestamp covers whole document.");
}
else {
throw new VerificationException((Certificate) null, "Signature doesn't cover whole document.");
}
if (pkcs7.verifySignatureIntegrityAndAuthenticity()) {
LOGGER.info("The signed document has not been modified.");
return pkcs7;
}
else {
throw new VerificationException((Certificate) null, "The document was altered after the final signature was applied.");
}
}
}