Skip to content

Commit 7934942

Browse files
author
Eugene Bochilo
committed
Support revocation data retrieval from DSS dictionary
DEVSIX-8304
1 parent 9c895e8 commit 7934942

34 files changed

+1063
-210
lines changed

sign/src/main/java/com/itextpdf/signatures/CertificateUtil.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,20 @@ public static CRL parseCrlFromStream(InputStream input) throws CertificateExcept
223223
return SignUtils.parseCrlFromStream(input);
224224
}
225225

226+
/**
227+
* Parses a CRL from bytes.
228+
*
229+
* @param crlBytes the bytes holding the unparsed CRL
230+
*
231+
* @return the parsed CRL object.
232+
*
233+
* @throws CertificateException thrown if there's no X509 implementation in the provider.
234+
* @throws CRLException thrown when encountering errors when parsing the CRL.
235+
*/
236+
public static CRL parseCrlFromBytes(byte[] crlBytes) throws CertificateException, CRLException {
237+
return SignUtils.parseCrlFromStream(new ByteArrayInputStream(crlBytes));
238+
}
239+
226240
/**
227241
* Retrieves the URL for the issuer certificate for the given CRL.
228242
*

sign/src/main/java/com/itextpdf/signatures/validation/v1/CRLValidator.java

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ This file is part of the iText (R) project.
4040
import com.itextpdf.signatures.validation.v1.context.ValidatorContext;
4141
import com.itextpdf.signatures.validation.v1.extensions.BasicConstraintsExtension;
4242
import com.itextpdf.signatures.validation.v1.report.CertificateReportItem;
43+
import com.itextpdf.signatures.validation.v1.report.ReportItem;
4344
import com.itextpdf.signatures.validation.v1.report.ReportItem.ReportItemStatus;
4445
import com.itextpdf.signatures.validation.v1.report.ValidationReport;
4546

@@ -54,8 +55,6 @@ This file is part of the iText (R) project.
5455
import java.util.HashMap;
5556
import java.util.Map;
5657

57-
import static com.itextpdf.signatures.validation.v1.RevocationDataValidator.SELF_SIGNED_CERTIFICATE;
58-
5958
/**
6059
* Class that allows you to validate a certificate against a Certificate Revocation List (CRL) Response.
6160
*/
@@ -79,8 +78,6 @@ public class CRLValidator {
7978
"not all reason codes are covered by the current CRL.";
8079
static final String SAME_REASONS_CHECK = "CRLs that cover the same reason codes were already verified.";
8180
static final String UPDATE_DATE_BEFORE_CHECK_DATE = "nextUpdate: {0} of CRLResponse is before validation date {1}.";
82-
static final String NEXT_UPDATE_VALIDATION = "Using crl nextUpdate date as validation date.";
83-
static final String THIS_UPDATE_VALIDATION = "Using crl thisUpdate date as validation date.";
8481

8582
// All reasons without unspecified.
8683
static final int ALL_REASONS = 32895;
@@ -111,18 +108,36 @@ protected CRLValidator(ValidatorChainBuilder builder) {
111108
* @param certificate the certificate to check against CRL response
112109
* @param crl the crl response to be validated
113110
* @param validationDate validation date to check for
111+
*
112+
* @deprecated starting from 8.0.5. TODO DEVSIX-8398 To be removed.
114113
*/
114+
@Deprecated
115115
public void validate(ValidationReport report, ValidationContext context, X509Certificate certificate, X509CRL crl,
116116
Date validationDate) {
117+
validate(report, context, certificate, crl, validationDate, DateTimeUtil.getCurrentTimeDate());
118+
}
119+
120+
/**
121+
* Validates a certificate against Certificate Revocation List (CRL) Responses.
122+
*
123+
* @param report to store all the chain verification results
124+
* @param context the context in which to perform the validation
125+
* @param certificate the certificate to check against CRL response
126+
* @param crl the crl response to be validated
127+
* @param validationDate validation date to check for
128+
* @param responseGenerationDate trusted date at which response is generated
129+
*/
130+
public void validate(ValidationReport report, ValidationContext context, X509Certificate certificate, X509CRL crl,
131+
Date validationDate, Date responseGenerationDate) {
117132
ValidationContext localContext = context.setValidatorContext(ValidatorContext.CRL_VALIDATOR);
118133
if (CertificateUtil.isSelfSigned(certificate)) {
119-
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK, SELF_SIGNED_CERTIFICATE,
120-
ReportItemStatus.INFO));
134+
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK,
135+
RevocationDataValidator.SELF_SIGNED_CERTIFICATE, ReportItemStatus.INFO));
121136
return;
122137
}
123-
// Check that thisUpdate >= (validationDate - freshness).
124138
Duration freshness = properties.getFreshness(localContext);
125-
if (crl.getThisUpdate().before(DateTimeUtil.addMillisToDate(validationDate, -(long) freshness.toMillis()))) {
139+
// Check that thisUpdate + freshness < validation.
140+
if (DateTimeUtil.addMillisToDate(crl.getThisUpdate(), (long) freshness.toMillis()).before(validationDate)) {
126141
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK,
127142
MessageFormatUtil.format(FRESHNESS_CHECK, crl.getThisUpdate(), validationDate, freshness),
128143
ReportItemStatus.INDETERMINATE));
@@ -175,16 +190,10 @@ public void validate(ValidationReport report, ValidationContext context, X509Cer
175190
Integer reasonsMask = checkedReasonsMask.get(certificate);
176191
if (reasonsMask != null) {
177192
interimReasonsMask |= (int) reasonsMask;
178-
179-
// Verify that interim_reasons_mask includes one or more reasons that are not included in the reasons_mask.
180-
if (interimReasonsMask == reasonsMask) {
181-
report.addReportItem(
182-
new CertificateReportItem(certificate, CRL_CHECK, SAME_REASONS_CHECK, ReportItemStatus.INFO));
183-
}
184193
}
185194

186195
// Verify the CRL issuer.
187-
verifyCrlIntegrity(report, localContext, certificate, crl);
196+
verifyCrlIntegrity(report, localContext, certificate, crl, responseGenerationDate);
188197

189198
// Check the status of the certificate.
190199
verifyRevocation(report, certificate, validationDate, crl);
@@ -272,7 +281,7 @@ private static int computeInterimReasonsMask(IIssuingDistributionPoint issuingDi
272281
}
273282

274283
private void verifyCrlIntegrity(ValidationReport report, ValidationContext context, X509Certificate certificate,
275-
X509CRL crl) {
284+
X509CRL crl, Date responseGenerationDate) {
276285
Certificate[] certs = certificateRetriever.getCrlIssuerCertificates(crl);
277286
if (certs.length == 0) {
278287
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_NOT_FOUND,
@@ -285,6 +294,7 @@ private void verifyCrlIntegrity(ValidationReport report, ValidationContext conte
285294
if (!crlIssuerRoot.equals(subjectRoot)) {
286295
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_NO_COMMON_ROOT,
287296
ReportItemStatus.INDETERMINATE));
297+
return;
288298
}
289299
try {
290300
crl.verify(crlIssuer.getPublicKey());
@@ -293,25 +303,23 @@ private void verifyCrlIntegrity(ValidationReport report, ValidationContext conte
293303
ReportItemStatus.INDETERMINATE));
294304
return;
295305
}
296-
// Ideally this date should be the date this response was retrieved from the server.
297-
Date crlIssuerDate;
298-
if (TimestampConstants.UNDEFINED_TIMESTAMP_DATE != crl.getNextUpdate()) {
299-
crlIssuerDate = crl.getNextUpdate();
300-
report.addReportItem(new CertificateReportItem((X509Certificate) crlIssuer, CRL_CHECK,
301-
NEXT_UPDATE_VALIDATION, ReportItemStatus.INFO));
302-
} else {
303-
crlIssuerDate = crl.getThisUpdate();
304-
report.addReportItem(new CertificateReportItem((X509Certificate) crlIssuer, CRL_CHECK,
305-
THIS_UPDATE_VALIDATION, ReportItemStatus.INFO));
306-
}
307306

308-
builder.getCertificateChainValidator().validate(report,
307+
ValidationReport responderReport = new ValidationReport();
308+
builder.getCertificateChainValidator().validate(responderReport,
309309
context.setCertificateSource(CertificateSource.CRL_ISSUER),
310-
(X509Certificate) crlIssuer, crlIssuerDate);
310+
(X509Certificate) crlIssuer, responseGenerationDate);
311+
addResponderValidationReport(report, responderReport);
311312
}
312313

313314
private Certificate getRoot(Certificate cert) {
314315
Certificate[] chain = certificateRetriever.retrieveMissingCertificates(new Certificate[] {cert});
315316
return chain[chain.length - 1];
316317
}
318+
319+
private static void addResponderValidationReport(ValidationReport report, ValidationReport responderReport) {
320+
for (ReportItem reportItem : responderReport.getLogs()) {
321+
report.addReportItem(ReportItemStatus.INVALID == reportItem.getStatus() ?
322+
reportItem.setStatus(ReportItemStatus.INDETERMINATE) : reportItem);
323+
}
324+
}
317325
}

sign/src/main/java/com/itextpdf/signatures/validation/v1/CertificateChainValidator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ public ValidationReport validate(ValidationReport result, ValidationContext cont
148148

149149
private boolean checkIfCertIsTrusted(ValidationReport result, ValidationContext context,
150150
X509Certificate certificate) {
151+
if (CertificateSource.TRUSTED == context.getCertificateSource()) {
152+
result.addReportItem(new CertificateReportItem(certificate, CERTIFICATE_CHECK, MessageFormatUtil.format(
153+
CERTIFICATE_TRUSTED, certificate.getSubjectX500Principal()), ReportItemStatus.INFO));
154+
return true;
155+
}
151156
TrustedCertificatesStore store = certificateRetriever.getTrustedCertificatesStore();
152157
if (store.isCertificateGenerallyTrusted(certificate)) {
153158
// Certificate is trusted for everything.

sign/src/main/java/com/itextpdf/signatures/validation/v1/DocumentRevisionsValidator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1195,8 +1195,8 @@ private static boolean comparePdfObjects(PdfObject pdfObject1, PdfObject pdfObje
11951195
if (pdfObject1.getClass() != pdfObject2.getClass()) {
11961196
return false;
11971197
}
1198-
// We don't allow objects to be direct and indirect.
1199-
// Acrobat however allows it, but such change can invalidate the document.
1198+
// We don't allow objects to change from being direct to indirect and vice versa.
1199+
// Acrobat allows it, but such change can invalidate the document.
12001200
if (pdfObject1.getIndirectReference() == null ^ pdfObject2.getIndirectReference() == null) {
12011201
return false;
12021202
}
@@ -1529,10 +1529,10 @@ private void addAllNestedArrayEntries(Set<PdfIndirectReference> allowedReference
15291529
allowedReferences.add(arrayEntry.getIndirectReference());
15301530

15311531
if (arrayEntry instanceof PdfDictionary) {
1532-
addAllNestedDictionaryEntries(allowedReferences, pdfArray.getAsDictionary(i));
1532+
addAllNestedDictionaryEntries(allowedReferences, (PdfDictionary) arrayEntry);
15331533
}
15341534
if (arrayEntry instanceof PdfArray) {
1535-
addAllNestedArrayEntries(allowedReferences, pdfArray.getAsArray(i));
1535+
addAllNestedArrayEntries(allowedReferences, (PdfArray) arrayEntry);
15361536
}
15371537
}
15381538
}

sign/src/main/java/com/itextpdf/signatures/validation/v1/OCSPValidator.java

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ This file is part of the iText (R) project.
4848
import java.time.Duration;
4949
import java.util.Date;
5050

51-
import static com.itextpdf.signatures.validation.v1.RevocationDataValidator.SELF_SIGNED_CERTIFICATE;
52-
5351
/**
5452
* Class that allows you to validate a single OCSP response.
5553
*/
@@ -98,13 +96,32 @@ protected OCSPValidator(ValidatorChainBuilder builder) {
9896
* @param singleResp single response to check
9997
* @param ocspResp basic OCSP response which contains single response to check
10098
* @param validationDate validation date to check for
99+
*
100+
* @deprecated starting from 8.0.5. TODO DEVSIX-8398 To be removed.
101101
*/
102+
@Deprecated
102103
public void validate(ValidationReport report, ValidationContext context, X509Certificate certificate,
103104
ISingleResp singleResp, IBasicOCSPResp ocspResp, Date validationDate) {
105+
validate(report, context, certificate, singleResp, ocspResp, validationDate, DateTimeUtil.getCurrentTimeDate());
106+
}
107+
108+
/**
109+
* Validates a certificate against single OCSP Response.
110+
*
111+
* @param report to store all the chain verification results
112+
* @param context the context in which to perform the validation
113+
* @param certificate the certificate to check for
114+
* @param singleResp single response to check
115+
* @param ocspResp basic OCSP response which contains single response to check
116+
* @param validationDate validation date to check for
117+
* @param responseGenerationDate trusted date at which response is generated
118+
*/
119+
public void validate(ValidationReport report, ValidationContext context, X509Certificate certificate,
120+
ISingleResp singleResp, IBasicOCSPResp ocspResp, Date validationDate, Date responseGenerationDate) {
104121
ValidationContext localContext = context.setValidatorContext(ValidatorContext.OCSP_VALIDATOR);
105122
if (CertificateUtil.isSelfSigned(certificate)) {
106-
report.addReportItem(new CertificateReportItem(certificate, OCSP_CHECK, SELF_SIGNED_CERTIFICATE,
107-
ReportItemStatus.INFO));
123+
report.addReportItem(new CertificateReportItem(certificate, OCSP_CHECK,
124+
RevocationDataValidator.SELF_SIGNED_CERTIFICATE, ReportItemStatus.INFO));
108125
return;
109126
}
110127
// SingleResp contains the basic information of the status of the certificate identified by the certID.
@@ -131,10 +148,10 @@ public void validate(ValidationReport report, ValidationContext context, X509Cer
131148
// So, since the issuer name and serial number identify a unique certificate, we found the single response
132149
// for the provided certificate.
133150

134-
// Check that thisUpdate >= (validationDate - freshness).
135151
Duration freshness = properties.getFreshness(localContext);
136-
if (singleResp.getThisUpdate().before(
137-
DateTimeUtil.addMillisToDate(validationDate, -(long)freshness.toMillis()))) {
152+
// Check that thisUpdate + freshness < validation.
153+
if (DateTimeUtil.addMillisToDate(singleResp.getThisUpdate(), (long) freshness.toMillis())
154+
.before(validationDate)) {
138155
report.addReportItem(new CertificateReportItem(certificate, OCSP_CHECK,
139156
MessageFormatUtil.format(FRESHNESS_CHECK, singleResp.getThisUpdate(), validationDate,
140157
freshness), ReportItemStatus.INDETERMINATE));
@@ -169,7 +186,7 @@ public void validate(ValidationReport report, ValidationContext context, X509Cer
169186

170187
if (isStatusGood || (revokedStatus != null && validationDate.before(revokedStatus.getRevocationTime()))) {
171188
// Check if the OCSP response is genuine.
172-
verifyOcspResponder(report, localContext, ocspResp, (X509Certificate) issuerCert);
189+
verifyOcspResponder(report, localContext, ocspResp, (X509Certificate) issuerCert, responseGenerationDate);
173190
if (!isStatusGood) {
174191
report.addReportItem(new CertificateReportItem(certificate, OCSP_CHECK,
175192
MessageFormatUtil.format(SignLogMessageConstant.VALID_CERTIFICATE_IS_REVOKED,
@@ -195,7 +212,7 @@ public void validate(ValidationReport report, ValidationContext context, X509Cer
195212
* @param issuerCert the issuer of the certificate for which the OCSP is checked
196213
*/
197214
private void verifyOcspResponder(ValidationReport report, ValidationContext context, IBasicOCSPResp ocspResp,
198-
X509Certificate issuerCert) {
215+
X509Certificate issuerCert, Date responseGenerationDate) {
199216
ValidationContext localContext = context.setCertificateSource(CertificateSource.OCSP_ISSUER);
200217
ValidationReport responderReport = new ValidationReport();
201218

@@ -236,18 +253,21 @@ private void verifyOcspResponder(ValidationReport report, ValidationContext cont
236253
// RFC6960 4.2.2.2.1. Revocation Checking of an Authorized Responder.
237254
builder.getCertificateChainValidator().validate(responderReport,
238255
localContext,
239-
responderCert, ocspResp.getProducedAt());
240-
addResponderValidationReport(report, responderReport);
241-
return;
256+
responderCert, responseGenerationDate);
257+
} else {
258+
builder.getCertificateChainValidator().validate(responderReport,
259+
localContext.setCertificateSource(CertificateSource.TRUSTED),
260+
responderCert, responseGenerationDate);
242261
}
262+
} else {
263+
builder.getCertificateChainValidator().validate(responderReport,
264+
localContext.setCertificateSource(CertificateSource.CERT_ISSUER),
265+
responderCert, responseGenerationDate);
243266
}
244-
builder.getCertificateChainValidator().validate(responderReport,
245-
localContext.setCertificateSource(CertificateSource.TRUSTED),
246-
responderCert, ocspResp.getProducedAt());
247267
addResponderValidationReport(report, responderReport);
248268
}
249269

250-
private void addResponderValidationReport(ValidationReport report, ValidationReport responderReport) {
270+
private static void addResponderValidationReport(ValidationReport report, ValidationReport responderReport) {
251271
for (ReportItem reportItem : responderReport.getLogs()) {
252272
report.addReportItem(ReportItemStatus.INVALID == reportItem.getStatus() ?
253273
reportItem.setStatus(ReportItemStatus.INDETERMINATE) : reportItem);

0 commit comments

Comments
 (0)