/**
* DSS - Digital Signature Services
* Copyright (C) 2015 European Commission, provided under the CEF programme
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package eu.europa.esig.dss.validation;
import java.security.cert.X509CRL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPResp;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSRevocationUtils;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.SignatureLevel;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.x509.CertificatePool;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.x509.RevocationOrigin;
import eu.europa.esig.dss.x509.RevocationToken;
import eu.europa.esig.dss.x509.SignaturePolicy;
import eu.europa.esig.dss.x509.crl.CRLToken;
import eu.europa.esig.dss.x509.crl.ListCRLSource;
import eu.europa.esig.dss.x509.crl.OfflineCRLSource;
import eu.europa.esig.dss.x509.ocsp.ListOCSPSource;
import eu.europa.esig.dss.x509.ocsp.OCSPToken;
import eu.europa.esig.dss.x509.ocsp.OfflineOCSPSource;
public abstract class DefaultAdvancedSignature implements AdvancedSignature {
/**
* This is the reference to the global (external) pool of certificates. All encapsulated certificates in the
* signature are added to this pool. See {@link
* eu.europa.esig.dss.x509.CertificatePool}
*/
protected final CertificatePool certPool;
/**
* In the case of a non AdES signature the signing certificate is not mandatory within the signature and can be
* provided by the driving application.
*/
protected CertificateToken providedSigningCertificateToken;
/**
* In case of a detached signature this is the signed document.
*/
protected List<DSSDocument> detachedContents;
/**
* This variable contains the result of the signature mathematical validation. It is initialised when the method
* {@code checkSignatureIntegrity} is called.
*/
protected SignatureCryptographicVerification signatureCryptographicVerification;
protected String structureValidation;
/**
* The reference to the object containing all candidates to the signing certificate.
*/
protected CandidatesForSigningCertificate candidatesForSigningCertificate;
// Enclosed content timestamps.
protected List<TimestampToken> contentTimestamps;
// Enclosed signature timestamps.
protected transient List<TimestampToken> signatureTimestamps;
// Enclosed SignAndRefs timestamps.
protected List<TimestampToken> sigAndRefsTimestamps;
// Enclosed RefsOnly timestamps.
protected List<TimestampToken> refsOnlyTimestamps;
// This variable contains the list of enclosed archive signature timestamps.
protected List<TimestampToken> archiveTimestamps;
// Cached {@code OfflineCRLSource}
protected OfflineCRLSource offlineCRLSource;
// Cached {@code OfflineOCSPSource}
protected OfflineOCSPSource offlineOCSPSource;
private AdvancedSignature masterSignature;
protected SignaturePolicy signaturePolicy;
/**
* This list represents all digest algorithms used to calculate the digest values of certificates.
*/
protected Set<DigestAlgorithm> usedCertificatesDigestAlgorithms = new HashSet<DigestAlgorithm>();
private List<SignatureScope> signatureScopes;
private String signatureFilename;
/**
* @param certPool
* can be null
*/
protected DefaultAdvancedSignature(final CertificatePool certPool) {
this.certPool = certPool;
}
@Override
public String getSignatureFilename() {
return signatureFilename;
}
@Override
public void setSignatureFilename(String signatureFilename) {
this.signatureFilename = signatureFilename;
}
@Override
public List<DSSDocument> getDetachedContents() {
return detachedContents;
}
@Override
public void setDetachedContents(final List<DSSDocument> detachedContents) {
this.detachedContents = detachedContents;
}
/**
* @return the upper level for which data have been found. Doesn't mean any validity of the data found. Null if
* unknown.
*/
@Override
public SignatureLevel getDataFoundUpToLevel() {
final SignatureLevel[] signatureLevels = getSignatureLevels();
final SignatureLevel dataFoundUpToProfile = getDataFoundUpToProfile(signatureLevels);
return dataFoundUpToProfile;
}
/**
* This method returns the {@code SignatureLevel} which was reached.
*
* @param signatureLevels
* the array of the all levels associated with the given signature type
* @return {@code SignatureLevel}
*/
private SignatureLevel getDataFoundUpToProfile(final SignatureLevel... signatureLevels) {
for (int ii = signatureLevels.length - 1; ii >= 0; ii--) {
final SignatureLevel signatureLevel = signatureLevels[ii];
if (isDataForSignatureLevelPresent(signatureLevel)) {
return signatureLevel;
}
}
return null;
}
/**
* This method validates the signing certificate and all timestamps.
*
* @return signature validation context containing all certificates and revocation data used during the validation
* process.
*/
public ValidationContext getSignatureValidationContext(final CertificateVerifier certificateVerifier) {
final ValidationContext validationContext = new SignatureValidationContext();
final List<CertificateToken> certificates = getCertificates();
for (final CertificateToken certificate : certificates) {
validationContext.addCertificateTokenForVerification(certificate);
}
prepareTimestamps(validationContext);
certificateVerifier.setSignatureCRLSource(new ListCRLSource(getCRLSource()));
certificateVerifier.setSignatureOCSPSource(new ListOCSPSource(getOCSPSource()));
// certificateVerifier.setAdjunctCertSource(getCertificateSource());
validationContext.initialize(certificateVerifier);
validationContext.validate();
return validationContext;
}
/**
* This method returns all certificates used during the validation process. If a certificate is already present
* within the signature then it is ignored.
*
* @param validationContext
* validation context containing all information about the validation process of the signing certificate
* and time-stamps
* @return set of certificates not yet present within the signature
*/
public Set<CertificateToken> getCertificatesForInclusion(final ValidationContext validationContext) {
final Set<CertificateToken> certificates = new HashSet<CertificateToken>();
final List<CertificateToken> certWithinSignatures = getCertificatesWithinSignatureAndTimestamps();
for (final CertificateToken certificateToken : validationContext.getProcessedCertificates()) {
if (certWithinSignatures.contains(certificateToken)) {
continue;
}
certificates.add(certificateToken);
}
return certificates;
}
public List<CertificateToken> getCertificatesWithinSignatureAndTimestamps() {
final List<CertificateToken> certWithinSignatures = new ArrayList<CertificateToken>();
certWithinSignatures.addAll(getCertificates());
for (final TimestampToken timestampToken : getSignatureTimestamps()) {
certWithinSignatures.addAll(timestampToken.getCertificates());
}
for (final TimestampToken timestampToken : getArchiveTimestamps()) {
certWithinSignatures.addAll(timestampToken.getCertificates());
}
for (final TimestampToken timestampToken : getContentTimestamps()) {
certWithinSignatures.addAll(timestampToken.getCertificates());
}
for (final TimestampToken timestampToken : getTimestampsX1()) {
certWithinSignatures.addAll(timestampToken.getCertificates());
}
for (final TimestampToken timestampToken : getTimestampsX2()) {
certWithinSignatures.addAll(timestampToken.getCertificates());
}
return certWithinSignatures;
}
/**
* This method returns revocation values (ocsp and crl) that will be included in the LT profile.
*
* @param validationContext
* {@code ValidationContext} contains all the revocation data retrieved during the validation process.
* @return {@code RevocationDataForInclusion}
*/
public RevocationDataForInclusion getRevocationDataForInclusion(final ValidationContext validationContext) {
// TODO: to be checked: there can be also CRL and OCSP in TimestampToken CMS data
final Set<RevocationToken> revocationTokens = validationContext.getProcessedRevocations();
final List<CRLToken> crlTokens = new ArrayList<CRLToken>();
final List<OCSPToken> ocspTokens = new ArrayList<OCSPToken>();
for (final RevocationToken revocationToken : revocationTokens) {
if (!RevocationOrigin.SIGNATURE.equals(revocationToken.getOrigin())) {
if (revocationToken instanceof CRLToken) {
final CRLToken crlToken = (CRLToken) revocationToken;
crlTokens.add(crlToken);
} else if (revocationToken instanceof OCSPToken) {
final OCSPToken ocspToken = (OCSPToken) revocationToken;
ocspTokens.add(ocspToken);
} else {
throw new DSSException("Unknown type for revocationToken: " + revocationToken.getClass().getName());
}
}
}
return new RevocationDataForInclusion(crlTokens, ocspTokens);
}
@Override
public void setMasterSignature(final AdvancedSignature masterSignature) {
this.masterSignature = masterSignature;
}
@Override
public AdvancedSignature getMasterSignature() {
return masterSignature;
}
@Override
public SignatureCryptographicVerification getSignatureCryptographicVerification() {
if (signatureCryptographicVerification == null) {
checkSignatureIntegrity();
}
return signatureCryptographicVerification;
}
public static class RevocationDataForInclusion {
public final List<CRLToken> crlTokens;
public final List<OCSPToken> ocspTokens;
public RevocationDataForInclusion(final List<CRLToken> crlTokens, final List<OCSPToken> ocspTokens) {
this.crlTokens = crlTokens;
this.ocspTokens = ocspTokens;
}
public boolean isEmpty() {
return crlTokens.isEmpty() && ocspTokens.isEmpty();
}
}
@Override
public CertificateToken getProvidedSigningCertificateToken() {
return providedSigningCertificateToken;
}
@Override
public void setProvidedSigningCertificateToken(final CertificateToken certificateToken) {
this.providedSigningCertificateToken = certificateToken;
}
@Override
public CertificateToken getSigningCertificateToken() {
// This ensures that the variable candidatesForSigningCertificate has been initialized
candidatesForSigningCertificate = getCandidatesForSigningCertificate();
// This ensures that the variable signatureCryptographicVerification has been initialized
checkSignatureIntegrity();
signatureCryptographicVerification = getSignatureCryptographicVerification();
final CertificateValidity theCertificateValidity = candidatesForSigningCertificate.getTheCertificateValidity();
if (theCertificateValidity != null) {
if (theCertificateValidity.isValid()) {
final CertificateToken signingCertificateToken = theCertificateValidity.getCertificateToken();
return signingCertificateToken;
}
}
final CertificateValidity theBestCandidate = candidatesForSigningCertificate.getTheBestCandidate();
return theBestCandidate == null ? null : theBestCandidate.getCertificateToken();
}
/**
* This method adds to the {@code ValidationContext} all timestamps to be validated.
*
* @param validationContext
* {@code ValidationContext} to which the timestamps must be added
*/
@Override
public void prepareTimestamps(final ValidationContext validationContext) {
/*
* This validates the signature timestamp tokens present in the signature.
*/
for (final TimestampToken timestampToken : getContentTimestamps()) {
validationContext.addTimestampTokenForVerification(timestampToken);
}
/*
* This validates the signature timestamp tokens present in the signature.
*/
for (final TimestampToken timestampToken : getSignatureTimestamps()) {
validationContext.addTimestampTokenForVerification(timestampToken);
}
/*
* This validates the SigAndRefs timestamp tokens present in the signature.
*/
for (final TimestampToken timestampToken : getTimestampsX1()) {
validationContext.addTimestampTokenForVerification(timestampToken);
}
/*
* This validates the RefsOnly timestamp tokens present in the signature.
*/
for (final TimestampToken timestampToken : getTimestampsX2()) {
validationContext.addTimestampTokenForVerification(timestampToken);
}
/*
* This validates the archive timestamp tokens present in the signature.
*/
for (final TimestampToken timestampToken : getArchiveTimestamps()) {
validationContext.addTimestampTokenForVerification(timestampToken);
}
}
/**
* This method adds all timestamps to be validated.
*/
@Override
public void validateTimestamps() {
/*
* This validates the content-timestamp tokensToProcess present in the signature.
*/
for (final TimestampToken timestampToken : getContentTimestamps()) {
final byte[] timestampBytes = getContentTimestampData(timestampToken);
timestampToken.matchData(timestampBytes);
}
/*
* This validates the signature timestamp tokensToProcess present in the signature.
*/
for (final TimestampToken timestampToken : getSignatureTimestamps()) {
final byte[] timestampBytes = getSignatureTimestampData(timestampToken, null);
timestampToken.matchData(timestampBytes);
}
/*
* This validates the SigAndRefs timestamp tokensToProcess present in the signature.
*/
for (final TimestampToken timestampToken : getTimestampsX1()) {
final byte[] timestampBytes = getTimestampX1Data(timestampToken, null);
timestampToken.matchData(timestampBytes);
}
/*
* This validates the RefsOnly timestamp tokensToProcess present in the signature.
*/
for (final TimestampToken timestampToken : getTimestampsX2()) {
final byte[] timestampBytes = getTimestampX2Data(timestampToken, null);
timestampToken.matchData(timestampBytes);
}
/*
* This validates the archive timestamp tokensToProcess present in the signature.
*/
for (final TimestampToken timestampToken : getArchiveTimestamps()) {
final byte[] timestampData = getArchiveTimestampData(timestampToken, null);
timestampToken.matchData(timestampData);
}
}
@Override
public void validateStructure() {
}
@Override
public String getStructureValidationResult() {
return structureValidation;
}
/**
* This method adds references to retrieved OCSP responses from LT level. With LTA level, we have a proof of
* existence
*
* @param references
*/
protected void addReferencesFromOfflineOCSPSource(List<TimestampReference> references) {
OfflineOCSPSource ocspSource = getOCSPSource();
if (ocspSource != null) {
List<BasicOCSPResp> containedOCSPResponses = ocspSource.getContainedOCSPResponses();
if (Utils.isCollectionNotEmpty(containedOCSPResponses)) {
usedCertificatesDigestAlgorithms.add(DigestAlgorithm.SHA1);
for (BasicOCSPResp basicOCSPResp : containedOCSPResponses) {
OCSPResp ocspResp = DSSRevocationUtils.fromBasicToResp(basicOCSPResp);
final byte[] digest = DSSUtils.digest(DigestAlgorithm.SHA1, DSSUtils.getEncoded(ocspResp));
references.add(new TimestampReference(DigestAlgorithm.SHA1, Utils.toBase64(digest), TimestampReferenceCategory.REVOCATION));
}
}
}
}
/**
* This method adds references to retrieved CRL responses from LT level. With LTA level, we have a proof of
* existence
*
* @param references
*/
protected void addReferencesFromOfflineCRLSource(List<TimestampReference> references) {
OfflineCRLSource crlSource = getCRLSource();
if (crlSource != null) {
List<X509CRL> containedX509CRLs = crlSource.getContainedX509CRLs();
if (Utils.isCollectionNotEmpty(containedX509CRLs)) {
usedCertificatesDigestAlgorithms.add(DigestAlgorithm.SHA1);
for (X509CRL x509crl : containedX509CRLs) {
final byte[] digest = DSSUtils.digest(DigestAlgorithm.SHA1, DSSUtils.getEncoded(x509crl));
references.add(new TimestampReference(DigestAlgorithm.SHA1, Utils.toBase64(digest), TimestampReferenceCategory.REVOCATION));
}
}
}
}
@Override
public Set<DigestAlgorithm> getUsedCertificatesDigestAlgorithms() {
return usedCertificatesDigestAlgorithms;
}
@Override
public SignaturePolicy getPolicyId() {
return signaturePolicy;
}
@Override
public void checkSignaturePolicy(SignaturePolicyProvider signaturePolicyDetector) {
}
@Override
public void findSignatureScope(SignatureScopeFinder signatureScopeFinder) {
signatureScopes = signatureScopeFinder.findSignatureScope(this);
}
@Override
public List<SignatureScope> getSignatureScopes() {
return signatureScopes;
}
}