Skip to content

Commit c728851

Browse files
author
Alexandr Pliushchou
committed
Add support for AdditionalServiceInformation extension
DEVSIX-9265
1 parent 4b3e357 commit c728851

File tree

10 files changed

+306
-55
lines changed

10 files changed

+306
-55
lines changed

sharpenConfiguration.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@
542542
<file path="com/itextpdf/signatures/testutils/report/xml/XmlReportTestTool.java"/>
543543
</fileset>
544544
<fileset reason="LocalDateTime and DateTime have different constructors and parsing">
545-
<file path="com/itextpdf/signatures/validation/lotl/ServiceStatusInfo.java"/>
545+
<file path="com/itextpdf/signatures/validation/lotl/ServiceChronologicalInfo.java"/>
546546
</fileset>
547547
<!-- jsoup -->
548548
<file path="com/itextpdf/styledxmlparser/jsoup/PortUtil.java" />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.itextpdf.signatures.validation.lotl;
2+
3+
import java.util.HashSet;
4+
import java.util.Set;
5+
6+
class AdditionalServiceInformationExtension {
7+
8+
private static final Set<String> invalidScopes = new HashSet<>();
9+
10+
static {
11+
invalidScopes.add("http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/ForWebSiteAuthentication");
12+
}
13+
14+
private String uri;
15+
16+
AdditionalServiceInformationExtension() {
17+
// Empty constructor.
18+
}
19+
20+
AdditionalServiceInformationExtension (String uri) {
21+
this.uri = uri;
22+
}
23+
24+
String getUri() {
25+
return uri;
26+
}
27+
28+
void setUri(String uri) {
29+
this.uri = uri;
30+
}
31+
32+
boolean isScopeValid() {
33+
return !invalidScopes.contains(uri);
34+
}
35+
}

sign/src/main/java/com/itextpdf/signatures/validation/lotl/CountryServiceContext.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class CountryServiceContext implements IServiceContext {
3434
private String serviceType;
3535

3636
//It is expected that service statuses are ordered starting from the newest one.
37-
private final List<ServiceStatusInfo> serviceStatusInfos = new ArrayList<>();
37+
private final List<ServiceChronologicalInfo> serviceChronologicalInfos = new ArrayList<>();
3838

3939
CountryServiceContext() {
4040
// Empty constructor
@@ -64,29 +64,29 @@ String getServiceType() {
6464
return serviceType;
6565
}
6666

67-
void addNewServiceStatus(ServiceStatusInfo serviceStatusInfo) {
68-
serviceStatusInfos.add(serviceStatusInfo);
67+
void addServiceChronologicalInfo(ServiceChronologicalInfo serviceChronologicalInfo) {
68+
serviceChronologicalInfos.add(serviceChronologicalInfo);
6969
}
7070

71-
String getServiceStatusByDate(long milliseconds) {
72-
return getServiceStatusByDate(DateTimeUtil.getTimeFromMillis(milliseconds));
71+
ServiceChronologicalInfo getServiceChronologicalInfoByDate(long milliseconds) {
72+
return getServiceChronologicalInfoByDate(DateTimeUtil.getTimeFromMillis(milliseconds));
7373
}
7474

75-
String getServiceStatusByDate(LocalDateTime time) {
76-
for (ServiceStatusInfo serviceStatusInfo: serviceStatusInfos) {
77-
if (serviceStatusInfo.getServiceStatusStartingTime().isBefore(time)) {
78-
return serviceStatusInfo.getServiceStatus();
75+
ServiceChronologicalInfo getServiceChronologicalInfoByDate(LocalDateTime time) {
76+
for (ServiceChronologicalInfo serviceChronologicalInfo : serviceChronologicalInfos) {
77+
if (serviceChronologicalInfo.getServiceStatusStartingTime().isBefore(time)) {
78+
return serviceChronologicalInfo;
7979
}
8080
}
8181

8282
return null;
8383
}
8484

85-
ServiceStatusInfo getCurrentStatusInfo() {
86-
return serviceStatusInfos.get(0);
85+
ServiceChronologicalInfo getCurrentChronologicalInfo() {
86+
return serviceChronologicalInfos.get(0);
8787
}
8888

89-
int getServiceStatusInfosSize() {
90-
return serviceStatusInfos.size();
89+
int getServiceChronologicalInfosSize() {
90+
return serviceChronologicalInfos.size();
9191
}
9292
}

sign/src/main/java/com/itextpdf/signatures/validation/lotl/LOTLTrustedStore.java

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public class LOTLTrustedStore {
5959
static final String CERTIFICATE_SERVICE_TYPE_NOT_RECOGNIZED =
6060
"Certificate {0} is trusted, but it's service type {1} is not recognized.";
6161
static final String NOT_YET_VALID_CERTIFICATE = "Certificate {0} is not yet valid.";
62+
static final String SCOPE_SPECIFIED_WITH_INVALID_TYPES = "Certificate {0} is trusted for {1}, " +
63+
"which is incorrect scope for pdf validation.";
64+
static final String EXTENSIONS_CHECK = "Certificate extensions check.";
65+
6266
private static final Map<String, Set<CertificateSource>> serviceTypeIdentifiersScope;
6367

6468
private final Set<CountryServiceContext> contexts = new HashSet<>();
@@ -176,7 +180,10 @@ public boolean checkIfCertIsTrusted(ValidationReport result, ValidationContext c
176180

177181
List<ReportItem> validationReportItems = new ArrayList<>();
178182
for (CountryServiceContext currentContext : currentContextSet) {
179-
if (!isCertificateValidInTime(validationReportItems, certificate, currentContext, validationDate)) {
183+
ServiceChronologicalInfo chronologicalInfo = getCertificateChronologicalInfoByTime(validationReportItems,
184+
certificate, currentContext, validationDate);
185+
if (chronologicalInfo == null ||
186+
!isScopeCorrectlySpecified(validationReportItems, certificate, chronologicalInfo.getExtensions())) {
180187
continue;
181188
}
182189

@@ -211,6 +218,37 @@ public boolean checkIfCertIsTrusted(ValidationReport result, ValidationContext c
211218
return false;
212219
}
213220

221+
/**
222+
* Check if scope specified by extensions contains valid types.
223+
*
224+
* @param reportItems {@link ValidationReport} which is populated with detailed validation results
225+
* @param certificate {@link X509Certificate} to be validated
226+
* @param extensions {@link AdditionalServiceInformationExtension} that specify scope
227+
*
228+
* @return false if extensions specify scope only with invalid types.
229+
*/
230+
boolean isScopeCorrectlySpecified(List<ReportItem> reportItems, X509Certificate certificate,
231+
List<AdditionalServiceInformationExtension> extensions) {
232+
List<ReportItem> currentReportItems = new ArrayList<>();
233+
for (AdditionalServiceInformationExtension extension : extensions) {
234+
if (extension.isScopeValid()) {
235+
return true;
236+
} else {
237+
currentReportItems.add(new CertificateReportItem(certificate, EXTENSIONS_CHECK,
238+
MessageFormatUtil.format(SCOPE_SPECIFIED_WITH_INVALID_TYPES,
239+
certificate.getSubjectX500Principal(), extension.getUri()),
240+
ReportItem.ReportItemStatus.INVALID));
241+
}
242+
}
243+
244+
if (currentReportItems.isEmpty()) {
245+
return true;
246+
} else {
247+
reportItems.addAll(currentReportItems);
248+
return false;
249+
}
250+
}
251+
214252
final void addCertificatesWithContext(Collection<CountryServiceContext> contexts) {
215253
this.contexts.addAll(contexts);
216254
}
@@ -225,23 +263,37 @@ private Set<CountryServiceContext> getCertificateContext(Certificate certificate
225263
return contextSet;
226264
}
227265

228-
private static boolean isCertificateValidInTime(List<ReportItem> reportItems, X509Certificate certificate,
229-
CountryServiceContext currentContext, Date validationDate) {
230-
String status = currentContext.getServiceStatusByDate(DateTimeUtil.getRelativeTime(validationDate));
266+
/**
267+
* Find {@link ServiceChronologicalInfo} corresponding to provided date. If Service wasn't operating at that date
268+
* report item will be added and null will be returned.
269+
*
270+
* @param reportItems {@link ValidationReport} which is populated with detailed validation results
271+
* @param certificate {@link X509Certificate} to be validated
272+
* @param currentContext {@link CountryServiceContext} which contains statuses and their starting time
273+
* @param validationDate {@link Date} against which certificate is expected to be validated. Usually signing
274+
* date
275+
*
276+
* @return {@link ServiceChronologicalInfo} which contains time specific service information.
277+
*/
278+
ServiceChronologicalInfo getCertificateChronologicalInfoByTime(
279+
List<ReportItem> reportItems, X509Certificate certificate, CountryServiceContext currentContext,
280+
Date validationDate) {
281+
ServiceChronologicalInfo status = currentContext.getServiceChronologicalInfoByDate(
282+
DateTimeUtil.getRelativeTime(validationDate));
231283
if (status == null) {
232284
reportItems.add(new CertificateReportItem(certificate, CERTIFICATE_CHECK,
233-
MessageFormatUtil.format(NOT_YET_VALID_CERTIFICATE, certificate.getSubjectX500Principal()),
234-
ReportItem.ReportItemStatus.INVALID));
235-
return false;
285+
MessageFormatUtil.format(NOT_YET_VALID_CERTIFICATE,
286+
certificate.getSubjectX500Principal()), ReportItem.ReportItemStatus.INVALID));
287+
return null;
236288
}
237289

238-
if (!ServiceStatusInfo.isStatusValid(status)) {
290+
if (!ServiceChronologicalInfo.isStatusValid(status.getServiceStatus())) {
239291
reportItems.add(new CertificateReportItem(certificate, CERTIFICATE_CHECK,
240292
MessageFormatUtil.format(REVOKED_CERTIFICATE, certificate.getSubjectX500Principal()),
241293
ReportItem.ReportItemStatus.INVALID));
242-
return false;
294+
return null;
243295
}
244296

245-
return true;
297+
return status;
246298
}
247299
}

sign/src/main/java/com/itextpdf/signatures/validation/lotl/ServiceStatusInfo.java renamed to sign/src/main/java/com/itextpdf/signatures/validation/lotl/ServiceChronologicalInfo.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,24 @@ This file is part of the iText (R) project.
2424

2525
import java.time.LocalDateTime;
2626
import java.time.format.DateTimeFormatter;
27+
import java.util.ArrayList;
2728
import java.util.HashSet;
29+
import java.util.List;
2830
import java.util.Set;
2931

30-
class ServiceStatusInfo {
32+
class ServiceChronologicalInfo {
3133
private String serviceStatus;
3234

3335
//Local time is used here because it is required to use UTC in a trusted lists, so no offset shall be presented.
3436
private LocalDateTime serviceStatusStartingTime;
3537

38+
private final List<AdditionalServiceInformationExtension> extensions = new ArrayList<>();
39+
3640
private final DateTimeFormatter statusStartDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
3741

3842
static final String GRANTED = "http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/granted";
3943
static final String GRANTED_NATIONALLY =
4044
"http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/recognisedatnationallevel";
41-
static final String WITHDRAWN = "http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/withdrawn";
4245
static final String ACCREDITED = "http://uri.etsi.org/TrstSvc/TrustedList/Svcstatus/accredited";
4346
private static final Set<String> validStatuses = new HashSet<>();
4447

@@ -48,11 +51,11 @@ class ServiceStatusInfo {
4851
validStatuses.add(ACCREDITED);
4952
}
5053

51-
ServiceStatusInfo() {
54+
ServiceChronologicalInfo() {
5255
// empty constructor
5356
}
5457

55-
ServiceStatusInfo(String serviceStatus, LocalDateTime serviceStatusStartingTime) {
58+
ServiceChronologicalInfo(String serviceStatus, LocalDateTime serviceStatusStartingTime) {
5659
this.serviceStatus = serviceStatus;
5760
this.serviceStatusStartingTime = serviceStatusStartingTime;
5861
}
@@ -80,4 +83,12 @@ LocalDateTime getServiceStatusStartingTime() {
8083
static boolean isStatusValid(String status) {
8184
return validStatuses.contains(status);
8285
}
86+
87+
void addExtension(AdditionalServiceInformationExtension extension) {
88+
extensions.add(extension);
89+
}
90+
91+
List<AdditionalServiceInformationExtension> getExtensions() {
92+
return extensions;
93+
}
8394
}

sign/src/main/java/com/itextpdf/signatures/validation/lotl/XmlCountryCertificateHandler.java

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ class XmlCountryCertificateHandler extends AbstractXmlCertificateHandler {
4141
INFORMATION_TAGS.add(XmlTagConstants.SERVICE_STATUS);
4242
INFORMATION_TAGS.add(XmlTagConstants.X509CERTIFICATE);
4343
INFORMATION_TAGS.add(XmlTagConstants.SERVICE_STATUS_STARTING_TIME);
44+
INFORMATION_TAGS.add(XmlTagConstants.URI);
4445
}
4546

4647
private StringBuilder information;
4748
private CountryServiceContext currentServiceContext = null;
48-
private ServiceStatusInfo currentServiceStatusInfo = null;
49+
private ServiceChronologicalInfo currentServiceChronologicalInfo = null;
50+
private AdditionalServiceInformationExtension currentExtension = null;
4951

5052
XmlCountryCertificateHandler(Set<String> serviceTypes) {
5153
this.serviceTypes = new HashSet<>(serviceTypes);
@@ -64,8 +66,10 @@ public void startElement(String uri, String localName, String qName, HashMap<Str
6466
startProvider();
6567
} else if (XmlTagConstants.SERVICE_HISTORY_INSTANCE.equals(localName)
6668
|| XmlTagConstants.SERVICE_INFORMATION.equals(localName)) {
67-
currentServiceStatusInfo = new ServiceStatusInfo();
68-
} else if (INFORMATION_TAGS.contains(localName)) {
69+
currentServiceChronologicalInfo = new ServiceChronologicalInfo();
70+
} else if (XmlTagConstants.ADDITIONAL_INFORMATION_EXTENSION.equals(localName)) {
71+
currentExtension = new AdditionalServiceInformationExtension();
72+
}else if (INFORMATION_TAGS.contains(localName)) {
6973
information = new StringBuilder();
7074
}
7175
}
@@ -84,8 +88,8 @@ public void endElement(String uri, String localName, String qName) {
8488
information = null;
8589
break;
8690
case XmlTagConstants.SERVICE_STATUS:
87-
if (currentServiceContext != null) {
88-
currentServiceStatusInfo.setServiceStatus(information.toString());
91+
if (currentServiceChronologicalInfo != null) {
92+
currentServiceChronologicalInfo.setServiceStatus(information.toString());
8993
}
9094

9195
information = null;
@@ -103,19 +107,32 @@ public void endElement(String uri, String localName, String qName) {
103107
information = null;
104108
break;
105109
case XmlTagConstants.SERVICE_STATUS_STARTING_TIME:
106-
if (currentServiceContext != null) {
107-
currentServiceStatusInfo.setServiceStatusStartingTime(information.toString());
110+
if (currentServiceChronologicalInfo != null) {
111+
currentServiceChronologicalInfo.setServiceStatusStartingTime(information.toString());
108112
}
109113

110114
information = null;
111115
break;
112116
case XmlTagConstants.SERVICE_INFORMATION:
113117
case XmlTagConstants.SERVICE_HISTORY_INSTANCE:
114118
if (currentServiceContext != null) {
115-
currentServiceContext.addNewServiceStatus(currentServiceStatusInfo);
119+
currentServiceContext.addServiceChronologicalInfo(currentServiceChronologicalInfo);
120+
}
121+
122+
currentServiceChronologicalInfo = null;
123+
break;
124+
case XmlTagConstants.URI:
125+
if (currentExtension != null) {
126+
currentExtension.setUri(information.toString());
127+
}
128+
129+
break;
130+
case XmlTagConstants.ADDITIONAL_INFORMATION_EXTENSION:
131+
if (currentServiceChronologicalInfo != null) {
132+
currentServiceChronologicalInfo.addExtension(currentExtension);
116133
}
117134

118-
currentServiceStatusInfo = null;
135+
currentExtension = null;
119136
}
120137
}
121138

sign/src/main/java/com/itextpdf/signatures/validation/lotl/XmlTagConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@ class XmlTagConstants {
3535
static final String SCHEME_TERRITORY = "SchemeTerritory";
3636
static final String TSLLOCATION = "TSLLocation";
3737
static final String MIME_TYPE = "MimeType";
38+
static final String ADDITIONAL_INFORMATION_EXTENSION = "AdditionalServiceInformation";
3839
static final String URI = "URI";
3940
}

0 commit comments

Comments
 (0)