Skip to content

Commit 063e6bf

Browse files
Eugene BochiloiText-CI
authored andcommitted
Add support for transition period and check for incompatible OJ certificates
DEVSIX-9285 Autoported commit. Original commit hash: [f33d3c977]
1 parent d051584 commit 063e6bf

File tree

11 files changed

+267
-71
lines changed

11 files changed

+267
-71
lines changed

itext.tests/itext.sign.tests/itext/signatures/validation/lotl/LotlServiceTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public virtual void TestCacheFailCallsDownloadOfPivotFile() {
109109
lotlService.WithLotlServiceCache(new LotlServiceTest.CacheReturnsNull());
110110
lotlService.WithPivotFetcher(new _PivotFetcher_125(lotlService));
111111
f = lotlService.GetAndValidatePivotFiles("abc".GetBytes(System.Text.Encoding.UTF8), JavaCollectionsUtil.EmptyList
112-
<IX509Certificate>());
112+
<IX509Certificate>(), null);
113113
}
114114
NUnit.Framework.Assert.IsNotNull(f);
115115
NUnit.Framework.Assert.AreEqual(1, f.GetPivotUrls().Count);
@@ -202,7 +202,7 @@ public virtual void TestCacheRefreshIsFiring() {
202202
AtomicLong refreshCounter = new AtomicLong(0);
203203
using (LotlService lotlService = new _LotlService_207(refreshCounter, lotlFetchingProperties)) {
204204
lotlService.SetupTimer();
205-
Thread.Sleep(1000);
205+
Thread.Sleep(2000);
206206
}
207207
NUnit.Framework.Assert.IsTrue(refreshCounter.Get() >= 8, "Refresh counter should be greater than 8, but was: "
208208
+ refreshCounter.Get());

itext.tests/itext.sign.tests/itext/signatures/validation/lotl/LotlValidatorTest.cs

Lines changed: 153 additions & 54 deletions
Large diffs are not rendered by default.

itext/itext.sign/itext/signatures/exceptions/SignExceptionMessageConstant.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ public sealed class SignExceptionMessageConstant {
192192
public const String CACHE_ALREADY_INITIALIZED = "Global LOTL service has already been initialized. " + "You cannot initialize it again. If you want to use a different configuration, please create a new "
193193
+ "instance of LotlService with the desired properties and use it in ValidatorChainBuilder.";
194194

195+
public const String OFFICIAL_JOURNAL_CERTIFICATES_OUTDATED = "Trusted certificates from Official Journal of European Union are outdated. "
196+
+ "LOTL file cannot be validated. " + "Please, provide OJ certificates, which match the ones used to sign European Union List of Trusted Lists.";
197+
195198
private SignExceptionMessageConstant() {
196199
}
197200
// Private constructor will prevent the instantiation of this class directly

itext/itext.sign/itext/signatures/logs/SignLogMessageConstant.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public sealed class SignLogMessageConstant {
5050

5151
public const String FAILED_TO_FETCH_EU_JOURNAL_CERTIFICATES = "Problem occurred while fetching " + "EU Journal certificates.\n{0}";
5252

53+
public const String OJ_TRANSITION_PERIOD = "Main LOTL file contains two Official Journal of European Union links. "
54+
+ "This usually indicates that transition period for Official Journal has started. " + "Newest version of Official Journal should be used from now on "
55+
+ "to retrieve trusted certificates and LOTL location.";
56+
5357
private SignLogMessageConstant() {
5458
}
5559
// Private constructor will prevent the instantiation of this class directly

itext/itext.sign/itext/signatures/validation/lotl/EuropeanResourceFetcher.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2020
You should have received a copy of the GNU Affero General Public License
2121
along with this program. If not, see <https://www.gnu.org/licenses/>.
2222
*/
23+
using System;
2324
using System.Collections.Generic;
2425
using iText.Commons.Bouncycastle.Cert;
2526
using iText.Signatures.Validation;
@@ -46,6 +47,7 @@ public EuropeanResourceFetcher() {
4647
public virtual EuropeanResourceFetcher.Result GetEUJournalCertificates() {
4748
EuropeanResourceFetcher.Result result = new EuropeanResourceFetcher.Result();
4849
EuropeanTrustedListConfigurationFactory factory = EuropeanTrustedListConfigurationFactory.GetFactory()();
50+
result.SetCurrentlySupportedPublication(factory.GetCurrentlySupportedPublication());
4951
SafeCalling.OnExceptionLog(() => result.SetCertificates(factory.GetCertificates()), result.GetLocalReport(
5052
), (e) => new ReportItem(LotlValidator.LOTL_VALIDATION, LotlValidator.JOURNAL_CERT_NOT_PARSABLE, e, ReportItem.ReportItemStatus
5153
.INFO));
@@ -62,6 +64,8 @@ public class Result {
6264

6365
private IList<IX509Certificate> certificates;
6466

67+
private String currentlySupportedPublication;
68+
6569
/// <summary>
6670
/// Create a new Instance of
6771
/// <see cref="Result"/>.
@@ -83,11 +87,31 @@ public virtual IList<IX509Certificate> GetCertificates() {
8387
return certificates;
8488
}
8589

90+
/// <summary>Gets string constant representing currently used Official Journal publication.</summary>
91+
/// <returns>
92+
///
93+
/// <see cref="System.String"/>
94+
/// constant representing currently used Official Journal publication
95+
/// </returns>
96+
public virtual String GetCurrentlySupportedPublication() {
97+
return currentlySupportedPublication;
98+
}
99+
86100
/// <summary>Sets the list of certificates.</summary>
87101
/// <param name="certificates">a list of Certificate objects to set</param>
88102
public virtual void SetCertificates(IList<IX509Certificate> certificates) {
89103
this.certificates = certificates;
90104
}
105+
106+
/// <summary>Sets string constant representing currently used Official Journal publication.</summary>
107+
/// <param name="currentlySuppostedPublication">
108+
///
109+
/// <see cref="System.String"/>
110+
/// constant representing currently used Official Journal publication
111+
/// </param>
112+
public virtual void SetCurrentlySupportedPublication(String currentlySuppostedPublication) {
113+
this.currentlySupportedPublication = currentlySuppostedPublication;
114+
}
91115
}
92116
}
93117
}

itext/itext.sign/itext/signatures/validation/lotl/LotlFetchingProperties.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ public virtual iText.Signatures.Validation.Lotl.LotlFetchingProperties SetCacheS
178178
/// Gets the calculation function for the cache refresh interval.
179179
/// <para />
180180
/// This function will be used to determine the refresh interval based on the staleness time.
181-
/// By default, it takes 70% of the staleness time as the refresh interval.
181+
/// By default, it takes 23% of the staleness time as the refresh interval.
182182
/// </remarks>
183183
/// <returns>
184184
/// a function that takes the staleness time in milliseconds and returns the refresh interval in

itext/itext.sign/itext/signatures/validation/lotl/LotlService.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,14 @@ public virtual void InitializeCache() {
178178
SetupTimer();
179179
EuropeanLotlFetcher.Result mainLotlResult = lotlByteFetcher.Fetch();
180180
if (!mainLotlResult.GetLocalReport().GetFailures().IsEmpty()) {
181-
//We throw on main Lotl fetch failure, so we don't proceed to pivot and country specific LOTL fetches
181+
// We throw on main LOTL fetch failure, so we don't proceed to pivot and country specific LOTL fetches
182182
ReportItem reportItem = mainLotlResult.GetLocalReport().GetFailures()[0];
183183
throw new PdfException(reportItem.GetMessage(), reportItem.GetExceptionCause());
184184
}
185185
EuropeanResourceFetcher.Result europeanResourceFetcherEUJournalCertificates = europeanResourceFetcher.GetEUJournalCertificates
186186
();
187+
pivotFetcher.SetCurrentJournalUri(europeanResourceFetcherEUJournalCertificates.GetCurrentlySupportedPublication
188+
());
187189
PivotFetcher.Result pivotsResult = pivotFetcher.DownloadAndValidatePivotFiles(mainLotlResult.GetLotlXml(),
188190
europeanResourceFetcherEUJournalCertificates.GetCertificates());
189191
if (!pivotsResult.GetLocalReport().GetFailures().IsEmpty()) {
@@ -359,9 +361,11 @@ protected internal virtual void TryAndRefreshCache() {
359361
EuropeanLotlFetcher.Result mainLotlResult = null;
360362
bool mainLotlFetchSuccessful = false;
361363
Exception mainLotlFetchException = null;
364+
String currentJournalUri;
362365
try {
363366
EuropeanResourceFetcher.Result europeanResourceFetcherEUJournalCertificates = europeanResourceFetcher.GetEUJournalCertificates
364367
();
368+
currentJournalUri = europeanResourceFetcherEUJournalCertificates.GetCurrentlySupportedPublication();
365369
if (europeanResourceFetcherEUJournalCertificates.GetLocalReport().GetValidationResult() != ValidationReport.ValidationResult
366370
.VALID) {
367371
throw new PdfException(MessageFormatUtil.Format(SignExceptionMessageConstant.FAILED_TO_FETCH_EU_JOURNAL_CERTIFICATES
@@ -388,6 +392,7 @@ protected internal virtual void TryAndRefreshCache() {
388392
if (mainLotlFetchSuccessful) {
389393
//Only if the main Lotl was fetched successfully, we proceed to re-fetch the new pivot files.
390394
try {
395+
pivotFetcher.SetCurrentJournalUri(currentJournalUri);
391396
pivotResult = pivotFetcher.DownloadAndValidatePivotFiles(mainLotlResult.GetLotlXml(), europeanResourceFetcher
392397
.GetEUJournalCertificates().GetCertificates());
393398
fetchPivotFilesSuccessful = pivotResult.GetLocalReport().GetValidationResult() == ValidationReport.ValidationResult
@@ -440,11 +445,12 @@ protected internal virtual void TryAndRefreshCache() {
440445

441446
//\cond DO_NOT_DOCUMENT
442447
internal virtual PivotFetcher.Result GetAndValidatePivotFiles(byte[] lotlXml, IList<IX509Certificate> certificates
443-
) {
448+
, String currentJournalUri) {
444449
PivotFetcher.Result result = cache.GetPivotResult();
445450
if (result != null) {
446451
return result;
447452
}
453+
pivotFetcher.SetCurrentJournalUri(currentJournalUri);
448454
PivotFetcher.Result newResult = pivotFetcher.DownloadAndValidatePivotFiles(lotlXml, certificates);
449455
cache.SetPivotResult(newResult);
450456
return newResult;

itext/itext.sign/itext/signatures/validation/lotl/LotlValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public virtual ValidationReport Validate() {
8686
EuropeanResourceFetcher.Result europeanResult = service.GetEUJournalCertificates();
8787
report.Merge(europeanResult.GetLocalReport());
8888
PivotFetcher.Result result = service.GetAndValidatePivotFiles(lotl.GetLotlXml(), europeanResult.GetCertificates
89-
());
89+
(), europeanResult.GetCurrentlySupportedPublication());
9090
report.Merge(result.GetLocalReport());
9191
if (result.GetLocalReport().GetValidationResult() != ValidationReport.ValidationResult.VALID) {
9292
return report;

itext/itext.sign/itext/signatures/validation/lotl/PivotFetcher.cs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,48 @@ You should have received a copy of the GNU Affero General Public License
2323
using System;
2424
using System.Collections.Generic;
2525
using System.IO;
26+
using System.Linq;
27+
using Microsoft.Extensions.Logging;
28+
using iText.Commons;
2629
using iText.Commons.Bouncycastle.Cert;
2730
using iText.Commons.Utils;
2831
using iText.Kernel.Exceptions;
32+
using iText.Signatures.Exceptions;
33+
using iText.Signatures.Logs;
2934
using iText.Signatures.Validation;
3035
using iText.Signatures.Validation.Lotl.Xml;
3136
using iText.Signatures.Validation.Report;
3237

3338
namespace iText.Signatures.Validation.Lotl {
3439
/// <summary>This class fetches and validates pivot files from a List of Trusted Lists (Lotl) XML.</summary>
3540
public class PivotFetcher {
41+
private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(iText.Signatures.Validation.Lotl.PivotFetcher
42+
));
43+
3644
private readonly LotlService service;
3745

46+
private String currentJournalUri;
47+
3848
/// <summary>Constructs a PivotFetcher with the specified LotlService and ValidatorChainBuilder.</summary>
3949
/// <param name="service">the LotlService used to retrieve resources</param>
4050
public PivotFetcher(LotlService service) {
4151
this.service = service;
4252
}
4353

54+
/// <summary>
55+
/// Sets
56+
/// <see cref="System.String"/>
57+
/// constant representing currently used Official Journal publication.
58+
/// </summary>
59+
/// <param name="currentJournalUri">
60+
///
61+
/// <see cref="System.String"/>
62+
/// constant representing currently used Official Journal publication
63+
/// </param>
64+
public virtual void SetCurrentJournalUri(String currentJournalUri) {
65+
this.currentJournalUri = currentJournalUri;
66+
}
67+
4468
/// <summary>Fetches and validates pivot files from the provided Lotl XML.</summary>
4569
/// <param name="lotlXml">the byte array of the Lotl XML</param>
4670
/// <param name="certificates">the list of trusted certificates</param>
@@ -50,20 +74,31 @@ public virtual PivotFetcher.Result DownloadAndValidatePivotFiles(byte[] lotlXml,
5074
if (lotlXml == null) {
5175
throw new PdfException(LotlValidator.UNABLE_TO_RETRIEVE_LOTL);
5276
}
53-
XmlPivotsHandler pivotsHandler = new XmlPivotsHandler();
54-
new XmlSaxProcessor().Process(new MemoryStream(lotlXml), pivotsHandler);
5577
PivotFetcher.Result result = new PivotFetcher.Result();
56-
IList<String> pivotsUrlList = pivotsHandler.GetPivots();
78+
IList<String> pivotsUrlList = GetPivotsUrlList(lotlXml);
79+
IList<String> ojUris = pivotsUrlList.Where((url) => XmlPivotsHandler.IsOfficialJournal(url)).ToList();
80+
if (ojUris.Count > 1) {
81+
LOGGER.LogWarning(SignLogMessageConstant.OJ_TRANSITION_PERIOD);
82+
}
5783
result.SetPivotUrls(pivotsUrlList);
5884
IList<byte[]> pivotFiles = new List<byte[]>();
85+
// If we weren't able to find any OJ links, or current OJ uri is null, we process all the pivots.
86+
bool startProcessing = ojUris.IsEmpty() || currentJournalUri == null;
5987
// We need to process pivots backwards.
6088
for (int i = pivotsUrlList.Count - 1; i >= 0; i--) {
6189
String pivotUrl = pivotsUrlList[i];
62-
SafeCalling.OnExceptionLog(() => pivotFiles.Add(service.GetResourceRetriever().GetByteArrayByUrl(new Uri(pivotUrl
63-
))), result.GetLocalReport(), (e) => new ReportItem(LotlValidator.LOTL_VALIDATION, MessageFormatUtil.Format
64-
(LotlValidator.UNABLE_TO_RETRIEVE_PIVOT, pivotUrl), e, ReportItem.ReportItemStatus.INVALID));
65-
if (result.GetLocalReport().GetValidationResult() != ValidationReport.ValidationResult.VALID) {
66-
return result;
90+
if (pivotUrl.Equals(currentJournalUri)) {
91+
// We only need to process pivots which, were created after OJ entry was added.
92+
startProcessing = true;
93+
continue;
94+
}
95+
if (startProcessing && !XmlPivotsHandler.IsOfficialJournal(pivotUrl)) {
96+
SafeCalling.OnExceptionLog(() => pivotFiles.Add(service.GetResourceRetriever().GetByteArrayByUrl(new Uri(pivotUrl
97+
))), result.GetLocalReport(), (e) => new ReportItem(LotlValidator.LOTL_VALIDATION, MessageFormatUtil.Format
98+
(LotlValidator.UNABLE_TO_RETRIEVE_PIVOT, pivotUrl), e, ReportItem.ReportItemStatus.INVALID));
99+
if (result.GetLocalReport().GetValidationResult() != ValidationReport.ValidationResult.VALID) {
100+
return result;
101+
}
67102
}
68103
}
69104
IList<IX509Certificate> trustedCertificates = certificates;
@@ -82,6 +117,9 @@ public virtual PivotFetcher.Result DownloadAndValidatePivotFiles(byte[] lotlXml,
82117
result.GetLocalReport().AddReportItem(new ReportItem(LotlValidator.LOTL_VALIDATION, LotlValidator.LOTL_VALIDATION_UNSUCCESSFUL
83118
, ReportItem.ReportItemStatus.INVALID));
84119
result.GetLocalReport().Merge(localReport);
120+
if (!ojUris.Any((ojUri) => ojUri.Equals(currentJournalUri))) {
121+
throw new PdfException(SignExceptionMessageConstant.OFFICIAL_JOURNAL_CERTIFICATES_OUTDATED);
122+
}
85123
return result;
86124
}
87125
XmlCertificateRetriever certificateRetriever = new XmlCertificateRetriever(new XmlDefaultCertificateHandler
@@ -91,6 +129,19 @@ public virtual PivotFetcher.Result DownloadAndValidatePivotFiles(byte[] lotlXml,
91129
return result;
92130
}
93131

132+
/// <summary>Gets list of pivots xml files, including OJ entries.</summary>
133+
/// <param name="lotlXml">
134+
///
135+
/// <c>byte</c>
136+
/// array representing main LOTL file
137+
/// </param>
138+
/// <returns>list of pivots xml files, including OJ entries</returns>
139+
protected internal virtual IList<String> GetPivotsUrlList(byte[] lotlXml) {
140+
XmlPivotsHandler pivotsHandler = new XmlPivotsHandler();
141+
new XmlSaxProcessor().Process(new MemoryStream(lotlXml), pivotsHandler);
142+
return pivotsHandler.GetPivots();
143+
}
144+
94145
/// <summary>Result class encapsulates the result of the pivot fetching and validation process.</summary>
95146
public class Result {
96147
private ValidationReport localReport = new ValidationReport();

itext/itext.sign/itext/signatures/validation/lotl/XmlPivotsHandler.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@ public virtual void EndElement(String uri, String localName, String qName) {
5555
schemeInformationContext = false;
5656
}
5757
else {
58-
if (XmlTagConstants.URI.Equals(localName) && IsPivot(uriLink.ToString())) {
59-
pivots.Add(uriLink.ToString());
58+
if (XmlTagConstants.URI.Equals(localName)) {
59+
String uriLinkString = uriLink.ToString();
60+
if (IsPivot(uriLinkString) || IsOfficialJournal(uriLinkString)) {
61+
pivots.Add(uriLinkString);
62+
}
6063
}
6164
}
6265
}
@@ -71,6 +74,12 @@ public virtual IList<String> GetPivots() {
7174
return new List<String>(pivots);
7275
}
7376

77+
//\cond DO_NOT_DOCUMENT
78+
internal static bool IsOfficialJournal(String uriLink) {
79+
return uriLink.Contains("eur-lex.europa.eu");
80+
}
81+
//\endcond
82+
7483
private static bool IsPivot(String uriLink) {
7584
return uriLink.Contains("eu-lotl-pivot");
7685
}

0 commit comments

Comments
 (0)