diff --git a/itext.tests/itext.sign.tests/itext/signatures/testutils/client/TestCrlClientWrapper.cs b/itext.tests/itext.sign.tests/itext/signatures/testutils/client/TestCrlClientWrapper.cs new file mode 100644 index 0000000000..04a56a3ead --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/testutils/client/TestCrlClientWrapper.cs @@ -0,0 +1,73 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using System.IO; +using iText.Commons.Bouncycastle.Cert; +using iText.Signatures; + +namespace iText.Signatures.Testutils.Client { + public class TestCrlClientWrapper : ICrlClient { + private readonly ICrlClient wrappedClient; + + private readonly IList calls = new List(); + + public TestCrlClientWrapper(ICrlClient wrappedClient) { + this.wrappedClient = wrappedClient; + } + + public virtual ICollection GetEncoded(IX509Certificate checkCert, String url) { + ICollection crlBytesCollection = wrappedClient.GetEncoded(checkCert, url); + IList crlResponses = new List(); + foreach (byte[] crlBytes in crlBytesCollection) { + try { + crlResponses.Add((IX509Crl)CertificateUtil.ParseCrlFromStream(new MemoryStream(crlBytes))); + } + catch (Exception e) { + throw new Exception("Deserializing CRL response failed", e); + } + } + calls.Add(new TestCrlClientWrapper.CrlClientCall(checkCert, url, crlResponses)); + return crlBytesCollection; + } + + public virtual IList GetCalls() { + return calls; + } + + public class CrlClientCall { + public readonly IX509Certificate checkCert; + + public readonly String url; + + public readonly IList responses; + + public CrlClientCall(IX509Certificate checkCert, String url, IList responses) { + this.checkCert = checkCert; + this.url = url; + this.responses = responses; + } + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/testutils/client/TestOcspClientWrapper.cs b/itext.tests/itext.sign.tests/itext/signatures/testutils/client/TestOcspClientWrapper.cs new file mode 100644 index 0000000000..687103e839 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/testutils/client/TestOcspClientWrapper.cs @@ -0,0 +1,80 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using iText.Bouncycastleconnector; +using iText.Commons.Bouncycastle; +using iText.Commons.Bouncycastle.Asn1.Ocsp; +using iText.Commons.Bouncycastle.Cert; +using iText.Signatures; + +namespace iText.Signatures.Testutils.Client { + public class TestOcspClientWrapper : IOcspClient { + private static readonly IBouncyCastleFactory BOUNCY_CASTLE_FACTORY = BouncyCastleFactoryCreator.GetFactory + (); + + private readonly IList calls = new List(); + + private readonly IOcspClient wrappedClient; + + public TestOcspClientWrapper(IOcspClient wrappedClient) { + this.wrappedClient = wrappedClient; + } + + public virtual byte[] GetEncoded(IX509Certificate checkCert, IX509Certificate issuerCert, String url) { + byte[] response = wrappedClient.GetEncoded(checkCert, issuerCert, url); + try { + IBasicOcspResponse basicOCSPResp = BOUNCY_CASTLE_FACTORY.CreateBasicOCSPResponse(BOUNCY_CASTLE_FACTORY.CreateASN1Primitive + (response)); + calls.Add(new TestOcspClientWrapper.OcspClientCall(checkCert, issuerCert, url, basicOCSPResp)); + } + catch (System.IO.IOException e) { + throw new Exception("deserializing ocsp response failed", e); + } + return response; + } + + public virtual IList GetCalls() { + return calls; + } + + public class OcspClientCall { + public readonly IX509Certificate checkCert; + + public readonly IX509Certificate issuerCert; + + public readonly String url; + + public readonly IBasicOcspResponse response; + + public OcspClientCall(IX509Certificate checkCert, IX509Certificate issuerCert, String url, IBasicOcspResponse + response) { + this.checkCert = checkCert; + this.issuerCert = issuerCert; + this.url = url; + this.response = response; + } + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/AssertValidationReport.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/AssertValidationReport.cs index 39a58e845d..108eb81d8c 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/AssertValidationReport.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/AssertValidationReport.cs @@ -21,26 +21,40 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ using System; +using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Utils; using iText.Signatures.Validation.V1.Report; namespace iText.Signatures.Validation.V1 { - public class AssertValidationReport { + public class AssertValidationReport : IDisposable { private readonly ValidationReport report; private readonly AssertValidationReport.CheckChain chain = new AssertValidationReport.StartOfChain(); - public AssertValidationReport(ValidationReport report) { + private bool asserted = false; + + private AssertValidationReport(ValidationReport report) { this.report = report; } - public virtual void DoAssert() { + public static void AssertThat(ValidationReport report, Action c) { + iText.Signatures.Validation.V1.AssertValidationReport assertion = new iText.Signatures.Validation.V1.AssertValidationReport + (report); + c(assertion); + assertion.DoAssert(); + } + + private void DoAssert() { + asserted = true; AssertValidationReport.CheckResult result = new AssertValidationReport.CheckResult(); chain.Run(report, result); if (!result.success) { - result.messageBuilder.Append("\n For item: ").Append(report); + result.messageBuilder.Append("\n For report: ").Append(report); throw new AssertionException(result.messageBuilder.ToString()); } } @@ -55,15 +69,26 @@ public virtual iText.Signatures.Validation.V1.AssertValidationReport HasNumberOf return this; } - public virtual iText.Signatures.Validation.V1.AssertValidationReport HasLogItem(Func check - , String itemDescription) { - chain.SetNext(new AssertValidationReport.ItemCheck(check, 1, itemDescription)); + public virtual iText.Signatures.Validation.V1.AssertValidationReport HasLogItem(ReportItem logItem) { + chain.SetNext(new AssertValidationReport.LogItemCheck(logItem)); return this; } - public virtual iText.Signatures.Validation.V1.AssertValidationReport HasLogItems(Func check - , int count, String itemDescription) { - chain.SetNext(new AssertValidationReport.ItemCheck(check, count, itemDescription)); + public virtual iText.Signatures.Validation.V1.AssertValidationReport HasLogItem(Action c) { + AssertValidationReport.AssertValidationReportLogItem asserter = new AssertValidationReport.AssertValidationReportLogItem + (1, 1); + c(asserter); + asserter.AddToChain(this); + return this; + } + + public virtual iText.Signatures.Validation.V1.AssertValidationReport HasLogItems(int minCount, int maxCount + , Action c) { + AssertValidationReport.AssertValidationReportLogItem asserter = new AssertValidationReport.AssertValidationReportLogItem + (minCount, maxCount); + c(asserter); + asserter.AddToChain(this); return this; } @@ -73,6 +98,53 @@ public virtual iText.Signatures.Validation.V1.AssertValidationReport HasStatus(V return this; } + public virtual void Close() { + if (!asserted) { + throw new InvalidOperationException("AssertValidationReport not asserted!"); + } + } + + public class AssertValidationReportLogItem { + private readonly AssertValidationReport.ValidationReportLogItemCheck check; + + public AssertValidationReportLogItem(int minCount, int maxCount) { + this.check = new AssertValidationReport.ValidationReportLogItemCheck(minCount, maxCount); + } + + public virtual AssertValidationReport.AssertValidationReportLogItem WithCheckName(String checkName) { + check.WithCheckName(checkName); + return this; + } + + public AssertValidationReport.AssertValidationReportLogItem WithMessage(String message, params Func[] @params) { + check.WithMessage(message, @params); + return this; + } + + public virtual AssertValidationReport.AssertValidationReportLogItem WithStatus(ReportItem.ReportItemStatus + status) { + check.WithStatus(status); + return this; + } + + public virtual AssertValidationReport.AssertValidationReportLogItem WithCertificate(IX509Certificate certificate + ) { + check.WithCertificate(certificate); + return this; + } + + public virtual AssertValidationReport.AssertValidationReportLogItem WithExceptionCauseType(Type exceptionType + ) { + check.WithExceptionCauseType(exceptionType); + return this; + } + + public virtual void AddToChain(AssertValidationReport asserter) { + asserter.chain.SetNext(check); + } + } + private class CheckResult { public StringBuilder messageBuilder = new StringBuilder("\n"); @@ -129,6 +201,110 @@ protected internal override void Check(ValidationReport report, AssertValidation } } + private class ValidationReportLogItemCheck : AssertValidationReport.CheckChain { + private readonly int minCount; + + private readonly int maxCount; + + private readonly IList> messageParams = new List>(); + + private readonly StringBuilder errorMessage = new StringBuilder(); + + private String checkName; + + private String message; + + private ReportItem.ReportItemStatus status; + + private bool checkStatus = false; + + private IX509Certificate certificate; + + private Type exceptionType; + + public ValidationReportLogItemCheck(int minCount, int maxCount) { + this.minCount = minCount; + this.maxCount = maxCount; + errorMessage.Append("\nExpected between ").Append(minCount).Append(" and ").Append(maxCount).Append(" message with " + ); + } + + public virtual void WithCheckName(String checkName) { + this.checkName = checkName; + errorMessage.Append(" check name '").Append(checkName).Append("'"); + } + + public virtual void WithMessage(String message, params Func[] @params) { + this.message = message; + messageParams.AddAll(@params); + errorMessage.Append(" message '").Append(message).Append("'"); + } + + public virtual void WithStatus(ReportItem.ReportItemStatus status) { + this.status = status; + checkStatus = true; + errorMessage.Append(" status '").Append(status).Append("'"); + } + + public virtual void WithCertificate(IX509Certificate certificate) { + this.certificate = certificate; + errorMessage.Append(" certificate '").Append(certificate.GetSubjectDN()).Append("'"); + } + + public virtual void WithExceptionCauseType(Type exceptionType) { + this.exceptionType = exceptionType; + errorMessage.Append(" with exception cause '").Append(exceptionType.FullName).Append("'"); + } + + protected internal override void Check(ValidationReport report, AssertValidationReport.CheckResult result) { + errorMessage.Append("\n"); + IList prefiltered; + if (message != null) { + prefiltered = report.GetLogs().Where((i) => { + Object[] @params = new Object[messageParams.Count]; + for (int p = 0; p < messageParams.Count; p++) { + @params[p] = messageParams[p].Invoke(i); + } + return i.GetMessage().Equals(MessageFormatUtil.Format(message, @params)); + } + ).ToList(); + errorMessage.Append("found ").Append(prefiltered.Count).Append(" matches after message filter\n"); + } + else { + prefiltered = report.GetLogs(); + } + if (checkName != null) { + prefiltered = prefiltered.Where((i) => (checkName.Equals(i.GetCheckName()))).ToList(); + errorMessage.Append("found ").Append(prefiltered.Count).Append(" matches after check name filter\n"); + } + if (checkStatus) { + prefiltered = prefiltered.Where((i) => (status.Equals(i.GetStatus()))).ToList(); + errorMessage.Append("found ").Append(prefiltered.Count).Append(" matches after status filter\n"); + } + if (certificate != null) { + prefiltered = prefiltered.Where((i) => certificate.Equals(((CertificateReportItem)i).GetCertificate())).ToList + (); + errorMessage.Append("found ").Append(prefiltered.Count).Append(" matches after certificate filter\n"); + } + if (exceptionType != null) { + prefiltered = prefiltered.Where((i) => i.GetExceptionCause() != null && exceptionType.IsAssignableFrom(i.GetExceptionCause + ().GetType())).ToList(); + errorMessage.Append("found ").Append(prefiltered.Count).Append(" matches after exception cause filter\n"); + } + long foundCount = prefiltered.Count; + if (foundCount < minCount || foundCount > maxCount) { + result.success = false; + result.messageBuilder.Append(errorMessage); + } + } + + public override String ToString() { + return "checkName='" + checkName + '\'' + ", message='" + message + '\'' + ", status=" + status + ", certificate=" + + (certificate == null ? "null" : certificate.GetSubjectDN().ToString()) + ", exceptionType=" + (exceptionType + == null ? "null" : exceptionType.FullName); + } + } + private class LogCountCheck : AssertValidationReport.CheckChain { private readonly int expected; @@ -146,26 +322,18 @@ protected internal override void Check(ValidationReport report, AssertValidation } } - private class ItemCheck : AssertValidationReport.CheckChain { - private readonly Func check; + private class LogItemCheck : AssertValidationReport.CheckChain { + private readonly ReportItem expectedItem; - private readonly String message; - - private readonly int expectedCount; - - public ItemCheck(Func check, int count, String itemDescription) + public LogItemCheck(ReportItem expectedItem) : base() { - this.check = check; - this.expectedCount = count; - this.message = itemDescription; + this.expectedItem = expectedItem; } protected internal override void Check(ValidationReport report, AssertValidationReport.CheckResult result) { - long foundCount = report.GetLogs().Where((i) => check.Invoke(i)).Count(); - if (foundCount != expectedCount) { + if (!report.GetLogs().Contains(expectedItem)) { result.success = false; - result.messageBuilder.Append("\nExpected ").Append(expectedCount).Append(" report logs like '").Append(message - ).Append("' but found ").Append(foundCount); + result.messageBuilder.Append("\nExpected report item not found:").Append(expectedItem); } } } @@ -186,5 +354,9 @@ protected internal override void Check(ValidationReport report, AssertValidation } } } + + void System.IDisposable.Dispose() { + Close(); + } } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CRLValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CRLValidatorTest.cs index 6fe1146891..d3f719f0f7 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CRLValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CRLValidatorTest.cs @@ -79,8 +79,7 @@ public virtual void HappyPathTest() { byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(-5), TimeTestUtil. TEST_DATE_TIME.AddDays(+5)); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.IsTrue(report.GetFailures().IsEmpty()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); } [NUnit.Framework.Test] @@ -89,11 +88,9 @@ public virtual void NextUpdateBeforeValidationTest() { DateTime nextUpdate = TimeTestUtil.TEST_DATE_TIME.AddDays(-5); byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(-15), nextUpdate); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CRLValidator.UPDATE_DATE_BEFORE_CHECK_DATE, nextUpdate - , TimeTestUtil.TEST_DATE_TIME), report.GetFailures()[0].GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithMessage(CRLValidator.UPDATE_DATE_BEFORE_CHECK_DATE, (l) => nextUpdate, (l) => + TimeTestUtil.TEST_DATE_TIME))); } [NUnit.Framework.Test] @@ -103,9 +100,12 @@ public virtual void ChainValidatorUsageTest() { TEST_DATE_TIME.AddDays(+5)); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.IsTrue(report.GetFailures().IsEmpty()); NUnit.Framework.Assert.AreEqual(1, mockChainValidator.verificationCalls.Count); NUnit.Framework.Assert.AreEqual(crlIssuerCert, mockChainValidator.verificationCalls[0].certificate); + NUnit.Framework.Assert.AreEqual(CertificateSource.CRL_ISSUER, mockChainValidator.verificationCalls[0].context + .GetCertificateSource()); + NUnit.Framework.Assert.AreEqual(ValidatorContext.CRL_VALIDATOR, mockChainValidator.verificationCalls[0].context + .GetValidatorContext()); } [NUnit.Framework.Test] @@ -114,9 +114,8 @@ public virtual void IssuerCertificateIsNotFoundTest() { byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(-5), TimeTestUtil. TEST_DATE_TIME.AddDays(+5)); ValidationReport report = PerformValidation("missingIssuer", TimeTestUtil.TEST_DATE_TIME, crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_ISSUER_NOT_FOUND, report.GetFailures()[0].GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithMessage(CRLValidator.CRL_ISSUER_NOT_FOUND))); } [NUnit.Framework.Test] @@ -126,10 +125,8 @@ public virtual void CrlIssuerAndSignCertHaveNoSharedRootTest() { TEST_DATE_TIME.AddDays(+5)); ValidationReport report = PerformValidation("crlIssuerAndSignCertHaveNoSharedRoot", TimeTestUtil.TEST_DATE_TIME , crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_ISSUER_NO_COMMON_ROOT, report.GetFailures()[0].GetMessage - ()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithMessage(CRLValidator.CRL_ISSUER_NO_COMMON_ROOT))); } [NUnit.Framework.Test] @@ -141,10 +138,9 @@ public virtual void CrlIssuerRevokedBeforeSigningDate() { TEST_DATE_TIME.AddDays(+5), signCert, revocationDate, 1); ValidationReport report = PerformValidation("crlIssuerRevokedBeforeSigningDate", TimeTestUtil.TEST_DATE_TIME , crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CRLValidator.CERTIFICATE_REVOKED, crlIssuerCert.GetSubjectDN - (), revocationDate), report.GetFailures()[0].GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((al) => al.WithStatus(ReportItem.ReportItemStatus + .INVALID).WithMessage(CRLValidator.CERTIFICATE_REVOKED, (i) => crlIssuerCert.GetSubjectDN(), (i) => revocationDate + ))); } [NUnit.Framework.Test] @@ -155,10 +151,9 @@ public virtual void CrlRevokedAfterSigningDate() { byte[] crl = CreateCrl(crlIssuerCert, crlIssuerKey, TimeTestUtil.TEST_DATE_TIME.AddDays(+18), TimeTestUtil .TEST_DATE_TIME.AddDays(+23), signCert, revocationDate, 1); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(2, report.GetLogs().Count); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(SignLogMessageConstant.VALID_CERTIFICATE_IS_REVOKED - , revocationDate), report.GetLogs()[1].GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithMessage(SignLogMessageConstant + .VALID_CERTIFICATE_IS_REVOKED, (i) => revocationDate).WithStatus(ReportItem.ReportItemStatus.INFO).WithCertificate + (signCert))); } [NUnit.Framework.Test] @@ -168,21 +163,16 @@ public virtual void CrlSignatureMismatch() { byte[] crl = CreateCrl(crlIssuerCert, intermediateKey, TimeTestUtil.TEST_DATE_TIME.AddDays(+18), TimeTestUtil .TEST_DATE_TIME.AddDays(+23), signCert, TimeTestUtil.TEST_DATE_TIME.AddDays(+20), 1); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_INVALID, report.GetFailures()[0].GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithMessage(CRLValidator.CRL_INVALID + ).WithStatus(ReportItem.ReportItemStatus.INDETERMINATE))); } [NUnit.Framework.Test] public virtual void CrlContainsOnlyCACertsTest() { String crlPath = SOURCE_FOLDER + "issuingDistributionPointTest/onlyCA.crl"; ValidationReport report = CheckCrlScope(crlPath); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(CRLValidator.CERTIFICATE_IS_NOT_IN_THE_CRL_SCOPE, report.GetFailures()[0]. - GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithMessage(CRLValidator.CERTIFICATE_IS_NOT_IN_THE_CRL_SCOPE + ).WithStatus(ReportItem.ReportItemStatus.INDETERMINATE))); } [NUnit.Framework.Test] @@ -190,18 +180,14 @@ public virtual void CrlContainsOnlyUserCertsTest() { String crlPath = SOURCE_FOLDER + "issuingDistributionPointTest/onlyUser.crl"; ValidationReport report = CheckCrlScope(crlPath); NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); } [NUnit.Framework.Test] public virtual void CrlContainsOnlyAttributeCertsTest() { String crlPath = SOURCE_FOLDER + "issuingDistributionPointTest/onlyAttr.crl"; ValidationReport report = CheckCrlScope(crlPath); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(CRLValidator.ATTRIBUTE_CERTS_ASSERTED, report.GetFailures()[0].GetMessage( - )); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithMessage(CRLValidator.ATTRIBUTE_CERTS_ASSERTED))); } [NUnit.Framework.Test] @@ -220,12 +206,9 @@ public virtual void OnlySomeReasonsTest() { .SIGNER_CERT, TimeBasedContext.PRESENT); validator.Validate(report, context, signCert, (IX509Crl)CertificateUtil.ParseCrlFromStream(new MemoryStream (builder.MakeCrl())), TimeTestUtil.TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - CertificateReportItem reportItem = (CertificateReportItem)report.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(signCert, reportItem.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.ONLY_SOME_REASONS_CHECKED, reportItem.GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((al) => al.WithMessage(CRLValidator.ONLY_SOME_REASONS_CHECKED).WithCertificate(signCert)) + ); } [NUnit.Framework.Test] @@ -249,11 +232,8 @@ public virtual void CheckLessReasonsTest() { // Validate CRL with onlySomeReasons. validator.Validate(report, context, signCert, (IX509Crl)CertificateUtil.ParseCrlFromStream(new MemoryStream (builder.MakeCrl())), TimeTestUtil.TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - CertificateReportItem reportItem = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(signCert, reportItem.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.SAME_REASONS_CHECK, reportItem.GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((al) => al.WithMessage(CRLValidator.SAME_REASONS_CHECK))); } [NUnit.Framework.Test] @@ -272,11 +252,9 @@ public virtual void RemoveFromCrlTest() { .SIGNER_CERT, TimeBasedContext.PRESENT); validator.Validate(report, context, signCert, (IX509Crl)CertificateUtil.ParseCrlFromStream(new MemoryStream (builder.MakeCrl())), TimeTestUtil.TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - CertificateReportItem reportItem = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(signCert, reportItem.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CERTIFICATE_IS_UNREVOKED, reportItem.GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((la) => la.WithCertificate(signCert).WithCheckName(CRLValidator.CRL_CHECK).WithMessage(CRLValidator.CERTIFICATE_IS_UNREVOKED + ))); } [NUnit.Framework.Test] @@ -299,13 +277,9 @@ public virtual void FullCrlButDistributionPointWithReasonsTest() { .SIGNER_CERT, TimeBasedContext.PRESENT); validator.Validate(report, context, cert, (IX509Crl)CertificateUtil.ParseCrlFromStream(new MemoryStream(builder .MakeCrl())), checkDate); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - CertificateReportItem reportItem = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INDETERMINATE, reportItem.GetStatus()); - NUnit.Framework.Assert.AreEqual(cert, reportItem.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.ONLY_SOME_REASONS_CHECKED, reportItem.GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithStatus(ReportItem.ReportItemStatus.INDETERMINATE).WithCertificate(cert).WithMessage + (CRLValidator.ONLY_SOME_REASONS_CHECKED))); } [NUnit.Framework.Test] @@ -316,11 +290,10 @@ public virtual void NoExpiredCertOnCrlExtensionTest() { (401)); byte[] crl = builder.MakeCrl(); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals(CRLValidator.CRL_CHECK) && l.GetMessage - ().Equals(MessageFormatUtil.Format(CRLValidator.CERTIFICATE_IS_EXPIRED, signCert.GetNotAfter())) && (( - CertificateReportItem)l).GetCertificate().Equals(signCert), CRLValidator.CERTIFICATE_IS_EXPIRED).DoAssert - (); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(CRLValidator.CRL_CHECK). + WithMessage(CRLValidator.CERTIFICATE_IS_EXPIRED, (i) => signCert.GetNotAfter()).WithCertificate(signCert + ))); } [NUnit.Framework.Test] @@ -333,11 +306,10 @@ public virtual void CertExpiredBeforeDateFromExpiredCertOnCrlTest() { (TimeTestUtil.TEST_DATE_TIME.AddYears(400))); byte[] crl = builder.MakeCrl(); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals(CRLValidator.CRL_CHECK) && l.GetMessage - ().Equals(MessageFormatUtil.Format(CRLValidator.CERTIFICATE_IS_EXPIRED, signCert.GetNotAfter())) && (( - CertificateReportItem)l).GetCertificate().Equals(signCert), CRLValidator.CERTIFICATE_IS_EXPIRED).DoAssert - (); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(CRLValidator.CRL_CHECK). + WithMessage(CRLValidator.CERTIFICATE_IS_EXPIRED, (i) => signCert.GetNotAfter()).WithCertificate(signCert + ))); } [NUnit.Framework.Test] @@ -350,8 +322,8 @@ public virtual void CertExpiredAfterDateFromExpiredCertOnCrlExtensionTest() { (TimeTestUtil.TEST_DATE_TIME.AddYears(399))); byte[] crl = builder.MakeCrl(); ValidationReport report = PerformValidation("happyPath", TimeTestUtil.TEST_DATE_TIME, crl); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures( - 0).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0)); } private ValidationReport CheckCrlScope(String crlPath) { diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CertificateChainValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CertificateChainValidatorTest.cs index 5d1beb6c5a..24a7081d4d 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CertificateChainValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/CertificateChainValidatorTest.cs @@ -46,14 +46,15 @@ public class CertificateChainValidatorTest : ExtendedITextTest { private readonly ValidationContext baseContext = new ValidationContext(ValidatorContext.CERTIFICATE_CHAIN_VALIDATOR , CertificateSource.SIGNER_CERT, TimeBasedContext.PRESENT); + private MockRevocationDataValidator mockRevocationDataValidator; + [NUnit.Framework.SetUp] public virtual void Setup() { + mockRevocationDataValidator = new MockRevocationDataValidator(); properties = new SignatureValidationProperties(); certificateRetriever = new IssuingCertificateRetriever(); validatorChainBuilder = new ValidatorChainBuilder().WithIssuingCertificateRetriever(certificateRetriever). - WithSignatureValidationProperties(properties); - validatorChainBuilder.WithRevocationDataValidator(new CertificateChainValidatorTest.MockRevocationDataValidator - (validatorChainBuilder)); + WithSignatureValidationProperties(properties).WithRevocationDataValidator(mockRevocationDataValidator); } [NUnit.Framework.Test] @@ -69,11 +70,38 @@ public virtual void ValidChainTest() { certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, TimeTestUtil.TEST_DATE_TIME ); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures( - 0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage( - ).Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), CertificateChainValidator.CERTIFICATE_TRUSTED - ).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((la) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK + ).WithMessage("Certificate {0} is trusted, revocation data checks are not required.", (l) => rootCert. + GetSubjectDN()).WithCertificate(rootCert))); + } + + [NUnit.Framework.Test] + public virtual void RevocationValidationCallTest() { + String chainName = CERTS_SRC + "chain.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate signingCert = (IX509Certificate)certificateChain[0]; + IX509Certificate intermediateCert = (IX509Certificate)certificateChain[1]; + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + CertificateChainValidator validator = validatorChainBuilder.BuildCertificateChainValidator(); + certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.SingletonList(intermediateCert + )); + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, TimeTestUtil.TEST_DATE_TIME + ); + NUnit.Framework.Assert.AreEqual(2, mockRevocationDataValidator.calls.Count); + MockRevocationDataValidator.RevocationDataValidatorCall call1 = mockRevocationDataValidator.calls[0]; + NUnit.Framework.Assert.AreEqual(signingCert, call1.certificate); + NUnit.Framework.Assert.AreEqual(CertificateSource.SIGNER_CERT, call1.context.GetCertificateSource()); + NUnit.Framework.Assert.AreEqual(ValidatorContext.CERTIFICATE_CHAIN_VALIDATOR, call1.context.GetValidatorContext + ()); + NUnit.Framework.Assert.AreEqual(TimeTestUtil.TEST_DATE_TIME, call1.validationDate); + MockRevocationDataValidator.RevocationDataValidatorCall call2 = mockRevocationDataValidator.calls[1]; + NUnit.Framework.Assert.AreEqual(intermediateCert, call2.certificate); + NUnit.Framework.Assert.AreEqual(CertificateSource.CERT_ISSUER, call2.context.GetCertificateSource()); + NUnit.Framework.Assert.AreEqual(ValidatorContext.CERTIFICATE_CHAIN_VALIDATOR, call2.context.GetValidatorContext + ()); + NUnit.Framework.Assert.AreEqual(TimeTestUtil.TEST_DATE_TIME, call2.validationDate); } [NUnit.Framework.Test] @@ -152,10 +180,9 @@ public virtual void IntermediateCertTrustedTest() { certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(intermediateCert)); ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, DateTimeUtil.GetCurrentUtcTime ()); - new AssertValidationReport(report).HasNumberOfFailures(0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName - ().Equals("Certificate check.") && l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED, intermediateCert.GetSubjectDN())), CertificateChainValidator.CERTIFICATE_TRUSTED - ).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(1).HasLogItem((la + ) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (l) => intermediateCert.GetSubjectDN()))); } [NUnit.Framework.Test] @@ -170,11 +197,10 @@ public virtual void ValidChainRequiredExtensionPositiveTest() { certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, DateTimeUtil.GetCurrentUtcTime ()); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures( - 0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage( - ).Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), CertificateChainValidator.CERTIFICATE_TRUSTED - ).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((la) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK + ).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED, (l) => rootCert.GetSubjectDN()).WithCertificate + (rootCert))); } [NUnit.Framework.Test] @@ -189,16 +215,13 @@ public virtual void ValidChainRequiredExtensionNegativeTest() { certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); ValidationReport report = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .CERT_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); - new AssertValidationReport(report).HasNumberOfFailures(2).HasNumberOfLogs(3).HasLogItem((l) => l.GetCheckName - ().Equals("Certificate check.") && l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED, rootCert.GetSubjectDN())) && ((CertificateReportItem)l).GetCertificate().Equals( - rootCert), CertificateChainValidator.CERTIFICATE_TRUSTED).HasLogItem((l) => l.GetCheckName().Equals("Required certificate extensions check." - ) && l.GetMessage().Equals(MessageFormatUtil.Format("Required extension {0} is missing or incorrect.", - OID.X509Extensions.KEY_USAGE)) && ((CertificateReportItem)l).GetCertificate().Equals(signingCert), "Required extension {0} is missing or incorrect." - ).HasLogItem((l) => l.GetCheckName().Equals("Required certificate extensions check.") && l.GetMessage( - ).Equals(MessageFormatUtil.Format("Required extension {0} is missing or incorrect.", OID.X509Extensions - .BASIC_CONSTRAINTS)) && ((CertificateReportItem)l).GetCertificate().Equals(signingCert), "Required extension {0} is missing or incorrect." - ).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(2).HasNumberOfLogs(3).HasLogItem((la + ) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (l) => rootCert.GetSubjectDN()).WithCertificate(rootCert)).HasLogItem((la) => la + .WithCheckName(CertificateChainValidator.EXTENSIONS_CHECK).WithMessage(CertificateChainValidator.EXTENSION_MISSING + , (l) => OID.X509Extensions.KEY_USAGE).WithCertificate(signingCert)).HasLogItem((la) => la.WithCheckName + (CertificateChainValidator.EXTENSIONS_CHECK).WithMessage(CertificateChainValidator.EXTENSION_MISSING, + (l) => OID.X509Extensions.BASIC_CONSTRAINTS).WithCertificate(signingCert))); } [NUnit.Framework.Test] @@ -211,11 +234,10 @@ public virtual void ValidChainTrustedRootIsnSetTest() { certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.SingletonList(intermediateCert)); ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, DateTimeUtil.GetCurrentUtcTime ()); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.ISSUER_MISSING, intermediateCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(intermediateCert), CertificateChainValidator - .ISSUER_MISSING).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((la) => la.WithCheckName(CertificateChainValidator + .CERTIFICATE_CHECK).WithMessage(CertificateChainValidator.ISSUER_MISSING, (l) => intermediateCert.GetSubjectDN + ()).WithCertificate(intermediateCert))); } [NUnit.Framework.Test] @@ -232,13 +254,12 @@ public virtual void IntermediateCertIsNotYetValidTest() { certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, TimeTestUtil.TEST_DATE_TIME ); - new AssertValidationReport(report).HasNumberOfFailures(1).HasNumberOfLogs(2).HasLogItem((l) => l.GetCheckName - ().Equals("Certificate check.") && l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED, rootCert.GetSubjectDN())) && ((CertificateReportItem)l).GetCertificate().Equals( - rootCert), CertificateChainValidator.CERTIFICATE_TRUSTED).HasLogItem((l) => l.GetCheckName().Equals("Certificate validity period check." - ) && l.GetMessage().Equals(MessageFormatUtil.Format("Certificate {0} is not yet valid.", intermediateCert - .GetSubjectDN())) && ((CertificateReportItem)l).GetCertificate().Equals(intermediateCert) && l.GetExceptionCause - () is AbstractCertificateNotYetValidException, "Certificate {0} is not yet valid.").DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(1).HasNumberOfLogs(2).HasLogItem((la + ) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (l) => rootCert.GetSubjectDN()).WithCertificate(rootCert)).HasLogItem((la) => la + .WithCheckName(CertificateChainValidator.VALIDITY_CHECK).WithMessage(CertificateChainValidator.NOT_YET_VALID_CERTIFICATE + , (l) => intermediateCert.GetSubjectDN()).WithCertificate(intermediateCert).WithExceptionCauseType(typeof( + AbstractCertificateNotYetValidException)))); } [NUnit.Framework.Test] @@ -255,14 +276,12 @@ public virtual void IntermediateCertIsExpiredTest() { certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); ValidationReport report = validator.ValidateCertificate(baseContext, signingCert, DateTimeUtil.GetCurrentUtcTime ()); - new AssertValidationReport(report).HasStatus(ValidationReport.ValidationResult.INVALID).HasNumberOfFailures - (1).HasNumberOfLogs(2).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), CertificateChainValidator.ISSUER_MISSING - ).HasLogItem((l) => l.GetCheckName().Equals("Certificate validity period check.") && l.GetMessage().Equals - (MessageFormatUtil.Format("Certificate {0} is expired.", intermediateCert.GetSubjectDN())) && ((CertificateReportItem - )l).GetCertificate().Equals(intermediateCert) && l.GetExceptionCause() is AbstractCertificateExpiredException - , CertificateChainValidator.ISSUER_MISSING).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasNumberOfFailures + (1).HasNumberOfLogs(2).HasLogItem((la) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK + ).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED, (l) => rootCert.GetSubjectDN()).WithCertificate + (rootCert)).HasLogItem((la) => la.WithCheckName(CertificateChainValidator.VALIDITY_CHECK).WithMessage( + CertificateChainValidator.EXPIRED_CERTIFICATE, (l) => intermediateCert.GetSubjectDN()).WithCertificate + (intermediateCert).WithExceptionCauseType(typeof(AbstractCertificateExpiredException)))); } [NUnit.Framework.Test] @@ -281,25 +300,19 @@ public virtual void CertificateGenerallyTrustedTest() { >()); ValidationReport report1 = validator.ValidateCertificate(baseContext, signingCert, DateTimeUtil.GetCurrentUtcTime ()); - new AssertValidationReport(report1).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report1, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report2 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .OCSP_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); - new AssertValidationReport(report2).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report3 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .TIMESTAMP), signingCert, DateTimeUtil.GetCurrentUtcTime()); - new AssertValidationReport(report3).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report3, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); } [NUnit.Framework.Test] @@ -318,25 +331,19 @@ public virtual void RootCertificateTrustedForCATest() { >()); ValidationReport report1 = validator.ValidateCertificate(baseContext, signingCert, DateTimeUtil.GetCurrentUtcTime ()); - new AssertValidationReport(report1).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report1, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report2 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .OCSP_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); - new AssertValidationReport(report2).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report3 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .TIMESTAMP), signingCert, DateTimeUtil.GetCurrentUtcTime()); - new AssertValidationReport(report3).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report3, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); } [NUnit.Framework.Test] @@ -355,20 +362,17 @@ public virtual void FirstCertificateTrustedForCATest() { ValidationReport report1 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .CERT_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This works fine because certificate in question has CertificateSource.CERT_ISSUER context. - new AssertValidationReport(report1).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, signingCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(signingCert), "Certificate {0} is trusted." - ).DoAssert(); + AssertValidationReport.AssertThat(report1, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => signingCert.GetSubjectDN()).WithCertificate(signingCert))); ValidationReport report2 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .TIMESTAMP), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This doesn't work because certificate in question has CertificateSource.TIMESTAMP context. - new AssertValidationReport(report2).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(2).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, signingCert.GetSubjectDN(), "certificates generation")), CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil. - Format(CertificateChainValidator.ISSUER_MISSING, intermediateCert.GetSubjectDN())), CertificateChainValidator - .ISSUER_MISSING).DoAssert(); + AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(2).HasLogItem((al) => al.WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, (i) => signingCert.GetSubjectDN(), (i) => "certificates generation" + )).HasLogItem((al) => al.WithMessage(CertificateChainValidator.ISSUER_MISSING, (i) => intermediateCert + .GetSubjectDN()))); } [NUnit.Framework.Test] @@ -389,21 +393,18 @@ public virtual void RootCertificateTrustedForOCSPTest() { .OCSP_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This works fine because even though root certificate has CertificateSource.CERT_ISSUER context, // the chain contains initial certificate with CertificateSource.OCSP_ISSUER context. - new AssertValidationReport(report1).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report1, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report2 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .TIMESTAMP), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This doesn't work because root certificate has CertificateSource.CERT_ISSUER context and // the chain doesn't contain any certificate with CertificateSource.OCSP_ISSUER context. - new AssertValidationReport(report2).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(2).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, rootCert.GetSubjectDN(), "OCSP response generation")), CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil. - Format(CertificateChainValidator.ISSUER_MISSING, rootCert.GetSubjectDN())), CertificateChainValidator. - ISSUER_MISSING).DoAssert(); + AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(2).HasLogItem((l) => l.WithMessage(CertificateChainValidator. + CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, (i) => rootCert.GetSubjectDN(), (i) => "OCSP response generation" + )).HasLogItem((l) => l.WithMessage(CertificateChainValidator.ISSUER_MISSING, (i) => rootCert.GetSubjectDN + ()))); } [NUnit.Framework.Test] @@ -424,21 +425,17 @@ public virtual void RootCertificateTrustedForCRLTest() { .CRL_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This works fine because even though root certificate has CertificateSource.CERT_ISSUER context, // the chain contains initial certificate with CertificateSource.CRL_ISSUER context. - new AssertValidationReport(report1).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report1, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report2 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .OCSP_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This doesn't work because root certificate has CertificateSource.CERT_ISSUER context and // the chain doesn't contain any certificate with CertificateSource.CRL_ISSUER context. - new AssertValidationReport(report2).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(2).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, rootCert.GetSubjectDN(), "CRL generation")), CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil. - Format(CertificateChainValidator.ISSUER_MISSING, rootCert.GetSubjectDN())), CertificateChainValidator. - ISSUER_MISSING).DoAssert(); + AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(2).HasLogItem((l) => l.WithMessage(CertificateChainValidator. + CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, (i) => rootCert.GetSubjectDN(), (i) => "CRL generation")).HasLogItem + ((l) => l.WithMessage(CertificateChainValidator.ISSUER_MISSING, (i) => rootCert.GetSubjectDN()))); } [NUnit.Framework.Test] @@ -459,31 +456,18 @@ public virtual void RootCertificateTrustedForTimestampTest() { .TIMESTAMP), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This works fine because even though root certificate has CertificateSource.CERT_ISSUER context, // the chain contains initial certificate with CertificateSource.TIMESTAMP context. - new AssertValidationReport(report1).HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures - (0).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName().Equals("Certificate check.") && l.GetMessage - ().Equals(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN - ())) && ((CertificateReportItem)l).GetCertificate().Equals(rootCert), "Certificate {0} is trusted.").DoAssert - (); + AssertValidationReport.AssertThat(report1, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfFailures + (0).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName("Certificate check.").WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); ValidationReport report2 = validator.ValidateCertificate(baseContext.SetCertificateSource(CertificateSource .CRL_ISSUER), signingCert, DateTimeUtil.GetCurrentUtcTime()); // This doesn't work because root certificate has CertificateSource.CERT_ISSUER context and // the chain doesn't contain any certificate with CertificateSource.TIMESTAMP context. - new AssertValidationReport(report2).HasStatus(ValidationReport.ValidationResult.INDETERMINATE).HasNumberOfFailures - (1).HasNumberOfLogs(2).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, rootCert.GetSubjectDN(), "timestamp generation")), CertificateChainValidator - .CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT).HasLogItem((l) => l.GetMessage().Equals(MessageFormatUtil. - Format(CertificateChainValidator.ISSUER_MISSING, rootCert.GetSubjectDN())), CertificateChainValidator. - ISSUER_MISSING).DoAssert(); - } - - private class MockRevocationDataValidator : RevocationDataValidator { - public MockRevocationDataValidator(ValidatorChainBuilder builder) - : base(builder) { - } - - public override void Validate(ValidationReport report, ValidationContext context, IX509Certificate certificate - , DateTime validationDate) { - } + AssertValidationReport.AssertThat(report2, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasNumberOfLogs(2).HasLogItem((l) => l.WithMessage(CertificateChainValidator. + CERTIFICATE_TRUSTED_FOR_DIFFERENT_CONTEXT, (i) => rootCert.GetSubjectDN(), (i) => "timestamp generation" + )).HasLogItem((l) => l.WithMessage(CertificateChainValidator.ISSUER_MISSING, (i) => rootCert.GetSubjectDN + ()))); } } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/DocumentRevisionsValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/DocumentRevisionsValidatorTest.cs index 86c9a806ea..8d86d2e9d9 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/DocumentRevisionsValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/DocumentRevisionsValidatorTest.cs @@ -43,8 +43,9 @@ public static void Before() { } [NUnit.Framework.Test] - public virtual void MultipleRevisionsDocument() { + public virtual void MultipleRevisionsDocumentLevel1Test() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "multipleRevisionsDocument.pdf" ))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); @@ -56,12 +57,13 @@ public virtual void MultipleRevisionsDocument() { validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[1]); } } - // Between these two revisions DSS is added, which is allowed, but also timestamp is added, which is not yet allowed. + // Between these two revisions DSS and timestamp are added, it is allowed. + // But PDF is generated with extra annotation (it was a bug). NUnit.Framework.Assert.AreEqual(1, validationReport.GetFailures().Count); ReportItem reportItem1 = validationReport.GetFailures()[0]; NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_CATALOG_CHANGES, reportItem1.GetMessage - ()); + NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(DocumentRevisionsValidator.UNEXPECTED_ENTRY_IN_XREF + , 27), reportItem1.GetMessage()); NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem1.GetStatus()); using (Stream inputStream_1 = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions [1])) { @@ -77,6 +79,7 @@ public virtual void MultipleRevisionsDocument() { [NUnit.Framework.Test] public virtual void HugeDocumentTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "hugeDocument.pdf"))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); IList documentRevisions = revisionsReader.GetAllRevisions(); @@ -94,6 +97,7 @@ public virtual void HugeDocumentTest() { [NUnit.Framework.Test] public virtual void ExtensionsModificationsTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "extensionsModifications.pdf") )) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); @@ -159,6 +163,7 @@ public virtual void ExtensionsModificationsTest() { [NUnit.Framework.Test] public virtual void CompletelyInvalidDocumentTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "completelyInvalidDocument.pdf" ))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); @@ -173,16 +178,17 @@ public virtual void CompletelyInvalidDocumentTest() { NUnit.Framework.Assert.AreEqual(1, validationReport.GetFailures().Count); ReportItem reportItem = validationReport.GetFailures()[0]; NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem.GetCheckName()); - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_CATALOG_CHANGES, reportItem.GetMessage - ()); + NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.PAGES_MODIFIED, reportItem.GetMessage()); NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem.GetStatus()); } } [NUnit.Framework.Test] - public virtual void MakePagesEntryDirectAndIndirectTest() { + public virtual void MakeFontDirectAndIndirectTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "makePagesDirect.pdf"))) { + validator.docMDP = 1; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "makeFontDirectAndIndirect.pdf" + ))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); IList documentRevisions = revisionsReader.GetAllRevisions(); ValidationReport validationReport; @@ -193,12 +199,17 @@ public virtual void MakePagesEntryDirectAndIndirectTest() { } } // Adobe Acrobat doesn't complain about such change. We consider this incorrect. - NUnit.Framework.Assert.AreEqual(1, validationReport.GetFailures().Count); + NUnit.Framework.Assert.AreEqual(2, validationReport.GetFailures().Count); ReportItem reportItem1 = validationReport.GetFailures()[0]; NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_CATALOG_CHANGES, reportItem1.GetMessage - ()); + NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(DocumentRevisionsValidator.FIELD_REMOVED, "Signature1" + ), reportItem1.GetMessage()); NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem1.GetStatus()); + ReportItem reportItem2 = validationReport.GetFailures()[1]; + NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem2.GetCheckName()); + NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_ACROFORM_CHANGES, reportItem2.GetMessage + ()); + NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem2.GetStatus()); using (Stream inputStream_1 = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions [1])) { using (PdfDocument previousDocument_1 = new PdfDocument(new PdfReader(inputStream_1))) { @@ -206,10 +217,15 @@ public virtual void MakePagesEntryDirectAndIndirectTest() { } } // Adobe Acrobat doesn't complain about such change. We consider this incorrect. - NUnit.Framework.Assert.AreEqual(1, validationReport.GetFailures().Count); - ReportItem reportItem2 = validationReport.GetFailures()[0]; + NUnit.Framework.Assert.AreEqual(2, validationReport.GetFailures().Count); + reportItem1 = validationReport.GetFailures()[0]; + NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem1.GetCheckName()); + NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(DocumentRevisionsValidator.FIELD_REMOVED, "Signature1" + ), reportItem1.GetMessage()); + NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem1.GetStatus()); + reportItem2 = validationReport.GetFailures()[1]; NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem2.GetCheckName()); - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_CATALOG_CHANGES, reportItem2.GetMessage + NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_ACROFORM_CHANGES, reportItem2.GetMessage ()); NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem2.GetStatus()); } @@ -218,6 +234,7 @@ public virtual void MakePagesEntryDirectAndIndirectTest() { [NUnit.Framework.Test] public virtual void RandomEntryAddedTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "randomEntryAdded.pdf"))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); IList documentRevisions = revisionsReader.GetAllRevisions(); @@ -241,6 +258,7 @@ public virtual void RandomEntryAddedTest() { [NUnit.Framework.Test] public virtual void RandomEntryWithoutUsageTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "randomEntryWithoutUsage.pdf") )) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); @@ -265,6 +283,7 @@ public virtual void RandomEntryWithoutUsageTest() { [NUnit.Framework.Test] public virtual void ChangeExistingFontTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "changeExistingFont.pdf"))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); IList documentRevisions = revisionsReader.GetAllRevisions(); @@ -275,18 +294,17 @@ public virtual void ChangeExistingFontTest() { validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[1]); } } - NUnit.Framework.Assert.AreEqual(1, validationReport.GetFailures().Count); - ReportItem reportItem1 = validationReport.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_CATALOG_CHANGES, reportItem1.GetMessage - ()); - NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem1.GetStatus()); + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.PAGE_MODIFIED).WithStatus(ReportItem.ReportItemStatus + .INVALID))); } } [NUnit.Framework.Test] public virtual void ChangeExistingFontAndAddAsDssTest() { DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "changeExistingFontAndAddAsDss.pdf" ))) { PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); @@ -299,12 +317,289 @@ public virtual void ChangeExistingFontAndAddAsDssTest() { } } // Adobe Acrobat doesn't complain about such change. We consider this incorrect. - NUnit.Framework.Assert.AreEqual(1, validationReport.GetFailures().Count); - ReportItem reportItem1 = validationReport.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.DOC_MDP_CHECK, reportItem1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(DocumentRevisionsValidator.NOT_ALLOWED_CATALOG_CHANGES, reportItem1.GetMessage - ()); - NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INVALID, reportItem1.GetStatus()); + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.PAGE_MODIFIED).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void FillInFieldAtLevel1Test() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 1; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "fillInField.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [0])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[1]); + } + } + // Between these two revisions forms were filled in, it is not allowed at docMDP level 1. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(2).HasNumberOfLogs(2).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.FIELD_REMOVED, (i) => "input").WithStatus(ReportItem.ReportItemStatus + .INVALID)).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator + .NOT_ALLOWED_ACROFORM_CHANGES).WithStatus(ReportItem.ReportItemStatus.INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void MultipleRevisionsDocumentLevel2Test() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "multipleRevisionsDocument2.pdf" + ))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [0])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[1]); + } + } + // Between these two revisions forms were filled in, it is allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID + ).HasNumberOfFailures(0).HasNumberOfLogs(0)); + using (Stream inputStream_1 = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [1])) { + using (PdfDocument previousDocument_1 = new PdfDocument(new PdfReader(inputStream_1))) { + validationReport = validator.ValidateRevision(document, previousDocument_1, documentRevisions[2]); + } + } + // Between these two revisions existing signature field was signed, it is allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID + ).HasNumberOfFailures(0).HasNumberOfLogs(0)); + using (Stream inputStream_2 = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [2])) { + using (PdfDocument previousDocument_2 = new PdfDocument(new PdfReader(inputStream_2))) { + validationReport = validator.ValidateRevision(document, previousDocument_2, documentRevisions[3]); + } + } + // Between these two revisions newly added signature field was signed, it is allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID + ).HasNumberOfFailures(0).HasNumberOfLogs(0)); + } + } + + [NUnit.Framework.Test] + public virtual void RemovePermissionsTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "removePermissions.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions /Perms key was removed, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.PERMISSIONS_REMOVED).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void RemoveDSSTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "removeDSS.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions /DSS key was removed, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.DSS_REMOVED).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void RemoveAcroformTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "removeAcroform.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions /Acroform key was removed, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.ACROFORM_REMOVED).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void RemoveFieldTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "removeField.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions field was removed, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.NOT_ALLOWED_ACROFORM_CHANGES).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void RenameFieldTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "renameField.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions field was renamed, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(2).HasNumberOfLogs(2).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.FIELD_REMOVED, (i) => "input").WithStatus(ReportItem.ReportItemStatus + .INVALID)).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator + .NOT_ALLOWED_ACROFORM_CHANGES).WithStatus(ReportItem.ReportItemStatus.INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void AddTextFieldTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "addTextField.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions new field was added, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(2).HasNumberOfLogs(2).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.UNEXPECTED_FORM_FIELD, (i) => "text").WithStatus + (ReportItem.ReportItemStatus.INVALID)).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK + ).WithMessage(DocumentRevisionsValidator.NOT_ALLOWED_ACROFORM_CHANGES).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void AddUnsignedSignatureFieldTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "addUnsignedSignatureField.pdf" + ))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions new unsigned signature field was added, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(2).HasNumberOfLogs(2).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.UNEXPECTED_FORM_FIELD, (i) => "signature").WithStatus + (ReportItem.ReportItemStatus.INVALID)).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK + ).WithMessage(DocumentRevisionsValidator.NOT_ALLOWED_ACROFORM_CHANGES).WithStatus(ReportItem.ReportItemStatus + .INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void BrokenSignatureFieldDictionaryTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "brokenSignatureFieldDictionary.pdf" + ))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions signature value was replaced by text, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(3).HasNumberOfLogs(3).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.SIGNATURE_MODIFIED, (i) => "Signature1").WithStatus + (ReportItem.ReportItemStatus.INVALID)).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK + ).WithMessage(DocumentRevisionsValidator.FIELD_REMOVED, (i) => "Signature1").WithStatus(ReportItem.ReportItemStatus + .INVALID)).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator.DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator + .NOT_ALLOWED_ACROFORM_CHANGES).WithStatus(ReportItem.ReportItemStatus.INVALID))); + } + } + + [NUnit.Framework.Test] + public virtual void ModifyPageAnnotsTest() { + DocumentRevisionsValidator validator = new DocumentRevisionsValidator(); + validator.docMDP = 2; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "modifyPageAnnots.pdf"))) { + PdfRevisionsReader revisionsReader = new PdfRevisionsReader(document.GetReader()); + IList documentRevisions = revisionsReader.GetAllRevisions(); + ValidationReport validationReport; + using (Stream inputStream = DocumentRevisionsValidator.CreateInputStreamFromRevision(document, documentRevisions + [documentRevisions.Count - 2])) { + using (PdfDocument previousDocument = new PdfDocument(new PdfReader(inputStream))) { + validationReport = validator.ValidateRevision(document, previousDocument, documentRevisions[documentRevisions + .Count - 1]); + } + } + // Between these two revisions circle annotation was added to the first page, it is not allowed. + AssertValidationReport.AssertThat(validationReport, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID + ).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.WithCheckName(DocumentRevisionsValidator + .DOC_MDP_CHECK).WithMessage(DocumentRevisionsValidator.PAGE_ANNOTATIONS_MODIFIED).WithStatus(ReportItem.ReportItemStatus + .INVALID))); } } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockChainValidator.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockChainValidator.cs index 0a96a11eb8..9e2d7c6efb 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockChainValidator.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockChainValidator.cs @@ -27,27 +27,45 @@ You should have received a copy of the GNU Affero General Public License using iText.Signatures.Validation.V1.Report; namespace iText.Signatures.Validation.V1 { - internal class MockChainValidator : CertificateChainValidator { + public class MockChainValidator : CertificateChainValidator { public IList verificationCalls = new List(); + private Action onCallHandler; + internal MockChainValidator() : base(new ValidatorChainBuilder()) { } public override ValidationReport Validate(ValidationReport result, ValidationContext context, IX509Certificate certificate, DateTime verificationDate) { - verificationCalls.Add(new MockChainValidator.ValidationCallBack(certificate, verificationDate)); + MockChainValidator.ValidationCallBack call = new MockChainValidator.ValidationCallBack(certificate, context + , result, verificationDate); + if (onCallHandler != null) { + onCallHandler(call); + } + verificationCalls.Add(call); return result; } - public class ValidationCallBack { - public IX509Certificate certificate; + public virtual void OnCallDo(Action c) { + onCallHandler = c; + } + + public sealed class ValidationCallBack { + public readonly IX509Certificate certificate; + + public readonly ValidationContext context; + + public readonly ValidationReport report; - public DateTime checkDate; + public readonly DateTime checkDate; - public ValidationCallBack(IX509Certificate certificate, DateTime checkDate) { + public ValidationCallBack(IX509Certificate certificate, ValidationContext context, ValidationReport report + , DateTime checkDate) { this.certificate = certificate; + this.context = context; + this.report = report; this.checkDate = checkDate; } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockCrlValidator.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockCrlValidator.cs new file mode 100644 index 0000000000..595bccf057 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockCrlValidator.cs @@ -0,0 +1,83 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Utils; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Report; + +namespace iText.Signatures.Validation.V1 { + public class MockCrlValidator : CRLValidator { + public readonly IList calls = new List + (); + + private Action onCallHandler; + + /// + /// Creates new + /// + /// instance. + /// + public MockCrlValidator() + : base(new ValidatorChainBuilder()) { + } + + public override void Validate(ValidationReport report, ValidationContext context, IX509Certificate certificate + , IX509Crl crl, DateTime validationDate) { + MockCrlValidator.CRLValidateCall call = new MockCrlValidator.CRLValidateCall(report, context, certificate, + crl, validationDate); + calls.Add(call); + if (onCallHandler != null) { + onCallHandler(calls[calls.Count - 1]); + } + } + + public virtual void OnCallDo(Action c) { + onCallHandler = c; + } + + public sealed class CRLValidateCall { + public readonly DateTime timeStamp = DateTimeUtil.GetCurrentUtcTime(); + + public readonly ValidationReport report; + + public readonly ValidationContext context; + + public readonly IX509Certificate certificate; + + public readonly IX509Crl crl; + + public readonly DateTime validationDate; + + public CRLValidateCall(ValidationReport report, ValidationContext context, IX509Certificate certificate, IX509Crl + crl, DateTime validationDate) { + this.report = report; + this.context = context; + this.certificate = certificate; + this.crl = crl; + this.validationDate = validationDate; + } + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockIssuingCertificateRetriever.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockIssuingCertificateRetriever.cs new file mode 100644 index 0000000000..9e3159f55d --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockIssuingCertificateRetriever.cs @@ -0,0 +1,120 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using iText.Commons.Bouncycastle.Cert; +using iText.Signatures; + +namespace iText.Signatures.Validation.V1 { + public class MockIssuingCertificateRetriever : IssuingCertificateRetriever { + public IList retrieveMissingCertificatesCalls = new List(); + + public IList getCrlIssuerCertificatesCalls = new List(); + + public IList> setTrustedCertificatesCalls = new List>(); + + public IList> addKnownCertificatesCalls = new List>(); + + public IList isCertificateTrustedDoCalls = new List(); + + private Func retrieveMissingCertificatesHandler; + + private Func getCrlIssuerCertificatesHandler; + + private Action> setTrustedCertificatesHandler; + + private Action> addKnownCertificatesHandler; + + private Func isCertificateTrustedDoHandler; + + public override IX509Certificate[] RetrieveMissingCertificates(IX509Certificate[] chain) { + retrieveMissingCertificatesCalls.Add(chain); + if (retrieveMissingCertificatesHandler != null) { + return retrieveMissingCertificatesHandler.Invoke(chain); + } + return new IX509Certificate[0]; + } + + public override IX509Certificate[] GetCrlIssuerCertificates(IX509Crl crl) { + getCrlIssuerCertificatesCalls.Add(crl); + if (getCrlIssuerCertificatesHandler != null) { + return getCrlIssuerCertificatesHandler.Invoke(crl); + } + return new IX509Certificate[0]; + } + + public override void SetTrustedCertificates(ICollection certificates) { + setTrustedCertificatesCalls.Add(certificates); + if (setTrustedCertificatesHandler != null) { + setTrustedCertificatesHandler(certificates); + } + } + + public override void AddKnownCertificates(ICollection certificates) { + addKnownCertificatesCalls.Add(certificates); + if (addKnownCertificatesHandler != null) { + addKnownCertificatesHandler(certificates); + } + } + + public override bool IsCertificateTrusted(IX509Certificate certificate) { + isCertificateTrustedDoCalls.Add(certificate); + if (isCertificateTrustedDoHandler != null) { + return isCertificateTrustedDoHandler.Invoke(certificate); + } + return true; + } + + public virtual MockIssuingCertificateRetriever OnRetrieveMissingCertificatesDo(Func callback) { + retrieveMissingCertificatesHandler = callback; + return this; + } + + public virtual MockIssuingCertificateRetriever OngetCrlIssuerCertificatesDo(Func callback) { + getCrlIssuerCertificatesHandler = callback; + return this; + } + + public virtual MockIssuingCertificateRetriever OnSetTrustedCertificatesDo(Action> callback) { + setTrustedCertificatesHandler = callback; + return this; + } + + public virtual MockIssuingCertificateRetriever OnAddKnownCertificatesDo(Action> callback) { + addKnownCertificatesHandler = callback; + return this; + } + + public virtual MockIssuingCertificateRetriever OnIsCertificateTrustedDo(Func callback + ) { + isCertificateTrustedDoHandler = callback; + return this; + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockOCSPValidator.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockOCSPValidator.cs new file mode 100644 index 0000000000..854ea95230 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockOCSPValidator.cs @@ -0,0 +1,88 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using iText.Commons.Bouncycastle.Asn1.Ocsp; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Bouncycastle.Cert.Ocsp; +using iText.Commons.Utils; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Report; + +namespace iText.Signatures.Validation.V1 { + public class MockOCSPValidator : OCSPValidator { + public readonly IList calls = new List(); + + private Action onCallHandler; + + /// + /// Creates new + /// + /// instance. + /// + public MockOCSPValidator() + : base(new ValidatorChainBuilder()) { + } + + public override void Validate(ValidationReport report, ValidationContext context, IX509Certificate certificate + , ISingleResponse singleResp, IBasicOcspResponse ocspResp, DateTime validationDate) { + MockOCSPValidator.OCSPValidatorCall call = new MockOCSPValidator.OCSPValidatorCall(report, context, certificate + , singleResp, ocspResp, validationDate); + calls.Add(call); + if (onCallHandler != null) { + onCallHandler(call); + } + } + + public virtual void OnCallDo(Action c) { + onCallHandler = c; + } + + public sealed class OCSPValidatorCall { + public readonly DateTime timeStamp = DateTimeUtil.GetCurrentUtcTime(); + + public readonly ValidationReport report; + + public readonly ValidationContext context; + + public readonly IX509Certificate certificate; + + public readonly ISingleResponse singleResp; + + public readonly IBasicOcspResponse ocspResp; + + public readonly DateTime validationDate; + + public OCSPValidatorCall(ValidationReport report, ValidationContext context, IX509Certificate certificate, + ISingleResponse singleResp, IBasicOcspResponse ocspResp, DateTime validationDate) { + this.report = report; + this.context = context; + this.certificate = certificate; + this.singleResp = singleResp; + this.ocspResp = ocspResp; + this.validationDate = validationDate; + } + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockRevocationDataValidator.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockRevocationDataValidator.cs new file mode 100644 index 0000000000..c1fc20578f --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockRevocationDataValidator.cs @@ -0,0 +1,82 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using iText.Commons.Bouncycastle.Cert; +using iText.Signatures; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Report; + +namespace iText.Signatures.Validation.V1 { + public class MockRevocationDataValidator : RevocationDataValidator { + public IList crlClientsAdded = new List(); + + public IList ocspClientsAdded = new List(); + + public IList calls = new List(); + + /// + /// Creates new + /// + /// instance to validate certificate revocation data. + /// + internal MockRevocationDataValidator() + : base(new ValidatorChainBuilder()) { + } + + public override RevocationDataValidator AddCrlClient(ICrlClient crlClient) { + crlClientsAdded.Add(crlClient); + return this; + } + + public override RevocationDataValidator AddOcspClient(IOcspClient ocspClient) { + ocspClientsAdded.Add(ocspClient); + return this; + } + + public override void Validate(ValidationReport report, ValidationContext context, IX509Certificate certificate + , DateTime validationDate) { + calls.Add(new MockRevocationDataValidator.RevocationDataValidatorCall(report, context, certificate, validationDate + )); + } + + public sealed class RevocationDataValidatorCall { + public readonly ValidationReport report; + + public readonly ValidationContext context; + + public readonly IX509Certificate certificate; + + public readonly DateTime validationDate; + + public RevocationDataValidatorCall(ValidationReport report, ValidationContext context, IX509Certificate certificate + , DateTime validationDate) { + this.report = report; + this.context = context; + this.certificate = certificate; + this.validationDate = validationDate; + } + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockSignatureValidationProperties.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockSignatureValidationProperties.cs new file mode 100644 index 0000000000..5ef9d6ba52 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/MockSignatureValidationProperties.cs @@ -0,0 +1,127 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using System.Collections.Generic; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Extensions; + +namespace iText.Signatures.Validation.V1 { + /// This mock class wrapper a real SignatureValidationProperties instance. + /// + /// This mock class wrapper a real SignatureValidationProperties instance. + /// It will track the calls made to it. + /// You can override a response by adding it with the add{someproperty}Response methods. + /// These will be served first, when there are no more responses left, the wrapped properties + /// will be returned. + /// + public class MockSignatureValidationProperties : SignatureValidationProperties { + private readonly SignatureValidationProperties wrappedProperties; + + public IList continueAfterFailureCalls = new List(); + + public IList freshnessCalls = new List(); + + public IList requiredExtensionsCalls = new List(); + + public IList revocationOnlineFetchingCalls = new List(); + + private readonly IList continueAfterFailureResponses = new List(); + + private int continueAfterFailureResponsesIndex = 0; + + private readonly IList freshnessResponses = new List(); + + private int freshnessResponsesIndex = 0; + + private readonly IList> requiredExtensionsResponses = new List>(); + + private int requiredExtensionsResponsesIndex = 0; + + private readonly IList revocationOnlineFetchingResponses = new + List(); + + private int revocationOnlineFetchingResponsesIndex = 0; + + public MockSignatureValidationProperties(SignatureValidationProperties properties) { + this.wrappedProperties = properties; + } + + public override bool GetContinueAfterFailure(ValidationContext validationContext) { + continueAfterFailureCalls.Add(validationContext); + if (continueAfterFailureResponsesIndex < continueAfterFailureResponses.Count) { + return continueAfterFailureResponses[continueAfterFailureResponsesIndex++]; + } + return wrappedProperties.GetContinueAfterFailure(validationContext); + } + + public override TimeSpan GetFreshness(ValidationContext validationContext) { + freshnessCalls.Add(validationContext); + if (freshnessResponsesIndex < freshnessResponses.Count) { + return freshnessResponses[freshnessResponsesIndex++]; + } + return wrappedProperties.GetFreshness(validationContext); + } + + public override IList GetRequiredExtensions(ValidationContext validationContext) { + requiredExtensionsCalls.Add(validationContext); + if (requiredExtensionsResponsesIndex < requiredExtensionsResponses.Count) { + return requiredExtensionsResponses[requiredExtensionsResponsesIndex++]; + } + return wrappedProperties.GetRequiredExtensions(validationContext); + } + + public override SignatureValidationProperties.OnlineFetching GetRevocationOnlineFetching(ValidationContext + validationContext) { + revocationOnlineFetchingCalls.Add(validationContext); + if (revocationOnlineFetchingResponsesIndex < revocationOnlineFetchingResponses.Count) { + return revocationOnlineFetchingResponses[revocationOnlineFetchingResponsesIndex++]; + } + return wrappedProperties.GetRevocationOnlineFetching(validationContext); + } + + public virtual iText.Signatures.Validation.V1.MockSignatureValidationProperties AddContinueAfterFailureResponse + (bool value) { + continueAfterFailureResponses.Add(value); + return this; + } + + public virtual iText.Signatures.Validation.V1.MockSignatureValidationProperties AddFreshnessResponse(TimeSpan + freshness) { + freshnessResponses.Add(freshness); + return this; + } + + public virtual iText.Signatures.Validation.V1.MockSignatureValidationProperties AddRequiredExtensionsResponses + (IList requiredExtensions) { + requiredExtensionsResponses.Add(requiredExtensions); + return this; + } + + public virtual iText.Signatures.Validation.V1.MockSignatureValidationProperties AddRevocationOnlineFetchingResponse + (SignatureValidationProperties.OnlineFetching value) { + revocationOnlineFetchingResponses.Add(value); + return this; + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorIntegrationTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorIntegrationTest.cs new file mode 100644 index 0000000000..be6f527a24 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorIntegrationTest.cs @@ -0,0 +1,267 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using iText.Bouncycastleconnector; +using iText.Commons.Bouncycastle; +using iText.Commons.Bouncycastle.Asn1.Ocsp; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Bouncycastle.Crypto; +using iText.Commons.Utils; +using iText.Signatures; +using iText.Signatures.Testutils; +using iText.Signatures.Testutils.Builder; +using iText.Signatures.Testutils.Client; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Report; +using iText.Test; + +namespace iText.Signatures.Validation.V1 { + [NUnit.Framework.Category("BouncyCastleUnitTest")] + public class OCSPValidatorIntegrationTest : ExtendedITextTest { + private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/signatures/validation/v1/OCSPValidatorTest/"; + + private static readonly IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); + + private static readonly char[] PASSWORD = "testpassphrase".ToCharArray(); + + private static IX509Certificate caCert; + + private static IPrivateKey caPrivateKey; + + private static IX509Certificate checkCert; + + private static IX509Certificate responderCert; + + private static IPrivateKey ocspRespPrivateKey; + + private IssuingCertificateRetriever certificateRetriever; + + private SignatureValidationProperties parameters; + + private readonly ValidationContext baseContext = new ValidationContext(ValidatorContext.REVOCATION_DATA_VALIDATOR + , CertificateSource.SIGNER_CERT, TimeBasedContext.PRESENT); + + private ValidatorChainBuilder validatorChainBuilder; + + [NUnit.Framework.OneTimeSetUp] + public static void Before() { + String rootCertFileName = SOURCE_FOLDER + "rootCert.pem"; + String checkCertFileName = SOURCE_FOLDER + "signCert.pem"; + String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCert.pem"; + caCert = (IX509Certificate)PemFileHelper.ReadFirstChain(rootCertFileName)[0]; + caPrivateKey = PemFileHelper.ReadFirstKey(rootCertFileName, PASSWORD); + checkCert = (IX509Certificate)PemFileHelper.ReadFirstChain(checkCertFileName)[0]; + responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName)[0]; + ocspRespPrivateKey = PemFileHelper.ReadFirstKey(ocspResponderCertFileName, PASSWORD); + } + + [NUnit.Framework.SetUp] + public virtual void SetUp() { + certificateRetriever = new IssuingCertificateRetriever(); + parameters = new SignatureValidationProperties(); + validatorChainBuilder = new ValidatorChainBuilder().WithSignatureValidationProperties(parameters).WithIssuingCertificateRetriever + (certificateRetriever); + } + + [NUnit.Framework.Test] + public virtual void ValidateResponderOcspNoCheckTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + ValidationReport report = ValidateTest(checkDate); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItem((al + ) => al.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .TRUSTED_OCSP_RESPONDER)).HasLogItem((al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK + ).WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED, (l) => ((CertificateReportItem)l).GetCertificate + ().GetSubjectDN())).HasStatus(ValidationReport.ValidationResult.VALID)); + } + + [NUnit.Framework.Test] + public virtual void ValidateAuthorizedOCSPResponderWithOcspTest() { + ValidationReport report = VerifyResponderWithOcsp(false); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItems(2 + , 2, (al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (l) => ((CertificateReportItem)l).GetCertificate().GetSubjectDN())).HasStatus(ValidationReport.ValidationResult + .VALID)); + } + + [NUnit.Framework.Test] + public virtual void ValidateAuthorizedOCSPResponderWithOcspRevokedTest() { + String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertForOcspTest.pem"; + IX509Certificate responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName) + [0]; + certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.Singleton(responderCert)); + ValidationReport report = VerifyResponderWithOcsp(true); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((al + ) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.CERT_IS_REVOKED).WithStatus( + ReportItem.ReportItemStatus.INDETERMINATE))); + } + + [NUnit.Framework.Test] + public virtual void ValidateAuthorizedOCSPResponderFromTheTrustedStoreTest() { + ValidationReport report = ValidateOcspWithoutCertsTest(true); + NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); + NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); + } + + [NUnit.Framework.Test] + public virtual void TrustedOcspResponderDoesNotHaveOcspSigningExtensionTest() { + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(caCert, caPrivateKey); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse caBasicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient + .GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + // Configure OCSP signing authority for the certificate in question + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, caBasicOCSPResp.GetResponses()[0], caBasicOCSPResp, TimeTestUtil + .TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasStatus(ValidationReport.ValidationResult + .VALID)); + } + + [NUnit.Framework.Test] + public virtual void AuthorizedOcspResponderDoesNotHaveOcspSigningExtensionTest() { + String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertWithoutOcspSigning.pem"; + IX509Certificate responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName) + [0]; + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetThisUpdate(DateTimeUtil.GetCalendar(TimeTestUtil.TEST_DATE_TIME.AddDays(1))); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil + .TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(1).HasLogItem((al) => al.WithCheckName + (CertificateChainValidator.EXTENSIONS_CHECK).WithMessage(CertificateChainValidator.EXTENSION_MISSING, + (l) => OID.X509Extensions.EXTENDED_KEY_USAGE)).HasStatus(ValidationReport.ValidationResult.INDETERMINATE + )); + } + + private ValidationReport ValidateTest(DateTime checkDate) { + return ValidateTest(checkDate, checkDate.AddDays(1), 0); + } + + private ValidationReport ValidateTest(DateTime checkDate, DateTime thisUpdate, long freshness) { + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetThisUpdate(DateTimeUtil.GetCalendar(thisUpdate)); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + parameters.SetFreshness(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays + (freshness)); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate + ); + return report; + } + + private ValidationReport ValidateRevokedTest(DateTime checkDate, DateTime revocationDate) { + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetCertificateStatus(FACTORY.CreateRevokedStatus(revocationDate, FACTORY.CreateCRLReason().GetKeyCompromise + ())); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate + ); + return report; + } + + private ValidationReport ValidateOcspWithoutCertsTest(bool addResponderToTrusted) { + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetOcspCertsChain(new IX509Certificate[0]); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + if (addResponderToTrusted) { + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(responderCert)); + } + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil + .TEST_DATE_TIME); + return report; + } + + private ValidationReport VerifyResponderWithOcsp(bool revokedOcsp) { + String rootCertFileName = SOURCE_FOLDER + "rootCertForOcspTest.pem"; + String checkCertFileName = SOURCE_FOLDER + "signCertForOcspTest.pem"; + String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertForOcspTest.pem"; + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + IX509Certificate caCert = (IX509Certificate)PemFileHelper.ReadFirstChain(rootCertFileName)[0]; + IPrivateKey caPrivateKey = PemFileHelper.ReadFirstKey(rootCertFileName, PASSWORD); + IX509Certificate checkCert = (IX509Certificate)PemFileHelper.ReadFirstChain(checkCertFileName)[0]; + IX509Certificate responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName) + [0]; + IPrivateKey ocspRespPrivateKey = PemFileHelper.ReadFirstKey(ocspResponderCertFileName, PASSWORD); + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(-5))); + builder.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(5))); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. + GetEncoded(checkCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + TestOcspResponseBuilder builder2 = revokedOcsp ? new TestOcspResponseBuilder(caCert, caPrivateKey, FACTORY + .CreateRevokedStatus(TimeTestUtil.TEST_DATE_TIME.AddDays(-5), FACTORY.CreateCRLReason().GetKeyCompromise + ())) : new TestOcspResponseBuilder(caCert, caPrivateKey); + builder2.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(20))); + builder2.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(30))); + TestOcspClient ocspClient2 = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder2); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() + , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(5)); + if (revokedOcsp) { + parameters.SetContinueAfterFailure(ValidatorContexts.All(), CertificateSources.All(), false); + } + validatorChainBuilder.GetRevocationDataValidator().AddOcspClient(ocspClient); + validatorChainBuilder.GetRevocationDataValidator().AddOcspClient(ocspClient2); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate + ); + return report; + } + + private class TestIssuingCertificateRetriever : IssuingCertificateRetriever { + internal IX509Certificate issuerCertificate; + + public TestIssuingCertificateRetriever(String issuerPath) + : base() { + this.issuerCertificate = PemFileHelper.ReadFirstChain(issuerPath)[0]; + } + + public override IX509Certificate RetrieveIssuerCertificate(IX509Certificate certificate) { + return issuerCertificate; + } + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorTest.cs index a08fd2a861..c3326764db 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/OCSPValidatorTest.cs @@ -56,15 +56,17 @@ public class OCSPValidatorTest : ExtendedITextTest { private static IPrivateKey ocspRespPrivateKey; + private readonly ValidationContext baseContext = new ValidationContext(ValidatorContext.REVOCATION_DATA_VALIDATOR + , CertificateSource.SIGNER_CERT, TimeBasedContext.PRESENT); + private IssuingCertificateRetriever certificateRetriever; private SignatureValidationProperties parameters; - private readonly ValidationContext baseContext = new ValidationContext(ValidatorContext.REVOCATION_DATA_VALIDATOR - , CertificateSource.SIGNER_CERT, TimeBasedContext.PRESENT); - private ValidatorChainBuilder validatorChainBuilder; + private MockChainValidator mockCertificateChainValidator; + [NUnit.Framework.OneTimeSetUp] public static void Before() { String rootCertFileName = SOURCE_FOLDER + "rootCert.pem"; @@ -81,79 +83,49 @@ public static void Before() { public virtual void SetUp() { certificateRetriever = new IssuingCertificateRetriever(); parameters = new SignatureValidationProperties(); + mockCertificateChainValidator = new MockChainValidator(); validatorChainBuilder = new ValidatorChainBuilder().WithSignatureValidationProperties(parameters).WithIssuingCertificateRetriever - (certificateRetriever); + (certificateRetriever).WithCertificateChainValidator(mockCertificateChainValidator); } [NUnit.Framework.Test] - public virtual void ValidateResponderOcspNoCheckTest() { + public virtual void HappyPathTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; ValidationReport report = ValidateTest(checkDate); - new AssertValidationReport(report).HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItem((l) => l.GetCheckName - ().Equals(RevocationDataValidator.REVOCATION_DATA_CHECK) && l.GetMessage().Equals(RevocationDataValidator - .TRUSTED_OCSP_RESPONDER), "Revocation data check with trusted responder").HasLogItem((l) => l.GetCheckName - ().Equals(CertificateChainValidator.CERTIFICATE_CHECK) && l.GetMessage().Equals(MessageFormatUtil.Format - (CertificateChainValidator.CERTIFICATE_TRUSTED, ((CertificateReportItem)l).GetCertificate().GetSubjectDN - ())), "ChainValidator certificate trusted").HasStatus(ValidationReport.ValidationResult.VALID).DoAssert - (); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); } [NUnit.Framework.Test] - public virtual void ValidateAuthorizedOCSPResponderWithOcspTest() { - ValidationReport report = VerifyResponderWithOcsp(false); - new AssertValidationReport(report).HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItems((l) => l.GetCheckName - ().Equals(CertificateChainValidator.CERTIFICATE_CHECK) && l.GetMessage().Equals(MessageFormatUtil.Format - (CertificateChainValidator.CERTIFICATE_TRUSTED, ((CertificateReportItem)l).GetCertificate().GetSubjectDN - ())), 2, "Certificate check with trusted certificate").HasStatus(ValidationReport.ValidationResult.VALID - ).DoAssert(); - } - - [NUnit.Framework.Test] - public virtual void ValidateAuthorizedOCSPResponderWithOcspRevokedTest() { - String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertForOcspTest.pem"; - IX509Certificate responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName) - [0]; - certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.Singleton(responderCert)); - ValidationReport report = VerifyResponderWithOcsp(true); - new AssertValidationReport(report).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName - ().Equals(OCSPValidator.OCSP_CHECK) && l.GetMessage().Equals(OCSPValidator.CERT_IS_REVOKED) && l.GetStatus - ().Equals(ReportItem.ReportItemStatus.INDETERMINATE), "Certificate revoked").DoAssert(); - } - - [NUnit.Framework.Test] - public virtual void ValidateAuthorizedOCSPResponderFromTheTrustedStoreTest() { - ValidationReport report = ValidateOcspWithoutCertsTest(true); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); + public virtual void OcpsIssuerChainValidationsUsesCorrectParametersTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + ValidationReport report = ValidateTest(checkDate); + NUnit.Framework.Assert.AreEqual(1, mockCertificateChainValidator.verificationCalls.Count); + NUnit.Framework.Assert.AreEqual(responderCert, mockCertificateChainValidator.verificationCalls[0].certificate + ); + NUnit.Framework.Assert.AreEqual(ValidatorContext.OCSP_VALIDATOR, mockCertificateChainValidator.verificationCalls + [0].context.GetValidatorContext()); + NUnit.Framework.Assert.AreEqual(CertificateSource.OCSP_ISSUER, mockCertificateChainValidator.verificationCalls + [0].context.GetCertificateSource()); + NUnit.Framework.Assert.AreEqual(checkDate, mockCertificateChainValidator.verificationCalls[0].checkDate); + NUnit.Framework.Assert.AreEqual(checkDate.AddDays(0), mockCertificateChainValidator.verificationCalls[0].checkDate + ); } [NUnit.Framework.Test] - public virtual void NoResponderFoundInCertsTest() { + public virtual void OcspForSelfSignedCertShouldNotValdateFurtherTest() { TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); - builder.SetOcspCertsChain(new IX509Certificate[] { caCert }); TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); - IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. - GetEncoded(checkCert, caCert, null))); + IBasicOcspResponse caBasicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient + .GetEncoded(caCert, caCert, null))); ValidationReport report = new ValidationReport(); certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); - validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil + validator.Validate(report, baseContext, caCert, caBasicOCSPResp.GetResponses()[0], caBasicOCSPResp, TimeTestUtil .TEST_DATE_TIME); - new AssertValidationReport(report).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName - ().Equals(OCSPValidator.OCSP_CHECK) && l.GetMessage().Equals(OCSPValidator.OCSP_COULD_NOT_BE_VERIFIED) - , "OCSP responder not found").HasStatus(ValidationReport.ValidationResult.INDETERMINATE).DoAssert(); - } - - [NUnit.Framework.Test] - public virtual void NoResponderFoundTest() { - ValidationReport report = ValidateOcspWithoutCertsTest(false); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_COULD_NOT_BE_VERIFIED, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasNumberOfLogs + (1).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(RevocationDataValidator. + SELF_SIGNED_CERTIFICATE).WithCertificate(caCert))); + NUnit.Framework.Assert.AreEqual(0, mockCertificateChainValidator.verificationCalls.Count); } [NUnit.Framework.Test] @@ -162,124 +134,117 @@ public virtual void ValidationDateAfterNextUpdateTest() { DateTime nextUpdate = TimeTestUtil.TEST_DATE_TIME.AddDays(30); DateTime checkDate = TimeTestUtil.TEST_DATE_TIME.AddDays(45); ValidationReport report = ValidateTest(checkDate, TimeTestUtil.TEST_DATE_TIME, 50); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(OCSPValidator.OCSP_IS_NO_LONGER_VALID, checkDate, - nextUpdate), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - } - - [NUnit.Framework.Test] - public virtual void CertificateWasRevokedAfterCheckDateTest() { - DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - DateTime revocationDate = TimeTestUtil.TEST_DATE_TIME.AddDays(10); - ValidationReport report = ValidateRevokedTest(checkDate, revocationDate); - new AssertValidationReport(report).HasNumberOfFailures(0).HasNumberOfLogs(3).HasLogItem((l) => l.GetCheckName - ().Equals(OCSPValidator.OCSP_CHECK) && l.GetMessage().Equals(MessageFormatUtil.Format(SignLogMessageConstant - .VALID_CERTIFICATE_IS_REVOKED, revocationDate)), "valid certificate is revoked").HasStatus(ValidationReport.ValidationResult - .VALID).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.OCSP_IS_NO_LONGER_VALID + , (l) => checkDate, (l) => nextUpdate))); } [NUnit.Framework.Test] - public virtual void CertificateWasRevokedBeforeCheckDateTest() { + public virtual void SerialNumbersDoNotMatchTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - DateTime revocationDate = TimeTestUtil.TEST_DATE_TIME.AddDays(-1); - ValidationReport report = ValidateRevokedTest(checkDate, revocationDate); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem ocspCheckItem = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, ocspCheckItem.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.CERT_IS_REVOKED, ocspCheckItem.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(1))); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse caBasicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient + .GetEncoded(caCert, caCert, null))); + ValidationReport report = new ValidationReport(); + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); + validator.Validate(report, baseContext, checkCert, caBasicOCSPResp.GetResponses()[0], caBasicOCSPResp, checkDate + ); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfLogs(1).HasStatus(ValidationReport.ValidationResult + .INDETERMINATE).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator + .SERIAL_NUMBERS_DO_NOT_MATCH).WithCertificate(checkCert))); + NUnit.Framework.Assert.AreEqual(0, mockCertificateChainValidator.verificationCalls.Count); } [NUnit.Framework.Test] - public virtual void CertificateStatusIsUnknownTest() { - DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + public virtual void IssuersDoNotMatchTest() { + String wrongRootCertFileName = SOURCE_FOLDER + "rootCertForOcspTest.pem"; TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); - builder.SetCertificateStatus(FACTORY.CreateUnknownStatus()); TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. GetEncoded(checkCert, caCert, null))); ValidationReport report = new ValidationReport(); - certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + validatorChainBuilder.WithIssuingCertificateRetriever(new OCSPValidatorTest.TestIssuingCertificateRetriever + (wrongRootCertFileName)); OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); - validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate - ); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem ocspCheckItem = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, ocspCheckItem.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.CERT_STATUS_IS_UNKNOWN, ocspCheckItem.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil + .TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((la + ) => la.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.ISSUERS_DO_NOT_MATCH).WithStatus + (ReportItem.ReportItemStatus.INDETERMINATE))); } [NUnit.Framework.Test] - public virtual void SerialNumbersDoesNotMatchTest() { + public virtual void PositiveFreshnessNegativeTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); - builder.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(1))); - TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); - IBasicOcspResponse caBasicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient - .GetEncoded(caCert, caCert, null))); + DateTime thisUpdate = checkDate.AddDays(-3); + ValidationReport report = ValidateTest(checkDate, thisUpdate, 2); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator + .FRESHNESS_CHECK, (l) => thisUpdate, (l) => checkDate, (l) => TimeSpan.FromDays(2)))); + } + + [NUnit.Framework.Test] + public virtual void NextUpdateNotSetResultsInValidStatusTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + TestOcspResponseBuilder builder = new TestOcspResponseBuilder(caCert, caPrivateKey); + builder.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(-20))); + builder.SetNextUpdate(DateTimeUtil.GetCalendar((DateTime)TimestampConstants.UNDEFINED_TIMESTAMP_DATE)); + builder.SetProducedAt(TimeTestUtil.TEST_DATE_TIME.AddDays(-20)); + TestOcspClient client = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(client.GetEncoded + (checkCert, caCert, ""))); + certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.Singleton(caCert)); ValidationReport report = new ValidationReport(); - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); - validator.Validate(report, baseContext, checkCert, caBasicOCSPResp.GetResponses()[0], caBasicOCSPResp, checkDate + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate ); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.SERIAL_NUMBERS_DO_NOT_MATCH, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID)); } [NUnit.Framework.Test] - public virtual void OcspForSelfSignedCertTest() { - TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); - TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); - IBasicOcspResponse caBasicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient - .GetEncoded(caCert, caCert, null))); - ValidationReport report = new ValidationReport(); - certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); - validator.Validate(report, baseContext, caCert, caBasicOCSPResp.GetResponses()[0], caBasicOCSPResp, TimeTestUtil - .TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.SELF_SIGNED_CERTIFICATE, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); + public virtual void CertificateWasRevokedBeforeCheckDateShouldFailTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + DateTime revocationDate = TimeTestUtil.TEST_DATE_TIME.AddDays(-1); + ValidationReport report = ValidateRevokedTestMocked(checkDate, revocationDate); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasLogItem + ((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.CERT_IS_REVOKED).WithCertificate + (checkCert))); } [NUnit.Framework.Test] - public virtual void IssuersDoesNotMatchTest() { - String wrongRootCertFileName = SOURCE_FOLDER + "rootCertForOcspTest.pem"; + public virtual void CertificateWasRevokedAfterCheckDateShouldSucceedTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + DateTime revocationDate = TimeTestUtil.TEST_DATE_TIME.AddDays(10); + ValidationReport report = ValidateRevokedTestMocked(checkDate, revocationDate); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithCheckName(OCSPValidator.OCSP_CHECK + ).WithMessage(SignLogMessageConstant.VALID_CERTIFICATE_IS_REVOKED, (l) => revocationDate)).HasStatus(ValidationReport.ValidationResult + .VALID)); + } + + [NUnit.Framework.Test] + public virtual void CertificateStatusIsUnknownTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder.SetCertificateStatus(FACTORY.CreateUnknownStatus()); TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. GetEncoded(checkCert, caCert, null))); ValidationReport report = new ValidationReport(); - validatorChainBuilder.WithIssuingCertificateRetriever(new OCSPValidatorTest.TestIssuingCertificateRetriever - (wrongRootCertFileName)); + certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); - validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil - .TEST_DATE_TIME); - new AssertValidationReport(report).HasNumberOfFailures(1).HasNumberOfLogs(1).HasLogItem((l) => l.GetCheckName - ().Equals(OCSPValidator.OCSP_CHECK) && l.GetMessage().Equals(OCSPValidator.ISSUERS_DO_NOT_MATCH) && l. - GetStatus().Equals(ReportItem.ReportItemStatus.INDETERMINATE), OCSPValidator.ISSUERS_DO_NOT_MATCH).DoAssert - (); + validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate + ); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.CERT_STATUS_IS_UNKNOWN + ).WithCertificate(checkCert))); + NUnit.Framework.Assert.AreEqual(0, mockCertificateChainValidator.verificationCalls.Count); } [NUnit.Framework.Test] - public virtual void CertificateDoesNotVerifyWithSuppliedKeyTest() { + public virtual void OcspIssuerCertificateDoesNotVerifyWithCaPKTest() { String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertForOcspTest.pem"; IX509Certificate responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName) [0]; @@ -293,37 +258,19 @@ public virtual void CertificateDoesNotVerifyWithSuppliedKeyTest() { OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil .TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem ocspCheckItem = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, ocspCheckItem.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.INVALID_OCSP, ocspCheckItem.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(1).HasStatus(ValidationReport.ValidationResult + .INVALID).HasLogItem((al) => al.WithCheckName(OCSPValidator.OCSP_CHECK).WithMessage(OCSPValidator.INVALID_OCSP + ) + // This should be the checked certificate, not the ocsp responder + + //.withCertificate(checkCert) + .WithCertificate(responderCert))); } [NUnit.Framework.Test] - public virtual void TrustedOcspResponderDoesNotHaveOcspSigningExtensionTest() { - TestOcspResponseBuilder builder = new TestOcspResponseBuilder(caCert, caPrivateKey); - TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); - IBasicOcspResponse caBasicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient - .GetEncoded(checkCert, caCert, null))); - ValidationReport report = new ValidationReport(); - // Configure OCSP signing authority for the certificate in question - certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); - validator.Validate(report, baseContext, checkCert, caBasicOCSPResp.GetResponses()[0], caBasicOCSPResp, TimeTestUtil - .TEST_DATE_TIME); - new AssertValidationReport(report).HasNumberOfFailures(0).HasStatus(ValidationReport.ValidationResult.VALID - ).DoAssert(); - } - - [NUnit.Framework.Test] - public virtual void AuthorizedOcspResponderDoesNotHaveOcspSigningExtensionTest() { - String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertWithoutOcspSigning.pem"; - IX509Certificate responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName) - [0]; + public virtual void NoResponderFoundInCertsTest() { TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); - builder.SetThisUpdate(DateTimeUtil.GetCalendar(TimeTestUtil.TEST_DATE_TIME.AddDays(1))); + builder.SetOcspCertsChain(new IX509Certificate[] { caCert }); TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); IBasicOcspResponse basicOCSPResp = FACTORY.CreateBasicOCSPResponse(FACTORY.CreateASN1Primitive(ocspClient. GetEncoded(checkCert, caCert, null))); @@ -332,56 +279,25 @@ public virtual void AuthorizedOcspResponderDoesNotHaveOcspSigningExtensionTest() OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, TimeTestUtil .TEST_DATE_TIME); - new AssertValidationReport(report).HasNumberOfFailures(1).HasLogItem((l) => l.GetCheckName().Equals(CertificateChainValidator - .EXTENSIONS_CHECK) && l.GetMessage().Equals(MessageFormatUtil.Format(CertificateChainValidator.EXTENSION_MISSING - , OID.X509Extensions.EXTENDED_KEY_USAGE)), "OCSP_SIGNING extended key usage is missing").HasStatus(ValidationReport.ValidationResult - .INDETERMINATE).DoAssert(); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithCheckName(OCSPValidator.OCSP_CHECK + ).WithMessage(OCSPValidator.OCSP_COULD_NOT_BE_VERIFIED)).HasStatus(ValidationReport.ValidationResult.INDETERMINATE + )); } [NUnit.Framework.Test] - public virtual void PositiveFreshnessPositiveTest() { + public virtual void ChainValidatorReportWrappingTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - ValidationReport report = ValidateTest(checkDate, checkDate.AddDays(-3), 5); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - } - - [NUnit.Framework.Test] - public virtual void PositiveFreshnessNegativeTest() { - DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - DateTime thisUpdate = checkDate.AddDays(-3); - ValidationReport report = ValidateTest(checkDate, thisUpdate, 2); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(OCSPValidator.FRESHNESS_CHECK, thisUpdate, checkDate - , TimeSpan.FromDays(2)), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - } - - [NUnit.Framework.Test] - public virtual void NegativeFreshnessPositiveTest() { - DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - ValidationReport report = ValidateTest(checkDate, checkDate.AddDays(5), -3); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - } - - [NUnit.Framework.Test] - public virtual void NegativeFreshnessNegativeTest() { - DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - DateTime thisUpdate = checkDate.AddDays(2); - ValidationReport report = ValidateTest(checkDate, thisUpdate, -3); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(OCSPValidator.FRESHNESS_CHECK, thisUpdate, checkDate - , TimeSpan.FromDays(-3)), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + mockCertificateChainValidator.OnCallDo((c) => { + c.report.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + c.report.AddReportItem(new ReportItem("test2", "test2", ReportItem.ReportItemStatus.INDETERMINATE)); + c.report.AddReportItem(new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INVALID)); + } + ); + ValidationReport report = ValidateTest(checkDate); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItems(0, 0, (la) => la.WithStatus(ReportItem.ReportItemStatus.INVALID)).HasLogItems(2, 2, (la) => + la.WithStatus(ReportItem.ReportItemStatus.INDETERMINATE)).HasLogItem((la) => la.WithStatus(ReportItem.ReportItemStatus + .INFO))); } private ValidationReport ValidateTest(DateTime checkDate) { @@ -404,7 +320,7 @@ private ValidationReport ValidateTest(DateTime checkDate, DateTime thisUpdate, l return report; } - private ValidationReport ValidateRevokedTest(DateTime checkDate, DateTime revocationDate) { + private ValidationReport ValidateRevokedTestMocked(DateTime checkDate, DateTime revocationDate) { TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); builder.SetCertificateStatus(FACTORY.CreateRevokedStatus(revocationDate, FACTORY.CreateCRLReason().GetKeyCompromise ())); @@ -419,7 +335,7 @@ private ValidationReport ValidateRevokedTest(DateTime checkDate, DateTime revoca return report; } - private ValidationReport ValidateOcspWithoutCertsTest(bool addResponderToTrusted) { + private ValidationReport ValidateOcspWithoutCertsTestMocked(bool addResponderToTrusted) { TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); builder.SetOcspCertsChain(new IX509Certificate[0]); TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); @@ -436,7 +352,7 @@ private ValidationReport ValidateOcspWithoutCertsTest(bool addResponderToTrusted return report; } - private ValidationReport VerifyResponderWithOcsp(bool revokedOcsp) { + private ValidationReport VerifyResponderWithOcspMocked(bool revokedOcsp) { String rootCertFileName = SOURCE_FOLDER + "rootCertForOcspTest.pem"; String checkCertFileName = SOURCE_FOLDER + "signCertForOcspTest.pem"; String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCertForOcspTest.pem"; @@ -467,8 +383,6 @@ private ValidationReport VerifyResponderWithOcsp(bool revokedOcsp) { if (revokedOcsp) { parameters.SetContinueAfterFailure(ValidatorContexts.All(), CertificateSources.All(), false); } - validatorChainBuilder.GetRevocationDataValidator().AddOcspClient(ocspClient); - validatorChainBuilder.GetRevocationDataValidator().AddOcspClient(ocspClient2); OCSPValidator validator = validatorChainBuilder.BuildOCSPValidator(); validator.Validate(report, baseContext, checkCert, basicOCSPResp.GetResponses()[0], basicOCSPResp, checkDate ); diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorIntegrationTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorIntegrationTest.cs new file mode 100644 index 0000000000..24edc017d2 --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorIntegrationTest.cs @@ -0,0 +1,111 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using iText.Bouncycastleconnector; +using iText.Commons.Bouncycastle; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Bouncycastle.Crypto; +using iText.Commons.Utils; +using iText.Signatures; +using iText.Signatures.Testutils; +using iText.Signatures.Testutils.Builder; +using iText.Signatures.Testutils.Client; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Report; +using iText.Test; + +namespace iText.Signatures.Validation.V1 { + [NUnit.Framework.Category("BouncyCastleUnitTest")] + public class RevocationDataValidatorIntegrationTest : ExtendedITextTest { + private static readonly IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); + + private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/signatures/validation/v1/RevocationDataValidatorTest/"; + + private static readonly char[] PASSWORD = "testpassphrase".ToCharArray(); + + private static IX509Certificate caCert; + + private static IPrivateKey caPrivateKey; + + private static IX509Certificate checkCert; + + private static IX509Certificate responderCert; + + private static IPrivateKey ocspRespPrivateKey; + + private IssuingCertificateRetriever certificateRetriever; + + private SignatureValidationProperties parameters; + + private ValidatorChainBuilder validatorChainBuilder; + + private ValidationContext baseContext = new ValidationContext(ValidatorContext.SIGNATURE_VALIDATOR, CertificateSource + .SIGNER_CERT, TimeBasedContext.PRESENT); + + [NUnit.Framework.OneTimeSetUp] + public static void Before() { + String rootCertFileName = SOURCE_FOLDER + "rootCert.pem"; + String checkCertFileName = SOURCE_FOLDER + "signCert.pem"; + String ocspResponderCertFileName = SOURCE_FOLDER + "ocspResponderCert.pem"; + caCert = (IX509Certificate)PemFileHelper.ReadFirstChain(rootCertFileName)[0]; + caPrivateKey = PemFileHelper.ReadFirstKey(rootCertFileName, PASSWORD); + checkCert = (IX509Certificate)PemFileHelper.ReadFirstChain(checkCertFileName)[0]; + responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName)[0]; + ocspRespPrivateKey = PemFileHelper.ReadFirstKey(ocspResponderCertFileName, PASSWORD); + } + + [NUnit.Framework.SetUp] + public virtual void SetUp() { + certificateRetriever = new IssuingCertificateRetriever(); + parameters = new SignatureValidationProperties(); + validatorChainBuilder = new ValidatorChainBuilder().WithIssuingCertificateRetriever(certificateRetriever). + WithSignatureValidationProperties(parameters); + } + + [NUnit.Framework.Test] + public virtual void CrlWithOnlySomeReasonsTest() { + TestCrlBuilder builder1 = new TestCrlBuilder(caCert, caPrivateKey); + builder1.AddExtension(FACTORY.CreateExtensions().GetIssuingDistributionPoint(), true, FACTORY.CreateIssuingDistributionPoint + (null, false, false, FACTORY.CreateReasonFlags(CRLValidator.ALL_REASONS - 31), false, false)); + TestCrlBuilder builder2 = new TestCrlBuilder(caCert, caPrivateKey); + builder2.AddExtension(FACTORY.CreateExtensions().GetIssuingDistributionPoint(), true, FACTORY.CreateIssuingDistributionPoint + (null, false, false, FACTORY.CreateReasonFlags(31), false, false)); + TestCrlClient crlClient = new TestCrlClient().AddBuilderForCertIssuer(builder1).AddBuilderForCertIssuer(builder2 + ); + TestOcspResponseBuilder ocspBuilder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + ocspBuilder.SetProducedAt(TimeTestUtil.TEST_DATE_TIME.AddDays(-100)); + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH); + ValidationReport report = new ValidationReport(); + RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); + validator.AddOcspClient(new TestOcspClient().AddBuilderForCertIssuer(caCert, ocspBuilder)).AddCrlClient(crlClient + ); + validator.Validate(report, baseContext, checkCert, TimeTestUtil.TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasLogItem((la) => la.WithCertificate + (checkCert).WithStatus(ReportItem.ReportItemStatus.INFO).WithMessage(CRLValidator.ONLY_SOME_REASONS_CHECKED + ))); + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorTest.cs index 098ae8ff74..68f70ea2a6 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/RevocationDataValidatorTest.cs @@ -22,6 +22,7 @@ You should have received a copy of the GNU Affero General Public License */ using System; using System.Collections.Generic; +using System.Threading; using iText.Bouncycastleconnector; using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Cert; @@ -45,8 +46,6 @@ public class RevocationDataValidatorTest : ExtendedITextTest { private static readonly char[] PASSWORD = "testpassphrase".ToCharArray(); - private const long MILLISECONDS_PER_DAY = 86_400_000L; - private static IX509Certificate caCert; private static IPrivateKey caPrivateKey; @@ -57,6 +56,8 @@ public class RevocationDataValidatorTest : ExtendedITextTest { private static IPrivateKey ocspRespPrivateKey; + private static IX509Certificate trustedOcspResponderCert; + private IssuingCertificateRetriever certificateRetriever; private SignatureValidationProperties parameters; @@ -66,6 +67,12 @@ public class RevocationDataValidatorTest : ExtendedITextTest { private ValidatorChainBuilder validatorChainBuilder; + private MockCrlValidator mockCrlValidator; + + private MockOCSPValidator mockOCSPValidator; + + private MockSignatureValidationProperties mockParameters; + [NUnit.Framework.OneTimeSetUp] public static void Before() { String rootCertFileName = SOURCE_FOLDER + "rootCert.pem"; @@ -76,161 +83,154 @@ public static void Before() { checkCert = (IX509Certificate)PemFileHelper.ReadFirstChain(checkCertFileName)[0]; responderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName)[0]; ocspRespPrivateKey = PemFileHelper.ReadFirstKey(ocspResponderCertFileName, PASSWORD); + trustedOcspResponderCert = (IX509Certificate)PemFileHelper.ReadFirstChain(ocspResponderCertFileName)[0]; } [NUnit.Framework.SetUp] public virtual void SetUp() { certificateRetriever = new IssuingCertificateRetriever(); parameters = new SignatureValidationProperties(); + mockCrlValidator = new MockCrlValidator(); + mockOCSPValidator = new MockOCSPValidator(); + mockParameters = new MockSignatureValidationProperties(parameters); validatorChainBuilder = new ValidatorChainBuilder().WithIssuingCertificateRetriever(certificateRetriever). - WithSignatureValidationProperties(parameters); + WithSignatureValidationProperties(mockParameters).WithCRLValidator(mockCrlValidator).WithOCSPValidator + (mockOCSPValidator); } [NUnit.Framework.Test] - public virtual void BasicValidationWithOcspClientTest() { + public virtual void BasicOCSPValidatorUsageTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; TestOcspResponseBuilder builder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); builder.SetProducedAt(checkDate.AddDays(5)); builder.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(5))); builder.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(10))); - TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder); + TestOcspClientWrapper ocspClient = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer( + caCert, builder)); ValidationReport report = new ValidationReport(); certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); + mockParameters.AddRevocationOnlineFetchingResponse(SignatureValidationProperties.OnlineFetching.NEVER_FETCH + ); + mockParameters.AddRevocationOnlineFetchingResponse(SignatureValidationProperties.OnlineFetching.NEVER_FETCH + ); + mockParameters.AddFreshnessResponse(TimeSpan.FromDays(-2)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); validator.AddOcspClient(ocspClient); + ReportItem reportItem = new ReportItem("validator", "message", ReportItem.ReportItemStatus.INFO); + mockOCSPValidator.OnCallDo((c) => c.report.AddReportItem(reportItem)); validator.Validate(report, baseContext, checkCert, checkDate); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(2, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.TRUSTED_OCSP_RESPONDER, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, item - .GetCertificate().GetSubjectDN()), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID) + // the logitem from the OCSP valdiation should be copied to the final report + .HasNumberOfLogs(1).HasLogItem(reportItem)); + // there should be one call per ocspClient + NUnit.Framework.Assert.AreEqual(1, ocspClient.GetCalls().Count); + // There was only one ocsp response so we expect 1 call to the ocsp validator + NUnit.Framework.Assert.AreEqual(1, mockOCSPValidator.calls.Count); + // the validationDate should be passed as is + NUnit.Framework.Assert.AreEqual(checkDate, mockOCSPValidator.calls[0].validationDate); + // the response should be passed as is + NUnit.Framework.Assert.AreEqual(ocspClient.GetCalls()[0].response, mockOCSPValidator.calls[0].ocspResp); + // There should be a new report generated and any logs must be copied the actual report. + NUnit.Framework.Assert.AreNotEqual(report, mockOCSPValidator.calls[0].report); } [NUnit.Framework.Test] - public virtual void BasicValidationWithCrlClientTest() { - // TODO what is being tested here? + public virtual void BasicCrlValidatorUsageTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; DateTime revocationDate = checkDate.AddDays(-1); TestCrlBuilder builder = new TestCrlBuilder(caCert, caPrivateKey, checkDate); builder.SetNextUpdate(checkDate.AddDays(10)); builder.AddCrlEntry(checkCert, revocationDate, FACTORY.CreateCRLReason().GetKeyCompromise()); - TestCrlClient crlClient = new TestCrlClient().AddBuilderForCertIssuer(builder); + TestCrlClientWrapper crlClient = new TestCrlClientWrapper(new TestCrlClient().AddBuilderForCertIssuer(builder + )); ValidationReport report = new ValidationReport(); certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); - parameters.SetFreshness(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays - (0)); + mockParameters.AddRevocationOnlineFetchingResponse(SignatureValidationProperties.OnlineFetching.NEVER_FETCH + ); + mockParameters.AddRevocationOnlineFetchingResponse(SignatureValidationProperties.OnlineFetching.NEVER_FETCH + ); + mockParameters.AddFreshnessResponse(TimeSpan.FromDays(0)); + ReportItem reportItem = new ReportItem("validator", "message", ReportItem.ReportItemStatus.INFO); + mockCrlValidator.OnCallDo((c) => c.report.AddReportItem(reportItem)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator().AddCrlClient(crlClient ); validator.Validate(report, baseContext, checkCert, checkDate); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(3, report.GetLogs().Count); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[0], report.GetLogs()[2]); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(caCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(CRLValidator.NEXT_UPDATE_VALIDATION, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(caCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, item - .GetCertificate().GetSubjectDN()), item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[2]; - NUnit.Framework.Assert.AreEqual(checkCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CRLValidator.CERTIFICATE_REVOKED, caCert.GetSubjectDN - (), revocationDate), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0) + // the logitem from the CRL valdiation should be copied to the final report + .HasNumberOfLogs(1).HasLogItem(reportItem)); + // there should be one call per CrlClient + NUnit.Framework.Assert.AreEqual(1, crlClient.GetCalls().Count); + // since there was one response there should be one validator call + NUnit.Framework.Assert.AreEqual(1, mockCrlValidator.calls.Count); + NUnit.Framework.Assert.AreEqual(checkCert, mockCrlValidator.calls[0].certificate); + NUnit.Framework.Assert.AreEqual(checkDate, mockCrlValidator.calls[0].validationDate); + // There should be a new report generated and any logs must be copied the actual report. + NUnit.Framework.Assert.AreNotEqual(report, mockCrlValidator.calls[0].report); + NUnit.Framework.Assert.AreEqual(crlClient.GetCalls()[0].responses[0], mockCrlValidator.calls[0].crl); } [NUnit.Framework.Test] - public virtual void UseFreshCrlResponseTest() { - // Add client with indeterminate CRL, then with CRL which contains revoked checkCert. + public virtual void CrlResponseOrderingTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; - DateTime revocationDate = checkDate.AddDays(-1); - TestCrlBuilder builder1 = new TestCrlBuilder(caCert, caPrivateKey, checkDate); - builder1.SetNextUpdate(checkDate.AddDays(2)); - builder1.AddCrlEntry(checkCert, revocationDate, FACTORY.CreateCRLReason().GetKeyCompromise()); - TestCrlClient crlClient1 = new TestCrlClient().AddBuilderForCertIssuer(builder1); - DateTime thisUpdate2 = checkDate.AddDays(-2); + DateTime thisUpdate1 = checkDate.AddDays(-2); + TestCrlBuilder builder1 = new TestCrlBuilder(caCert, caPrivateKey, thisUpdate1); + builder1.SetNextUpdate(checkDate.AddDays(-2)); + TestCrlClientWrapper crlClient1 = new TestCrlClientWrapper(new TestCrlClient().AddBuilderForCertIssuer(builder1 + )); + DateTime thisUpdate2 = checkDate; TestCrlBuilder builder2 = new TestCrlBuilder(caCert, caPrivateKey, thisUpdate2); builder2.SetNextUpdate(checkDate); - TestCrlClient crlClient2 = new TestCrlClient().AddBuilderForCertIssuer(builder2); - ValidationReport report = new ValidationReport(); - certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); - parameters.SetFreshness(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays - (0)); + TestCrlClientWrapper crlClient2 = new TestCrlClientWrapper(new TestCrlClient().AddBuilderForCertIssuer(builder2 + )); + DateTime thisUpdate3 = checkDate.AddDays(+2); + TestCrlBuilder builder3 = new TestCrlBuilder(caCert, caPrivateKey, thisUpdate3); + builder3.SetNextUpdate(checkDate.AddDays(-2)); + TestCrlClientWrapper crlClient3 = new TestCrlClientWrapper(new TestCrlClient().AddBuilderForCertIssuer(builder3 + )); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator().AddCrlClient(crlClient1 - ).AddCrlClient(crlClient2); + ).AddCrlClient(crlClient2).AddCrlClient(crlClient3); + mockCrlValidator.OnCallDo((c) => c.report.AddReportItem(new ReportItem("test", "test", ReportItem.ReportItemStatus + .INDETERMINATE))); + ValidationReport report = new ValidationReport(); validator.Validate(report, baseContext, checkCert, checkDate); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(3, report.GetLogs().Count); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[0], report.GetLogs()[2]); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(caCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(CRLValidator.NEXT_UPDATE_VALIDATION, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(caCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, item - .GetCertificate().GetSubjectDN()), item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[2]; - NUnit.Framework.Assert.AreEqual(checkCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CRLValidator.CERTIFICATE_REVOKED, caCert.GetSubjectDN - (), revocationDate), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); + NUnit.Framework.Assert.AreEqual(crlClient3.GetCalls()[0].responses[0], mockCrlValidator.calls[0].crl); + NUnit.Framework.Assert.AreEqual(crlClient2.GetCalls()[0].responses[0], mockCrlValidator.calls[1].crl); + NUnit.Framework.Assert.AreEqual(crlClient1.GetCalls()[0].responses[0], mockCrlValidator.calls[2].crl); } [NUnit.Framework.Test] - public virtual void UseFreshOcspResponseTest() { - // Add client with indeterminate OCSP, then with valid OCSP. + public virtual void OcspResponseOrderingTest() { DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; TestOcspResponseBuilder builder1 = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); builder1.SetProducedAt(checkDate); builder1.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate)); builder1.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(5))); - builder1.SetCertificateStatus(FACTORY.CreateUnknownStatus()); - TestOcspClient ocspClient1 = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder1); + TestOcspClientWrapper ocspClient1 = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer + (caCert, builder1)); TestOcspResponseBuilder builder2 = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); builder2.SetProducedAt(checkDate.AddDays(5)); builder2.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(5))); builder2.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(10))); - TestOcspClient ocspClient2 = new TestOcspClient().AddBuilderForCertIssuer(caCert, builder2); + TestOcspClientWrapper ocspClient2 = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer + (caCert, builder2)); + TestOcspResponseBuilder builder3 = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); + builder3.SetProducedAt(checkDate.AddDays(2)); + builder3.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(2))); + builder3.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(8))); + TestOcspClientWrapper ocspClient3 = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer + (caCert, builder3)); + mockOCSPValidator.OnCallDo((c) => c.report.AddReportItem(new ReportItem("", "", ReportItem.ReportItemStatus + .INDETERMINATE))); ValidationReport report = new ValidationReport(); certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); - parameters.SetFreshness(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays - (-2)); + mockParameters.AddRevocationOnlineFetchingResponse(SignatureValidationProperties.OnlineFetching.NEVER_FETCH + ).AddFreshnessResponse(TimeSpan.FromDays(-2)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator().AddOcspClient(ocspClient1 - ).AddOcspClient(ocspClient2); + ).AddOcspClient(ocspClient2).AddOcspClient(ocspClient3); validator.Validate(report, baseContext, checkCert, checkDate); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(2, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.TRUSTED_OCSP_RESPONDER, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, item - .GetCertificate().GetSubjectDN()), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); + NUnit.Framework.Assert.AreEqual(ocspClient2.GetCalls()[0].response, mockOCSPValidator.calls[0].ocspResp); + NUnit.Framework.Assert.AreEqual(ocspClient3.GetCalls()[0].response, mockOCSPValidator.calls[1].ocspResp); + NUnit.Framework.Assert.AreEqual(ocspClient1.GetCalls()[0].response, mockOCSPValidator.calls[2].ocspResp); } [NUnit.Framework.Test] @@ -241,13 +241,33 @@ public virtual void ValidityAssuredTest() { ValidationReport report = new ValidationReport(); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); validator.Validate(report, baseContext, certificate, checkDate); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(certificate, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.VALIDITY_ASSURED, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((la) => la.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .VALIDITY_ASSURED).WithCertificate(certificate))); + } + + [NUnit.Framework.Test] + public virtual void SelfSignedCertificateIsNotValidatedTest() { + DateTime checkDate = TimeTestUtil.TEST_DATE_TIME; + ValidationReport report = new ValidationReport(); + RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); + validator.Validate(report, baseContext, caCert, checkDate); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((la) => la.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .SELF_SIGNED_CERTIFICATE).WithCertificate(caCert))); + } + + [NUnit.Framework.Test] + public virtual void NocheckExtensionShouldNotFurtherValdiateTest() { + ValidationReport report = new ValidationReport(); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH); + RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); + validator.Validate(report, baseContext.SetCertificateSource(CertificateSource.OCSP_ISSUER), trustedOcspResponderCert + , TimeTestUtil.TEST_DATE_TIME); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithStatus(ReportItem.ReportItemStatus + .INFO).WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .TRUSTED_OCSP_RESPONDER))); } [NUnit.Framework.Test] @@ -258,30 +278,22 @@ public virtual void NoRevocationDataTest() { , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); validator.Validate(report, baseContext, checkCert, TimeTestUtil.TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.NO_REVOCATION_DATA, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + AssertValidationReport.AssertThat(report, (a) => a.HasLogItem((la) => la.WithStatus(ReportItem.ReportItemStatus + .INDETERMINATE).WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .NO_REVOCATION_DATA))); } [NUnit.Framework.Test] public virtual void TryFetchRevocationDataOnlineTest() { ValidationReport report = new ValidationReport(); parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); + .All(), SignatureValidationProperties.OnlineFetching.FETCH_IF_NO_OTHER_DATA_AVAILABLE).SetFreshness(ValidatorContexts + .All(), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); validator.Validate(report, baseContext, checkCert, TimeTestUtil.TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(1, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.NO_REVOCATION_DATA, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .NO_REVOCATION_DATA))); } [NUnit.Framework.Test] @@ -295,20 +307,16 @@ public virtual void CrlEncodingErrorTest() { parameters.SetFreshness(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays (2)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); - validator.AddCrlClient(new _ICrlClient_355(crl)).Validate(report, baseContext, checkCert, TimeTestUtil.TEST_DATE_TIME + validator.AddCrlClient(new _ICrlClient_398(crl)).Validate(report, baseContext, checkCert, TimeTestUtil.TEST_DATE_TIME ); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.CRL_PARSING_ERROR, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.NO_REVOCATION_DATA, item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((la) => la.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .CRL_PARSING_ERROR)).HasLogItem((la) => la.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK + ).WithMessage(RevocationDataValidator.NO_REVOCATION_DATA))); } - private sealed class _ICrlClient_355 : ICrlClient { - public _ICrlClient_355(byte[] crl) { + private sealed class _ICrlClient_398 : ICrlClient { + public _ICrlClient_398(byte[] crl) { this.crl = crl; } @@ -327,25 +335,28 @@ public virtual void SortResponsesTest() { ocspBuilder1.SetProducedAt(checkDate); ocspBuilder1.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate)); ocspBuilder1.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(3))); - TestOcspClient ocspClient1 = new TestOcspClient().AddBuilderForCertIssuer(caCert, ocspBuilder1); + TestOcspClientWrapper ocspClient1 = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer + (caCert, ocspBuilder1)); TestOcspResponseBuilder ocspBuilder2 = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); ocspBuilder2.SetProducedAt(checkDate.AddDays(3)); ocspBuilder2.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(3))); ocspBuilder2.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(5))); ocspBuilder2.SetCertificateStatus(FACTORY.CreateUnknownStatus()); - TestOcspClient ocspClient2 = new TestOcspClient().AddBuilderForCertIssuer(caCert, ocspBuilder2); + TestOcspClientWrapper ocspClient2 = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer + (caCert, ocspBuilder2)); TestOcspResponseBuilder ocspBuilder3 = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); ocspBuilder3.SetProducedAt(checkDate.AddDays(5)); ocspBuilder3.SetThisUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(5))); ocspBuilder3.SetNextUpdate(DateTimeUtil.GetCalendar(checkDate.AddDays(10))); ocspBuilder3.SetCertificateStatus(FACTORY.CreateUnknownStatus()); - TestOcspClient ocspClient3 = new TestOcspClient().AddBuilderForCertIssuer(caCert, ocspBuilder3); + TestOcspClientWrapper ocspClient3 = new TestOcspClientWrapper(new TestOcspClient().AddBuilderForCertIssuer + (caCert, ocspBuilder3)); TestCrlBuilder crlBuilder1 = new TestCrlBuilder(caCert, caPrivateKey, checkDate); crlBuilder1.SetNextUpdate(checkDate.AddDays(2)); TestCrlBuilder crlBuilder2 = new TestCrlBuilder(caCert, caPrivateKey, checkDate.AddDays(2)); crlBuilder2.SetNextUpdate(checkDate.AddDays(5)); - TestCrlClient crlClient = new TestCrlClient().AddBuilderForCertIssuer(crlBuilder1).AddBuilderForCertIssuer - (crlBuilder2); + TestCrlClientWrapper crlClient = new TestCrlClientWrapper(new TestCrlClient().AddBuilderForCertIssuer(crlBuilder1 + ).AddBuilderForCertIssuer(crlBuilder2)); ValidationReport report = new ValidationReport(); certificateRetriever.AddTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts @@ -353,64 +364,38 @@ public virtual void SortResponsesTest() { .CRL_VALIDATOR), CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-5)); RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator().AddCrlClient(crlClient ).AddOcspClient(ocspClient1).AddOcspClient(ocspClient2).AddOcspClient(ocspClient3); + mockCrlValidator.OnCallDo((c) => { + c.report.AddReportItem(new ReportItem("1", "2", ReportItem.ReportItemStatus.INDETERMINATE)); + try { + Thread.Sleep(10); + } + catch (ThreadInterruptedException) { + } + } + ); + mockOCSPValidator.OnCallDo((c) => { + c.report.AddReportItem(new ReportItem("1", "2", ReportItem.ReportItemStatus.INDETERMINATE)); + try { + Thread.Sleep(10); + } + catch (ThreadInterruptedException) { + } + } + ); validator.Validate(report, baseContext, checkCert, checkDate); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(6, report.GetLogs().Count); - CertificateReportItem item = (CertificateReportItem)report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(checkCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.CERT_STATUS_IS_UNKNOWN, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[1]; - NUnit.Framework.Assert.AreEqual(checkCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.OCSP_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(OCSPValidator.CERT_STATUS_IS_UNKNOWN, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[2]; - NUnit.Framework.Assert.AreEqual(checkCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CRLValidator.FRESHNESS_CHECK, checkDate.AddDays(2 - ), checkDate, TimeSpan.FromDays(-5)), item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[3]; - NUnit.Framework.Assert.AreEqual(checkCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.CRL_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CRLValidator.FRESHNESS_CHECK, checkDate, checkDate - , TimeSpan.FromDays(-5)), item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[4]; - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.TRUSTED_OCSP_RESPONDER, item.GetMessage()); - item = (CertificateReportItem)report.GetLogs()[5]; - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, item - .GetCertificate().GetSubjectDN()), item.GetMessage()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - } - - [NUnit.Framework.Test] - public virtual void CrlWithOnlySomeReasonsTest() { - TestCrlBuilder builder1 = new TestCrlBuilder(caCert, caPrivateKey); - builder1.AddExtension(FACTORY.CreateExtensions().GetIssuingDistributionPoint(), true, FACTORY.CreateIssuingDistributionPoint - (null, false, false, FACTORY.CreateReasonFlags(CRLValidator.ALL_REASONS - 31), false, false)); - TestCrlBuilder builder2 = new TestCrlBuilder(caCert, caPrivateKey); - builder2.AddExtension(FACTORY.CreateExtensions().GetIssuingDistributionPoint(), true, FACTORY.CreateIssuingDistributionPoint - (null, false, false, FACTORY.CreateReasonFlags(31), false, false)); - TestCrlClient crlClient = new TestCrlClient().AddBuilderForCertIssuer(builder1).AddBuilderForCertIssuer(builder2 - ); - TestOcspResponseBuilder ocspBuilder = new TestOcspResponseBuilder(responderCert, ocspRespPrivateKey); - ocspBuilder.SetProducedAt(TimeTestUtil.TEST_DATE_TIME.AddDays(-100)); - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(caCert)); - ValidationReport report = new ValidationReport(); - RevocationDataValidator validator = validatorChainBuilder.BuildRevocationDataValidator(); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH); - validator.AddOcspClient(new TestOcspClient().AddBuilderForCertIssuer(caCert, ocspBuilder)).AddCrlClient(crlClient - ); - validator.Validate(report, baseContext, checkCert, TimeTestUtil.TEST_DATE_TIME); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(0, report.GetFailures().Count); - CertificateReportItem reportItem = (CertificateReportItem)report.GetLogs()[2]; - NUnit.Framework.Assert.AreEqual(ReportItem.ReportItemStatus.INFO, reportItem.GetStatus()); - NUnit.Framework.Assert.AreEqual(checkCert, reportItem.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CRLValidator.ONLY_SOME_REASONS_CHECKED, reportItem.GetMessage()); + NUnit.Framework.Assert.IsTrue(mockOCSPValidator.calls[0].timeStamp.Before(mockOCSPValidator.calls[1].timeStamp + )); + NUnit.Framework.Assert.IsTrue(mockOCSPValidator.calls[1].timeStamp.Before(mockCrlValidator.calls[0].timeStamp + )); + NUnit.Framework.Assert.IsTrue(mockCrlValidator.calls[0].timeStamp.Before(mockCrlValidator.calls[1].timeStamp + )); + NUnit.Framework.Assert.IsTrue(mockCrlValidator.calls[1].timeStamp.Before(mockOCSPValidator.calls[2].timeStamp + )); + NUnit.Framework.Assert.AreEqual(ocspClient1.GetCalls()[0].response, mockOCSPValidator.calls[2].ocspResp); + NUnit.Framework.Assert.AreEqual(ocspClient2.GetCalls()[0].response, mockOCSPValidator.calls[1].ocspResp); + NUnit.Framework.Assert.AreEqual(ocspClient3.GetCalls()[0].response, mockOCSPValidator.calls[0].ocspResp); + NUnit.Framework.Assert.AreEqual(crlClient.GetCalls()[0].responses[0], mockCrlValidator.calls[1].crl); + NUnit.Framework.Assert.AreEqual(crlClient.GetCalls()[0].responses[1], mockCrlValidator.calls[0].crl); } } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidationPropertiesTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidationPropertiesTest.cs index 0a85e313cb..fa5b34ff91 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidationPropertiesTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidationPropertiesTest.cs @@ -144,10 +144,10 @@ public virtual void SetRequiredExtensionsTest() { } private class IncrementalFreshnessValueSetter { - private int value; - private readonly int increment; + private int value; + public IncrementalFreshnessValueSetter(int initialValue, int increment) { this.value = initialValue; this.increment = increment; diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorIntegrationTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorIntegrationTest.cs new file mode 100644 index 0000000000..b58b8861bf --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorIntegrationTest.cs @@ -0,0 +1,201 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using iText.Bouncycastleconnector; +using iText.Commons.Bouncycastle; +using iText.Commons.Bouncycastle.Cert; +using iText.Commons.Bouncycastle.Crypto; +using iText.Commons.Utils; +using iText.Kernel.Pdf; +using iText.Signatures; +using iText.Signatures.Testutils; +using iText.Signatures.Testutils.Builder; +using iText.Signatures.Testutils.Client; +using iText.Signatures.Validation.V1.Context; +using iText.Signatures.Validation.V1.Report; +using iText.Test; + +namespace iText.Signatures.Validation.V1 { + [NUnit.Framework.Category("BouncyCastleIntegrationTest")] + public class SignatureValidatorIntegrationTest : ExtendedITextTest { + private static readonly String CERTS_SRC = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/signatures/validation/v1/SignatureValidatorTest/certs/"; + + private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/signatures/validation/v1/SignatureValidatorTest/"; + + private static readonly IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.GetFactory(); + + private static readonly char[] PASSWORD = "testpassphrase".ToCharArray(); + + private SignatureValidationProperties parameters; + + private IssuingCertificateRetriever certificateRetriever; + + private ValidatorChainBuilder builder; + + [NUnit.Framework.OneTimeSetUp] + public static void Before() { + } + + [NUnit.Framework.SetUp] + public virtual void SetUp() { + parameters = new SignatureValidationProperties(); + certificateRetriever = new IssuingCertificateRetriever(); + builder = new ValidatorChainBuilder().WithIssuingCertificateRetriever(certificateRetriever).WithSignatureValidationProperties + (parameters); + } + + [NUnit.Framework.Test] + public virtual void ValidLatestSignatureTest() { + String chainName = CERTS_SRC + "validCertsChain.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + ValidationReport report; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDoc.pdf"))) { + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + AddRevDataClients(); + SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); + report = signatureValidator.ValidateLatestSignature(); + } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItems + (3, 3, (al) => al.WithCertificate(rootCert).WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK) + .WithMessage(CertificateChainValidator.CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()))); + } + + [NUnit.Framework.Test] + public virtual void LatestSignatureIsTimestampTest() { + String chainName = CERTS_SRC + "validCertsChain.pem"; + String privateKeyName = CERTS_SRC + "rootCertKey.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + IPrivateKey rootPrivateKey = PemFileHelper.ReadFirstKey(privateKeyName, PASSWORD); + ValidationReport report; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "timestampSignatureDoc.pdf"))) { + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + TestOcspResponseBuilder ocspBuilder = new TestOcspResponseBuilder(rootCert, rootPrivateKey); + DateTime currentDate = DateTimeUtil.GetCurrentUtcTime(); + ocspBuilder.SetProducedAt(currentDate); + ocspBuilder.SetThisUpdate(DateTimeUtil.GetCalendar(currentDate.AddDays(3))); + ocspBuilder.SetNextUpdate(DateTimeUtil.GetCalendar(currentDate.AddDays(30))); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(rootCert, ocspBuilder); + builder.GetRevocationDataValidator().AddOcspClient(ocspClient); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() + , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); + SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); + report = signatureValidator.ValidateLatestSignature(); + } + AssertValidationReport.AssertThat(report, (a) => a.HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItems(2 + , 2, (la) => la.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (l) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); + } + + [NUnit.Framework.Test] + public virtual void CertificatesNotInLatestSignatureTest() { + String chainName = CERTS_SRC + "validCertsChain.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate signingCert = (IX509Certificate)certificateChain[0]; + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + ValidationReport report; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDocWithoutChain.pdf"))) { + SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() + , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); + report = signatureValidator.ValidateLatestSignature(); + } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasLogItem((al) => al.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .NO_REVOCATION_DATA).WithCertificate(signingCert).WithStatus(ReportItem.ReportItemStatus.INDETERMINATE + )).HasLogItem((al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .ISSUER_MISSING, (i) => signingCert.GetSubjectDN()).WithStatus(ReportItem.ReportItemStatus.INDETERMINATE + ).WithCertificate(signingCert))); + } + + [NUnit.Framework.Test] + public virtual void CertificatesNotInLatestSignatureButSetAsKnownTest() { + String chainName = CERTS_SRC + "validCertsChain.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate intermediateCert = (IX509Certificate)certificateChain[1]; + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + ValidationReport report; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDocWithoutChain.pdf"))) { + certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.SingletonList(intermediateCert)); + AddRevDataClients(); + SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); + report = signatureValidator.ValidateLatestSignature(); + } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItems + (3, 3, (al) => al.WithCheckName(CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator + .CERTIFICATE_TRUSTED, (i) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); + } + + [NUnit.Framework.Test] + public virtual void RootIsNotTrustedInLatestSignatureTest() { + String chainName = CERTS_SRC + "validCertsChain.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + ValidationReport report; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDoc.pdf"))) { + SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() + , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); + report = signatureValidator.ValidateLatestSignature(); + } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(3).HasLogItem((al) => al.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK + ).WithMessage(RevocationDataValidator.NO_REVOCATION_DATA).WithCertificate((IX509Certificate)certificateChain + [0])).HasLogItem((al) => al.WithCheckName(RevocationDataValidator.REVOCATION_DATA_CHECK).WithMessage(RevocationDataValidator + .NO_REVOCATION_DATA).WithCertificate((IX509Certificate)certificateChain[1])).HasLogItem((al) => al.WithCheckName + (CertificateChainValidator.CERTIFICATE_CHECK).WithMessage(CertificateChainValidator.ISSUER_MISSING, (i + ) => rootCert.GetSubjectDN()).WithCertificate(rootCert))); + } + + private void AddRevDataClients() { + String chainName = CERTS_SRC + "validCertsChain.pem"; + String privateKeyName = CERTS_SRC + "rootCertKey.pem"; + IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); + IX509Certificate intermediateCert = (IX509Certificate)certificateChain[1]; + IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + IPrivateKey rootPrivateKey = PemFileHelper.ReadFirstKey(privateKeyName, PASSWORD); + DateTime currentDate = DateTimeUtil.GetCurrentUtcTime(); + TestOcspResponseBuilder builder1 = new TestOcspResponseBuilder(rootCert, rootPrivateKey); + builder1.SetProducedAt(currentDate); + builder1.SetThisUpdate(DateTimeUtil.GetCalendar(currentDate)); + builder1.SetNextUpdate(DateTimeUtil.GetCalendar(currentDate.AddDays(30))); + TestOcspResponseBuilder builder2 = new TestOcspResponseBuilder(rootCert, rootPrivateKey); + builder2.SetProducedAt(currentDate); + builder2.SetThisUpdate(DateTimeUtil.GetCalendar(currentDate)); + builder2.SetNextUpdate(DateTimeUtil.GetCalendar(currentDate.AddDays(30))); + TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(rootCert, builder1).AddBuilderForCertIssuer + (intermediateCert, builder2); + builder.GetRevocationDataValidator().AddOcspClient(ocspClient); + parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts + .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH); + } + } +} diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorTest.cs index ff77711bf8..0abc1ce0a1 100644 --- a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorTest.cs +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/SignatureValidatorTest.cs @@ -21,6 +21,8 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ using System; +using System.Collections.Generic; +using System.Linq; using iText.Bouncycastleconnector; using iText.Commons.Bouncycastle; using iText.Commons.Bouncycastle.Cert; @@ -28,7 +30,6 @@ You should have received a copy of the GNU Affero General Public License using iText.Commons.Bouncycastle.Security; using iText.Commons.Utils; using iText.Kernel.Pdf; -using iText.Signatures; using iText.Signatures.Testutils; using iText.Signatures.Testutils.Builder; using iText.Signatures.Testutils.Client; @@ -37,7 +38,7 @@ You should have received a copy of the GNU Affero General Public License using iText.Test; namespace iText.Signatures.Validation.V1 { - [NUnit.Framework.Category("BouncyCastleIntegrationTest")] + [NUnit.Framework.Category("BouncyCastleUnitTest")] public class SignatureValidatorTest : ExtendedITextTest { private static readonly String CERTS_SRC = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext .CurrentContext.TestDirectory) + "/resources/itext/signatures/validation/v1/SignatureValidatorTest/certs/"; @@ -51,43 +52,24 @@ public class SignatureValidatorTest : ExtendedITextTest { private SignatureValidationProperties parameters; - private IssuingCertificateRetriever certificateRetriever; + private MockIssuingCertificateRetriever mockCertificateRetriever; private ValidatorChainBuilder builder; + private MockChainValidator mockCertificateChainValidator; + [NUnit.Framework.OneTimeSetUp] public static void Before() { } [NUnit.Framework.SetUp] public virtual void SetUp() { + mockCertificateChainValidator = new MockChainValidator(); parameters = new SignatureValidationProperties(); - certificateRetriever = new IssuingCertificateRetriever(); - builder = new ValidatorChainBuilder().WithIssuingCertificateRetriever(certificateRetriever).WithSignatureValidationProperties - (parameters); - } - - [NUnit.Framework.Test] - public virtual void ValidLatestSignatureTest() { - String chainName = CERTS_SRC + "validCertsChain.pem"; - IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); - IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; - ValidationReport report; - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDoc.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - AddRevDataClients(); - SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); - report = signatureValidator.ValidateLatestSignature(); - } - for (int i = 0; i < 3; ++i) { - CertificateReportItem item = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(rootCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item.GetMessage()); - } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(3, report.GetLogs().Count); + mockCertificateRetriever = new MockIssuingCertificateRetriever(); + builder = new ValidatorChainBuilder().WithIssuingCertificateRetriever(mockCertificateRetriever).WithSignatureValidationProperties + (parameters).WithCertificateChainValidator(mockCertificateChainValidator).WithRevocationDataValidator( + new MockRevocationDataValidator()); } [NUnit.Framework.Test] @@ -97,9 +79,11 @@ public virtual void LatestSignatureIsTimestampTest() { IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; IPrivateKey rootPrivateKey = PemFileHelper.ReadFirstKey(privateKeyName, PASSWORD); + IX509Certificate timeStampCert = (IX509Certificate)PemFileHelper.ReadFirstChain(CERTS_SRC + "timestamp.pem" + )[0]; ValidationReport report; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "timestampSignatureDoc.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + mockCertificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); TestOcspResponseBuilder ocspBuilder = new TestOcspResponseBuilder(rootCert, rootPrivateKey); DateTime currentDate = DateTimeUtil.GetCurrentUtcTime(); ocspBuilder.SetProducedAt(currentDate); @@ -113,33 +97,11 @@ public virtual void LatestSignatureIsTimestampTest() { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(); } - new AssertValidationReport(report).HasNumberOfFailures(0).HasNumberOfLogs(2).HasLogItems((l) => l.GetCheckName - ().Equals(CertificateChainValidator.CERTIFICATE_CHECK) && l.GetMessage().Equals(MessageFormatUtil.Format - (CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert.GetSubjectDN())) && ((CertificateReportItem)l - ).GetCertificate().Equals(rootCert), 2, CertificateChainValidator.CERTIFICATE_TRUSTED).DoAssert(); - } - - [NUnit.Framework.Test] - public virtual void ValidLatestSignatureWithTimestampTest() { - String chainName = CERTS_SRC + "validCertsChain.pem"; - IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); - IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; - ValidationReport report; - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDocWithTimestamp.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - AddRevDataClients(); - SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); - report = signatureValidator.ValidateLatestSignature(); - } - for (int i = 0; i < 5; ++i) { - CertificateReportItem item1 = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item1.GetMessage()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(rootCert, item1.GetCertificate()); - } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(5, report.GetLogs().Count); + NUnit.Framework.Assert.AreEqual(1, mockCertificateChainValidator.verificationCalls.Count); + MockChainValidator.ValidationCallBack call = mockCertificateChainValidator.verificationCalls[0]; + NUnit.Framework.Assert.AreEqual(CertificateSource.TIMESTAMP, call.context.GetCertificateSource()); + NUnit.Framework.Assert.AreEqual(ValidatorContext.SIGNATURE_VALIDATOR, call.context.GetValidatorContext()); + NUnit.Framework.Assert.AreEqual(timeStampCert.GetSubjectDN(), call.certificate.GetSubjectDN()); } [NUnit.Framework.Test] @@ -150,25 +112,13 @@ public virtual void LatestSignatureWithBrokenTimestampTest() { ValidationReport report; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "docWithBrokenTimestamp.pdf")) ) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - AddRevDataClients(); + mockCertificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(); } - NUnit.Framework.Assert.AreEqual(report.GetFailures()[0], report.GetLogs()[0]); - ReportItem failure = report.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(SignatureValidator.CANNOT_VERIFY_TIMESTAMP, failure.GetMessage()); - NUnit.Framework.Assert.AreEqual(SignatureValidator.TIMESTAMP_VERIFICATION, failure.GetCheckName()); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(1, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(6, report.GetLogs().Count); - for (int i = 0; i < 5; ++i) { - CertificateReportItem item1 = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(rootCert, item1.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item1.GetMessage()); - } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasLogItem + ((al) => al.WithCheckName(SignatureValidator.TIMESTAMP_VERIFICATION).WithMessage(SignatureValidator.CANNOT_VERIFY_TIMESTAMP + ).WithStatus(ReportItem.ReportItemStatus.INVALID))); } [NUnit.Framework.Test] @@ -178,31 +128,14 @@ public virtual void DocumentModifiedLatestSignatureTest() { IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; ValidationReport report; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "modifiedDoc.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - AddRevDataClients(); + mockCertificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(); } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(2, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(5, report.GetLogs().Count); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[0], report.GetLogs()[0]); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[1], report.GetLogs()[1]); - ReportItem item1 = report.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(SignatureValidator.SIGNATURE_VERIFICATION, item1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(SignatureValidator.DOCUMENT_IS_NOT_COVERED, "Signature1" - ), item1.GetMessage()); - ReportItem item2 = report.GetFailures()[1]; - NUnit.Framework.Assert.AreEqual(SignatureValidator.SIGNATURE_VERIFICATION, item2.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(SignatureValidator.CANNOT_VERIFY_SIGNATURE, "Signature1" - ), item2.GetMessage()); - for (int i = 0; i < 3; ++i) { - CertificateReportItem item = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(rootCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item.GetMessage()); - } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasLogItem + ((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION).WithMessage(SignatureValidator.DOCUMENT_IS_NOT_COVERED + , (i) => "Signature1")).HasLogItem((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION) + .WithMessage(SignatureValidator.CANNOT_VERIFY_SIGNATURE, (i) => "Signature1"))); } [NUnit.Framework.Test] @@ -214,184 +147,79 @@ public virtual void LatestSignatureInvalidStopValidationTest() { parameters.SetContinueAfterFailure(ValidatorContexts.All(), CertificateSources.All(), false); using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "modifiedDoc.pdf"))) { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); + mockCertificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); report = signatureValidator.ValidateLatestSignature(); } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(2, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(2, report.GetLogs().Count); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[0], report.GetLogs()[0]); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[1], report.GetLogs()[1]); - ReportItem item1 = report.GetFailures()[0]; - NUnit.Framework.Assert.AreEqual(SignatureValidator.SIGNATURE_VERIFICATION, item1.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(SignatureValidator.DOCUMENT_IS_NOT_COVERED, "Signature1" - ), item1.GetMessage()); - ReportItem item2 = report.GetFailures()[1]; - NUnit.Framework.Assert.AreEqual(SignatureValidator.SIGNATURE_VERIFICATION, item2.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(SignatureValidator.CANNOT_VERIFY_SIGNATURE, "Signature1" - ), item2.GetMessage()); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasLogItem + ((al) => al.WithCheckName(SignatureValidator.SIGNATURE_VERIFICATION).WithMessage(SignatureValidator.DOCUMENT_IS_NOT_COVERED + , (i) => "Signature1").WithStatus(ReportItem.ReportItemStatus.INVALID)).HasLogItem((al) => al.WithCheckName + (SignatureValidator.SIGNATURE_VERIFICATION).WithMessage(SignatureValidator.CANNOT_VERIFY_SIGNATURE, (i + ) => "Signature1").WithStatus(ReportItem.ReportItemStatus.INVALID))); + // check that no requests are made after failure + NUnit.Framework.Assert.AreEqual(0, mockCertificateChainValidator.verificationCalls.Count); } [NUnit.Framework.Test] - public virtual void CertificatesNotInLatestSignatureTest() { + public virtual void CertificatesNotInLatestSignatureButTakenFromDSSTest() { String chainName = CERTS_SRC + "validCertsChain.pem"; IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); - IX509Certificate signingCert = (IX509Certificate)certificateChain[0]; IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; - ValidationReport report; - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDocWithoutChain.pdf"))) { - SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); - report = signatureValidator.ValidateLatestSignature(); - } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(2, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(2, report.GetLogs().Count); - CertificateReportItem item = report.GetCertificateFailures()[0]; - NUnit.Framework.Assert.AreEqual(signingCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.NO_REVOCATION_DATA, item.GetMessage()); - item = report.GetCertificateFailures()[1]; - NUnit.Framework.Assert.AreEqual(signingCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.ISSUER_MISSING, signingCert - .GetSubjectDN()), item.GetMessage()); - } - - [NUnit.Framework.Test] - public virtual void CertificatesNotInLatestSignatureButSetAsKnownTest() { - String chainName = CERTS_SRC + "validCertsChain.pem"; - IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); IX509Certificate intermediateCert = (IX509Certificate)certificateChain[1]; - IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; - ValidationReport report; - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDocWithoutChain.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - certificateRetriever.AddKnownCertificates(JavaCollectionsUtil.SingletonList(intermediateCert)); - AddRevDataClients(); + IX509Certificate signCert = (IX509Certificate)certificateChain[0]; + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "docWithDss.pdf"))) { + mockCertificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); - report = signatureValidator.ValidateLatestSignature(); + signatureValidator.ValidateLatestSignature(); } - for (int i = 0; i < 3; ++i) { - CertificateReportItem item = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(rootCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item.GetMessage()); - } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(3, report.GetLogs().Count); + NUnit.Framework.Assert.AreEqual(2, mockCertificateRetriever.addKnownCertificatesCalls.Count); + ICollection dssCall = mockCertificateRetriever.addKnownCertificatesCalls[0]; + NUnit.Framework.Assert.AreEqual(3, dssCall.Count); + NUnit.Framework.Assert.AreEqual(1, dssCall.Where((c) => ((IX509Certificate)c).Equals(rootCert)).Count()); + NUnit.Framework.Assert.AreEqual(1, dssCall.Where((c) => ((IX509Certificate)c).Equals(intermediateCert)).Count + ()); + NUnit.Framework.Assert.AreEqual(1, dssCall.Where((c) => ((IX509Certificate)c).Equals(signCert)).Count()); } [NUnit.Framework.Test] - public virtual void CertificatesNotInLatestSignatureButTakenFromDSSTest() { + public virtual void CertificatesNotInLatestSignatureButTakenFromDSSOneCertIsBrokenTest() { String chainName = CERTS_SRC + "validCertsChain.pem"; IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; ValidationReport report; - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "docWithDss.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - AddRevDataClients(); + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "docWithBrokenDss.pdf"))) { + mockCertificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(); } - for (int i = 0; i < 3; ++i) { - CertificateReportItem item = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(rootCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item.GetMessage()); - } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(3, report.GetLogs().Count); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.VALID).HasLogItem + ((al) => al.WithCheckName(SignatureValidator.CERTS_FROM_DSS).WithExceptionCauseType(typeof(AbstractGeneralSecurityException + )))); } [NUnit.Framework.Test] - public virtual void CertificatesNotInLatestSignatureButTakenFromDSSOneCertIsBrokenTest() { - String chainName = CERTS_SRC + "validCertsChain.pem"; - IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); - IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + public virtual void IndeterminateChainValidationLeadsToIndeterminateResultTest() { + mockCertificateChainValidator.OnCallDo((c) => c.report.AddReportItem(new ReportItem("test", "test", ReportItem.ReportItemStatus + .INDETERMINATE))); ValidationReport report; - using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "docWithBrokenDss.pdf"))) { - certificateRetriever.SetTrustedCertificates(JavaCollectionsUtil.SingletonList(rootCert)); - AddRevDataClients(); + using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDoc.pdf"))) { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); report = signatureValidator.ValidateLatestSignature(); } - ReportItem reportItem = report.GetLogs()[0]; - NUnit.Framework.Assert.AreEqual(SignatureValidator.CERTS_FROM_DSS, reportItem.GetCheckName()); - NUnit.Framework.Assert.IsTrue(reportItem.GetExceptionCause() is AbstractGeneralSecurityException); - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, report.GetValidationResult()); - NUnit.Framework.Assert.AreEqual(4, report.GetLogs().Count); - for (int i = 0; i < 3; ++i) { - CertificateReportItem item = report.GetCertificateLogs()[i]; - NUnit.Framework.Assert.AreEqual(rootCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.CERTIFICATE_TRUSTED, rootCert - .GetSubjectDN()), item.GetMessage()); - } + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INDETERMINATE + ).HasNumberOfFailures(1).HasLogItem((al) => al.WithCheckName("test").WithMessage("test"))); } [NUnit.Framework.Test] - public virtual void RootIsNotTrustedInLatestSignatureTest() { - String chainName = CERTS_SRC + "validCertsChain.pem"; - IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); - IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; + public virtual void InvalidChainValidationLeadsToInvalidResultTest() { + mockCertificateChainValidator.OnCallDo((c) => c.report.AddReportItem(new ReportItem("test", "test", ReportItem.ReportItemStatus + .INVALID))); ValidationReport report; using (PdfDocument document = new PdfDocument(new PdfReader(SOURCE_FOLDER + "validDoc.pdf"))) { SignatureValidator signatureValidator = builder.BuildSignatureValidator(document); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH).SetFreshness(ValidatorContexts.All() - , CertificateSources.All(), TimeBasedContexts.All(), TimeSpan.FromDays(-2)); report = signatureValidator.ValidateLatestSignature(); } - NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, report.GetValidationResult - ()); - NUnit.Framework.Assert.AreEqual(3, report.GetFailures().Count); - NUnit.Framework.Assert.AreEqual(4, report.GetLogs().Count); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[0], report.GetLogs()[0]); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[1], report.GetLogs()[1]); - NUnit.Framework.Assert.AreEqual(report.GetFailures()[2], report.GetLogs()[3]); - CertificateReportItem item = report.GetCertificateFailures()[0]; - NUnit.Framework.Assert.AreEqual(certificateChain[0], item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.NO_REVOCATION_DATA, item.GetMessage()); - item = report.GetCertificateFailures()[1]; - NUnit.Framework.Assert.AreEqual(certificateChain[1], item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.REVOCATION_DATA_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(RevocationDataValidator.NO_REVOCATION_DATA, item.GetMessage()); - item = report.GetCertificateFailures()[2]; - NUnit.Framework.Assert.AreEqual(rootCert, item.GetCertificate()); - NUnit.Framework.Assert.AreEqual(CertificateChainValidator.CERTIFICATE_CHECK, item.GetCheckName()); - NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(CertificateChainValidator.ISSUER_MISSING, rootCert - .GetSubjectDN()), item.GetMessage()); - } - - private void AddRevDataClients() { - String chainName = CERTS_SRC + "validCertsChain.pem"; - String privateKeyName = CERTS_SRC + "rootCertKey.pem"; - IX509Certificate[] certificateChain = PemFileHelper.ReadFirstChain(chainName); - IX509Certificate intermediateCert = (IX509Certificate)certificateChain[1]; - IX509Certificate rootCert = (IX509Certificate)certificateChain[2]; - IPrivateKey rootPrivateKey = PemFileHelper.ReadFirstKey(privateKeyName, PASSWORD); - DateTime currentDate = DateTimeUtil.GetCurrentUtcTime(); - TestOcspResponseBuilder builder1 = new TestOcspResponseBuilder(rootCert, rootPrivateKey); - builder1.SetProducedAt(currentDate); - builder1.SetThisUpdate(DateTimeUtil.GetCalendar(currentDate)); - builder1.SetNextUpdate(DateTimeUtil.GetCalendar(currentDate.AddDays(30))); - TestOcspResponseBuilder builder2 = new TestOcspResponseBuilder(rootCert, rootPrivateKey); - builder2.SetProducedAt(currentDate); - builder2.SetThisUpdate(DateTimeUtil.GetCalendar(currentDate)); - builder2.SetNextUpdate(DateTimeUtil.GetCalendar(currentDate.AddDays(30))); - TestOcspClient ocspClient = new TestOcspClient().AddBuilderForCertIssuer(rootCert, builder1).AddBuilderForCertIssuer - (intermediateCert, builder2); - builder.GetRevocationDataValidator().AddOcspClient(ocspClient); - parameters.SetRevocationOnlineFetching(ValidatorContexts.All(), CertificateSources.All(), TimeBasedContexts - .All(), SignatureValidationProperties.OnlineFetching.NEVER_FETCH); + AssertValidationReport.AssertThat(report, (a) => a.HasStatus(ValidationReport.ValidationResult.INVALID).HasNumberOfFailures + (1).HasLogItem((al) => al.WithCheckName("test").WithMessage("test"))); } } } diff --git a/itext.tests/itext.sign.tests/itext/signatures/validation/v1/report/ValidationReportTest.cs b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/report/ValidationReportTest.cs new file mode 100644 index 0000000000..edd75cfede --- /dev/null +++ b/itext.tests/itext.sign.tests/itext/signatures/validation/v1/report/ValidationReportTest.cs @@ -0,0 +1,149 @@ +/* +This file is part of the iText (R) project. +Copyright (c) 1998-2024 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 . +*/ +using System; +using iText.Commons.Bouncycastle.Cert; +using iText.Signatures.Testutils; +using iText.Test; + +namespace iText.Signatures.Validation.V1.Report { + [NUnit.Framework.Category("BouncyCastleUnitTest")] + public class ValidationReportTest : ExtendedITextTest { + private static readonly String CERTS_SRC = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext + .CurrentContext.TestDirectory) + "/resources/itext/signatures/certs/"; + + [NUnit.Framework.Test] + public virtual void GetValidationResultWithNoLogsShouldBeValid() { + ValidationReport sut = new ValidationReport(); + NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, sut.GetValidationResult()); + } + + [NUnit.Framework.Test] + public virtual void GetValidationResultWithOnlyValidLogsShouldBeValid() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + sut.AddReportItem(new ReportItem("test2", "test2", ReportItem.ReportItemStatus.INFO)); + sut.AddReportItem(new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INFO)); + NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.VALID, sut.GetValidationResult()); + } + + [NUnit.Framework.Test] + public virtual void GetValidationResultWithValidAndIndeterminateLogsShouldBeIndeterminate() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + sut.AddReportItem(new ReportItem("test2", "test2", ReportItem.ReportItemStatus.INDETERMINATE)); + sut.AddReportItem(new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INFO)); + NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INDETERMINATE, sut.GetValidationResult() + ); + } + + [NUnit.Framework.Test] + public virtual void GetValidationResultWithInvalidLogsShouldBeInvalid() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + sut.AddReportItem(new ReportItem("test2", "test2", ReportItem.ReportItemStatus.INVALID)); + sut.AddReportItem(new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INDETERMINATE)); + NUnit.Framework.Assert.AreEqual(ValidationReport.ValidationResult.INVALID, sut.GetValidationResult()); + } + + [NUnit.Framework.Test] + public virtual void TestGetFailures() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + ReportItem failure1 = new ReportItem("test2", "test2", ReportItem.ReportItemStatus.INVALID); + sut.AddReportItem(failure1); + ReportItem failure2 = new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INDETERMINATE); + sut.AddReportItem(failure2); + NUnit.Framework.Assert.IsTrue(sut.GetFailures().Contains(failure1)); + NUnit.Framework.Assert.IsTrue(sut.GetFailures().Contains(failure2)); + NUnit.Framework.Assert.AreEqual(2, sut.GetFailures().Count); + } + + [NUnit.Framework.Test] + public virtual void GetCertificateFailuresTest() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + IX509Certificate cert = (IX509Certificate)PemFileHelper.ReadFirstChain(CERTS_SRC + "adobeExtensionCert.pem" + )[0]; + CertificateReportItem failure1 = new CertificateReportItem(cert, "test2", "test2", ReportItem.ReportItemStatus + .INVALID); + sut.AddReportItem(failure1); + ReportItem failure2 = new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INDETERMINATE); + sut.AddReportItem(failure2); + NUnit.Framework.Assert.IsTrue(sut.GetCertificateFailures().Contains(failure1)); + NUnit.Framework.Assert.AreEqual(1, sut.GetCertificateFailures().Count); + } + + [NUnit.Framework.Test] + public virtual void GetLogsTest() { + ValidationReport sut = new ValidationReport(); + ReportItem item1 = new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO); + sut.AddReportItem(item1); + IX509Certificate cert = (IX509Certificate)PemFileHelper.ReadFirstChain(CERTS_SRC + "adobeExtensionCert.pem" + )[0]; + CertificateReportItem failure1 = new CertificateReportItem(cert, "test2", "test2", ReportItem.ReportItemStatus + .INVALID); + sut.AddReportItem(failure1); + ReportItem failure2 = new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INDETERMINATE); + sut.AddReportItem(failure2); + NUnit.Framework.Assert.AreEqual(item1, sut.GetLogs()[0]); + NUnit.Framework.Assert.AreEqual(failure1, sut.GetLogs()[1]); + NUnit.Framework.Assert.AreEqual(failure2, sut.GetLogs()[2]); + NUnit.Framework.Assert.AreEqual(3, sut.GetLogs().Count); + } + + [NUnit.Framework.Test] + public virtual void GetCertificateLogsTest() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1", "test1", ReportItem.ReportItemStatus.INFO)); + IX509Certificate cert = (IX509Certificate)PemFileHelper.ReadFirstChain(CERTS_SRC + "adobeExtensionCert.pem" + )[0]; + CertificateReportItem failure1 = new CertificateReportItem(cert, "test2", "test2", ReportItem.ReportItemStatus + .INVALID); + sut.AddReportItem(failure1); + ReportItem failure2 = new ReportItem("test3", "test3", ReportItem.ReportItemStatus.INDETERMINATE); + sut.AddReportItem(failure2); + NUnit.Framework.Assert.IsTrue(sut.GetCertificateLogs().Contains(failure1)); + NUnit.Framework.Assert.AreEqual(1, sut.GetCertificateLogs().Count); + } + + [NUnit.Framework.Test] + public virtual void ToStringTest() { + ValidationReport sut = new ValidationReport(); + sut.AddReportItem(new ReportItem("test1check", "test1message", ReportItem.ReportItemStatus.INFO)); + IX509Certificate cert = (IX509Certificate)PemFileHelper.ReadFirstChain(CERTS_SRC + "adobeExtensionCert.pem" + )[0]; + CertificateReportItem failure1 = new CertificateReportItem(cert, "test2check", "test2message", ReportItem.ReportItemStatus + .INVALID); + sut.AddReportItem(failure1); + ReportItem failure2 = new ReportItem("test3check", "test3message", ReportItem.ReportItemStatus.INDETERMINATE + ); + sut.AddReportItem(failure2); + NUnit.Framework.Assert.IsTrue(sut.ToString().Contains("INVALID")); + NUnit.Framework.Assert.IsTrue(sut.ToString().Contains("test1check")); + NUnit.Framework.Assert.IsTrue(sut.ToString().Contains("test1message")); + NUnit.Framework.Assert.IsTrue(sut.ToString().Contains("test2check")); + NUnit.Framework.Assert.IsTrue(sut.ToString().Contains("test2message")); + NUnit.Framework.Assert.IsTrue(sut.ToString().Contains("test3check")); + } + } +} diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/addTextField.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/addTextField.pdf new file mode 100644 index 0000000000..81eaeb6b4e Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/addTextField.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/addUnsignedSignatureField.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/addUnsignedSignatureField.pdf new file mode 100644 index 0000000000..c45881fb5f Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/addUnsignedSignatureField.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/brokenSignatureFieldDictionary.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/brokenSignatureFieldDictionary.pdf new file mode 100644 index 0000000000..3ac68c0aaa Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/brokenSignatureFieldDictionary.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/padesSignatureLevelBTest1.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/fillInField.pdf similarity index 64% rename from itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/padesSignatureLevelBTest1.pdf rename to itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/fillInField.pdf index 286726cfc2..6df17c07af 100644 Binary files a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/padesSignatureLevelBTest1.pdf and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/fillInField.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/makePagesDirect.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/makeFontDirectAndIndirect.pdf similarity index 77% rename from itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/makePagesDirect.pdf rename to itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/makeFontDirectAndIndirect.pdf index c5fc4fc433..9d180f3c53 100644 Binary files a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/makePagesDirect.pdf and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/makeFontDirectAndIndirect.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/modifyPageAnnots.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/modifyPageAnnots.pdf new file mode 100644 index 0000000000..c7f4a173c9 Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/modifyPageAnnots.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/multipleRevisionsDocument2.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/multipleRevisionsDocument2.pdf new file mode 100644 index 0000000000..cafcfb94c3 Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/multipleRevisionsDocument2.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeAcroform.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeAcroform.pdf new file mode 100644 index 0000000000..4c1e90b704 Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeAcroform.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeDSS.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeDSS.pdf new file mode 100644 index 0000000000..63f8ad0a41 Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeDSS.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeField.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeField.pdf new file mode 100644 index 0000000000..7ffac83690 Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removeField.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removePermissions.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removePermissions.pdf new file mode 100644 index 0000000000..915e2ad8dd Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/removePermissions.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/renameField.pdf b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/renameField.pdf new file mode 100644 index 0000000000..5292794a09 Binary files /dev/null and b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/DocumentRevisionsValidatorTest/renameField.pdf differ diff --git a/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/RevocationDataValidatorTest/trustedOcspResponderCert.pem b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/RevocationDataValidatorTest/trustedOcspResponderCert.pem new file mode 100644 index 0000000000..87bd404436 --- /dev/null +++ b/itext.tests/itext.sign.tests/resources/itext/signatures/validation/v1/RevocationDataValidatorTest/trustedOcspResponderCert.pem @@ -0,0 +1,51 @@ +-----BEGIN CERTIFICATE----- +MIIDbzCCAlegAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwODELMAkGA1UEBhMCQlkx +DjAMBgNVBAoMBWlUZXh0MRkwFwYDVQQDDBBpVGV4dFRlc3RSb290UnNhMCAXDTAw +MDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjA+MQswCQYDVQQGEwJCWTEOMAwG +A1UECgwFaVRleHQxHzAdBgNVBAMMFmlUZXh0VGVzdE9jc3BSZXNwb25kZXIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTpG5XNA+s7WqXukyPwb5UCmhl +JdJuY/5Qa/48cY5qLoOAT5EYAofxmaZU3BmLWupkvIAm3HkkBNzrvOR830uuomzv +1kxoPyycBm9jlvap3PRzFfOeqBqjoRVcw9EXbnCZQWFDdRaPB5zyG/Vwi0ycwhXb +Zs98JXZeD8uYIdRjpd8ygLAb4+vpV+kyIOrSuQHsa/HdCGBTmu1uQSU9J/vjHmQy +INgCHVhue1t7VHu5rAtyOXFoE8ydyoE0VgrO50ibfp83CZNYjE8oBfkM/B92kcMI +9O5Qg+C/1U6/r/i4YG49/6Muvd6y9++TJU1QVsYIpiWcoWR/apHhOh7flSg7AgMB +AAGjezB5MB0GA1UdDgQWBBQ/1FBILdb8EtHROA6izhhyDJ7rLjAfBgNVHSMEGDAW +gBQpg7aJjDLCulYvlE5pwqzcCjSFQTAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/ +BAwwCgYIKwYBBQUHAwkwDwYJKwYBBQUHMAEFBAIFADANBgkqhkiG9w0BAQsFAAOC +AQEALBJSsM3YgGzkHsdNSUDO5EFWRlXu9oDFPltywa/9f6rzI+bY+udAETCNPCoQ +xWr66Of2f3MwgUqOrhLNpB1OQekHWDCfdcFdMYw08TjwURSrMT7LejiujQLBI4Sz +HZXUSuCDbyTaCqhAxEknSkyp3ujZ1zvKz40/EWSgwdx+bJP02AfEzhRigMoMB2P9 +yzaoJrIFuqrZv9LDqh2DCZnybMjXlMA4VshTuEIMNwIViG1t/gNIMeRvLKB8Mnx4 +5ep0gRorY7+BTI0hLp8TgNmDiKuA/rzQlaIsWihT/KrWOfdfLeZ6BgtZB5J/B3IH +r7yOzAb6zvBJRWCfKFgCYHBUaA== +-----END CERTIFICATE----- +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQcdIPPqgvPd5xSXiw +hD4NKwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEKX2ZZrPTL+wipvQ +1dczNlwEggTQg5i86SShEScxyx6AEtBhbjqfBiOC25jYXIaL6WyucwgUsEC/vu0r +sNJ2ik9BozSOLg073hyBhrGIf5ZYOuj5cRnvebxW7sj1lpWq/92hax/+t5jFpbOh +ekEZFTdI3crwFtj8AWyRIty9vx/X6NhfGh7cjH5XsthgkxdNUja+C/+Kop3vn49V +d4JIuqRLVzlnIzEVn+3sDjduG25hV3cHW5T7PBQkCzsZXPPK45pIryzt5UQ3Ylfu +QiOGAQ6+vIw1ws+Fp9YRiROFgQgLgvJzo1W77S21AGZ41pWtdQfBIKDu2i4lC8Wp +xHa+/KxmhKkE4hH7FgJkBzwjX4BRjhh5OtXGLVsbu0NC6LWqMqyuxyx89EST+J/W +PxMUDWO3cU9MzwgcK/un0D3OiISuIR72JEEG4da/5rIj0zoP8DZmBPHQarsNDF2y +rHxzC20WwtMO7691AsYPSzeM2DNxoyCdd4pmzM/zZNUxJkb2k0d83cbff7osGuQ1 +RJZlAGxpS89Ki5n72UVBec2hSaAIvxeyJAIsVcwU3C+md+OwQNid1h8avPtvXRzB +J/olvIheSKbRZ4RFtnatkGtAGqWmawC76kmqmCOGjq/8q3itgNhrDT2F8r85e7mz +ZPRv+uHn9+mLaDy07r6FLlAnKIQZktSZ2luVbW1EpBShcYNahqFzMkSgb078WttX +VHfhQToYqc2mUAo2+FwPbGh82WqtDDgdcYKOJ7JwWnnz4bQhqRXS3w2p81xX/dMN +rGkrM9SHHa7VpH45ZtgrUK6rwXx0376S4C6uoZm6ENdWesStAIiU7PEC4TrKwJKY +PxpBkdV1zCHii5gvbx1K9PIU8DFPGqRDHNNrW8VCjn6/8dnyKwQxUrVM3Na6wfcU +y3Cw4q25DyJXSqzowIi+pAN0KVgMMZnzLz380eS94wRkL+fOb34XATvbBW5btwMA +ctLOPhDEI6KipF7suXU0d7GkR12sTcKWYGKGaJtfjjuGM3Qi5gvCen/zV6HtEnxf +hZrPNhrlR7t53iqxX8Vib3I1lLihnS8/L/EDc1YG6Ew4bV3WllJj+7bwctY0hMgN +f2/JzZwrdoY6dUTGtQN2MoCBAJlEpFjrvysAuNkNrbpSHx/Z4LHaqrlBhnbVl5qp +FGP6Li7GE19sWH1rDmz4JFdLJ8z3WBvjWfSjl6xGk3Xiinkv72PoZGHjhIsVuW2r ++iUXiAcm0NnvTcM7u1lr+oaXhScUVTmlH8drtLhEDJGXxM7aEUYR0+zIl15+r3YV +sOr9c8uH/ctrYyr6SMFyHI6V7rxj/HtVI/UssCC7SqqEKdzea2Tr1ojPHakT0VVc +05FXAhyOrdhJegwn7RuEtl2TcKok+zlJK27bLLYjhKwlBd/Q+LKMYwfQQ7qTZEfR +IOCo5jOI4xq3Nhq1t2L0yWMMB5y6x+xx9A5AQGvbF4HLf9Cw+q0nk9EBY0THwFhS +9ilOznX+oI6DmK5uhN8ghxdiv8IfZR7IQunGhKD7bdSLsncFAP/MOkUIZkCOhxFD +fI2E0uxQ8wyh0aewPETFTioG2iva9poT9v7bIVBaCG6W3YK+jU/4fs2HZQe/6jr1 +IXimv/KBWTSsv5LdIyhLRIDY0cPgyOZkgeFwkawAY4bPwh/CUcdqUEE= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/itext/itext.forms/itext/forms/fields/PdfFormAnnotationUtil.cs b/itext/itext.forms/itext/forms/fields/PdfFormAnnotationUtil.cs index 49c8a0229a..59db7dd841 100644 --- a/itext/itext.forms/itext/forms/fields/PdfFormAnnotationUtil.cs +++ b/itext/itext.forms/itext/forms/fields/PdfFormAnnotationUtil.cs @@ -102,6 +102,7 @@ public static void MergeWidgetWithParentField(PdfFormField field) { kidDict.Remove(PdfName.Parent); field.GetPdfObject().MergeDifferent(kidDict); field.RemoveChildren(); + kidDict.GetIndirectReference().SetFree(); field.SetChildField(PdfFormAnnotation.MakeFormAnnotation(field.GetPdfObject(), field.GetDocument())); ReplaceAnnotationOnPage(kidDict, field.GetPdfObject()); } diff --git a/itext/itext.sign/itext/signatures/validation/v1/DocumentRevisionsValidator.cs b/itext/itext.sign/itext/signatures/validation/v1/DocumentRevisionsValidator.cs index 3ab770af81..e82dbc7215 100644 --- a/itext/itext.sign/itext/signatures/validation/v1/DocumentRevisionsValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/v1/DocumentRevisionsValidator.cs @@ -26,6 +26,8 @@ You should have received a copy of the GNU Affero General Public License using System.Linq; using iText.Commons.Actions.Contexts; using iText.Commons.Utils; +using iText.Forms; +using iText.Forms.Fields; using iText.IO.Source; using iText.Kernel.Pdf; using iText.Signatures.Validation.V1.Report; @@ -35,20 +37,56 @@ namespace iText.Signatures.Validation.V1 { internal class DocumentRevisionsValidator { internal const String DOC_MDP_CHECK = "DocMDP check."; - internal const String NOT_ALLOWED_CATALOG_CHANGES = "PDF document catalog contains changes other than DSS dictionary addition, which is not allowed."; + internal const String ACROFORM_REMOVED = "AcroForm dictionary was removed from catalog."; + + internal const String ANNOTATIONS_MODIFIED = "Field annotations were removed, added or unexpectedly modified."; + + internal const String DEVELOPER_EXTENSION_REMOVED = "Developer extension \"{0}\" dictionary was removed or unexpectedly modified."; + + internal const String DIRECT_OBJECT = "{0} must be an indirect reference."; internal const String DSS_REMOVED = "DSS dictionary was removed from catalog."; - internal const String EXTENSIONS_REMOVED = "Extensions dictionary was removed from catalog."; + internal const String EXTENSIONS_REMOVED = "Extensions dictionary was removed from the catalog."; - internal const String DEVELOPER_EXTENSION_REMOVED = "Developer extension \"{0}\" dictionary was removed or unexpectedly modified."; + internal const String EXTENSIONS_TYPE = "Developer extensions must be a dictionary."; internal const String EXTENSION_LEVEL_DECREASED = "Extension level number in developer extension \"{0}\" dictionary was decreased."; + internal const String FIELD_REMOVED = "Form field {0} was removed or unexpectedly modified."; + + internal const String NOT_ALLOWED_ACROFORM_CHANGES = "PDF document AcroForm contains changes other than " + + "document timestamp (docMDP level >= 1), form fill-in and digital signatures (docMDP level >= 2), " + + "adding or editing annotations (docMDP level 3), which are not allowed."; + + internal const String NOT_ALLOWED_CATALOG_CHANGES = "PDF document catalog contains changes other than " + + "DSS dictionary and DTS addition (docMDP level >= 1), " + "form fill-in and digital signatures (docMDP level >= 2), " + + "adding or editing annotations (docMDP level 3), which are not allowed."; + internal const String OBJECT_REMOVED = "Object \"{0}\", which is not allowed to be removed, was removed from the document through XREF table."; + internal const String PAGES_MODIFIED = "Pages structure was unexpectedly modified."; + + internal const String PAGE_ANNOTATIONS_MODIFIED = "Page annotations were unexpectedly modified."; + + internal const String PAGE_MODIFIED = "Page was unexpectedly modified."; + + internal const String PERMISSIONS_REMOVED = "Permissions dictionary was removed from the catalog."; + + internal const String PERMISSIONS_TYPE = "Permissions must be a dictionary."; + + internal const String PERMISSION_REMOVED = "Permission \"{0}\" dictionary was removed or unexpectedly modified."; + + internal const String REFERENCE_REMOVED = "Signature reference dictionary was removed or unexpectedly modified."; + + internal const String SIGNATURE_MODIFIED = "Signature {0} was unexpectedly modified."; + internal const String UNEXPECTED_ENTRY_IN_XREF = "New PDF document revision contains unexpected entry \"{0}\" in XREF table."; + internal const String UNEXPECTED_FORM_FIELD = "New PDF document revision contains unexpected form field \"{0}\"."; + + internal int docMDP = 2; + private IMetaInfo metaInfo; internal DocumentRevisionsValidator() { @@ -67,6 +105,14 @@ public virtual void SetEventCountingMetaInfo(IMetaInfo metaInfo) { this.metaInfo = metaInfo; } + internal static Stream CreateInputStreamFromRevision(PdfDocument originalDocument, DocumentRevision revision + ) { + RandomAccessFileOrArray raf = originalDocument.GetReader().GetSafeFile(); + WindowRandomAccessSource source = new WindowRandomAccessSource(raf.CreateSourceView(), 0, revision.GetEofOffset + ()); + return new RASInputStream(source); + } + internal virtual ValidationReport ValidateRevision(PdfDocument originalDocument, PdfDocument documentWithoutRevision , DocumentRevision revision) { ValidationReport validationReport = new ValidationReport(); @@ -112,15 +158,23 @@ internal virtual ValidationReport ValidateRevision(PdfDocument originalDocument, return validationReport; } - private bool CheckAllowedReferences(IList allowedReferences, PdfIndirectReference - indirectReference, PdfDocument documentWithoutRevision) { - foreach (DocumentRevisionsValidator.ReferencesPair allowedReference in allowedReferences) { - if (IsSameReference(allowedReference.GetCurrentReference(), indirectReference)) { - return documentWithoutRevision.GetPdfObject(indirectReference.GetObjNumber()) == null || allowedReferences - .Any((reference) => IsSameReference(reference.GetPreviousReference(), indirectReference)); - } + private bool CompareCatalogs(PdfDocument documentWithoutRevision, PdfDocument documentWithRevision, ValidationReport + report) { + PdfDictionary previousCatalog = documentWithoutRevision.GetCatalog().GetPdfObject(); + PdfDictionary currentCatalog = documentWithRevision.GetCatalog().GetPdfObject(); + PdfDictionary previousCatalogCopy = CopyCatalogEntriesToCompare(previousCatalog); + PdfDictionary currentCatalogCopy = CopyCatalogEntriesToCompare(currentCatalog); + if (!ComparePdfObjects(previousCatalogCopy, currentCatalogCopy)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, NOT_ALLOWED_CATALOG_CHANGES, ReportItem.ReportItemStatus + .INVALID)); + return false; } - return false; + return CompareExtensions(previousCatalog.Get(PdfName.Extensions), currentCatalog.Get(PdfName.Extensions), + report) && ComparePermissions(previousCatalog.Get(PdfName.Perms), currentCatalog.Get(PdfName.Perms), report + ) && CompareDss(previousCatalog.Get(PdfName.DSS), currentCatalog.Get(PdfName.DSS), report) && ComparePages + (previousCatalog.GetAsDictionary(PdfName.Pages), currentCatalog.GetAsDictionary(PdfName.Pages), report + ) && CompareAcroForms(previousCatalog.GetAsDictionary(PdfName.AcroForm), currentCatalog.GetAsDictionary + (PdfName.AcroForm), report); } private IList CreateAllowedReferences(PdfDocument documentWithRevision @@ -148,17 +202,505 @@ private bool CheckAllowedReferences(IList previousDssDictionary.GetIndirectReference()))); + allowedReferences.AddAll(CreateAllowedDssEntries(documentWithRevision, documentWithoutRevision)); + } + PdfDictionary currentAcroForm = documentWithRevision.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.AcroForm + ); + if (currentAcroForm != null) { + PdfDictionary previousAcroForm = documentWithoutRevision.GetCatalog().GetPdfObject().GetAsDictionary(PdfName + .AcroForm); + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentAcroForm.GetIndirectReference() + , GetIndirectReferenceOrNull(() => previousAcroForm.GetIndirectReference()))); + allowedReferences.AddAll(CreateAllowedAcroFormEntries(documentWithRevision, documentWithoutRevision)); + } + PdfDictionary currentPagesDictionary = documentWithRevision.GetCatalog().GetPdfObject().GetAsDictionary(PdfName + .Pages); + if (currentPagesDictionary != null) { + PdfDictionary previousPagesDictionary = documentWithoutRevision.GetCatalog().GetPdfObject().GetAsDictionary + (PdfName.Pages); + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentPagesDictionary.GetIndirectReference + (), GetIndirectReferenceOrNull(() => previousPagesDictionary.GetIndirectReference()))); + allowedReferences.AddAll(CreateAllowedPagesEntries(currentPagesDictionary, previousPagesDictionary)); } - allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentDssDictionary.GetIndirectReference - (), GetIndirectReferenceOrNull(() => previousDssDictionary.GetIndirectReference()))); - allowedReferences.AddAll(CreateAllowedDssEntries(documentWithRevision, documentWithoutRevision)); return allowedReferences; } + private bool CheckAllowedReferences(IList allowedReferences, PdfIndirectReference + indirectReference, PdfDocument documentWithoutRevision) { + foreach (DocumentRevisionsValidator.ReferencesPair allowedReference in allowedReferences) { + if (IsSameReference(allowedReference.GetCurrentReference(), indirectReference)) { + return documentWithoutRevision.GetPdfObject(indirectReference.GetObjNumber()) == null || allowedReferences + .Any((reference) => IsSameReference(reference.GetPreviousReference(), indirectReference)); + } + } + return false; + } + + // Compare catalogs nested methods section: + private bool CompareExtensions(PdfObject previousExtensions, PdfObject currentExtensions, ValidationReport + report) { + if (previousExtensions == null || ComparePdfObjects(previousExtensions, currentExtensions)) { + return true; + } + if (currentExtensions == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, EXTENSIONS_REMOVED, ReportItem.ReportItemStatus.INVALID + )); + return false; + } + if (!(previousExtensions is PdfDictionary) || !(currentExtensions is PdfDictionary)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, EXTENSIONS_TYPE, ReportItem.ReportItemStatus.INVALID)); + return false; + } + PdfDictionary previousExtensionsDictionary = (PdfDictionary)previousExtensions; + PdfDictionary currentExtensionsDictionary = (PdfDictionary)currentExtensions; + foreach (KeyValuePair previousExtension in previousExtensionsDictionary.EntrySet()) { + PdfDictionary currentExtension = currentExtensionsDictionary.GetAsDictionary(previousExtension.Key); + if (currentExtension == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(DEVELOPER_EXTENSION_REMOVED, previousExtension + .Key), ReportItem.ReportItemStatus.INVALID)); + return false; + } + else { + PdfDictionary currentExtensionCopy = new PdfDictionary(currentExtension); + currentExtensionCopy.Remove(PdfName.ExtensionLevel); + PdfDictionary previousExtensionCopy = new PdfDictionary((PdfDictionary)previousExtension.Value); + previousExtensionCopy.Remove(PdfName.ExtensionLevel); + // Apart from extension level dictionaries are expected to be equal. + if (!ComparePdfObjects(previousExtensionCopy, currentExtensionCopy)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(DEVELOPER_EXTENSION_REMOVED, previousExtension + .Key), ReportItem.ReportItemStatus.INVALID)); + return false; + } + PdfNumber previousExtensionLevel = ((PdfDictionary)previousExtension.Value).GetAsNumber(PdfName.ExtensionLevel + ); + PdfNumber currentExtensionLevel = currentExtension.GetAsNumber(PdfName.ExtensionLevel); + if (previousExtensionLevel != null) { + if (currentExtensionLevel == null || previousExtensionLevel.IntValue() > currentExtensionLevel.IntValue()) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(EXTENSION_LEVEL_DECREASED, previousExtension + .Key), ReportItem.ReportItemStatus.INVALID)); + return false; + } + } + } + } + return true; + } + + private bool ComparePermissions(PdfObject previousPerms, PdfObject currentPerms, ValidationReport report) { + if (previousPerms == null || ComparePdfObjects(previousPerms, currentPerms)) { + return true; + } + if (currentPerms == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PERMISSIONS_REMOVED, ReportItem.ReportItemStatus.INVALID + )); + return false; + } + if (!(previousPerms is PdfDictionary) || !(currentPerms is PdfDictionary)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PERMISSIONS_TYPE, ReportItem.ReportItemStatus.INVALID)); + return false; + } + PdfDictionary previousPermsDictionary = (PdfDictionary)previousPerms; + PdfDictionary currentPermsDictionary = (PdfDictionary)currentPerms; + foreach (KeyValuePair previousPermission in previousPermsDictionary.EntrySet()) { + PdfDictionary currentPermission = currentPermsDictionary.GetAsDictionary(previousPermission.Key); + if (currentPermission == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(PERMISSION_REMOVED, previousPermission + .Key), ReportItem.ReportItemStatus.INVALID)); + return false; + } + else { + // Perms dictionary is the signature dictionary. + if (!CompareSignatureDictionaries(previousPermission.Value, currentPermission, report)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(PERMISSION_REMOVED, previousPermission + .Key), ReportItem.ReportItemStatus.INVALID)); + return false; + } + } + } + return true; + } + + private bool CompareDss(PdfObject previousDss, PdfObject currentDss, ValidationReport report) { + if (previousDss == null) { + return true; + } + if (currentDss == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, DSS_REMOVED, ReportItem.ReportItemStatus.INVALID)); + return false; + } + return true; + } + + private bool ComparePages(PdfDictionary prevPages, PdfDictionary currPages, ValidationReport report) { + if (prevPages == null ^ currPages == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PAGES_MODIFIED, ReportItem.ReportItemStatus.INVALID)); + return false; + } + if (prevPages == null) { + return true; + } + PdfDictionary previousPagesCopy = new PdfDictionary(prevPages); + previousPagesCopy.Remove(PdfName.Kids); + previousPagesCopy.Remove(PdfName.Parent); + PdfDictionary currentPagesCopy = new PdfDictionary(currPages); + currentPagesCopy.Remove(PdfName.Kids); + currentPagesCopy.Remove(PdfName.Parent); + if (!ComparePdfObjects(previousPagesCopy, currentPagesCopy) || !CompareIndirectReferencesObjNums(prevPages + .Get(PdfName.Parent), currPages.Get(PdfName.Parent), report, "Page tree node parent")) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PAGES_MODIFIED, ReportItem.ReportItemStatus.INVALID)); + return false; + } + PdfArray prevKids = prevPages.GetAsArray(PdfName.Kids); + PdfArray currKids = currPages.GetAsArray(PdfName.Kids); + if (prevKids.Size() != currKids.Size()) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PAGES_MODIFIED, ReportItem.ReportItemStatus.INVALID)); + return false; + } + for (int i = 0; i < currKids.Size(); ++i) { + PdfDictionary previousKid = prevKids.GetAsDictionary(i); + PdfDictionary currentKid = currKids.GetAsDictionary(i); + if (PdfName.Pages.Equals(previousKid.GetAsName(PdfName.Type))) { + // Compare page tree nodes. + if (!ComparePages(previousKid, currentKid, report)) { + return false; + } + } + else { + // Compare page objects (leaf node in the page tree). + PdfDictionary previousPageCopy = new PdfDictionary(previousKid); + previousPageCopy.Remove(PdfName.Annots); + previousPageCopy.Remove(PdfName.Parent); + PdfDictionary currentPageCopy = new PdfDictionary(currentKid); + currentPageCopy.Remove(PdfName.Annots); + currentPageCopy.Remove(PdfName.Parent); + if (!ComparePdfObjects(previousPageCopy, currentPageCopy) || !CompareIndirectReferencesObjNums(previousKid + .Get(PdfName.Parent), currentKid.Get(PdfName.Parent), report, "Page parent")) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PAGE_MODIFIED, ReportItem.ReportItemStatus.INVALID)); + return false; + } + PdfArray prevAnnots = GetAnnotsNotAllowedToBeModified(previousKid); + PdfArray currAnnots = GetAnnotsNotAllowedToBeModified(currentKid); + if (!ComparePdfObjects(prevAnnots, currAnnots)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, PAGE_ANNOTATIONS_MODIFIED, ReportItem.ReportItemStatus. + INVALID)); + return false; + } + } + } + return true; + } + + private bool CompareAcroForms(PdfDictionary prevAcroForm, PdfDictionary currAcroForm, ValidationReport report + ) { + if (prevAcroForm == null) { + if (currAcroForm == null) { + return true; + } + PdfArray fields = currAcroForm.GetAsArray(PdfName.Fields); + foreach (PdfObject field in fields) { + PdfDictionary fieldDict = (PdfDictionary)field; + if (!IsAllowedSignatureField(fieldDict, report)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, NOT_ALLOWED_ACROFORM_CHANGES, ReportItem.ReportItemStatus + .INVALID)); + return false; + } + } + return true; + } + if (currAcroForm == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, ACROFORM_REMOVED, ReportItem.ReportItemStatus.INVALID)); + return false; + } + PdfDictionary previousAcroFormCopy = CopyAcroformDictionary(prevAcroForm); + PdfDictionary currentAcroFormCopy = CopyAcroformDictionary(currAcroForm); + PdfArray prevFields = prevAcroForm.GetAsArray(PdfName.Fields); + PdfArray currFields = currAcroForm.GetAsArray(PdfName.Fields); + if (!ComparePdfObjects(previousAcroFormCopy, currentAcroFormCopy) || (prevFields.Size() > currFields.Size( + )) || !CompareFormFields(prevFields, currFields, report)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, NOT_ALLOWED_ACROFORM_CHANGES, ReportItem.ReportItemStatus + .INVALID)); + return false; + } + return true; + } + + private bool CompareFormFields(PdfArray prevFields, PdfArray currFields, ValidationReport report) { + IDictionary prevFieldsMap = PopulateFormFieldsMap(prevFields); + IDictionary currFieldsMap = PopulateFormFieldsMap(currFields); + foreach (KeyValuePair fieldEntry in prevFieldsMap) { + PdfDictionary previousField = fieldEntry.Value; + PdfDictionary currentField = currFieldsMap.Get(fieldEntry.Key); + if (currentField == null || !CompareFields(previousField, currentField, report)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(FIELD_REMOVED, fieldEntry.Key) + , ReportItem.ReportItemStatus.INVALID)); + return false; + } + currFieldsMap.JRemove(fieldEntry.Key); + } + foreach (KeyValuePair fieldEntry in currFieldsMap) { + if (!IsAllowedSignatureField(fieldEntry.Value, report)) { + return false; + } + } + return CompareAnnotations(prevFields, currFields, report); + } + + /// DocMDP level >= 2 allows setting values of the fields and accordingly update the widget appearances of them. + /// + /// + /// DocMDP level >= 2 allows setting values of the fields and accordingly update the widget appearances of them. But + /// you cannot change the form structure, so it is not allowed to add, remove or rename fields, change most of their + /// properties. + /// + /// field from the previous revision to check + /// field from the current revision to check + /// validation report + /// + /// + /// + /// if the changes of the field are allowed, + /// + /// otherwise. + /// + private bool CompareFields(PdfDictionary previousField, PdfDictionary currentField, ValidationReport report + ) { + PdfDictionary prevFormDict = CopyFieldDictionary(previousField); + PdfDictionary currFormDict = CopyFieldDictionary(currentField); + if (!ComparePdfObjects(prevFormDict, currFormDict) || !CompareIndirectReferencesObjNums(prevFormDict.Get(PdfName + .Parent), currFormDict.Get(PdfName.Parent), report, "Form field parent") || !CompareIndirectReferencesObjNums + (prevFormDict.Get(PdfName.P), currFormDict.Get(PdfName.P), report, "Page object with which field annotation is associated" + )) { + return false; + } + PdfObject prevValue = previousField.Get(PdfName.V); + PdfObject currValue = currentField.Get(PdfName.V); + if (prevValue == null && currValue == null && PdfName.Ch.Equals(currentField.GetAsName(PdfName.FT))) { + // Choice field: if the items in the I entry differ from those in the V entry, the V entry shall be used. + prevValue = previousField.Get(PdfName.I); + currValue = currentField.Get(PdfName.I); + } + if (PdfName.Sig.Equals(currentField.GetAsName(PdfName.FT))) { + if (!CompareSignatureDictionaries(prevValue, currValue, report)) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(SIGNATURE_MODIFIED, currentField + .GetAsString(PdfName.T).GetValue()), ReportItem.ReportItemStatus.INVALID)); + return false; + } + } + else { + if (docMDP == 1 && !ComparePdfObjects(prevValue, currValue)) { + return false; + } + } + return CompareFormFields(previousField.GetAsArray(PdfName.Kids), currentField.GetAsArray(PdfName.Kids), report + ); + } + + private bool CompareAnnotations(PdfArray prevFields, PdfArray currFields, ValidationReport report) { + IList prevAnnots = PopulateAnnotations(prevFields); + IList currAnnots = PopulateAnnotations(currFields); + if (prevAnnots.Count != currAnnots.Count) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, ANNOTATIONS_MODIFIED, ReportItem.ReportItemStatus.INVALID + )); + return false; + } + for (int i = 0; i < prevAnnots.Count; i++) { + PdfDictionary prevAnnot = new PdfDictionary(prevAnnots[i]); + RemoveAppearanceRelatedProperties(prevAnnot); + PdfDictionary currAnnot = new PdfDictionary(currAnnots[i]); + RemoveAppearanceRelatedProperties(currAnnot); + if (!ComparePdfObjects(prevAnnot, currAnnot) || !CompareIndirectReferencesObjNums(prevAnnots[i].Get(PdfName + .P), currAnnots[i].Get(PdfName.P), report, "Page object with which annotation is associated")) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, ANNOTATIONS_MODIFIED, ReportItem.ReportItemStatus.INVALID + )); + return false; + } + } + return true; + } + + private bool CompareSignatureDictionaries(PdfObject prevSigDict, PdfObject curSigDict, ValidationReport report + ) { + if (prevSigDict == null) { + return true; + } + if (curSigDict == null) { + return false; + } + if (!(prevSigDict is PdfDictionary) || !(curSigDict is PdfDictionary)) { + return false; + } + PdfDictionary currentSigDictCopy = new PdfDictionary((PdfDictionary)curSigDict); + currentSigDictCopy.Remove(PdfName.Reference); + PdfDictionary previousSigDictCopy = new PdfDictionary((PdfDictionary)prevSigDict); + previousSigDictCopy.Remove(PdfName.Reference); + // Apart from the reference, dictionaries are expected to be equal. + if (!ComparePdfObjects(previousSigDictCopy, currentSigDictCopy)) { + return false; + } + PdfArray previousReference = ((PdfDictionary)prevSigDict).GetAsArray(PdfName.Reference); + PdfArray currentReference = ((PdfDictionary)curSigDict).GetAsArray(PdfName.Reference); + return CompareSignatureReferenceDictionaries(previousReference, currentReference, report); + } + + private bool CompareSignatureReferenceDictionaries(PdfArray previousReferences, PdfArray currentReferences + , ValidationReport report) { + if (previousReferences == null || ComparePdfObjects(previousReferences, currentReferences)) { + return true; + } + if (currentReferences == null || previousReferences.Size() != currentReferences.Size()) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, REFERENCE_REMOVED, ReportItem.ReportItemStatus.INVALID) + ); + return false; + } + else { + for (int i = 0; i < previousReferences.Size(); ++i) { + PdfDictionary currentReferenceCopy = new PdfDictionary(currentReferences.GetAsDictionary(i)); + currentReferenceCopy.Remove(PdfName.Data); + PdfDictionary previousReferenceCopy = new PdfDictionary(previousReferences.GetAsDictionary(i)); + previousReferenceCopy.Remove(PdfName.Data); + // Apart from the data, dictionaries are expected to be equal. Data is an indirect reference + // to the object in the document upon which the object modification analysis should be performed. + if (!ComparePdfObjects(previousReferenceCopy, currentReferenceCopy) || !CompareIndirectReferencesObjNums(previousReferences + .GetAsDictionary(i).Get(PdfName.Data), currentReferences.GetAsDictionary(i).Get(PdfName.Data), report, + "Data entry in the signature reference dictionary")) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, REFERENCE_REMOVED, ReportItem.ReportItemStatus.INVALID) + ); + return false; + } + } + } + return true; + } + + private bool CompareIndirectReferencesObjNums(PdfObject prevObj, PdfObject currObj, ValidationReport report + , String type) { + if (prevObj == null ^ currObj == null) { + return false; + } + if (prevObj == null) { + return true; + } + PdfIndirectReference prevObjRef = prevObj.GetIndirectReference(); + PdfIndirectReference currObjRef = currObj.GetIndirectReference(); + if (prevObjRef == null || currObjRef == null) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(DIRECT_OBJECT, type), ReportItem.ReportItemStatus + .INVALID)); + return false; + } + return IsSameReference(prevObjRef, currObjRef); + } + + /// + /// DocMDP level <=2 allows adding new fields in the following cases: + /// docMDP level 1: allows adding only DocTimeStamp signature fields; + /// docMDP level 2: same as level 1 and also adding and then signing signature fields, + /// so signature dictionary shouldn't be null. + /// + /// newly added field entry + /// validation report + /// true if newly added field is allowed to be added, false otherwise. + private bool IsAllowedSignatureField(PdfDictionary field, ValidationReport report) { + PdfDictionary value = field.GetAsDictionary(PdfName.V); + if (!PdfName.Sig.Equals(field.GetAsName(PdfName.FT)) || value == null || (docMDP == 1 && !PdfName.DocTimeStamp + .Equals(value.GetAsName(PdfName.Type)))) { + report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(UNEXPECTED_FORM_FIELD, field.GetAsString + (PdfName.T).GetValue()), ReportItem.ReportItemStatus.INVALID)); + return false; + } + return true; + } + + private IDictionary PopulateFormFieldsMap(PdfArray fieldsArray) { + IDictionary fields = new Dictionary(); + if (fieldsArray != null) { + for (int i = 0; i < fieldsArray.Size(); ++i) { + PdfDictionary fieldDict = (PdfDictionary)fieldsArray.Get(i); + if (PdfFormField.IsFormField(fieldDict)) { + String fieldName = fieldDict.GetAsString(PdfName.T).GetValue(); + fields.Put(fieldName, fieldDict); + } + } + } + return fields; + } + + private IList PopulateAnnotations(PdfArray fieldsArray) { + IList annotations = new List(); + if (fieldsArray != null) { + for (int i = 0; i < fieldsArray.Size(); ++i) { + PdfDictionary annotDict = (PdfDictionary)fieldsArray.Get(i); + if (PdfFormAnnotationUtil.IsPureWidget(annotDict)) { + annotations.Add(annotDict); + } + } + } + return annotations; + } + + private PdfArray GetAnnotsNotAllowedToBeModified(PdfDictionary page) { + PdfArray annots = page.GetAsArray(PdfName.Annots); + if (annots == null) { + return null; + } + PdfArray annotsCopy = new PdfArray(annots); + foreach (PdfObject annot in annots) { + PdfDictionary annotDict = (PdfDictionary)annot; + if (PdfFormAnnotationUtil.IsPureWidgetOrMergedField(annotDict)) { + // Ideally we should also distinguish between docMDP level 1 (DTS) or 2 allowed annotations + // (we check them only on the acroform level, but they could be added to the page) + annotsCopy.Remove(annot); + } + } + return annotsCopy; + } + + private PdfDictionary CopyCatalogEntriesToCompare(PdfDictionary catalog) { + PdfDictionary catalogCopy = new PdfDictionary(catalog); + catalogCopy.Remove(PdfName.Metadata); + catalogCopy.Remove(PdfName.Extensions); + catalogCopy.Remove(PdfName.Perms); + catalogCopy.Remove(PdfName.DSS); + catalogCopy.Remove(PdfName.AcroForm); + catalogCopy.Remove(PdfName.Pages); + return catalogCopy; + } + + private PdfDictionary CopyAcroformDictionary(PdfDictionary acroForm) { + PdfDictionary acroFormCopy = new PdfDictionary(acroForm); + acroFormCopy.Remove(PdfName.Fields); + acroFormCopy.Remove(PdfName.DR); + acroFormCopy.Remove(PdfName.DA); + return acroFormCopy; + } + + private PdfDictionary CopyFieldDictionary(PdfDictionary field) { + PdfDictionary formDict = new PdfDictionary(field); + formDict.Remove(PdfName.V); + // Value for the choice fields could be specified by the /I key. + formDict.Remove(PdfName.I); + formDict.Remove(PdfName.Parent); + formDict.Remove(PdfName.Kids); + // Remove also annotation related properties (e.g. in case of the merged field). + RemoveAppearanceRelatedProperties(formDict); + return formDict; + } + + private void RemoveAppearanceRelatedProperties(PdfDictionary annotDict) { + annotDict.Remove(PdfName.P); + if (docMDP > 1) { + annotDict.Remove(PdfName.AP); + annotDict.Remove(PdfName.AS); + annotDict.Remove(PdfName.M); + annotDict.Remove(PdfName.F); + } + } + + // Allowed references creation nested methods section: private IList CreateAllowedDssEntries(PdfDocument documentWithRevision , PdfDocument documentWithoutRevision) { IList allowedReferences = new List CreateAllowedPagesEntries(PdfDictionary currentPagesDictionary + , PdfDictionary previousPagesDictionary) { + IList allowedReferences = new List(); + PdfArray currentKids = currentPagesDictionary.GetAsArray(PdfName.Kids); + if (currentKids != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentKids.GetIndirectReference(), GetIndirectReferenceOrNull + (() => previousPagesDictionary.Get(PdfName.Kids).GetIndirectReference()))); + for (int i = 0; i < currentKids.Size(); ++i) { + int finalI = i; + PdfDictionary currentPageNode = currentKids.GetAsDictionary(i); + PdfDictionary previousPageNode = null; + try { + previousPageNode = previousPagesDictionary.GetAsArray(PdfName.Kids).GetAsDictionary(i); + } + catch (NullReferenceException) { + } + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentKids.Get(i).GetIndirectReference + (), GetIndirectReferenceOrNull(() => previousPagesDictionary.GetAsArray(PdfName.Kids).Get(finalI).GetIndirectReference + ()))); + if (currentPageNode != null) { + if (PdfName.Pages.Equals(currentPageNode.GetAsName(PdfName.Type))) { + allowedReferences.AddAll(CreateAllowedPagesEntries(currentPageNode, previousPageNode)); + } + else { + PdfObject currentAnnots = currentPageNode.Get(PdfName.Annots); + if (currentAnnots != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentAnnots.GetIndirectReference(), + GetIndirectReferenceOrNull(() => previousPagesDictionary.GetAsArray(PdfName.Kids).GetAsDictionary(finalI + ).Get(PdfName.Annots).GetIndirectReference()))); + } + } + } + } } - return CompareExtensions(previousCatalog.Get(PdfName.Extensions), currentCatalog.Get(PdfName.Extensions), - report) && CompareDss(previousCatalog.Get(PdfName.DSS), currentCatalog.Get(PdfName.DSS), report); + // We don't need to add annotations because all the allowed ones are already added during acroform processing. + return allowedReferences; } - private bool CompareExtensions(PdfObject previousExtensions, PdfObject currentExtensions, ValidationReport - report) { - if (previousExtensions == null || ComparePdfObjects(previousExtensions, currentExtensions)) { - return true; + private ICollection CreateAllowedAcroFormEntries(PdfDocument documentWithRevision + , PdfDocument documentWithoutRevision) { + IList allowedReferences = new List(); + PdfAcroForm prevAcroForm = PdfFormCreator.GetAcroForm(documentWithoutRevision, false); + PdfAcroForm currAcroForm = PdfFormCreator.GetAcroForm(documentWithRevision, false); + IDictionary prevFields = prevAcroForm == null ? new Dictionary + () : prevAcroForm.GetAllFormFields(); + foreach (KeyValuePair fieldEntry in currAcroForm.GetAllFormFields()) { + PdfFormField previousField = prevFields.Get(fieldEntry.Key); + PdfFormField currentField = fieldEntry.Value; + PdfObject value = currentField.GetValue(); + if (docMDP >= 2 || (value is PdfDictionary && PdfName.DocTimeStamp.Equals(((PdfDictionary)value).GetAsName + (PdfName.Type)))) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentField.GetPdfObject().GetIndirectReference + (), GetIndirectReferenceOrNull(() => previousField.GetPdfObject().GetIndirectReference()))); + if (previousField == null) { + // For newly generated form field all references are allowed to be added. + AddAllNestedDictionaryEntries(allowedReferences, currentField.GetPdfObject(), null); + } + else { + // For already existing form field only several entries are allowed to be updated. + allowedReferences.AddAll(CreateAllowedExistingFormFieldEntries(currentField, previousField)); + } + } } - if (currentExtensions == null) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, EXTENSIONS_REMOVED, ReportItem.ReportItemStatus.INVALID - )); - return false; + PdfDictionary currentResources = currAcroForm.GetDefaultResources(); + if (currentResources != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentResources.GetIndirectReference( + ), GetIndirectReferenceOrNull(() => prevAcroForm.GetDefaultResources().GetIndirectReference()))); + AddAllNestedDictionaryEntries(allowedReferences, currentResources, prevAcroForm == null ? null : prevAcroForm + .GetDefaultResources()); } - if (!(previousExtensions is PdfDictionary) || !(currentExtensions is PdfDictionary)) { - return false; + return allowedReferences; + } + + private ICollection CreateAllowedExistingFormFieldEntries(PdfFormField + currentField, PdfFormField previousField) { + IList allowedReferences = new List(); + PdfObject currentValue = currentField.GetValue(); + if (currentValue != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentValue.GetIndirectReference(), GetIndirectReferenceOrNull + (() => previousField.GetValue().GetIndirectReference()))); } - PdfDictionary previousExtensionsDictionary = (PdfDictionary)previousExtensions; - PdfDictionary currentExtensionsDictionary = (PdfDictionary)currentExtensions; - foreach (KeyValuePair previousExtension in previousExtensionsDictionary.EntrySet()) { - PdfDictionary currentExtension = currentExtensionsDictionary.GetAsDictionary(previousExtension.Key); - if (currentExtension == null) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(DEVELOPER_EXTENSION_REMOVED, previousExtension - .Key), ReportItem.ReportItemStatus.INVALID)); - return false; - } - else { - PdfDictionary currentExtensionCopy = new PdfDictionary(currentExtension); - currentExtensionCopy.Remove(PdfName.ExtensionLevel); - PdfDictionary previousExtensionCopy = new PdfDictionary((PdfDictionary)previousExtension.Value); - previousExtensionCopy.Remove(PdfName.ExtensionLevel); - // Apart from extension level dictionaries are expected to be equal. - if (!ComparePdfObjects(previousExtensionCopy, currentExtensionCopy)) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(DEVELOPER_EXTENSION_REMOVED, previousExtension - .Key), ReportItem.ReportItemStatus.INVALID)); - return false; - } - PdfNumber previousExtensionLevel = ((PdfDictionary)previousExtension.Value).GetAsNumber(PdfName.ExtensionLevel - ); - PdfNumber currentExtensionLevel = currentExtension.GetAsNumber(PdfName.ExtensionLevel); - if (previousExtensionLevel != null) { - if (currentExtensionLevel == null || previousExtensionLevel.IntValue() > currentExtensionLevel.IntValue()) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, MessageFormatUtil.Format(EXTENSION_LEVEL_DECREASED, previousExtension - .Key), ReportItem.ReportItemStatus.INVALID)); - return false; + IList currAnnots = currentField.GetChildFormAnnotations(); + if (!currAnnots.IsEmpty()) { + IList prevAnnots = previousField == null ? null : previousField.GetChildFormAnnotations + (); + for (int i = 0; i < currAnnots.Count; i++) { + int finalI = i; + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currAnnots[i].GetPdfObject().GetIndirectReference + (), GetIndirectReferenceOrNull(() => prevAnnots[finalI].GetPdfObject().GetIndirectReference()))); + PdfObject currentAppearance = currAnnots[i].GetPdfObject().Get(PdfName.AP); + if (currentAppearance != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentAppearance.GetIndirectReference + (), GetIndirectReferenceOrNull(() => prevAnnots[finalI].GetPdfObject().Get(PdfName.AP).GetIndirectReference + ()))); + if (currentAppearance is PdfDictionary) { + PdfObject previousAppearance; + try { + previousAppearance = prevAnnots[finalI].GetPdfObject().Get(PdfName.AP); + } + catch (NullReferenceException) { + previousAppearance = null; + } + AddAllNestedDictionaryEntries(allowedReferences, (PdfDictionary)currentAppearance, previousAppearance); } } + PdfObject currentAppearanceState = currAnnots[i].GetPdfObject().Get(PdfName.AS); + if (currentAppearanceState != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentAppearanceState.GetIndirectReference + (), GetIndirectReferenceOrNull(() => prevAnnots[finalI].GetPdfObject().Get(PdfName.AS).GetIndirectReference + ()))); + } + PdfObject currentTimeStamp = currAnnots[i].GetPdfObject().Get(PdfName.M); + if (currentTimeStamp != null) { + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentTimeStamp.GetIndirectReference( + ), GetIndirectReferenceOrNull(() => prevAnnots[finalI].GetPdfObject().Get(PdfName.M).GetIndirectReference + ()))); + } } } - return true; + return allowedReferences; } - private bool CompareDss(PdfObject previousDss, PdfObject currentDss, ValidationReport report) { - if (previousDss == null) { - return true; - } - if (currentDss == null) { - report.AddReportItem(new ReportItem(DOC_MDP_CHECK, DSS_REMOVED, ReportItem.ReportItemStatus.INVALID)); - return false; + private void AddAllNestedDictionaryEntries(IList allowedReferences + , PdfDictionary currentDictionary, PdfObject previousDictionary) { + foreach (KeyValuePair entry in currentDictionary.EntrySet()) { + PdfObject currValue = entry.Value; + if (currValue.GetIndirectReference() != null && allowedReferences.Any((pair) => IsSameReference(pair.GetCurrentReference + (), currValue.GetIndirectReference()))) { + // Required to not end up in an infinite loop. + continue; + } + PdfObject prevValue = previousDictionary is PdfDictionary ? ((PdfDictionary)previousDictionary).Get(entry. + Key) : null; + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currValue.GetIndirectReference(), GetIndirectReferenceOrNull + (() => prevValue.GetIndirectReference()))); + if (currValue is PdfDictionary) { + AddAllNestedDictionaryEntries(allowedReferences, (PdfDictionary)currValue, prevValue); + } + if (currValue is PdfArray) { + AddAllNestedArrayEntries(allowedReferences, (PdfArray)currValue, prevValue); + } } - return true; } - internal static Stream CreateInputStreamFromRevision(PdfDocument originalDocument, DocumentRevision revision - ) { - RandomAccessFileOrArray raf = originalDocument.GetReader().GetSafeFile(); - WindowRandomAccessSource source = new WindowRandomAccessSource(raf.CreateSourceView(), 0, revision.GetEofOffset - ()); - return new RASInputStream(source); + private void AddAllNestedArrayEntries(IList allowedReferences, + PdfArray currentArray, PdfObject previousArray) { + for (int i = 0; i < currentArray.Size(); ++i) { + PdfObject currentArrayEntry = currentArray.Get(i); + if (currentArrayEntry.GetIndirectReference() != null && allowedReferences.Any((pair) => IsSameReference(pair + .GetCurrentReference(), currentArrayEntry.GetIndirectReference()))) { + // Required to not end up in an infinite loop. + continue; + } + PdfObject previousArrayEntry = previousArray is PdfArray ? ((PdfArray)previousArray).Get(i) : null; + allowedReferences.Add(new DocumentRevisionsValidator.ReferencesPair(currentArrayEntry.GetIndirectReference + (), GetIndirectReferenceOrNull(() => previousArrayEntry.GetIndirectReference()))); + if (currentArrayEntry is PdfDictionary) { + AddAllNestedDictionaryEntries(allowedReferences, currentArray.GetAsDictionary(i), previousArrayEntry); + } + if (currentArrayEntry is PdfArray) { + AddAllNestedArrayEntries(allowedReferences, currentArray.GetAsArray(i), previousArrayEntry); + } + } } + // Compare PDF objects util section: private static bool ComparePdfObjects(PdfObject pdfObject1, PdfObject pdfObject2) { return ComparePdfObjects(pdfObject1, pdfObject2, new HashSet()); } diff --git a/itext/itext.sign/itext/signatures/validation/v1/OCSPValidator.cs b/itext/itext.sign/itext/signatures/validation/v1/OCSPValidator.cs index 4358877144..8ece9e99b9 100644 --- a/itext/itext.sign/itext/signatures/validation/v1/OCSPValidator.cs +++ b/itext/itext.sign/itext/signatures/validation/v1/OCSPValidator.cs @@ -113,9 +113,9 @@ public virtual void Validate(ValidationReport report, ValidationContext context, return; } } - catch (Exception) { + catch (Exception e) { report.AddReportItem(new CertificateReportItem(certificate, OCSP_CHECK, UNABLE_TO_CHECK_IF_ISSUERS_MATCH, - ReportItem.ReportItemStatus.INDETERMINATE)); + e, ReportItem.ReportItemStatus.INDETERMINATE)); return; } // So, since the issuer name and serial number identify a unique certificate, we found the single response diff --git a/itext/itext.sign/itext/signatures/validation/v1/report/ValidationReport.cs b/itext/itext.sign/itext/signatures/validation/v1/report/ValidationReport.cs index 02f21cdda2..7d64b1de4e 100644 --- a/itext/itext.sign/itext/signatures/validation/v1/report/ValidationReport.cs +++ b/itext/itext.sign/itext/signatures/validation/v1/report/ValidationReport.cs @@ -23,6 +23,7 @@ You should have received a copy of the GNU Affero General Public License using System; using System.Collections.Generic; using System.Linq; +using System.Text; using iText.Commons.Utils; namespace iText.Signatures.Validation.V1.Report { @@ -111,7 +112,13 @@ public virtual void AddReportItem(ReportItem item) { } public override String ToString() { - return "ValidationReport{" + "reportItems=" + reportItems + '}'; + StringBuilder sb = new StringBuilder("ValidationReport{validationResult="); + sb.Append(GetValidationResult()).Append("\nreportItems="); + foreach (ReportItem i in reportItems) { + sb.Append(i).Append(", "); + } + sb.Append("}"); + return sb.ToString(); } /// Enum representing possible validation results. diff --git a/port-hash b/port-hash index 59cf833bc3..96f345e6ee 100644 --- a/port-hash +++ b/port-hash @@ -1 +1 @@ -0a2ca4821d4c6e36a237a41ca08cefa72b277dbd +6ef2c8cfcaa16536cd70d0a324f580464783c7bf