Skip to content

Commit b8c5e6b

Browse files
committed
Check wether the validated certificate is part of the crl issuer chain
DEVSIX-8568
1 parent 1579f6d commit b8c5e6b

File tree

31 files changed

+985
-369
lines changed

31 files changed

+985
-369
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ This file is part of the iText (R) project.
5151
import java.security.cert.X509CRLEntry;
5252
import java.security.cert.X509Certificate;
5353
import java.time.Duration;
54+
import java.util.Arrays;
5455
import java.util.Date;
5556
import java.util.HashMap;
5657
import java.util.Map;
@@ -85,6 +86,8 @@ public class CRLValidator {
8586
"not all reason codes are covered by the current CRL.";
8687
static final String SAME_REASONS_CHECK = "CRLs that cover the same reason codes were already verified.";
8788
static final String UPDATE_DATE_BEFORE_CHECK_DATE = "nextUpdate: {0} of CRLResponse is before validation date {1}.";
89+
static final String CERTIFICATE_IN_ISSUER_CHAIN = "Unable to validate CRL response: validated certificate is"
90+
+ " part of issuer certificate chain.";
8891

8992
// All reasons without unspecified.
9093
static final int ALL_REASONS = 32895;
@@ -120,6 +123,7 @@ protected CRLValidator(ValidatorChainBuilder builder) {
120123
public void validate(ValidationReport report, ValidationContext context, X509Certificate certificate, X509CRL crl,
121124
Date validationDate, Date responseGenerationDate) {
122125
ValidationContext localContext = context.setValidatorContext(ValidatorContext.CRL_VALIDATOR);
126+
123127
if (CertificateUtil.isSelfSigned(certificate)) {
124128
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK,
125129
RevocationDataValidator.SELF_SIGNED_CERTIFICATE, ReportItemStatus.INFO));
@@ -281,13 +285,17 @@ private void verifyCrlIntegrity(ValidationReport report, ValidationContext conte
281285
ReportItemStatus.INDETERMINATE));
282286
return;
283287
}
284-
285288
if (certs == null || certs.length == 0) {
286289
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK, CRL_ISSUER_NOT_FOUND,
287290
ReportItemStatus.INDETERMINATE));
288291
return;
289292
}
290293

294+
if (Arrays.stream(certs).anyMatch(c -> c.equals(certificate))) {
295+
report.addReportItem(new CertificateReportItem(certificate, CRL_CHECK, CERTIFICATE_IN_ISSUER_CHAIN,
296+
ReportItemStatus.INDETERMINATE));
297+
return;
298+
}
291299
Certificate crlIssuer = certs[0];
292300
Certificate crlIssuerRoot = getRoot(crlIssuer);
293301
Certificate subjectRoot = getRoot(certificate);

sign/src/test/java/com/itextpdf/signatures/validation/RevocationDataValidatorIntegrationTest.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ This file is part of the iText (R) project.
2727
import com.itextpdf.commons.bouncycastle.operator.AbstractOperatorCreationException;
2828
import com.itextpdf.commons.bouncycastle.pkcs.AbstractPKCSException;
2929
import com.itextpdf.commons.utils.DateTimeUtil;
30+
import com.itextpdf.signatures.CertificateUtil;
3031
import com.itextpdf.signatures.IssuingCertificateRetriever;
3132
import com.itextpdf.signatures.testutils.PemFileHelper;
3233
import com.itextpdf.signatures.testutils.TimeTestUtil;
3334
import com.itextpdf.signatures.testutils.builder.TestCrlBuilder;
3435
import com.itextpdf.signatures.testutils.builder.TestOcspResponseBuilder;
3536
import com.itextpdf.signatures.testutils.client.TestCrlClient;
3637
import com.itextpdf.signatures.testutils.client.TestOcspClient;
38+
import com.itextpdf.signatures.validation.SignatureValidationProperties.OnlineFetching;
3739
import com.itextpdf.signatures.validation.context.CertificateSource;
3840
import com.itextpdf.signatures.validation.context.CertificateSources;
3941
import com.itextpdf.signatures.validation.context.TimeBasedContext;
@@ -44,6 +46,10 @@ This file is part of the iText (R) project.
4446
import com.itextpdf.signatures.validation.report.ReportItem;
4547
import com.itextpdf.signatures.validation.report.ValidationReport;
4648
import com.itextpdf.test.ExtendedITextTest;
49+
50+
import java.security.cert.X509CRL;
51+
import java.time.Duration;
52+
import java.util.Date;
4753
import org.junit.jupiter.api.BeforeEach;
4854
import org.junit.jupiter.api.BeforeAll;
4955
import org.junit.jupiter.api.Test;
@@ -61,6 +67,10 @@ public class RevocationDataValidatorIntegrationTest extends ExtendedITextTest {
6167
private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();
6268
private static final String SOURCE_FOLDER =
6369
"./src/test/resources/com/itextpdf/signatures/validation/RevocationDataValidatorTest/";
70+
71+
private static final String CRL_TEST_SOURCE_FOLDER =
72+
"./src/test/resources/com/itextpdf/signatures/validation/CRLValidatorTest/";
73+
6474
private static final char[] PASSWORD = "testpassphrase".toCharArray();
6575

6676

@@ -140,4 +150,101 @@ public void crlWithOnlySomeReasonsTest() throws Exception {
140150
));
141151

142152
}
153+
154+
@Test
155+
public void crlSignerIsValidatedCertificate() throws Exception {
156+
157+
String rootCertFileName = CRL_TEST_SOURCE_FOLDER + "happyPath/ca.cert.pem";
158+
String crlSignerKeyFileName = CRL_TEST_SOURCE_FOLDER + "keys/crl-key.pem";
159+
String crlSignerFileName = CRL_TEST_SOURCE_FOLDER + "happyPath/crl-issuer.cert.pem";
160+
String checkCertFileName = CRL_TEST_SOURCE_FOLDER + "happyPath/sign.cert.pem";
161+
162+
X509Certificate caCert = (X509Certificate) PemFileHelper.readFirstChain(rootCertFileName)[0];
163+
X509Certificate crlSigner = (X509Certificate) PemFileHelper.readFirstChain(crlSignerFileName)[0];
164+
PrivateKey crlPrivateKey = PemFileHelper.readFirstKey(crlSignerKeyFileName, PASSWORD);
165+
X509Certificate checkCert = (X509Certificate) PemFileHelper.readFirstChain(checkCertFileName)[0];
166+
167+
168+
certificateRetriever.addTrustedCertificates(Collections.singletonList(caCert));
169+
certificateRetriever.addKnownCertificates(Collections.singletonList(crlSigner));
170+
171+
Date checkDate = TimeTestUtil.TEST_DATE_TIME;
172+
Date revocationDate = DateTimeUtil.addDaysToDate(checkDate, -1);
173+
TestCrlBuilder builder = new TestCrlBuilder(crlSigner, crlPrivateKey, checkDate);
174+
builder.setNextUpdate(DateTimeUtil.addDaysToDate(checkDate, 10));
175+
//builder.addCrlEntry(caCert, revocationDate, FACTORY.createCRLReason().getKeyCompromise());
176+
//TestCrlClientWrapper crlClient = new TestCrlClientWrapper(new TestCrlClient().addBuilderForCertIssuer(builder));
177+
178+
ValidationCrlClient crlClient = (ValidationCrlClient) parameters.getCrlClients().get(0);
179+
crlClient.addCrl((X509CRL) CertificateUtil.parseCrlFromBytes(builder.makeCrl()), checkDate, TimeBasedContext.HISTORICAL );
180+
181+
ValidationReport report = new ValidationReport();
182+
certificateRetriever.addTrustedCertificates(Collections.singletonList(caCert));
183+
184+
parameters.setRevocationOnlineFetching(ValidatorContexts.all(), CertificateSources.all(), TimeBasedContexts.all(), OnlineFetching.FETCH_IF_NO_OTHER_DATA_AVAILABLE);
185+
parameters.setFreshness(ValidatorContexts.all(), CertificateSources.all(), TimeBasedContexts.all(),Duration.ofDays(0));
186+
187+
188+
RevocationDataValidator validator = validatorChainBuilder.buildRevocationDataValidator();
189+
validatorChainBuilder.withRevocationDataValidatorFactory(()->validator);
190+
191+
validator.validate(report, baseContext, crlSigner, checkDate);
192+
193+
AssertValidationReport.assertThat(report, a -> a
194+
.hasNumberOfFailures(1)
195+
.hasLogItem(l-> l.withMessage(CRLValidator.CERTIFICATE_IN_ISSUER_CHAIN))
196+
);
197+
}
198+
199+
@Test
200+
public void crlSignerIssuerIsValidatedCertificate() throws Exception {
201+
202+
String rootCertFileName = CRL_TEST_SOURCE_FOLDER + "crlSignerInValidatedChain/ca.cert.pem";
203+
String intermediateFileName = CRL_TEST_SOURCE_FOLDER + "crlSignerInValidatedChain/intermediate.cert.pem";
204+
String intermediate2FileName = CRL_TEST_SOURCE_FOLDER + "crlSignerInValidatedChain/intermediate2.cert.pem";
205+
String crlSignerKeyFileName = CRL_TEST_SOURCE_FOLDER + "keys/crl-key.pem";
206+
String crlSignerFileName = CRL_TEST_SOURCE_FOLDER + "crlSignerInValidatedChain/crl-issuer.cert.pem";
207+
String checkCertFileName = CRL_TEST_SOURCE_FOLDER + "crlSignerInValidatedChain/sign.cert.pem";
208+
209+
X509Certificate caCert = (X509Certificate) PemFileHelper.readFirstChain(rootCertFileName)[0];
210+
X509Certificate intermediateCert = (X509Certificate) PemFileHelper.readFirstChain(intermediateFileName)[0];
211+
X509Certificate intermediate2Cert = (X509Certificate) PemFileHelper.readFirstChain(intermediate2FileName)[0];
212+
X509Certificate crlSigner = (X509Certificate) PemFileHelper.readFirstChain(crlSignerFileName)[0];
213+
PrivateKey crlPrivateKey = PemFileHelper.readFirstKey(crlSignerKeyFileName, PASSWORD);
214+
X509Certificate checkCert = (X509Certificate) PemFileHelper.readFirstChain(checkCertFileName)[0];
215+
216+
217+
certificateRetriever.addTrustedCertificates(Collections.singletonList(caCert));
218+
certificateRetriever.addKnownCertificates(Collections.singletonList(crlSigner));
219+
certificateRetriever.addKnownCertificates(Collections.singletonList(intermediateCert));
220+
certificateRetriever.addKnownCertificates(Collections.singletonList(intermediate2Cert));
221+
222+
Date checkDate = TimeTestUtil.TEST_DATE_TIME;
223+
Date revocationDate = DateTimeUtil.addDaysToDate(checkDate, -1);
224+
TestCrlBuilder builder = new TestCrlBuilder(crlSigner, crlPrivateKey, checkDate);
225+
builder.setNextUpdate(DateTimeUtil.addDaysToDate(checkDate, 10));
226+
//builder.addCrlEntry(caCert, revocationDate, FACTORY.createCRLReason().getKeyCompromise());
227+
//TestCrlClientWrapper crlClient = new TestCrlClientWrapper(new TestCrlClient().addBuilderForCertIssuer(builder));
228+
229+
ValidationCrlClient crlClient = (ValidationCrlClient) parameters.getCrlClients().get(0);
230+
crlClient.addCrl((X509CRL) CertificateUtil.parseCrlFromBytes(builder.makeCrl()), checkDate, TimeBasedContext.HISTORICAL );
231+
232+
ValidationReport report = new ValidationReport();
233+
//certificateRetriever.addTrustedCertificates(Collections.singletonList(caCert));
234+
235+
parameters.setRevocationOnlineFetching(ValidatorContexts.all(), CertificateSources.all(), TimeBasedContexts.all(), OnlineFetching.FETCH_IF_NO_OTHER_DATA_AVAILABLE);
236+
parameters.setFreshness(ValidatorContexts.all(), CertificateSources.all(), TimeBasedContexts.all(),Duration.ofDays(0));
237+
238+
239+
RevocationDataValidator validator = validatorChainBuilder.buildRevocationDataValidator();
240+
validatorChainBuilder.withRevocationDataValidatorFactory(()->validator);
241+
242+
validator.validate(report, baseContext, intermediateCert, checkDate);
243+
244+
AssertValidationReport.assertThat(report, a -> a
245+
.hasNumberOfFailures(1)
246+
.hasLogItem(l-> l.withMessage(CRLValidator.CERTIFICATE_IN_ISSUER_CHAIN))
247+
);
248+
}
143249
}
250+

sign/src/test/resources/com/itextpdf/signatures/validation/CRLValidatorTest/createTestData.cmd

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
REM create the test keys
22
IF [%1] == [] goto continue
33
md keys
4-
openssl genrsa -out keys/root_key.pem -passout pass:testpassphrase 2048
5-
openssl genrsa -out keys/im_key.pem -passout pass:testpassphrase 2048
6-
openssl genrsa -out keys/sign-key.pem -passout pass:testpassphrase 2048
7-
openssl genrsa -out keys/crl-key.pem -passout pass:testpassphrase 2048
4+
openssl genrsa -out keys/root_key.pem -aes256 -passout pass:testpassphrase 2048
5+
openssl genrsa -out keys/im_key.pem -aes256 -passout pass:testpassphrase 2048
6+
openssl genrsa -out keys/im2_key.pem -aes256 -passout pass:testpassphrase 2048
7+
openssl genrsa -out keys/sign-key.pem -aes256 -passout pass:testpassphrase 2048
8+
openssl genrsa -out keys/crl-key.pem -aes256 -passout pass:testpassphrase 2048
89
:continue
910

1011
call :runTestCase happyPath
1112
call :runTestCase crlIssuerRevokedBeforeSigningDate
1213
call :runTestCase crlIssuerAndSignCertHaveNoSharedRoot
14+
call :runTestCase crlSignerInValidatedChain
1315
EXIT
1416

1517
:runTestCase
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDSzCCAjOgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwNTELMAkGA1UEBhMCQkUx
3+
DjAMBgNVBAoMBWlUZXh0MRYwFAYDVQQDDA1pVGV4dFRlc3RSb290MCAXDTAwMDEw
4+
MTAwMDAwMFoYDzI1MDAwMTAxMDAwMDAwWjA1MQswCQYDVQQGEwJCRTEOMAwGA1UE
5+
CgwFaVRleHQxFjAUBgNVBAMMDWlUZXh0VGVzdFJvb3QwggEiMA0GCSqGSIb3DQEB
6+
AQUAA4IBDwAwggEKAoIBAQDYxUs+SgZkRkOhOfddCnPeuE1QP2kAm6gAsg4qjNzr
7+
rZpZvwziyWaz6bXXq8fATjXEAdypAiWL/BDZscdCM/M11jds9mMv7dMvCtLns6Oe
8+
4GbChYxxloN2rVxElFPK2siKBaEeWyItr2Ms0P+hSR5uHjFt+krzl2zv828fyEnH
9+
fPvln42SAcuCKsLmfjtutus5jFKBFF8oiqDFlI2eXYggKV7JELttgPLobv2ZyFa5
10+
nXo/xbtzlPb8AvV3/mxpX4prFhBXupJXuCwmqmzVRqGbwKSz/Bewb/4aJGSpg32x
11+
yIWeXTld58f+Jntfes8xHwK0aJB/Tm2WrH+RWoV+TN/pAgMBAAGjYzBhMB0GA1Ud
12+
DgQWBBR0MlTFwFzoq8mPVusDjIEnL5avqzAfBgNVHSMEGDAWgBR0MlTFwFzoq8mP
13+
VusDjIEnL5avqzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIC5DANBgkq
14+
hkiG9w0BAQsFAAOCAQEAK2dbxbQ7jEdtqvnMpKOZsB55PDO5OZ2hPYilT5ZVZNt1
15+
jdE94HdCBHED3upmOeBH+XtsK/MLk+eYICM5SZwWVCBY0aQQR0URyTsX4SA/12A5
16+
VhrnP6bqy4b+3mO3J9s0go59cZOtSkxQ7191teEQMsDeh8GxXGLW/7Tf4x6v7U34
17+
B7UghMhUD2bHJ7U8MOqto9fVar/9S93tc9vRtYOXZbfRwjFKOYD4IFm7cH3VnrX7
18+
od3KUEKGKhQ2ZaqrtnW19xNTbdRcMT3+8QSai9DLV0kGGSSY09AHDO3WvxkAs9ZI
19+
cIsMM/n+mLK0Km9fh5uE3k6NmiN3GV+QMyjbmFnVNg==
20+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)