From 0acf0838240a903e3dd083a1d2f2d5b6e96b2bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 29 Nov 2023 08:28:31 +0100 Subject: [PATCH 01/12] validation api WIP --- .../autogram/core/SignatureValidator.java | 16 +++-- .../errors/DocumentNotSignedYetException.java | 7 +++ .../autogram/server/AutogramServer.java | 4 ++ .../autogram/server/ValidateEndpoint.java | 63 +++++++++++++++++++ .../autogram/server/dto/ErrorResponse.java | 3 +- .../server/dto/ValidateRequestBody.java | 4 ++ .../autogram/server/dto/ValidateResponse.java | 4 ++ .../slovensko/autogram/server/server.yml | 47 ++++++++++++++ 8 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/main/java/digital/slovensko/autogram/core/errors/DocumentNotSignedYetException.java create mode 100644 src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java create mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java create mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java diff --git a/src/main/java/digital/slovensko/autogram/core/SignatureValidator.java b/src/main/java/digital/slovensko/autogram/core/SignatureValidator.java index ae82d15ef..6e1e6e177 100644 --- a/src/main/java/digital/slovensko/autogram/core/SignatureValidator.java +++ b/src/main/java/digital/slovensko/autogram/core/SignatureValidator.java @@ -63,13 +63,21 @@ public synchronized static SignatureValidator getInstance() { return instance; } - public synchronized Reports validate(SignedDocumentValidator docValidator) { + private synchronized Reports validate(SignedDocumentValidator docValidator) { docValidator.setCertificateVerifier(verifier); // TODO: do not print stack trace inside DSS return docValidator.validateDocument(); } + public synchronized Reports validate(DSSDocument document) { + var documentValidator = createDocumentValidator(document); + if (documentValidator == null) + return null; + + return validate(documentValidator); + } + public synchronized void refresh() { validationJob.offlineRefresh(); } @@ -126,11 +134,7 @@ private CertificateSource getJournalCertificateSource() throws AssertionError { } public synchronized ValidationReports getSignatureValidationReport(SigningJob job) { - var documentValidator = createDocumentValidator(job.getDocument()); - if (documentValidator == null) - return new ValidationReports(null, job); - - return new ValidationReports(validate(documentValidator), job); + return new ValidationReports(validate(job.getDocument()), job); } public static String getSignatureValidationReportHTML(Reports signatureValidationReport) { diff --git a/src/main/java/digital/slovensko/autogram/core/errors/DocumentNotSignedYetException.java b/src/main/java/digital/slovensko/autogram/core/errors/DocumentNotSignedYetException.java new file mode 100644 index 000000000..783a00d50 --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/core/errors/DocumentNotSignedYetException.java @@ -0,0 +1,7 @@ +package digital.slovensko.autogram.core.errors; + +public class DocumentNotSignedYetException extends AutogramException { + public DocumentNotSignedYetException() { + super("Document not signed", "Document is not signed yet", "The provided document is not eligible for signature validation because the document is not signed yet."); + } +} diff --git a/src/main/java/digital/slovensko/autogram/server/AutogramServer.java b/src/main/java/digital/slovensko/autogram/server/AutogramServer.java index 815afd4ca..1120d7e75 100644 --- a/src/main/java/digital/slovensko/autogram/server/AutogramServer.java +++ b/src/main/java/digital/slovensko/autogram/server/AutogramServer.java @@ -46,6 +46,10 @@ public void start() { server.createContext("/batch", new BatchEndpoint(autogram)).getFilters() .add(new AutogramCorsFilter(List.of("POST", "DELETE"))); + // Validate + server.createContext("/validate", new ValidateEndpoint(autogram)).getFilters() + .add(new AutogramCorsFilter(List.of("POST"))); + // Start server server.start(); } diff --git a/src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java b/src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java new file mode 100644 index 000000000..e38bed00d --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java @@ -0,0 +1,63 @@ +package digital.slovensko.autogram.server; + +import com.google.gson.JsonSyntaxException; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import digital.slovensko.autogram.core.Autogram; +import digital.slovensko.autogram.core.SignatureValidator; +import digital.slovensko.autogram.core.errors.AutogramException; +import digital.slovensko.autogram.core.errors.DocumentNotSignedYetException; +import digital.slovensko.autogram.core.errors.ResponseNetworkErrorException; +import digital.slovensko.autogram.server.dto.ErrorResponse; +import digital.slovensko.autogram.server.dto.ValidateRequestBody; +import digital.slovensko.autogram.server.dto.ValidateResponse; +import digital.slovensko.autogram.server.errors.MalformedBodyException; +import eu.europa.esig.dss.model.DSSDocument; +import eu.europa.esig.dss.model.InMemoryDocument; + +import java.io.IOException; +import java.util.Base64; + +public class ValidateEndpoint implements HttpHandler { + private final Autogram autogram; + + public ValidateEndpoint(Autogram autogram) { + this.autogram = autogram; + } + + @Override + public void handle(HttpExchange exchange) { + DSSDocument document = null; + try { + var body = EndpointUtils.loadFromJsonExchange(exchange, ValidateRequestBody.class); + document = new InMemoryDocument(Base64.getDecoder().decode(body.dataB64())); + } catch (JsonSyntaxException | IOException | IllegalArgumentException e) { + var response = ErrorResponse.buildFromException(new MalformedBodyException(e.getMessage(), e)); + EndpointUtils.respondWithError(response, exchange); + } + + try { + var reports = SignatureValidator.getInstance().validate(document); + if (reports == null) { + EndpointUtils.respondWithError(ErrorResponse.buildFromException(new DocumentNotSignedYetException()), exchange); + return; + } + + var html = SignatureValidator.getSignatureValidationReportHTML(reports); + + try { + exchange.getResponseHeaders().add("Content-Type", "txt/xml;utf-8"); + exchange.sendResponseHeaders(200, 0); + exchange.getResponseBody().write(reports.getXmlSimpleReport().getBytes()); + exchange.getResponseBody().close(); + } catch (IOException e) { + throw new ResponseNetworkErrorException("Externá aplikácia nečakala na odpoveď", e); + } + +// EndpointUtils.respondWith(new ValidateResponse(reports.getXmlSimpleReport()), exchange); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ErrorResponse.java b/src/main/java/digital/slovensko/autogram/server/dto/ErrorResponse.java index e2b91105d..2c50eaa59 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ErrorResponse.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ErrorResponse.java @@ -48,7 +48,8 @@ public static ErrorResponse buildFromException(Exception e) { case "BatchNotStartedException" -> new ErrorResponse(400, "BATCH_NOT_STARTED", (AutogramException) e); case "BatchInvalidIdException" -> new ErrorResponse(404, "BATCH_NOT_FOUND", (AutogramException) e); case "BatchConflictException" -> new ErrorResponse(400, "BATCH_CONFLICT", (AutogramException) e); - case "KeyPinDifferentFromTokenPin" -> new ErrorResponse(400, "CARD_NOT_SUPPORTED", (AutogramException) e); + case "KeyPinDifferentFromTokenPinException" -> new ErrorResponse(400, "CARD_NOT_SUPPORTED", (AutogramException) e); + case "DocumentNotSignedYetException" -> new ErrorResponse(422, "DOCUMENT_NOT_SIGNED", (AutogramException) e); default -> new ErrorResponse(500, "INTERNAL_ERROR", "Unexpected exception signing document", e.getMessage()); }; } diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java new file mode 100644 index 000000000..fc8cca5aa --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java @@ -0,0 +1,4 @@ +package digital.slovensko.autogram.server.dto; + +public record ValidateRequestBody(String dataB64) { +} diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java new file mode 100644 index 000000000..5c5d2ef66 --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java @@ -0,0 +1,4 @@ +package digital.slovensko.autogram.server.dto; + +public record ValidateResponse(String simpleReportXml) { +} diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index c7b4f67ed..27afc523b 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -313,6 +313,33 @@ paths: application/json: schema: $ref: "#/components/schemas/BatchEndResponseBody" + + /validate: + post: + tags: + - Validation + summary: Get signature validation report for a signed document + operationId: validateDocument + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ValidateRequestBody" + examples: + Signed PDF with timestamp: + $ref: "#/components/examples/Signed-PDF-TS" + Signed EForm: + $ref: "#/components/examples/Signed-EForm" + Not signed TXT: + $ref: "#/components/examples/Not-signed-TXT" + responses: + 200: + description: Signature validation report + content: + application/json: + schema: + type: string + components: schemas: Info: @@ -581,6 +608,14 @@ components: required: - level + ValidateRequestBody: + type: object + properties: + dataB64: + type: string + example: PD94bWwgdmVyc2lvbj0iMS4wIiBlb + description: Base64 encoded document to validate signatures in + examples: XAdES-XML-Base64-HTML_md: value: @@ -913,3 +948,15 @@ components: PdfContent: value: "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dGFsb2cvUGFnZXMgNiAwIFIKL09wZW5BY3Rpb25bMSAwIFIgL1hZWiBudWxsIG51bGwgMF0KL0xhbmcoZW4tVVMpCj4+CmVuZG9iagoKMTUgMCBvYmoKPDwvQ3JlYXRvcjxGRUZGMDA1NzAwNzIwMDY5MDA3NDAwNjUwMDcyPgovUHJvZHVjZXI8RkVGRjAwNEMwMDY5MDA2MjAwNzIwMDY1MDA0RjAwNjYwMDY2MDA2OTAwNjMwMDY1MDAyMDAwMzYwMDJFMDAzND4KL0NyZWF0aW9uRGF0ZShEOjIwMjExMTA5MjAzMjUyKzAxJzAwJyk+PgplbmRvYmoKCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMTA1MTMgMDAwMDAgbiAKMDAwMDAwMDAxOSAwMDAwMCBuIAowMDAwMDAwMzE1IDAwMDAwIG4gCjAwMDAwMDAzMzUgMDAwMDAgbiAKMDAwMDAwMjA4MyAwMDAwMCBuIAowMDAwMDEwNjgyIDAwMDAwIG4gCjAwMDAwMDIxMDQgMDAwMDAgbiAKMDAwMDAwOTUyOSAwMDAwMCBuIAowMDAwMDA5NTUwIDAwMDAwIG4gCjAwMDAwMDk3NDUgMDAwMDAgbiAKMDAwMDAxMDEzNSAwMDAwMCBuIAowMDAwMDEwMzgxIDAwMDAwIG4gCjAwMDAwMTA0MTQgMDAwMDAgbiAKMDAwMDAxMDc4MSAwMDAwMCBuIAowMDAwMDEwODc4IDAwMDAwIG4gCnRyYWlsZXIKPDwvU2l6ZSAxNi9Sb290IDE0IDAgUgovSW5mbyAxNSAwIFIKL0lEIFsgPDhGMDUyNjI1QTE1QzdBQkNBODQ2RUY5MDZBQzJGNDc4Pgo8OEYwNTI2MjVBMTVDN0FCQ0E4NDZFRjkwNkFDMkY0Nzg+IF0KL0RvY0NoZWNrc3VtIC9FNTVCMzQ1RUZEQzY5MkFFNTI4OEJFQ0Y5Nzg5NDgxNQo+PgpzdGFydHhyZWYKMTEwNTMKJSVFT0YK" + + Signed-PDF-TS: + value: + dataB64: "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dGFsb2cvUGFnZXMgNiAwIFIKL09wZW5BY3Rpb25bMSAwIFIgL1hZWiBudWxsIG51bGwgMF0KL0xhbmcoZW4tVVMpCj4+CmVuZG9iagoKMTUgMCBvYmoKPDwvQ3JlYXRvcjxGRUZGMDA1NzAwNzIwMDY5MDA3NDAwNjUwMDcyPgovUHJvZHVjZXI8RkVGRjAwNEMwMDY5MDA2MjAwNzIwMDY1MDA0RjAwNjYwMDY2MDA2OTAwNjMwMDY1MDAyMDAwMzYwMDJFMDAzND4KL0NyZWF0aW9uRGF0ZShEOjIwMjExMTA5MjAzMjUyKzAxJzAwJyk+PgplbmRvYmoKCnhyZWYKMCAxNgowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMTA1MTMgMDAwMDAgbiAKMDAwMDAwMDAxOSAwMDAwMCBuIAowMDAwMDAwMzE1IDAwMDAwIG4gCjAwMDAwMDAzMzUgMDAwMDAgbiAKMDAwMDAwMjA4MyAwMDAwMCBuIAowMDAwMDEwNjgyIDAwMDAwIG4gCjAwMDAwMDIxMDQgMDAwMDAgbiAKMDAwMDAwOTUyOSAwMDAwMCBuIAowMDAwMDA5NTUwIDAwMDAwIG4gCjAwMDAwMDk3NDUgMDAwMDAgbiAKMDAwMDAxMDEzNSAwMDAwMCBuIAowMDAwMDEwMzgxIDAwMDAwIG4gCjAwMDAwMTA0MTQgMDAwMDAgbiAKMDAwMDAxMDc4MSAwMDAwMCBuIAowMDAwMDEwODc4IDAwMDAwIG4gCnRyYWlsZXIKPDwvU2l6ZSAxNi9Sb290IDE0IDAgUgovSW5mbyAxNSAwIFIKL0lEIFsgPDhGMDUyNjI1QTE1QzdBQkNBODQ2RUY5MDZBQzJGNDc4Pgo8OEYwNTI2MjVBMTVDN0FCQ0E4NDZFRjkwNkFDMkY0Nzg+IF0KL0RvY0NoZWNrc3VtIC9FNTVCMzQ1RUZEQzY5MkFFNTI4OEJFQ0Y5Nzg5NDgxNQo+PgpzdGFydHhyZWYKMTEwNTMKJSVFT0YKDQoxNCAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgNiAwIFIKL09wZW5BY3Rpb24gWzEgMCBSIC9YWVogbnVsbCBudWxsIDBdCi9MYW5nIChlbi1VUykKL0Fjcm9Gb3JtIDw8Ci9GaWVsZHMgWzE2IDAgUl0KL1NpZ0ZsYWdzIDMKPj4KPj4KZW5kb2JqCjEgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCA2IDAgUgovUmVzb3VyY2VzIDEzIDAgUgovTWVkaWFCb3ggWzAgMCA1OTUuMzAzOTM3MDA3ODc0IDg0MS44ODk3NjM3Nzk1MjhdCi9Hcm91cCA8PAovUyAvVHJhbnNwYXJlbmN5Ci9DUyAvRGV2aWNlUkdCCi9JIHRydWUKPj4KL0NvbnRlbnRzIDIgMCBSCi9Bbm5vdHMgWzE2IDAgUl0KPj4KZW5kb2JqCjE2IDAgb2JqCjw8Ci9GVCAvU2lnCi9UeXBlIC9Bbm5vdAovU3VidHlwZSAvV2lkZ2V0Ci9GIDEzMgovVCAoU2lnbmF0dXJlMSkKL1YgMTcgMCBSCi9QIDEgMCBSCi9SZWN0IFswLjAgMC4wIDAuMCAwLjBdCi9BUCAxOCAwIFIKPj4KZW5kb2JqCjE3IDAgb2JqCjw8Ci9UeXBlIC9TaWcKL0ZpbHRlciAvQWRvYmUuUFBLTGl0ZQovU3ViRmlsdGVyIC9FVFNJLkNBZEVTLmRldGFjaGVkCi9NIChEOjIwMjMxMTI4MTQ0MzU1KzAxJzAwJykKL0NvbnRlbnRzIDwzMDgyMTcwMTA2MDkyYTg2NDg4NmY3MGQwMTA3MDJhMDgyMTZmMjMwODIxNmVlMDIwMTAxMzEwZDMwMGIwNjA5NjA4NjQ4MDE2NTAzMDQwMjAxMzAwYjA2MDkyYTg2NDg4NmY3MGQwMTA3MDFhMDgyMDM2YTMwODIwMzY2MzA4MjAyNGVhMDAzMDIwMTAyMDIwNDY1NTFmODUzMzAwZDA2MDkyYTg2NDg4NmY3MGQwMTAxMGIwNTAwMzA3NTMxMGIzMDA5MDYwMzU1MDQwNjEzMDI1MzRiMzExMzMwMTEwNjAzNTUwNDA4MGMwYTQyNzI2MTc0Njk3MzZjNjE3NjYxMzExMzMwMTEwNjAzNTUwNDA3MGMwYTQyNzI2MTc0Njk3MzZjNjE3NjYxMzExMTMwMGYwNjAzNTUwNDBhMGMwODQxNzU3NDZmNjc3MjYxNmQzMTExMzAwZjA2MDM1NTA0MGIwYzA4NDE3NTc0NmY2NzcyNjE2ZDMxMTYzMDE0MDYwMzU1MDQwMzBjMGQ0MTc1NzQ2ZjY3NzI2MTZkMjA1NDY1NzM3NDMwMWUxNzBkMzIzMzMxMzEzMTMzMzEzMDMyMzAzMDMzNWExNzBkMzIzNDMxMzEzMTMyMzEzMDMyMzAzMDMzNWEzMDc1MzEwYjMwMDkwNjAzNTUwNDA2MTMwMjUzNGIzMTEzMzAxMTA2MDM1NTA0MDgwYzBhNDI3MjYxNzQ2OTczNmM2MTc2NjEzMTEzMzAxMTA2MDM1NTA0MDcwYzBhNDI3MjYxNzQ2OTczNmM2MTc2NjEzMTExMzAwZjA2MDM1NTA0MGEwYzA4NDE3NTc0NmY2NzcyNjE2ZDMxMTEzMDBmMDYwMzU1MDQwYjBjMDg0MTc1NzQ2ZjY3NzI2MTZkMzExNjMwMTQwNjAzNTUwNDAzMGMwZDQxNzU3NDZmNjc3MjYxNmQyMDU0NjU3Mzc0MzA4MjAxMjIzMDBkMDYwOTJhODY0ODg2ZjcwZDAxMDEwMTA1MDAwMzgyMDEwZjAwMzA4MjAxMGEwMjgyMDEwMTAwZGYxZGRkZTFmNmM1MTI1ZTdhY2EwZDk4MzIyMGJhZTFjMWJmOTg2MWE5MTNlYzM3ODJjNjhjNjlhMTQzZDM2YjA0MDM3NTBjYTViN2Q3MDg3NGY5MzdkZGM3OWYwNjM3M2I2MDJkNWE0NDk2OTQwMWVmZmM4MWE2YmE4ZDAwYTAwMzYxN2IzZWEyNzkxNDZlMGUxN2VmZGYyNTAxYjg5YTY4Y2JkMGNkOGY1ZDU1Nzg0NTg1YmQ0OGQ5OTEwNmJhYjM2MTY2YTNmNGQ3MDZlMzQ2Y2EzODNiNDUwOWRiMDQ4OGMzOWFmZmIxOTY2MzhiNWVkNTg1ZDg2YWI5NWNjMzNmZTQ3MzI1ZTUzN2U4MjQ2ODg0NGVkM2Q1YzY4NmU5MGQ3ZjRlNWU1YWFkMmJjNDcwOTMzM2ZmYzYxNDYyM2I5ZDA3NDA1YjhmZWQ1OWQzYTI1MzU4YWY4NTViZmVmMzI2NjNjZGIxMTE5MjljNWZkMzJjODMwM2JjODY5YjMwM2ExNGIzMDNmYmMwMTNkYjllODViMmY4MWZlZDRkZDJhNzk3Y2Q3YzM4YThhNTQ2NTY1ZmU2NjhlNjIwZGJkOWYwOGRkZTVlNWQxNjQ0MGZkNDk1NDQ2ZTAyOWRjZmE2YjcxOTljMTc1ZThjMjA0NzM2ODlhMGM4YzJmYzFiZGIwMjAzMDEwMDAxMzAwZDA2MDkyYTg2NDg4NmY3MGQwMTAxMGIwNTAwMDM4MjAxMDEwMDI4M2ZiNDdjY2Y5YzQ1NTkwMzNhODRkNGY2YWM4NDQ5MGVhZDk5YWYxYzcyMThkNWYxZDU2ZjRkYmJjNzk4YjRkOTYwMjA3MTI5NDRlZTgxOTNiM2Y2ZDEwZDc3ZWY2OWZiNWI4NjczYWEyOTcwMmFlNjg4N2NhYWQyODFiYzBmYzYzZDE0YmNjNzE0YTgyYzk2ODc3OGY4ODFkNDM4ZTM3ZmE1ZWIzZWFmYTZmOGZmNDY3OGZhYjhlZTY3OTI2YjBmMjc4ODVhYzFhZGMxNTQxYTMzYTA1ZTBiNTY5ZmFmZjFmYmI5M2Y2NGFmYzEyZWQ0NDk0ZGMzZjQ4MDU4MTk5MDY2MmNmNzdiOGQxNzE3NjhhMzNkZGY1YmUzN2E3NGU0ZWQxZGE4MTE3NGFiN2YzNzZjM2Q4YWQ3OTQwOWExZmNjMjMzZWQwOGQwZWFhMTI3MGIxNmJjNzk2ZWE2ODNlNTcwNWUyMDQ2YTE1NWVjYzUxMGU1MDFkNzNhYmM5NDA4ODBhOWYyMWQ4ZWYzZGYzYzI0NWJkN2M1YTI0YTE4YzA5NGY1OTdlMjcyNGRjYTI3NzUyYzc0OWZiNDhkYjA4YmUyOTM5ZWE3YmU1YTZjMTBmYWE1Yzc0M2Y2YTk1NDE3OGIxZThhMjQyMjcxZTMxYjlhNjc5MzQxZmE5MzgyMzE4MjEzNWQzMDgyMTM1OTAyMDEwMTMwN2QzMDc1MzEwYjMwMDkwNjAzNTUwNDA2MTMwMjUzNGIzMTEzMzAxMTA2MDM1NTA0MDgwYzBhNDI3MjYxNzQ2OTczNmM2MTc2NjEzMTEzMzAxMTA2MDM1NTA0MDcwYzBhNDI3MjYxNzQ2OTczNmM2MTc2NjEzMTExMzAwZjA2MDM1NTA0MGEwYzA4NDE3NTc0NmY2NzcyNjE2ZDMxMTEzMDBmMDYwMzU1MDQwYjBjMDg0MTc1NzQ2ZjY3NzI2MTZkMzExNjMwMTQwNjAzNTUwNDAzMGMwZDQxNzU3NDZmNjc3MjYxNmQyMDU0NjU3Mzc0MDIwNDY1NTFmODUzMzAwYjA2MDk2MDg2NDgwMTY1MDMwNDAyMDFhMDgyMDEwZDMwMTgwNjA5MmE4NjQ4ODZmNzBkMDEwOTAzMzEwYjA2MDkyYTg2NDg4NmY3MGQwMTA3MDEzMDJmMDYwOTJhODY0ODg2ZjcwZDAxMDkwNDMxMjIwNDIwMjE1MzAwZWQ5NzM2MDIwZjhmOTdhYzhhZmNjMGEzMDVmZjBiMDYyYWVlNTRmZGRiNjRkODY2MWM2MjgxYTBhNTMwODFiZjA2MGIyYTg2NDg4NmY3MGQwMTA5MTAwMjJmMzE4MWFmMzA4MWFjMzA4MWE5MzA4MWE2MDQyMDNmMWY5Y2I0MzkwNTQwYzNhMTdiM2M4YThhNWVlNGJjMGQ1YzMzNTFmNmZmN2Y0OTRkMmIyZTdkYjlhOTI0MTIzMDgxODEzMDc5YTQ3NzMwNzUzMTBiMzAwOTA2MDM1NTA0MDYxMzAyNTM0YjMxMTMzMDExMDYwMzU1MDQwODBjMGE0MjcyNjE3NDY5NzM2YzYxNzY2MTMxMTMzMDExMDYwMzU1MDQwNzBjMGE0MjcyNjE3NDY5NzM2YzYxNzY2MTMxMTEzMDBmMDYwMzU1MDQwYTBjMDg0MTc1NzQ2ZjY3NzI2MTZkMzExMTMwMGYwNjAzNTUwNDBiMGMwODQxNzU3NDZmNjc3MjYxNmQzMTE2MzAxNDA2MDM1NTA0MDMwYzBkNDE3NTc0NmY2NzcyNjE2ZDIwNTQ2NTczNzQwMjA0NjU1MWY4NTMzMDBkMDYwOTJhODY0ODg2ZjcwZDAxMDEwYjA1MDAwNDgyMDEwMGRkNmM3MTA3NGEzMDkwOTY4MGM4YTFmYjM1ZWQwZmU4ZjZlYWFiNzY5YjEwZWIyZjFiNTdkMzdmOGE1M2Q4MzFjODNlYTViNmMwZGZjMDc0MjU2Zjg1M2Q0YmJmYjU1NTM3YTViZDRjN2RiODlmMzcwM2NiMTljNTIzOWZmOTQxNWUyYjY4MmRhZjNiMThiMmY5ZjYxMWU3ZTJmYTA3Yzc0Njc1Yzg0OWRmN2JjMmMwNTUzYTE3OTlkMmY1OTA4ODgyYmZlYTFiYTAxMGMyNTI0MjE3Y2EyYjRmNDllN2I1YWEzZmRiYTQ4M2M1M2EwNzljNDc1MWUzNGY4NWE4NjU1ZGVmYTRjOGViMDkwNmRjNTk1MjUxNzExMTUyMTEwZmY4ODM5MTM4OTgxMTdmMzhkZmFkMmQ4MmU2ODRkODFmZjQ3ODg3ODYxYjdjNGRkYjE4NTM0M2UwN2Y1ZjE0MTUxOTM4YjUwOGU4YzYwZTZkMWZmYTc0YTUyMzE5MDMyNTI1YzlmMDI1NGFhM2I4Y2ZhYmY2Nzk4ZmY2OTkxZTc2YTAyZjY4NmIyODM4MTQ5OTJhNzM2ODg4M2YxMTAyNTkzNWUxNGFjOGUxYjRmNTQ3ZDM5N2ExMTRjOTY5YTc3NWU2MjE1OTJlNTkxY2YzMTIxM2Q0YjQyZGI1NTYxMzM3YTE4MjEwYTIzMDgyMTA5ZTA2MGIyYTg2NDg4NmY3MGQwMTA5MTAwMjBlMzE4MjEwOGQzMDgyMTA4OTA2MDkyYTg2NDg4NmY3MGQwMTA3MDJhMDgyMTA3YTMwODIxMDc2MDIwMTAzMzEwZDMwMGIwNjA5NjA4NjQ4MDE2NTAzMDQwMjAxMzA4MjAxNWEwNjBiMmE4NjQ4ODZmNzBkMDEwOTEwMDEwNGEwODIwMTQ5MDQ4MjAxNDUzMDgyMDE0MTAyMDEwMTA2MDg2MDM4MGQwNjAzMDE4NzY4MzAyZjMwMGIwNjA5NjA4NjQ4MDE2NTAzMDQwMjAxMDQyMGIyYjdiYjBlMGU3NjE3NDI5YjU4ZmNhZjQ3YTVjODZkZjQ0YTczNDU1NDQxZTNjNjg1Yzc2NmVhMzBiNTBmNmIwMjA4M2M4ZGIzMGY0MGRlNDUxMTE4MGYzMjMwMzIzMzMxMzEzMjM4MzEzMzM0MzMzNTM2NWEzMDAzMDIwMTAxYTA4MWMzYTQ4MWMwMzA4MWJkMzEwYjMwMDkwNjAzNTUwNDA2MTMwMjQyNDUzMTExMzAwZjA2MDM1NTA0MDcwYzA4NDI3Mjc1NzM3MzY1NmM3MzMxMzAzMDJlMDYwMzU1MDQwYTBjMjc0YjY5NmU2NzY0NmY2ZDIwNmY2NjIwNDI2NTZjNjc2OTc1NmQyMDJkMjA0NjY1NjQ2NTcyNjE2YzIwNDc2Zjc2NjU3MjZlNmQ2NTZlNzQzMTNmMzAzZDA2MDM1NTA0MGIwYzM2NTE1NDUzNTAzYTIwNDY1MDUzMjA1MDZmNmM2OTYzNzkyMDYxNmU2NDIwNTM3NTcwNzA2ZjcyNzQyMDJkMjA0MjRmNTM0MTIwMjg0ZTU0NTI0MjQ1MmQzMDM2MzczMTM1MzEzNjM2MzQzNzI5MzEwZjMwMGQwNjAzNTUwNDA1MTMwNjMyMzAzMjMxMzAzMjMxMTczMDE1MDYwMzU1MDQwMzBjMGU1NDY5NmQ2NTczNzQ2MTZkNzAyMDU1NmU2OTc0YTExYjMwMTkwNjA4MmIwNjAxMDUwNTA3MDEwMzA0MGQzMDBiMzAwOTA2MDcwNDAwODE5NzVlMDEwMWEwODIwYzA2MzA4MjAzNjgzMDgyMDJlZGEwMDMwMjAxMDIwMjE0NzE4YjU3ZmY2YjY5M2U1YTFjMjM1ZWQ4ODdhM2VmNTFmNDAxMGYyNjMwMGEwNjA4MmE4NjQ4Y2UzZDA0MDMwMzMwODFlMDMxMGIzMDA5MDYwMzU1MDQwNjEzMDI0MjQ1MzExMTMwMGYwNjAzNTUwNDA3MGMwODQyNzI3NTczNzM2NTZjNzMzMTMwMzAyZTA2MDM1NTA0MGEwYzI3NGI2OTZlNjc2NDZmNmQyMDZmNjYyMDQyNjU2YzY3Njk3NTZkMjAyZDIwNDY2NTY0NjU3MjYxNmMyMDQ3NmY3NjY1NzI2ZTZkNjU2ZTc0MzEzNjMwMzQwNjAzNTUwNDBiMGMyZDQ2NTA1MzIwNDg2ZjZkNjUyMDQxNjY2NjYxNjk3MjczMjAyZDIwNDI0OTRiMmQ0NzQzNDkyMDI4NGU1NDUyNDI0NTJkMzAzMzM2MzIzNDM3MzUzNTMzMzgyOTMxMzkzMDM3MDYwMzU1MDQwYjBjMzA0NjUwNTMyMDUwNmY2YzY5NjM3OTIwNjE2ZTY0MjA1Mzc1NzA3MDZmNzI3NDIwMmQyMDQyNGY1MzQxMjAyODRlNTQ1MjQyNDUyZDMwMzYzNzMxMzUzMTM2MzYzNDM3MjkzMTE5MzAxNzA2MDM1NTA0MDMwYzEwNDI2NTZjNjc2OTc1NmQyMDUyNmY2Zjc0MjA0MzQxMzYzMDFlMTcwZDMyMzAzMDM2MzAzMzMxMzAzMDMxMzMzMTVhMTcwZDM0MzAzMDM2MzAzMzMxMzAzMDMxMzMzMTVhMzA4MWUwMzEwYjMwMDkwNjAzNTUwNDA2MTMwMjQyNDUzMTExMzAwZjA2MDM1NTA0MDcwYzA4NDI3Mjc1NzM3MzY1NmM3MzMxMzAzMDJlMDYwMzU1MDQwYTBjMjc0YjY5NmU2NzY0NmY2ZDIwNmY2NjIwNDI2NTZjNjc2OTc1NmQyMDJkMjA0NjY1NjQ2NTcyNjE2YzIwNDc2Zjc2NjU3MjZlNmQ2NTZlNzQzMTM2MzAzNDA2MDM1NTA0MGIwYzJkNDY1MDUzMjA0ODZmNmQ2NTIwNDE2NjY2NjE2OTcyNzMyMDJkMjA0MjQ5NGIyZDQ3NDM0OTIwMjg0ZTU0NTI0MjQ1MmQzMDMzMzYzMjM0MzczNTM1MzMzODI5MzEzOTMwMzcwNjAzNTUwNDBiMGMzMDQ2NTA1MzIwNTA2ZjZjNjk2Mzc5MjA2MTZlNjQyMDUzNzU3MDcwNmY3Mjc0MjAyZDIwNDI0ZjUzNDEyMDI4NGU1NDUyNDI0NTJkMzAzNjM3MzEzNTMxMzYzNjM0MzcyOTMxMTkzMDE3MDYwMzU1MDQwMzBjMTA0MjY1NmM2NzY5NzU2ZDIwNTI2ZjZmNzQyMDQzNDEzNjMwNzYzMDEwMDYwNzJhODY0OGNlM2QwMjAxMDYwNTJiODEwNDAwMjIwMzYyMDAwNDc5ZGY2MDExNjhiNTZjM2QyN2Y5M2YzZjdlNmY5MTMxNmIxYTQyN2JjODg2MWE0MGUwOTFiNTI1MThjZDJlOTViYWY0NjJhNmNhYmRhZWM0YTBlMDE4Zjk4MmY5NjQ5YjNjNGI0Mjg4YjJlNDBkNzM0N2Y2YmUwMjNjYzVjYzhmNDA3ODFlMDlhMmRjYzM0M2IzYmU2N2MyMWRmZmNlMzBlMjI4Y2M0MmEwMjlhMDBhZWE5MWVmNjgxNWNmZDJkOGEzNjYzMDY0MzAxMjA2MDM1NTFkMTMwMTAxZmYwNDA4MzAwNjAxMDFmZjAyMDEwMTMwMWYwNjAzNTUxZDIzMDQxODMwMTY4MDE0MmVhMDg4YjAwYjBkNjI4OWVjMWQzZmQ0OWZjYzkyNDQ4ZTQ4Njk0NjMwMWQwNjAzNTUxZDBlMDQxNjA0MTQyZWEwODhiMDBiMGQ2Mjg5ZWMxZDNmZDQ5ZmNjOTI0NDhlNDg2OTQ2MzAwZTA2MDM1NTFkMGYwMTAxZmYwNDA0MDMwMjAxMDYzMDBhMDYwODJhODY0OGNlM2QwNDAzMDMwMzY5MDAzMDY2MDIzMTAwYjc2N2I2YmQ1MWI4ZmRhMzIzMjI4OTFiNDEwYjgyOWVmMjNjYjI4ZjJiNjY1ZmM4NmNhYmE5MzAyMjYyYjVmMTYyZWU4NTY1MWU1OTU4MTQzNzU4MzAzNGNhNThiNjYwMDIzMTAwYjFhZGIwYmIzZGM0YzNhZmZhZjE3NWVkMjEzZmI5ZDZjOWVhOTI1ZDhhMmU1Njk1ODBhNWQ4OTY5NGUxZGIzYTkwMDQ4MDVkMjhlZDY3MzBlZmViNDExN2VlNjdiZDM1MzA4MjA0NDczMDgyMDNjZWEwMDMwMjAxMDIwMjE0MTA1YjA0NjJlODY3ZjhkYzRlZjA0NmM0YTJmNmQ3MzUwNGJlNjJjOTMwMGEwNjA4MmE4NjQ4Y2UzZDA0MDMwMzMwODFiYjMxMGIzMDA5MDYwMzU1MDQwNjEzMDI0MjQ1MzExMTMwMGYwNjAzNTUwNDA3MGMwODQyNzI3NTczNzM2NTZjNzMzMTMwMzAyZTA2MDM1NTA0MGEwYzI3NGI2OTZlNjc2NDZmNmQyMDZmNjYyMDQyNjU2YzY3Njk3NTZkMjAyZDIwNDY2NTY0NjU3MjYxNmMyMDQ3NmY3NjY1NzI2ZTZkNjU2ZTc0MzEzZjMwM2QwNjAzNTUwNDBiMGMzNjUxNTQ1MzUwM2EyMDQ2NTA1MzIwNTA2ZjZjNjk2Mzc5MjA2MTZlNjQyMDUzNzU3MDcwNmY3Mjc0MjAyZDIwNDI0ZjUzNDEyMDI4NGU1NDUyNDI0NTJkMzAzNjM3MzEzNTMxMzYzNjM0MzcyOTMxMGYzMDBkMDYwMzU1MDQwNTEzMDYzMjMwMzIzMTMwMzEzMTE1MzAxMzA2MDM1NTA0MDMwYzBjNTQ2OTZkNjU3Mzc0NjE2ZDcwMjA0MzQxMzAxZTE3MGQzMjMxMzAzMzMxMzYzMDM5MzQzMDMyMzQ1YTE3MGQzMjM3MzAzMzMxMzYzMDM5MzQzMDMyMzQ1YTMwODFiZDMxMGIzMDA5MDYwMzU1MDQwNjEzMDI0MjQ1MzExMTMwMGYwNjAzNTUwNDA3MGMwODQyNzI3NTczNzM2NTZjNzMzMTMwMzAyZTA2MDM1NTA0MGEwYzI3NGI2OTZlNjc2NDZmNmQyMDZmNjYyMDQyNjU2YzY3Njk3NTZkMjAyZDIwNDY2NTY0NjU3MjYxNmMyMDQ3NmY3NjY1NzI2ZTZkNjU2ZTc0MzEzZjMwM2QwNjAzNTUwNDBiMGMzNjUxNTQ1MzUwM2EyMDQ2NTA1MzIwNTA2ZjZjNjk2Mzc5MjA2MTZlNjQyMDUzNzU3MDcwNmY3Mjc0MjAyZDIwNDI0ZjUzNDEyMDI4NGU1NDUyNDI0NTJkMzAzNjM3MzEzNTMxMzYzNjM0MzcyOTMxMGYzMDBkMDYwMzU1MDQwNTEzMDYzMjMwMzIzMTMwMzIzMTE3MzAxNTA2MDM1NTA0MDMwYzBlNTQ2OTZkNjU3Mzc0NjE2ZDcwMjA1NTZlNjk3NDMwNTkzMDEzMDYwNzJhODY0OGNlM2QwMjAxMDYwODJhODY0OGNlM2QwMzAxMDcwMzQyMDAwNDIwNWI0ZTM5OWZjM2ZlOTFmZmY3ZDgwNjdlMWRmMGMyZmEyYjdkZDQzNWFmNTIwZDU1YmIyMmMxM2Q3OTI1MDU3NGI2Njg5ZGQyYTE2OTVlMmU2ZjVkNmMxMjExZjlkMzc2NTY3MThkYTcxMjVlNTg5YzI0YWNiNTdhNzlkZTgyYTM4MjAxYWEzMDgyMDFhNjMwMGMwNjAzNTUxZDEzMDEwMWZmMDQwMjMwMDAzMDFmMDYwMzU1MWQyMzA0MTgzMDE2ODAxNGUwNGUxYmQzNzU0ZGI1ODc0ZTAyYjZmYWE5NWE2M2NjM2NhYTk4OTkzMDdiMDYwODJiMDYwMTA1MDUwNzAxMDEwNDZmMzA2ZDMwM2EwNjA4MmIwNjAxMDUwNTA3MzAwMjg2MmU2ODc0NzQ3MDNhMmYyZjYzNzI3NDJlNjU2OTY0NzA2YjY5MmU2MjY1NmM2NzY5NzU2ZDJlNjI2NTJmNzQ3MzJmNzQ3MzYzNjEzMjMwMzIzMTMwMzEyZTYzNzI3NDMwMmYwNjA4MmIwNjAxMDUwNTA3MzAwMTg2MjM2ODc0NzQ3MDNhMmYyZjZmNjM3MzcwMmU2NTY5NjQ3MDZiNjkyZTYyNjU2YzY3Njk3NTZkMmU2MjY1MmY2NTY5NjQyZjMwMzA1NjA2MDM1NTFkMjAwNDRmMzA0ZDMwNDAwNjA3NjAzODBkMDYwMzg3NjgzMDM1MzAzMzA2MDgyYjA2MDEwNTA1MDcwMjAxMTYyNzY4NzQ3NDcwNzMzYTJmMmY3MjY1NzA2ZjczNjk3NDZmNzI3OTJlNjU2OTY0NzA2YjY5MmU2MjY1NmM2NzY5NzU2ZDJlNjI2NTJmNzQ3MzMwMDkwNjA3MDQwMDhiZWM0MDAxMDEzMDE2MDYwMzU1MWQyNTAxMDFmZjA0MGMzMDBhMDYwODJiMDYwMTA1MDUwNzAzMDgzMDE4MDYwODJiMDYwMTA1MDUwNzAxMDMwNDBjMzAwYTMwMDgwNjA2MDQwMDhlNDYwMTAxMzAzZjA2MDM1NTFkMWYwNDM4MzAzNjMwMzRhMDMyYTAzMDg2MmU2ODc0NzQ3MDNhMmYyZjYzNzI2YzJlNjU2OTY0NzA2YjY5MmU2MjY1NmM2NzY5NzU2ZDJlNjI2NTJmNzQ3MzJmNzQ3MzYzNjEzMjMwMzIzMTMwMzEyZTYzNzI2YzMwMWQwNjAzNTUxZDBlMDQxNjA0MTQyY2Y5NDJhNzNiYzc2Nzk4MDg0NWYyMzA3ZTI5ZTgwNjAyMWI5MTEwMzAwZTA2MDM1NTFkMGYwMTAxZmYwNDA0MDMwMjA3ODAzMDBhMDYwODJhODY0OGNlM2QwNDAzMDMwMzY3MDAzMDY0MDIzMDc0YTJiNzkxOTBkNjdiMDMxNzZkMjAyMWZhOTY0NWJjOWM0NDQwYzAzM2Q2M2ZjYzM5MDU0NDk5MmZjYjA5ZTRhYWU4YTJkNjNkODFlYmVkMjI3ZDdlMTViMTUzMjUxMDAyMzAwOGJjODcyZDMyOWY0YTRlZDBmOTc4YzMzMGE2ODFjNDYxYTVhM2U2YjI2Nzg5ZjIzZGRiYTM5ZWY3OTIxZDRjMmI4YTZjNmQ4ZDkwZGVjYzI0MzZlZmJjNDE2OWEyZGUzMDgyMDQ0YjMwODIwM2QxYTAwMzAyMDEwMjAyMTQwNWMzMWQzYzY2NjU2NWUzODYyYjAzYWQyZmM0NTUyNTc4MDViNGI4MzAwYTA2MDgyYTg2NDhjZTNkMDQwMzAzMzA4MWUwMzEwYjMwMDkwNjAzNTUwNDA2MTMwMjQyNDUzMTExMzAwZjA2MDM1NTA0MDcwYzA4NDI3Mjc1NzM3MzY1NmM3MzMxMzAzMDJlMDYwMzU1MDQwYTBjMjc0YjY5NmU2NzY0NmY2ZDIwNmY2NjIwNDI2NTZjNjc2OTc1NmQyMDJkMjA0NjY1NjQ2NTcyNjE2YzIwNDc2Zjc2NjU3MjZlNmQ2NTZlNzQzMTM2MzAzNDA2MDM1NTA0MGIwYzJkNDY1MDUzMjA0ODZmNmQ2NTIwNDE2NjY2NjE2OTcyNzMyMDJkMjA0MjQ5NGIyZDQ3NDM0OTIwMjg0ZTU0NTI0MjQ1MmQzMDMzMzYzMjM0MzczNTM1MzMzODI5MzEzOTMwMzcwNjAzNTUwNDBiMGMzMDQ2NTA1MzIwNTA2ZjZjNjk2Mzc5MjA2MTZlNjQyMDUzNzU3MDcwNmY3Mjc0MjAyZDIwNDI0ZjUzNDEyMDI4NGU1NDUyNDI0NTJkMzAzNjM3MzEzNTMxMzYzNjM0MzcyOTMxMTkzMDE3MDYwMzU1MDQwMzBjMTA0MjY1NmM2NzY5NzU2ZDIwNTI2ZjZmNzQyMDQzNDEzNjMwMWUxNzBkMzIzMTMwMzIzMjMzMzEzMTMwMzEzMTM4NWExNzBkMzMzMzMwMzIzMjMzMzEzMTMwMzEzMTM4NWEzMDgxYmIzMTBiMzAwOTA2MDM1NTA0MDYxMzAyNDI0NTMxMTEzMDBmMDYwMzU1MDQwNzBjMDg0MjcyNzU3MzczNjU2YzczMzEzMDMwMmUwNjAzNTUwNDBhMGMyNzRiNjk2ZTY3NjQ2ZjZkMjA2ZjY2MjA0MjY1NmM2NzY5NzU2ZDIwMmQyMDQ2NjU2NDY1NzI2MTZjMjA0NzZmNzY2NTcyNmU2ZDY1NmU3NDMxM2YzMDNkMDYwMzU1MDQwYjBjMzY1MTU0NTM1MDNhMjA0NjUwNTMyMDUwNmY2YzY5NjM3OTIwNjE2ZTY0MjA1Mzc1NzA3MDZmNzI3NDIwMmQyMDQyNGY1MzQxMjAyODRlNTQ1MjQyNDUyZDMwMzYzNzMxMzUzMTM2MzYzNDM3MjkzMTBmMzAwZDA2MDM1NTA0MDUxMzA2MzIzMDMyMzEzMDMxMzExNTMwMTMwNjAzNTUwNDAzMGMwYzU0Njk2ZDY1NzM3NDYxNmQ3MDIwNDM0MTMwNzYzMDEwMDYwNzJhODY0OGNlM2QwMjAxMDYwNTJiODEwNDAwMjIwMzYyMDAwNGVhMWNjM2RjZWYyZWM4OGRhNTBhODQ4MDZjZWNlZWY1YzQwYjRiNjNkMGVhZGUwZDczNzNjMDg3YzA3OGRiNDk4ZmVhNmQ5NzJlYmM3NTA2NGE4YjcwYWNkNGI2MzM4YWVkZjE4YzE4YmI5NGE4MGM3ODkwNzY1MmRkNDAxMGFiZWI2MTQ4ZjY3MDI2MGU5NGYxY2U5NWZlMTA4Y2NlNzUxNGQ5YzYzMWY4OTEwMDgxZjQyYjQ2OWEwN2RiOWRhOGEzODIwMTZkMzA4MjAxNjkzMDEyMDYwMzU1MWQxMzAxMDFmZjA0MDgzMDA2MDEwMWZmMDIwMTAwMzAxZjA2MDM1NTFkMjMwNDE4MzAxNjgwMTQyZWEwODhiMDBiMGQ2Mjg5ZWMxZDNmZDQ5ZmNjOTI0NDhlNDg2OTQ2MzA3YjA2MDgyYjA2MDEwNTA1MDcwMTAxMDQ2ZjMwNmQzMDM2MDYwODJiMDYwMTA1MDUwNzMwMDI4NjJhNjg3NDc0NzAzYTJmMmY2MzcyNzQyZTY1Njk2NDcwNmI2OTJlNjI2NTZjNjc2OTc1NmQyZTYyNjUyZjY1Njk2NDJmNjI3MjYzNjEzNjJlNjM3Mjc0MzAzMzA2MDgyYjA2MDEwNTA1MDczMDAxODYyNzY4NzQ3NDcwM2EyZjJmNmY2MzczNzAyZTY1Njk2NDcwNmI2OTJlNjI2NTZjNjc2OTc1NmQyZTYyNjUyZjY1Njk2NDJmNjI3MjYzNjEzNjMwNDkwNjAzNTUxZDIwMDQ0MjMwNDAzMDNlMDYwNDU1MWQyMDAwMzAzNjMwMzQwNjA4MmIwNjAxMDUwNTA3MDIwMTE2Mjg2ODc0NzQ3MDczM2EyZjJmNzI2NTcwNmY3MzY5NzQ2ZjcyNzkyZTY1Njk2NDcwNmI2OTJlNjI2NTZjNjc2OTc1NmQyZTYyNjUyZjY1Njk2NDMwM2IwNjAzNTUxZDFmMDQzNDMwMzIzMDMwYTAyZWEwMmM4NjJhNjg3NDc0NzAzYTJmMmY2MzcyNmMyZTY1Njk2NDcwNmI2OTJlNjI2NTZjNjc2OTc1NmQyZTYyNjUyZjY1Njk2NDJmNjI3MjYzNjEzNjJlNjM3MjZjMzAxZDA2MDM1NTFkMGUwNDE2MDQxNGUwNGUxYmQzNzU0ZGI1ODc0ZTAyYjZmYWE5NWE2M2NjM2NhYTk4OTkzMDBlMDYwMzU1MWQwZjAxMDFmZjA0MDQwMzAyMDEwNjMwMGEwNjA4MmE4NjQ4Y2UzZDA0MDMwMzAzNjgwMDMwNjUwMjMwMGZkMjRkOTRmMmIwYjg5OTlmYjEzMWEwMjVhOTBlODljNzQyMDU2OThlZGI3Y2MzOGM3OTAxM2VkMzUwOTBiNzk0NGQ5YmU2NTg5OWIxOTk2MGI1Y2VkMDAyY2RhNGY5MDIzMTAwODFiM2MwZGQyNGVlNWZiZWUzNWRjMjZmMzFjNjA1YmUyNDBhZDEwM2M3Y2VlZjEzYmIzZmQ5OTYwZWIxZGY3Y2M5N2Q0YzA1M2E4NDhjMDAwMmU2YWQ3YjA2ZTM4MjYxMzE4MjAyZjgzMDgyMDJmNDAyMDEwMTMwODFkNDMwODFiYjMxMGIzMDA5MDYwMzU1MDQwNjEzMDI0MjQ1MzExMTMwMGYwNjAzNTUwNDA3MGMwODQyNzI3NTczNzM2NTZjNzMzMTMwMzAyZTA2MDM1NTA0MGEwYzI3NGI2OTZlNjc2NDZmNmQyMDZmNjYyMDQyNjU2YzY3Njk3NTZkMjAyZDIwNDY2NTY0NjU3MjYxNmMyMDQ3NmY3NjY1NzI2ZTZkNjU2ZTc0MzEzZjMwM2QwNjAzNTUwNDBiMGMzNjUxNTQ1MzUwM2EyMDQ2NTA1MzIwNTA2ZjZjNjk2Mzc5MjA2MTZlNjQyMDUzNzU3MDcwNmY3Mjc0MjAyZDIwNDI0ZjUzNDEyMDI4NGU1NDUyNDI0NTJkMzAzNjM3MzEzNTMxMzYzNjM0MzcyOTMxMGYzMDBkMDYwMzU1MDQwNTEzMDYzMjMwMzIzMTMwMzEzMTE1MzAxMzA2MDM1NTA0MDMwYzBjNTQ2OTZkNjU3Mzc0NjE2ZDcwMjA0MzQxMDIxNDEwNWIwNDYyZTg2N2Y4ZGM0ZWYwNDZjNGEyZjZkNzM1MDRiZTYyYzkzMDBiMDYwOTYwODY0ODAxNjUwMzA0MDIwMWEwODIwMWI0MzAxYTA2MDkyYTg2NDg4NmY3MGQwMTA5MDMzMTBkMDYwYjJhODY0ODg2ZjcwZDAxMDkxMDAxMDQzMDFjMDYwOTJhODY0ODg2ZjcwZDAxMDkwNTMxMGYxNzBkMzIzMzMxMzEzMjM4MzEzMzM0MzMzNTM2NWEzMDI4MDYwOTJhODY0ODg2ZjcwZDAxMDkzNDMxMWIzMDE5MzAwYjA2MDk2MDg2NDgwMTY1MDMwNDAyMDFhMTBhMDYwODJhODY0OGNlM2QwNDAzMDIzMDJmMDYwOTJhODY0ODg2ZjcwZDAxMDkwNDMxMjIwNDIwOTViZjk1ODA2YjQ2OWY4ZDJhYzJjNGMyNjZiY2Q0MTJjZWY5NDc2Mjk4Zjc3N2IzNzdlMTcxZTdhYzczMWY2MjMwODIwMTFiMDYwYjJhODY0ODg2ZjcwZDAxMDkxMDAyMmYzMTgyMDEwYTMwODIwMTA2MzA4MjAxMDIzMDgxZmYwNDIwMzkxN2U0OTk1MDkzOTU0YmMxNjI1NjhjNzE4OGMwMGEyMjIwMjMxNjY4MjQ0ODBlOGY4NGE0ZTFmMzQ0MWRjMTMwODFkYTMwODFjMWE0ODFiZTMwODFiYjMxMGIzMDA5MDYwMzU1MDQwNjEzMDI0MjQ1MzExMTMwMGYwNjAzNTUwNDA3MGMwODQyNzI3NTczNzM2NTZjNzMzMTMwMzAyZTA2MDM1NTA0MGEwYzI3NGI2OTZlNjc2NDZmNmQyMDZmNjYyMDQyNjU2YzY3Njk3NTZkMjAyZDIwNDY2NTY0NjU3MjYxNmMyMDQ3NmY3NjY1NzI2ZTZkNjU2ZTc0MzEzZjMwM2QwNjAzNTUwNDBiMGMzNjUxNTQ1MzUwM2EyMDQ2NTA1MzIwNTA2ZjZjNjk2Mzc5MjA2MTZlNjQyMDUzNzU3MDcwNmY3Mjc0MjAyZDIwNDI0ZjUzNDEyMDI4NGU1NDUyNDI0NTJkMzAzNjM3MzEzNTMxMzYzNjM0MzcyOTMxMGYzMDBkMDYwMzU1MDQwNTEzMDYzMjMwMzIzMTMwMzEzMTE1MzAxMzA2MDM1NTA0MDMwYzBjNTQ2OTZkNjU3Mzc0NjE2ZDcwMjA0MzQxMDIxNDEwNWIwNDYyZTg2N2Y4ZGM0ZWYwNDZjNGEyZjZkNzM1MDRiZTYyYzkzMDBhMDYwODJhODY0OGNlM2QwNDAzMDIwNDQ3MzA0NTAyMjEwMGUwNDk2YThiN2JlZjk1OWI3ZTM1YjRmNTdmYmZlZWNiYWY5MTEzMjljZTJmZWYxODhkNWQ4NzM0NmNlNmJkYTIwMjIwMmI3YTNjNmY5NjdlOGVlYjBmZTliOTE3MjJjOGQxM2Q3NWEwZmExZDM1MTdkZWI5MDlhMGFhMmZkMjE1MGNjZjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD4KL0J5dGVSYW5nZSBbMCAxMjE4MSAzMTEyNyA1MTVdICAgICAgICAgICAgICAgICAKPj4KZW5kb2JqCjE4IDAgb2JqCjw8Ci9OIDE5IDAgUgo+PgplbmRvYmoKMTkgMCBvYmoKPDwKL0xlbmd0aCAwCi9UeXBlIC9YT2JqZWN0Ci9TdWJ0eXBlIC9Gb3JtCi9CQm94IFswLjAgMC4wIDAuMCAwLjBdCj4+CnN0cmVhbQ0KDQplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCAyCjAwMDAwMDAwMDAgNjU1MzUgZg0KMDAwMDAxMTcyNSAwMDAwMCBuDQoxNCAxCjAwMDAwMTE1NzkgMDAwMDAgbg0KMTYgNAowMDAwMDExOTI2IDAwMDAwIG4NCjAwMDAwMTIwNjQgMDAwMDAgbg0KMDAwMDAzMTE4NiAwMDAwMCBuDQowMDAwMDMxMjE4IDAwMDAwIG4NCnRyYWlsZXIKPDwKL1NpemUgMjAKL1Jvb3QgMTQgMCBSCi9JbmZvIDE1IDAgUgovSUQgWzw4RjA1MjYyNUExNUM3QUJDQTg0NkVGOTA2QUMyRjQ3OD4gPDQwQzVENjVENzA3Qzc3RkEyNENCMDU4NkUyN0Q2REUzPl0KL1ByZXYgMTEwNTMKPj4Kc3RhcnR4cmVmCjMxMzI0CiUlRU9GCg==" + + Signed-EForm: + value: + dataB64: "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" + + Not-signed-TXT: + value: + dataB64: "VGhpcyBpcyBhIHRlc3QgZG9jdW1lbnQgY29udGVudCEK" From 02a7d4d70b0f3d49d75a91d50794bbacf25905af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Thu, 30 Nov 2023 12:10:47 +0100 Subject: [PATCH 02/12] validation api wip --- .../autogram/server/AutogramServer.java | 2 +- ...eEndpoint.java => ValidationEndpoint.java} | 16 +++-- .../server/dto/ValidateRequestBody.java | 4 -- .../autogram/server/dto/ValidateResponse.java | 4 -- .../server/dto/ValidationRequestBody.java | 4 ++ .../server/dto/ValidationResponse.java | 4 ++ .../slovensko/autogram/server/server.yml | 68 ++++++++++++++++++- 7 files changed, 86 insertions(+), 16 deletions(-) rename src/main/java/digital/slovensko/autogram/server/{ValidateEndpoint.java => ValidationEndpoint.java} (79%) delete mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java delete mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java create mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidationRequestBody.java create mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java diff --git a/src/main/java/digital/slovensko/autogram/server/AutogramServer.java b/src/main/java/digital/slovensko/autogram/server/AutogramServer.java index 1120d7e75..26535d58d 100644 --- a/src/main/java/digital/slovensko/autogram/server/AutogramServer.java +++ b/src/main/java/digital/slovensko/autogram/server/AutogramServer.java @@ -47,7 +47,7 @@ public void start() { .add(new AutogramCorsFilter(List.of("POST", "DELETE"))); // Validate - server.createContext("/validate", new ValidateEndpoint(autogram)).getFilters() + server.createContext("/validate", new ValidationEndpoint(autogram)).getFilters() .add(new AutogramCorsFilter(List.of("POST"))); // Start server diff --git a/src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java b/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java similarity index 79% rename from src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java rename to src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java index e38bed00d..7f55de73f 100644 --- a/src/main/java/digital/slovensko/autogram/server/ValidateEndpoint.java +++ b/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java @@ -9,8 +9,7 @@ import digital.slovensko.autogram.core.errors.DocumentNotSignedYetException; import digital.slovensko.autogram.core.errors.ResponseNetworkErrorException; import digital.slovensko.autogram.server.dto.ErrorResponse; -import digital.slovensko.autogram.server.dto.ValidateRequestBody; -import digital.slovensko.autogram.server.dto.ValidateResponse; +import digital.slovensko.autogram.server.dto.ValidationRequestBody; import digital.slovensko.autogram.server.errors.MalformedBodyException; import eu.europa.esig.dss.model.DSSDocument; import eu.europa.esig.dss.model.InMemoryDocument; @@ -18,10 +17,10 @@ import java.io.IOException; import java.util.Base64; -public class ValidateEndpoint implements HttpHandler { +public class ValidationEndpoint implements HttpHandler { private final Autogram autogram; - public ValidateEndpoint(Autogram autogram) { + public ValidationEndpoint(Autogram autogram) { this.autogram = autogram; } @@ -29,8 +28,13 @@ public ValidateEndpoint(Autogram autogram) { public void handle(HttpExchange exchange) { DSSDocument document = null; try { - var body = EndpointUtils.loadFromJsonExchange(exchange, ValidateRequestBody.class); + var body = EndpointUtils.loadFromJsonExchange(exchange, ValidationRequestBody.class); + if (body.dataB64() == null) + throw new IllegalArgumentException("Document to validate is not provided."); + document = new InMemoryDocument(Base64.getDecoder().decode(body.dataB64())); + if (document == null || document.openStream().readAllBytes().length < 1) + throw new IllegalArgumentException("Document to validate is null."); } catch (JsonSyntaxException | IOException | IllegalArgumentException e) { var response = ErrorResponse.buildFromException(new MalformedBodyException(e.getMessage(), e)); EndpointUtils.respondWithError(response, exchange); @@ -48,7 +52,7 @@ public void handle(HttpExchange exchange) { try { exchange.getResponseHeaders().add("Content-Type", "txt/xml;utf-8"); exchange.sendResponseHeaders(200, 0); - exchange.getResponseBody().write(reports.getXmlSimpleReport().getBytes()); + exchange.getResponseBody().write(reports.getXmlValidationReport().getBytes()); exchange.getResponseBody().close(); } catch (IOException e) { throw new ResponseNetworkErrorException("Externá aplikácia nečakala na odpoveď", e); diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java deleted file mode 100644 index fc8cca5aa..000000000 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidateRequestBody.java +++ /dev/null @@ -1,4 +0,0 @@ -package digital.slovensko.autogram.server.dto; - -public record ValidateRequestBody(String dataB64) { -} diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java deleted file mode 100644 index 5c5d2ef66..000000000 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidateResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package digital.slovensko.autogram.server.dto; - -public record ValidateResponse(String simpleReportXml) { -} diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationRequestBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationRequestBody.java new file mode 100644 index 000000000..0d1d7e8ab --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationRequestBody.java @@ -0,0 +1,4 @@ +package digital.slovensko.autogram.server.dto; + +public record ValidationRequestBody(String dataB64) { +} diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java new file mode 100644 index 000000000..2f1bae452 --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java @@ -0,0 +1,4 @@ +package digital.slovensko.autogram.server.dto; + +public record ValidationResponse(String simpleReportXml) { +} diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index 27afc523b..fff5a334c 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -338,7 +338,7 @@ paths: content: application/json: schema: - type: string + $ref: "#/components/schemas/ValidationResponseBody" components: schemas: @@ -616,6 +616,72 @@ components: example: PD94bWwgdmVyc2lvbj0iMS4wIiBlb description: Base64 encoded document to validate signatures in + ValidationResponseBody: + type: object + properties: + formatSuboru: + type: string + example: ASiC-E + podpisy: + type: array + items: + type: object + properties: + vysledokOvereniaPodpisu: + type: object + properties: + kod: + type: integer + example: 0 + popis: + type: string + example: VALID + informaciaOPodpise: + type: object + properties: + datumACasPodpisuUtc: + type: string + datumACasCasovejPeciatkyPodpisuUtc: + type: string + typ: + type: string + enum: + - XAdES_BASELINE_B + - XAdES_BASELINE_T + - XAdES_BASELINE_LT + - XAdES_BASELINE_LTA + - PAdES_BASELINE_B + - PAdES_BASELINE_T + - PAdES_BASELINE_LT + - PAdES_BASELINE_LTA + - CAdES_BASELINE_B + - CAdES_BASELINE_T + - CAdES_BASELINE_LT + - CAdES_BASELINE_LTA + legislativnyTyp: + type: object + properties: + kod: + type: integer + example: 4 + popis: + type: string + example: Iný elektronický podpis/pečať + podpisovycertifikat: + type: object + propertie: + subjekt: + type: string + example: SERIALNUMBER=PNOSK-1234567890, C=SK, L=Bratislava, SURNAME=Smith, GIVENNAME=John, CN=John Smith + description: + obsahujeCasovuPeciatku: + type: boolean + example: true + + + podpisaneDatoveObjekty: + type: object + examples: XAdES-XML-Base64-HTML_md: value: From 3d60dd0526e2eff60327e0bb0a9899b063284263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 13:10:10 +0100 Subject: [PATCH 03/12] add validation api --- .../autogram/core/eforms/XDCValidator.java | 3 + .../autogram/server/ValidationEndpoint.java | 16 +- .../server/dto/ValidationResponse.java | 4 - .../server/dto/ValidationResponseBody.java | 140 ++++++++++++++++++ .../slovensko/autogram/server/server.yml | 76 ++++++++-- 5 files changed, 209 insertions(+), 30 deletions(-) delete mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java create mode 100644 src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java diff --git a/src/main/java/digital/slovensko/autogram/core/eforms/XDCValidator.java b/src/main/java/digital/slovensko/autogram/core/eforms/XDCValidator.java index ac1d1bf7e..2d04dfd40 100644 --- a/src/main/java/digital/slovensko/autogram/core/eforms/XDCValidator.java +++ b/src/main/java/digital/slovensko/autogram/core/eforms/XDCValidator.java @@ -77,6 +77,9 @@ public static boolean validateXmlContentAgainstXsd(String xmlContent, String xsd return true; try { + if (!xsdSchema.isEmpty() && xsdSchema.charAt(0) == '\uFEFF') + xsdSchema = xsdSchema.substring(1); + var schema = XMLUtils.getSecureSchemaFactory().newSchema(new StreamSource(new StringReader(xsdSchema))); var validator = schema.newValidator(); validator.validate(new StreamSource(new StringReader(xmlContent))); diff --git a/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java b/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java index 7f55de73f..9b5cd4269 100644 --- a/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java +++ b/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java @@ -5,11 +5,10 @@ import com.sun.net.httpserver.HttpHandler; import digital.slovensko.autogram.core.Autogram; import digital.slovensko.autogram.core.SignatureValidator; -import digital.slovensko.autogram.core.errors.AutogramException; import digital.slovensko.autogram.core.errors.DocumentNotSignedYetException; -import digital.slovensko.autogram.core.errors.ResponseNetworkErrorException; import digital.slovensko.autogram.server.dto.ErrorResponse; import digital.slovensko.autogram.server.dto.ValidationRequestBody; +import digital.slovensko.autogram.server.dto.ValidationResponseBody; import digital.slovensko.autogram.server.errors.MalformedBodyException; import eu.europa.esig.dss.model.DSSDocument; import eu.europa.esig.dss.model.InMemoryDocument; @@ -47,19 +46,12 @@ public void handle(HttpExchange exchange) { return; } - var html = SignatureValidator.getSignatureValidationReportHTML(reports); - try { - exchange.getResponseHeaders().add("Content-Type", "txt/xml;utf-8"); - exchange.sendResponseHeaders(200, 0); - exchange.getResponseBody().write(reports.getXmlValidationReport().getBytes()); - exchange.getResponseBody().close(); - } catch (IOException e) { - throw new ResponseNetworkErrorException("Externá aplikácia nečakala na odpoveď", e); + EndpointUtils.respondWith(ValidationResponseBody.build(reports, document), exchange); + } catch (Exception e) { + EndpointUtils.respondWithError(ErrorResponse.buildFromException(e), exchange); } -// EndpointUtils.respondWith(new ValidateResponse(reports.getXmlSimpleReport()), exchange); - } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java deleted file mode 100644 index 2f1bae452..000000000 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package digital.slovensko.autogram.server.dto; - -public record ValidationResponse(String simpleReportXml) { -} diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java new file mode 100644 index 000000000..d88f28fcd --- /dev/null +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -0,0 +1,140 @@ +package digital.slovensko.autogram.server.dto; + +import digital.slovensko.autogram.core.errors.DocumentNotSignedYetException; +import eu.europa.esig.dss.asic.cades.ASiCWithCAdESContainerExtractor; +import eu.europa.esig.dss.asic.xades.ASiCWithXAdESContainerExtractor; +import eu.europa.esig.dss.diagnostic.SignerDataWrapper; +import eu.europa.esig.dss.enumerations.MimeTypeEnum; +import eu.europa.esig.dss.model.DSSDocument; +import eu.europa.esig.dss.validation.reports.Reports; + +import java.util.List; + +public record ValidationResponseBody(String formatSuboru, List podpisy, + List podpisaneObjekty) { + + public static ValidationResponseBody build(Reports reports, DSSDocument document) { + var sr = reports.getSimpleReport(); + var dd = reports.getDiagnosticData(); + var dr = reports.getDetailedReport(); + + if (sr.getSignatureIdList().isEmpty()) + throw new DocumentNotSignedYetException(); + + var f = sr.getContainerType(); + var formatSuboru = f == null ? sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm().name() : f.name(); + + List podpisy = dr.getSignatures().stream().map((e) -> { + var conclusion = e.getConclusion(); + var timestamps = e.getTimestamps(); + var signingCertificate = dd.getCertificateById(dd.getSigningCertificateId(e.getId())); + + return new Podpis( + new VysledokOvereniaPodpisu( + conclusion.getIndication().ordinal(), + conclusion.getIndication().name() + ), + new InformaciaOPodpise( + sr.getSigningTime(e.getId()).toString(), + timestamps.isEmpty() ? null : sr.getProductionTime(timestamps.get(0).getId()).toString(), + sr.getSignatureFormat(e.getId()).name(), + new Podpisovycertifikat( + signingCertificate.getCertificateIssuerDN(), + signingCertificate.getCertificateDN(), + signingCertificate.getSerialNumber(), + signingCertificate.getNotBefore().toString(), + signingCertificate.getNotAfter().toString(), + new Typ( + sr.getSignatureQualification(e.getId()).ordinal(), + sr.getSignatureQualification(e.getId()).getReadable() + ) + ), + !timestamps.isEmpty(), + timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { + var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); + return new CertifikatCasovejPeciatky( + tsCertificate.getCertificateIssuerDN(), + tsCertificate.getCertificateDN(), + tsCertificate.getSerialNumber(), + tsCertificate.getNotBefore().toString(), + tsCertificate.getNotAfter().toString() + );}).toList(), + dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() + )); + }).toList(); + + List podpisaneObjekty = List.of(); + + switch (sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm()) { + case PAdES: { + podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> new PodpisanyObjekt( + d.getId(), + MimeTypeEnum.PDF.getMimeTypeString(), + d.getReferencedName() + )).toList(); + break; + } + case XAdES: { + var extractor = new ASiCWithXAdESContainerExtractor(document); + var docs = extractor.extract().getSignedDocuments(); + + podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> { + var r = docs.stream().filter((i) -> i.getName().equals(d.getReferencedName())).toList(); + if (r.size() != 1) + return null; + var doc = r.get(0); + return new PodpisanyObjekt( + d.getId(), + doc.getMimeType().getMimeTypeString(), + d.getReferencedName() + ); + }).toList(); + break; + } + case CAdES: { + var extractor = new ASiCWithCAdESContainerExtractor(document); + var docs = extractor.extract().getSignedDocuments(); + + podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> { + var r = docs.stream().filter((i) -> i.getName().equals(d.getReferencedName())).toList(); + if (r.size() != 1) + return null; + var doc = r.get(0); + return new PodpisanyObjekt( + d.getId(), + doc.getMimeType().getMimeTypeString(), + d.getReferencedName() + ); + }).toList(); + break; + } + default: + } + + return new ValidationResponseBody(formatSuboru, podpisy, podpisaneObjekty); + } +} + +record Podpis(VysledokOvereniaPodpisu vysledokOvereniaPodpisu, InformaciaOPodpise informaciaOPodpise) { +} + +record VysledokOvereniaPodpisu(int kod, String popis) { +} + +record InformaciaOPodpise(String datumACasPodpisuUtc, String datumACasCasovejPeciatkyPodpisuUtc, String typ, + Podpisovycertifikat podpisovycertifikat, + boolean obsahujeCasovuPeciatku, List certifikatyCasovejPeciatky, + List podpisaneObjekty) { +} + +record Podpisovycertifikat(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo, Typ typ) { +} + +record Typ(int kod, String popis) { +} + +record CertifikatCasovejPeciatky(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo) { +} + +record PodpisanyObjekt(String id, String mimeType, String nazov) { +} diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index fff5a334c..d185ba89c 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -332,6 +332,8 @@ paths: $ref: "#/components/examples/Signed-EForm" Not signed TXT: $ref: "#/components/examples/Not-signed-TXT" + Signed MIRRI message: + $ref: "#/components/examples/Signed-MIRRI-thing" responses: 200: description: Signature validation report @@ -621,7 +623,9 @@ components: properties: formatSuboru: type: string - example: ASiC-E + enum: + - ASiC-E + - PAdES podpisy: type: array items: @@ -658,29 +662,73 @@ components: - CAdES_BASELINE_T - CAdES_BASELINE_LT - CAdES_BASELINE_LTA - legislativnyTyp: + podpisovycertifikat: type: object properties: - kod: - type: integer - example: 4 - popis: + vydavatel: type: string - example: Iný elektronický podpis/pečať - podpisovycertifikat: - type: object - propertie: + example: C=SK, L=Bratislava, CN=Disig subjekt: type: string example: SERIALNUMBER=PNOSK-1234567890, C=SK, L=Bratislava, SURNAME=Smith, GIVENNAME=John, CN=John Smith description: + serioveCislo: + type: string + example: 12578200234 + platnostOd: + type: string + platnostDo: + type: string + typ: + type: object + properties: + kod: + type: integer + example: 10 + popis: + type: string + example: Iný certifikát pre elektronický podpis/pečať obsahujeCasovuPeciatku: type: boolean example: true - - - podpisaneDatoveObjekty: - type: object + certifikatyCasovejPeciatky: + type: array + items: + type: object + properties: + vydavatel: + type: string + example: C=SK,O=Ditec\, a.s.,CN=TS Signer + subjekt: + type: string + example: C=SK,O=Ditec\, a.s.,CN=TS Signer + description: + serioveCislo: + type: string + example: 12578200234 + platnostOd: + type: string + platnostDo: + type: string + podpisaneObjekty: + type: array + items: + type: string + example: document.xdcf + podpisaneObjekty: + type: array + items: + type: object + properties: + id: + type: string + example: document.xdcf + mimeType: + type: string + example: application/vnd.gov.sk.xmldatacontainer+xml + nazov: + type: string + example: document.xdcf examples: XAdES-XML-Base64-HTML_md: From 13bcd4e6ac870dc0012dca208b22648a22afc311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 13:25:49 +0100 Subject: [PATCH 04/12] minor changes --- .../server/dto/ValidationResponseBody.java | 28 +++++++++++-------- .../slovensko/autogram/server/server.yml | 17 ++++++++--- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java index d88f28fcd..4fb4f0fe7 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -8,12 +8,12 @@ import eu.europa.esig.dss.model.DSSDocument; import eu.europa.esig.dss.validation.reports.Reports; +import javax.security.auth.x500.X500Principal; import java.util.List; -public record ValidationResponseBody(String formatSuboru, List podpisy, - List podpisaneObjekty) { +public record ValidationResponseBody(String formatSuboru, List podpisy, List podpisaneObjekty) { - public static ValidationResponseBody build(Reports reports, DSSDocument document) { + public static ValidationResponseBody build(Reports reports, DSSDocument document) throws DocumentNotSignedYetException { var sr = reports.getSimpleReport(); var dd = reports.getDiagnosticData(); var dr = reports.getDetailedReport(); @@ -39,8 +39,8 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document timestamps.isEmpty() ? null : sr.getProductionTime(timestamps.get(0).getId()).toString(), sr.getSignatureFormat(e.getId()).name(), new Podpisovycertifikat( - signingCertificate.getCertificateIssuerDN(), - signingCertificate.getCertificateDN(), + new X500Principal(signingCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), + new X500Principal(signingCertificate.getCertificateDN()).getName(X500Principal.RFC1779), signingCertificate.getSerialNumber(), signingCertificate.getNotBefore().toString(), signingCertificate.getNotAfter().toString(), @@ -53,11 +53,16 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); return new CertifikatCasovejPeciatky( - tsCertificate.getCertificateIssuerDN(), - tsCertificate.getCertificateDN(), + new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), + new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), tsCertificate.getSerialNumber(), tsCertificate.getNotBefore().toString(), - tsCertificate.getNotAfter().toString() + tsCertificate.getNotAfter().toString(), + new Typ( + sr.getTimestampQualification(t.getId()).ordinal(), + sr.getTimestampQualification(t.getId()).getReadable() + ) + );}).toList(), dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() )); @@ -122,9 +127,8 @@ record VysledokOvereniaPodpisu(int kod, String popis) { } record InformaciaOPodpise(String datumACasPodpisuUtc, String datumACasCasovejPeciatkyPodpisuUtc, String typ, - Podpisovycertifikat podpisovycertifikat, - boolean obsahujeCasovuPeciatku, List certifikatyCasovejPeciatky, - List podpisaneObjekty) { + Podpisovycertifikat podpisovycertifikat, boolean obsahujeCasovuPeciatku, + List certifikatyCasovejPeciatky, List podpisaneObjekty) { } record Podpisovycertifikat(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo, Typ typ) { @@ -133,7 +137,7 @@ record Podpisovycertifikat(String vydavatel, String subjekt, String serioveCislo record Typ(int kod, String popis) { } -record CertifikatCasovejPeciatky(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo) { +record CertifikatCasovejPeciatky(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo, Typ typ) { } record PodpisanyObjekt(String id, String mimeType, String nazov) { diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index d185ba89c..5e959c14c 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -639,7 +639,7 @@ components: example: 0 popis: type: string - example: VALID + example: TOTAL_PASSED informaciaOPodpise: type: object properties: @@ -684,10 +684,10 @@ components: properties: kod: type: integer - example: 10 + example: 1 popis: type: string - example: Iný certifikát pre elektronický podpis/pečať + example: QESeal obsahujeCasovuPeciatku: type: boolean example: true @@ -710,6 +710,15 @@ components: type: string platnostDo: type: string + typ: + type: object + properties: + kod: + type: integer + example: 0 + popis: + type: string + example: QTSA podpisaneObjekty: type: array items: @@ -725,7 +734,7 @@ components: example: document.xdcf mimeType: type: string - example: application/vnd.gov.sk.xmldatacontainer+xml + example: text/xml nazov: type: string example: document.xdcf From ebf1d644d5a977e7f747a4df114e633336eecb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 14:30:33 +0100 Subject: [PATCH 05/12] translate the validaiton api response objects to english --- .../server/dto/ValidationResponseBody.java | 46 ++++++-------- .../slovensko/autogram/server/server.yml | 62 +++++++++---------- 2 files changed, 51 insertions(+), 57 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java index 4fb4f0fe7..b6414a3f3 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -11,7 +11,7 @@ import javax.security.auth.x500.X500Principal; import java.util.List; -public record ValidationResponseBody(String formatSuboru, List podpisy, List podpisaneObjekty) { +public record ValidationResponseBody(String fileFormat, List signatures, List signedObjects) { public static ValidationResponseBody build(Reports reports, DSSDocument document) throws DocumentNotSignedYetException { var sr = reports.getSimpleReport(); @@ -24,27 +24,27 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document var f = sr.getContainerType(); var formatSuboru = f == null ? sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm().name() : f.name(); - List podpisy = dr.getSignatures().stream().map((e) -> { + List podpisy = dr.getSignatures().stream().map((e) -> { var conclusion = e.getConclusion(); var timestamps = e.getTimestamps(); var signingCertificate = dd.getCertificateById(dd.getSigningCertificateId(e.getId())); - return new Podpis( - new VysledokOvereniaPodpisu( + return new Signature( + new Result( conclusion.getIndication().ordinal(), conclusion.getIndication().name() ), - new InformaciaOPodpise( + new SignatureInfo( sr.getSigningTime(e.getId()).toString(), timestamps.isEmpty() ? null : sr.getProductionTime(timestamps.get(0).getId()).toString(), sr.getSignatureFormat(e.getId()).name(), - new Podpisovycertifikat( + new CertificateInfo( new X500Principal(signingCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), new X500Principal(signingCertificate.getCertificateDN()).getName(X500Principal.RFC1779), signingCertificate.getSerialNumber(), signingCertificate.getNotBefore().toString(), signingCertificate.getNotAfter().toString(), - new Typ( + new Result( sr.getSignatureQualification(e.getId()).ordinal(), sr.getSignatureQualification(e.getId()).getReadable() ) @@ -52,13 +52,13 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document !timestamps.isEmpty(), timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); - return new CertifikatCasovejPeciatky( + return new CertificateInfo( new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), tsCertificate.getSerialNumber(), tsCertificate.getNotBefore().toString(), tsCertificate.getNotAfter().toString(), - new Typ( + new Result( sr.getTimestampQualification(t.getId()).ordinal(), sr.getTimestampQualification(t.getId()).getReadable() ) @@ -68,11 +68,11 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document )); }).toList(); - List podpisaneObjekty = List.of(); + List podpisaneObjekty = List.of(); switch (sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm()) { case PAdES: { - podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> new PodpisanyObjekt( + podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> new SignedObject( d.getId(), MimeTypeEnum.PDF.getMimeTypeString(), d.getReferencedName() @@ -88,7 +88,7 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document if (r.size() != 1) return null; var doc = r.get(0); - return new PodpisanyObjekt( + return new SignedObject( d.getId(), doc.getMimeType().getMimeTypeString(), d.getReferencedName() @@ -105,7 +105,7 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document if (r.size() != 1) return null; var doc = r.get(0); - return new PodpisanyObjekt( + return new SignedObject( d.getId(), doc.getMimeType().getMimeTypeString(), d.getReferencedName() @@ -120,25 +120,19 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document } } -record Podpis(VysledokOvereniaPodpisu vysledokOvereniaPodpisu, InformaciaOPodpise informaciaOPodpise) { +record Signature(Result validaitonResult, SignatureInfo signatureInfo) { } -record VysledokOvereniaPodpisu(int kod, String popis) { +record Result(int code, String description) { } -record InformaciaOPodpise(String datumACasPodpisuUtc, String datumACasCasovejPeciatkyPodpisuUtc, String typ, - Podpisovycertifikat podpisovycertifikat, boolean obsahujeCasovuPeciatku, - List certifikatyCasovejPeciatky, List podpisaneObjekty) { +record SignatureInfo(String claimedSigningTime, String timestampSigningTime, String level, + CertificateInfo signingCertificate, boolean isTimestamped, + List timestamps, List signedObjectsIds) { } -record Podpisovycertifikat(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo, Typ typ) { +record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, String notBefore, String notAfter, Result qualification) { } -record Typ(int kod, String popis) { -} - -record CertifikatCasovejPeciatky(String vydavatel, String subjekt, String serioveCislo, String platnostOd, String platnostDo, Typ typ) { -} - -record PodpisanyObjekt(String id, String mimeType, String nazov) { +record SignedObject(String id, String mimeType, String filename) { } diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index 5e959c14c..9a3e6cb10 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -621,33 +621,33 @@ components: ValidationResponseBody: type: object properties: - formatSuboru: + fileFormat: type: string enum: - ASiC-E - PAdES - podpisy: + signatures: type: array items: type: object properties: - vysledokOvereniaPodpisu: + validaitonResult: type: object properties: - kod: + code: type: integer example: 0 - popis: + description: type: string example: TOTAL_PASSED - informaciaOPodpise: + signatureInfo: type: object properties: - datumACasPodpisuUtc: + claimedSigningTime: type: string - datumACasCasovejPeciatkyPodpisuUtc: + timestampSigningTime: type: string - typ: + level: type: string enum: - XAdES_BASELINE_B @@ -662,69 +662,69 @@ components: - CAdES_BASELINE_T - CAdES_BASELINE_LT - CAdES_BASELINE_LTA - podpisovycertifikat: + signingCertificate: type: object properties: - vydavatel: + issuerDN: type: string example: C=SK, L=Bratislava, CN=Disig - subjekt: + subjectDN: type: string example: SERIALNUMBER=PNOSK-1234567890, C=SK, L=Bratislava, SURNAME=Smith, GIVENNAME=John, CN=John Smith description: - serioveCislo: + serialNumber: type: string example: 12578200234 - platnostOd: + notBefore: type: string - platnostDo: + notAfter: type: string - typ: + qualification: type: object properties: - kod: + code: type: integer example: 1 - popis: + description: type: string example: QESeal - obsahujeCasovuPeciatku: + isTimestamped: type: boolean example: true - certifikatyCasovejPeciatky: + timestamps: type: array items: type: object properties: - vydavatel: + issuerDN: type: string example: C=SK,O=Ditec\, a.s.,CN=TS Signer - subjekt: + subjectDN: type: string example: C=SK,O=Ditec\, a.s.,CN=TS Signer description: - serioveCislo: + serialNumber: type: string example: 12578200234 - platnostOd: + notBefore: type: string - platnostDo: + notAfter: type: string - typ: + qualification: type: object properties: - kod: + code: type: integer example: 0 - popis: + description: type: string example: QTSA - podpisaneObjekty: + signedObjectsIds: type: array items: type: string example: document.xdcf - podpisaneObjekty: + signedObjects: type: array items: type: object @@ -735,7 +735,7 @@ components: mimeType: type: string example: text/xml - nazov: + filename: type: string example: document.xdcf From 94cefd3c57d9b3e523461b21660228845026581b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 15:09:48 +0100 Subject: [PATCH 06/12] refactor, add production time to all ts and format datetimes --- .../server/dto/ValidationResponseBody.java | 118 ++++++++++-------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java index b6414a3f3..f307ae4d9 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -9,9 +9,11 @@ import eu.europa.esig.dss.validation.reports.Reports; import javax.security.auth.x500.X500Principal; +import java.text.SimpleDateFormat; import java.util.List; public record ValidationResponseBody(String fileFormat, List signatures, List signedObjects) { + private static final SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss Z"); public static ValidationResponseBody build(Reports reports, DSSDocument document) throws DocumentNotSignedYetException { var sr = reports.getSimpleReport(); @@ -22,60 +24,63 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document throw new DocumentNotSignedYetException(); var f = sr.getContainerType(); - var formatSuboru = f == null ? sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm().name() : f.name(); + var fileFormat = f == null ? sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm().name() : f.name(); - List podpisy = dr.getSignatures().stream().map((e) -> { + List signatures = dr.getSignatures().stream().map((e) -> { var conclusion = e.getConclusion(); var timestamps = e.getTimestamps(); var signingCertificate = dd.getCertificateById(dd.getSigningCertificateId(e.getId())); return new Signature( - new Result( - conclusion.getIndication().ordinal(), - conclusion.getIndication().name() + new Result( + conclusion.getIndication().ordinal(), + conclusion.getIndication().name() + ), + new SignatureInfo( + sr.getSignatureFormat(e.getId()).name(), + format.format(sr.getSigningTime(e.getId())), + !timestamps.isEmpty(), + timestamps.isEmpty() ? null : format.format(sr.getProductionTime(timestamps.get(0).getId())), + new CertificateInfo( + new X500Principal(signingCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), + new X500Principal(signingCertificate.getCertificateDN()).getName(X500Principal.RFC1779), + signingCertificate.getSerialNumber(), + format.format(sr.getSigningTime(e.getId())), + format.format(signingCertificate.getNotBefore()), + format.format(signingCertificate.getNotAfter()), + new Result( + sr.getSignatureQualification(e.getId()).ordinal(), + sr.getSignatureQualification(e.getId()).getReadable() + ) ), - new SignatureInfo( - sr.getSigningTime(e.getId()).toString(), - timestamps.isEmpty() ? null : sr.getProductionTime(timestamps.get(0).getId()).toString(), - sr.getSignatureFormat(e.getId()).name(), - new CertificateInfo( - new X500Principal(signingCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), - new X500Principal(signingCertificate.getCertificateDN()).getName(X500Principal.RFC1779), - signingCertificate.getSerialNumber(), - signingCertificate.getNotBefore().toString(), - signingCertificate.getNotAfter().toString(), - new Result( - sr.getSignatureQualification(e.getId()).ordinal(), - sr.getSignatureQualification(e.getId()).getReadable() - ) - ), - !timestamps.isEmpty(), - timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { - var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); - return new CertificateInfo( - new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), - new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), - tsCertificate.getSerialNumber(), - tsCertificate.getNotBefore().toString(), - tsCertificate.getNotAfter().toString(), - new Result( - sr.getTimestampQualification(t.getId()).ordinal(), - sr.getTimestampQualification(t.getId()).getReadable() - ) - - );}).toList(), - dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() - )); + timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { + var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); + + return new CertificateInfo( + new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), + new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), + tsCertificate.getSerialNumber(), + format.format(sr.getProductionTime(t.getId())), + format.format(tsCertificate.getNotBefore()), + format.format(tsCertificate.getNotAfter()), + new Result( + sr.getTimestampQualification(t.getId()).ordinal(), + sr.getTimestampQualification(t.getId()).getReadable() + ) + ); + }).toList(), + dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() + )); }).toList(); - List podpisaneObjekty = List.of(); + List signedObjects = List.of(); switch (sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm()) { case PAdES: { - podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> new SignedObject( - d.getId(), - MimeTypeEnum.PDF.getMimeTypeString(), - d.getReferencedName() + signedObjects = dd.getAllSignerDocuments().stream().map((d) -> new SignedObject( + d.getId(), + MimeTypeEnum.PDF.getMimeTypeString(), + d.getReferencedName() )).toList(); break; } @@ -83,15 +88,16 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document var extractor = new ASiCWithXAdESContainerExtractor(document); var docs = extractor.extract().getSignedDocuments(); - podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> { + signedObjects = dd.getAllSignerDocuments().stream().map((d) -> { var r = docs.stream().filter((i) -> i.getName().equals(d.getReferencedName())).toList(); if (r.size() != 1) return null; + var doc = r.get(0); return new SignedObject( - d.getId(), - doc.getMimeType().getMimeTypeString(), - d.getReferencedName() + d.getId(), + doc.getMimeType().getMimeTypeString(), + d.getReferencedName() ); }).toList(); break; @@ -100,15 +106,16 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document var extractor = new ASiCWithCAdESContainerExtractor(document); var docs = extractor.extract().getSignedDocuments(); - podpisaneObjekty = dd.getAllSignerDocuments().stream().map((d) -> { + signedObjects = dd.getAllSignerDocuments().stream().map((d) -> { var r = docs.stream().filter((i) -> i.getName().equals(d.getReferencedName())).toList(); if (r.size() != 1) return null; + var doc = r.get(0); return new SignedObject( - d.getId(), - doc.getMimeType().getMimeTypeString(), - d.getReferencedName() + d.getId(), + doc.getMimeType().getMimeTypeString(), + d.getReferencedName() ); }).toList(); break; @@ -116,7 +123,7 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document default: } - return new ValidationResponseBody(formatSuboru, podpisy, podpisaneObjekty); + return new ValidationResponseBody(fileFormat, signatures, signedObjects); } } @@ -126,12 +133,13 @@ record Signature(Result validaitonResult, SignatureInfo signatureInfo) { record Result(int code, String description) { } -record SignatureInfo(String claimedSigningTime, String timestampSigningTime, String level, - CertificateInfo signingCertificate, boolean isTimestamped, - List timestamps, List signedObjectsIds) { +record SignatureInfo(String level, String claimedSigningTime, boolean isTimestamped, String timestampSigningTime, + CertificateInfo signingCertificate, List timestamps, + List signedObjectsIds) { } -record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, String notBefore, String notAfter, Result qualification) { +record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, String productionTime, String notBefore, + String notAfter, Result qualification) { } record SignedObject(String id, String mimeType, String filename) { From 2c583cf9f41fa957eaadb08e5251909400597acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 15:11:28 +0100 Subject: [PATCH 07/12] add productionTime to api description --- .../resources/digital/slovensko/autogram/server/server.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index 9a3e6cb10..d3fb34fe0 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -675,6 +675,9 @@ components: serialNumber: type: string example: 12578200234 + productionTime: + type: string + example: "2022-12-20T21:29:13 +0100" notBefore: type: string notAfter: @@ -706,6 +709,9 @@ components: serialNumber: type: string example: 12578200234 + productionTime: + type: string + example: "2022-12-20T21:29:13 +0100" notBefore: type: string notAfter: From 847af5525c0549c9ecaaa870bca18a63c446a02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 16:41:02 +0100 Subject: [PATCH 08/12] add unsigned objects to response --- .../server/dto/ValidationResponseBody.java | 73 ++++++++++--------- .../slovensko/autogram/server/server.yml | 17 ++++- 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java index f307ae4d9..b07d6fedb 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -2,6 +2,7 @@ import digital.slovensko.autogram.core.errors.DocumentNotSignedYetException; import eu.europa.esig.dss.asic.cades.ASiCWithCAdESContainerExtractor; +import eu.europa.esig.dss.asic.common.AbstractASiCContainerExtractor; import eu.europa.esig.dss.asic.xades.ASiCWithXAdESContainerExtractor; import eu.europa.esig.dss.diagnostic.SignerDataWrapper; import eu.europa.esig.dss.enumerations.MimeTypeEnum; @@ -12,7 +13,8 @@ import java.text.SimpleDateFormat; import java.util.List; -public record ValidationResponseBody(String fileFormat, List signatures, List signedObjects) { +public record ValidationResponseBody(String fileFormat, List signatures, List signedObjects, + List unsignedObjects) { private static final SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss Z"); public static ValidationResponseBody build(Reports reports, DSSDocument document) throws DocumentNotSignedYetException { @@ -73,7 +75,9 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document )); }).toList(); - List signedObjects = List.of(); + List signedObjects = null; + List unsignedObjects = null; + AbstractASiCContainerExtractor extractor = null; switch (sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm()) { case PAdES: { @@ -85,45 +89,45 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document break; } case XAdES: { - var extractor = new ASiCWithXAdESContainerExtractor(document); - var docs = extractor.extract().getSignedDocuments(); - - signedObjects = dd.getAllSignerDocuments().stream().map((d) -> { - var r = docs.stream().filter((i) -> i.getName().equals(d.getReferencedName())).toList(); - if (r.size() != 1) - return null; - - var doc = r.get(0); - return new SignedObject( - d.getId(), - doc.getMimeType().getMimeTypeString(), - d.getReferencedName() - ); - }).toList(); + extractor = new ASiCWithXAdESContainerExtractor(document); break; } case CAdES: { - var extractor = new ASiCWithCAdESContainerExtractor(document); - var docs = extractor.extract().getSignedDocuments(); - - signedObjects = dd.getAllSignerDocuments().stream().map((d) -> { - var r = docs.stream().filter((i) -> i.getName().equals(d.getReferencedName())).toList(); - if (r.size() != 1) - return null; - - var doc = r.get(0); - return new SignedObject( - d.getId(), - doc.getMimeType().getMimeTypeString(), - d.getReferencedName() - ); - }).toList(); + extractor = new ASiCWithCAdESContainerExtractor(document); break; } default: } - return new ValidationResponseBody(fileFormat, signatures, signedObjects); + if (extractor != null) { + signedObjects = getSignedObjects(extractor.extract().getSignedDocuments(), dd.getAllSignerDocuments()); + unsignedObjects = getUnsignedObjects(extractor.extract().getSignedDocuments(), dd.getAllSignerDocuments()); + } + + return new ValidationResponseBody(fileFormat, signatures, signedObjects, unsignedObjects); + } + + private static List getSignedObjects(List docs, List signedObjects) { + return signedObjects.stream().map((signedObject) -> { + var r = docs.stream().filter((doc) -> doc.getName().equals(signedObject.getReferencedName())).toList(); + if (r.isEmpty()) + return null; + + return new SignedObject( + signedObject.getId(), + r.get(0).getMimeType().getMimeTypeString(), + signedObject.getReferencedName() + ); + }).toList(); + } + + private static List getUnsignedObjects(List docs, List signedObjects) { + return docs.stream().filter((o) -> signedObjects.stream().filter((s) -> o.getName().equals(s.getReferencedName())).toList().isEmpty()).map((generalObject) -> + new UnsignedObject( + generalObject.getMimeType().getMimeTypeString(), + generalObject.getName() + ) + ).toList(); } } @@ -144,3 +148,6 @@ record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, S record SignedObject(String id, String mimeType, String filename) { } + +record UnsignedObject(String mimeType, String filename) { +} diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index d3fb34fe0..b5b66804a 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -332,8 +332,6 @@ paths: $ref: "#/components/examples/Signed-EForm" Not signed TXT: $ref: "#/components/examples/Not-signed-TXT" - Signed MIRRI message: - $ref: "#/components/examples/Signed-MIRRI-thing" responses: 200: description: Signature validation report @@ -729,7 +727,7 @@ components: type: array items: type: string - example: document.xdcf + example: "D-CE70D85E47F41DE68616A3695FE7569BF8F7409F052B74AE0356663393A68D8A" signedObjects: type: array items: @@ -737,13 +735,24 @@ components: properties: id: type: string - example: document.xdcf + example: "D-CE70D85E47F41DE68616A3695FE7569BF8F7409F052B74AE0356663393A68D8A" mimeType: type: string example: text/xml filename: type: string example: document.xdcf + unsignedObjects: + type: array + items: + type: object + properties: + mimeType: + type: string + example: text/xml + filename: + type: string + example: scam.xdcf examples: XAdES-XML-Base64-HTML_md: From 563a5f659829060c8d230d3273ac925e25b08555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Wed, 6 Dec 2023 18:07:23 +0100 Subject: [PATCH 09/12] add ts type to response --- .../autogram/server/dto/ValidationResponseBody.java | 11 ++++++++--- .../digital/slovensko/autogram/server/server.yml | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java index b07d6fedb..4e2077bb9 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -58,7 +58,7 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); - return new CertificateInfo( + return new TimestampCertificateInfo( new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), tsCertificate.getSerialNumber(), @@ -68,7 +68,8 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document new Result( sr.getTimestampQualification(t.getId()).ordinal(), sr.getTimestampQualification(t.getId()).getReadable() - ) + ), + dd.getTimestampType(t.getId()).name() ); }).toList(), dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() @@ -138,7 +139,7 @@ record Result(int code, String description) { } record SignatureInfo(String level, String claimedSigningTime, boolean isTimestamped, String timestampSigningTime, - CertificateInfo signingCertificate, List timestamps, + CertificateInfo signingCertificate, List timestamps, List signedObjectsIds) { } @@ -146,6 +147,10 @@ record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, S String notAfter, Result qualification) { } +record TimestampCertificateInfo(String issuerDN, String subjectDN, String serialNumber, String productionTime, String notBefore, + String notAfter, Result qualification, String timestampType) { +} + record SignedObject(String id, String mimeType, String filename) { } diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index b5b66804a..62cc13934 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -723,6 +723,9 @@ components: description: type: string example: QTSA + timestampType: + type: string + example: ARCHIVE_TIMESTAMP signedObjectsIds: type: array items: From f23c0ad12f558de58aca68fb2b659d61ae73b549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Thu, 7 Dec 2023 10:42:21 +0100 Subject: [PATCH 10/12] fix typo and minor refactor --- .../server/dto/ValidationResponseBody.java | 133 +++++++++--------- 1 file changed, 68 insertions(+), 65 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java index 4e2077bb9..43e0a6749 100644 --- a/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java +++ b/src/main/java/digital/slovensko/autogram/server/dto/ValidationResponseBody.java @@ -34,46 +34,46 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document var signingCertificate = dd.getCertificateById(dd.getSigningCertificateId(e.getId())); return new Signature( - new Result( - conclusion.getIndication().ordinal(), - conclusion.getIndication().name() - ), - new SignatureInfo( - sr.getSignatureFormat(e.getId()).name(), - format.format(sr.getSigningTime(e.getId())), - !timestamps.isEmpty(), - timestamps.isEmpty() ? null : format.format(sr.getProductionTime(timestamps.get(0).getId())), - new CertificateInfo( - new X500Principal(signingCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), - new X500Principal(signingCertificate.getCertificateDN()).getName(X500Principal.RFC1779), - signingCertificate.getSerialNumber(), - format.format(sr.getSigningTime(e.getId())), - format.format(signingCertificate.getNotBefore()), - format.format(signingCertificate.getNotAfter()), - new Result( - sr.getSignatureQualification(e.getId()).ordinal(), - sr.getSignatureQualification(e.getId()).getReadable() - ) + new Result( + conclusion.getIndication().ordinal(), + conclusion.getIndication().name() ), - timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { - var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); - - return new TimestampCertificateInfo( - new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), - new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), - tsCertificate.getSerialNumber(), - format.format(sr.getProductionTime(t.getId())), - format.format(tsCertificate.getNotBefore()), - format.format(tsCertificate.getNotAfter()), - new Result( - sr.getTimestampQualification(t.getId()).ordinal(), - sr.getTimestampQualification(t.getId()).getReadable() + new SignatureInfo( + sr.getSignatureFormat(e.getId()).name(), + format.format(sr.getSigningTime(e.getId())), + !timestamps.isEmpty(), + timestamps.isEmpty() ? null : format.format(sr.getProductionTime(timestamps.get(0).getId())), + new CertificateInfo( + new X500Principal(signingCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), + new X500Principal(signingCertificate.getCertificateDN()).getName(X500Principal.RFC1779), + signingCertificate.getSerialNumber(), + format.format(sr.getSigningTime(e.getId())), + format.format(signingCertificate.getNotBefore()), + format.format(signingCertificate.getNotAfter()), + new Result( + sr.getSignatureQualification(e.getId()).ordinal(), + sr.getSignatureQualification(e.getId()).getReadable() + ) ), - dd.getTimestampType(t.getId()).name() - ); - }).toList(), - dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() - )); + timestamps.isEmpty() ? null : timestamps.stream().map((t) -> { + var tsCertificate = dd.getCertificateById(dd.getTimestampSigningCertificateId(t.getId())); + + return new TimestampCertificateInfo( + new X500Principal(tsCertificate.getCertificateIssuerDN()).getName(X500Principal.RFC1779), + new X500Principal(tsCertificate.getCertificateDN()).getName(X500Principal.RFC1779), + tsCertificate.getSerialNumber(), + format.format(sr.getProductionTime(t.getId())), + format.format(tsCertificate.getNotBefore()), + format.format(tsCertificate.getNotAfter()), + new Result( + sr.getTimestampQualification(t.getId()).ordinal(), + sr.getTimestampQualification(t.getId()).getReadable() + ), + dd.getTimestampType(t.getId()).name() + ); + }).toList(), + dd.getSignerDocuments(e.getId()).stream().map(SignerDataWrapper::getId).toList() + )); }).toList(); List signedObjects = null; @@ -83,9 +83,9 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document switch (sr.getSignatureFormat(sr.getSignatureIdList().get(0)).getSignatureForm()) { case PAdES: { signedObjects = dd.getAllSignerDocuments().stream().map((d) -> new SignedObject( - d.getId(), - MimeTypeEnum.PDF.getMimeTypeString(), - d.getReferencedName() + d.getId(), + MimeTypeEnum.PDF.getMimeTypeString(), + d.getReferencedName() )).toList(); break; } @@ -101,8 +101,9 @@ public static ValidationResponseBody build(Reports reports, DSSDocument document } if (extractor != null) { - signedObjects = getSignedObjects(extractor.extract().getSignedDocuments(), dd.getAllSignerDocuments()); - unsignedObjects = getUnsignedObjects(extractor.extract().getSignedDocuments(), dd.getAllSignerDocuments()); + var allObjects = extractor.extract().getSignedDocuments(); + signedObjects = getSignedObjects(allObjects, dd.getAllSignerDocuments()); + unsignedObjects = getUnsignedObjects(allObjects, dd.getAllSignerDocuments()); } return new ValidationResponseBody(fileFormat, signatures, signedObjects, unsignedObjects); @@ -124,35 +125,37 @@ private static List getSignedObjects(List docs, List< private static List getUnsignedObjects(List docs, List signedObjects) { return docs.stream().filter((o) -> signedObjects.stream().filter((s) -> o.getName().equals(s.getReferencedName())).toList().isEmpty()).map((generalObject) -> - new UnsignedObject( - generalObject.getMimeType().getMimeTypeString(), - generalObject.getName() - ) + new UnsignedObject( + generalObject.getMimeType().getMimeTypeString(), + generalObject.getName() + ) ).toList(); } -} -record Signature(Result validaitonResult, SignatureInfo signatureInfo) { -} + record Signature(Result validationResult, SignatureInfo signatureInfo) { + } -record Result(int code, String description) { -} + record Result(int code, String description) { + } -record SignatureInfo(String level, String claimedSigningTime, boolean isTimestamped, String timestampSigningTime, - CertificateInfo signingCertificate, List timestamps, - List signedObjectsIds) { -} + record SignatureInfo(String level, String claimedSigningTime, boolean isTimestamped, String timestampSigningTime, + CertificateInfo signingCertificate, List timestamps, + List signedObjectsIds) { + } -record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, String productionTime, String notBefore, - String notAfter, Result qualification) { -} + record CertificateInfo(String issuerDN, String subjectDN, String serialNumber, String productionTime, + String notBefore, + String notAfter, Result qualification) { + } -record TimestampCertificateInfo(String issuerDN, String subjectDN, String serialNumber, String productionTime, String notBefore, - String notAfter, Result qualification, String timestampType) { -} + record TimestampCertificateInfo(String issuerDN, String subjectDN, String serialNumber, String productionTime, + String notBefore, + String notAfter, Result qualification, String timestampType) { + } -record SignedObject(String id, String mimeType, String filename) { -} + record SignedObject(String id, String mimeType, String filename) { + } -record UnsignedObject(String mimeType, String filename) { + record UnsignedObject(String mimeType, String filename) { + } } From a7e36961f25c2e1d5ffc999f3134c83468cd79a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Thu, 7 Dec 2023 13:25:30 +0100 Subject: [PATCH 11/12] minor refactor and add better documentation for validation api --- .../autogram/server/AutogramServer.java | 2 +- .../autogram/server/ValidationEndpoint.java | 7 - .../slovensko/autogram/server/server.yml | 177 ++++++++++++++++-- 3 files changed, 161 insertions(+), 25 deletions(-) diff --git a/src/main/java/digital/slovensko/autogram/server/AutogramServer.java b/src/main/java/digital/slovensko/autogram/server/AutogramServer.java index 26535d58d..0bb38d413 100644 --- a/src/main/java/digital/slovensko/autogram/server/AutogramServer.java +++ b/src/main/java/digital/slovensko/autogram/server/AutogramServer.java @@ -47,7 +47,7 @@ public void start() { .add(new AutogramCorsFilter(List.of("POST", "DELETE"))); // Validate - server.createContext("/validate", new ValidationEndpoint(autogram)).getFilters() + server.createContext("/validate", new ValidationEndpoint()).getFilters() .add(new AutogramCorsFilter(List.of("POST"))); // Start server diff --git a/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java b/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java index 9b5cd4269..e097fa5f3 100644 --- a/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java +++ b/src/main/java/digital/slovensko/autogram/server/ValidationEndpoint.java @@ -3,7 +3,6 @@ import com.google.gson.JsonSyntaxException; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import digital.slovensko.autogram.core.Autogram; import digital.slovensko.autogram.core.SignatureValidator; import digital.slovensko.autogram.core.errors.DocumentNotSignedYetException; import digital.slovensko.autogram.server.dto.ErrorResponse; @@ -17,12 +16,6 @@ import java.util.Base64; public class ValidationEndpoint implements HttpHandler { - private final Autogram autogram; - - public ValidationEndpoint(Autogram autogram) { - this.autogram = autogram; - } - @Override public void handle(HttpExchange exchange) { DSSDocument document = null; diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index 62cc13934..e6c2d0d85 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -318,13 +318,13 @@ paths: post: tags: - Validation - summary: Get signature validation report for a signed document + summary: Get signature validation report for a signed document. Returns 422 if no signatures are present. operationId: validateDocument requestBody: content: application/json: schema: - $ref: "#/components/schemas/ValidateRequestBody" + $ref: "#/components/schemas/ValidationRequestBody" examples: Signed PDF with timestamp: $ref: "#/components/examples/Signed-PDF-TS" @@ -339,6 +339,46 @@ paths: application/json: schema: $ref: "#/components/schemas/ValidationResponseBody" + 422: + description: File could not be validated + content: + application/json: + schema: + type: object + properties: + code: + type: string + enum: + - DOCUMENT_NOT_SIGNED + description: Code that can be used to identify the error. + message: + type: string + example: Document is not signed yet. + description: Human readable error message. + details: + type: string + example: The provided document is not eligible for signature validation because the document is not signed yet. + description: Optional details. + 400: + description: Bad request + content: + application/json: + schema: + type: object + properties: + code: + type: string + enum: + - MALFORMED_INPUT + description: Code that can be used to identify the error. + message: + type: string + example: JsonSyntaxException parsing request body. + description: Human readable error message. + details: + type: string + example: Document to validate is null. + description: Optional details. components: schemas: @@ -608,7 +648,7 @@ components: required: - level - ValidateRequestBody: + ValidationRequestBody: type: object properties: dataB64: @@ -622,29 +662,54 @@ components: fileFormat: type: string enum: - - ASiC-E + - ASiC_E + - ASiC_S - PAdES + - XAdES + description: Format of the validated file. ASiC_E or ASiC_S for an ASiC container. XAdES for standalone XAdES XML file and PAdES for PAdES. + example: ASiC_E signatures: type: array items: type: object properties: - validaitonResult: + validationResult: type: object + description: | + The standard ETSI EN 319 102-1 specifies a complete validation model and procedures for the validation of “AdES digital signatures”, which are implemented in the underlying DSS module. + The validation result can have these values: + + "0 TOTAL_PASSED: indicating that the signature has passed verification and it complies with the signature validation policy" + + "1 TOTAL_FAILED: indicating that either the signature format is incorrect or that the digital signature value fails the verification" + + "2 INDETERMINATE: indicating that the format and digital signature verifications have not failed but there is insufficient information to determine if the electronic signature is valid" properties: code: type: integer + enum: + - 0 + - 1 + - 2 example: 0 description: type: string + enum: + - TOTAL_PASSED + - TOTAL_FAILED + - INDETERMINATE example: TOTAL_PASSED signatureInfo: type: object properties: claimedSigningTime: type: string + description: Claimed signing time based on the signature only. + example: "2022-12-20T21:29:13 +0100" timestampSigningTime: type: string + description: NotAfter signing time based on the first timestamp in signature. + example: "2022-12-20T21:29:13 +0100" level: type: string enum: @@ -660,102 +725,180 @@ components: - CAdES_BASELINE_T - CAdES_BASELINE_LT - CAdES_BASELINE_LTA + description: Signature level of the signature. + example: XAdES_BASELINE_LTA signingCertificate: type: object + description: Signing certificate details. properties: issuerDN: type: string - example: C=SK, L=Bratislava, CN=Disig + description: RFC1779 of the signing certificate issuer name. + example: CN=CA Disig QCA3, OU=ACA-307-2007-2, O=Disig a.s., OID.2.5.4.5=NTRSK-35975946, L=Bratislava, C=SK subjectDN: type: string - example: SERIALNUMBER=PNOSK-1234567890, C=SK, L=Bratislava, SURNAME=Smith, GIVENNAME=John, CN=John Smith - description: + description: RFC1779 of the signing certificate name. + example: C=SK, L=Bratislava, OID.2.5.4.5=NTRSK-30807484, O=Sociálna poisťovňa, CN=Sociálna poisťovňa serialNumber: type: string - example: 12578200234 + description: SerialNumber of the signing certificate. + example: 81308597867087210236466 productionTime: type: string + description: Claimed signing time. example: "2022-12-20T21:29:13 +0100" notBefore: type: string + description: The NotBefore (issuance) time of the signing certificate. + example: "2019-07-03T15:21:51 +0200" notAfter: type: string + description: The NotAfter time of the signing certificate. + example: "2023-07-02T15:21:51 +0200" qualification: type: object + description: Qualification of the signature at validation time. For more info check out Java class eu.europa.esig.dss.enumerations.SignatureQualification properties: code: type: integer example: 1 description: type: string + enum: + - QESIG + - QESEAL + - UNKNOWN_QC_QSCD-QC-QSCD + - ADESIG_QC-QC + - ADESEAL_QC-QC + - UNKNOWN_QC-QC + - ADESIG + - ADESEAL + - UNKNOWN + - INDETERMINATE_QESIG + - INDETERMINATE_QESEAL + - INDETERMINATE_UNKNOWN_QC_QSCD + - INDETERMINATE_ADESIG_QC + - INDETERMINATE_ADESEAL_QC + - INDETERMINATE_UNKNOWN_QC + - INDETERMINATE_ADESIG + - INDETERMINATE_ADESEAL + - INDETERMINATE_UNKNOWN + - NOT_ADES_QC_QSCD + - NOT_ADES_QC + - NOT_ADES + - NA example: QESeal isTimestamped: type: boolean + description: Boolean indicating if the signature has any timestamp. example: true timestamps: type: array + description: List of timestamps on the signature. items: type: object properties: issuerDN: type: string - example: C=SK,O=Ditec\, a.s.,CN=TS Signer + description: RFC1779 of the timestamp certificate issuer name. + example: CN=SNCA4, O=Narodna agentura pre sietove a elektronicke sluzby, OID.2.5.4.97=NTRSK-42156424, OU=SNCA, C=SK subjectDN: type: string - example: C=SK,O=Ditec\, a.s.,CN=TS Signer - description: + description: RFC1779 of the timestamp certificate name. + example: CN=NASES Time Stamp Authority 2, O=Národná agentúra pre sieťové a elektronické služby, OID.2.5.4.97=NTRSK-42156424, OU=SNCA, C=SK serialNumber: type: string - example: 12578200234 + description: SerialNumber of the timestamp certificate. + example: 21220574739238913835018 productionTime: type: string + description: ProductionTime of the timestamp. example: "2022-12-20T21:29:13 +0100" notBefore: type: string + description: The NotBefore (issuance) time of the timestamp certificate. + example: "2021-04-15T13:31:24 +0200" notAfter: type: string + description: The NotAfter time of the timestamp certificate. + example: "2026-04-14T13:31:24 +0200" qualification: type: object + description: | + Qualification status fo the timestamp at validation time. + + QTSA - Qualified timestamp" - "urn:cef:dss:timestampQualification:QTSA" + + TSA - Not qualified timestamp" - "urn:cef:dss:timestampQualification:TSA" + + NA - Not applicable" - "urn:cef:dss:timestampQualification:notApplicable" properties: code: type: integer + enum: + - 0 + - 1 + - 2 example: 0 description: type: string + enum: + - QTSA + - TSA + - NA example: QTSA timestampType: type: string - example: ARCHIVE_TIMESTAMP + enum: + - CONTENT_TIMESTAMP + - ALL_DATA_OBJECTS_TIMESTAMP + - INDIVIDUAL_DATA_OBJECTS_TIMESTAMP + - SIGNATURE_TIMESTAMP + - VRI_TIMESTAMP + - VALIDATION_DATA_REFSONLY_TIMESTAMP + - VALIDATION_DATA_TIMESTAMP + - DOCUMENT_TIMESTAMP + - ARCHIVE_TIMESTAMP + description: Type of the timestamp + example: SIGNATURE_TIMESTAMP signedObjectsIds: type: array items: type: string + description: List of IDs referencing files this signature have signed. example: "D-CE70D85E47F41DE68616A3695FE7569BF8F7409F052B74AE0356663393A68D8A" signedObjects: type: array + description: List of files in the container that are signed by at least one signature items: type: object properties: id: type: string + description: ID of the file used to reference the file in signatures example: "D-CE70D85E47F41DE68616A3695FE7569BF8F7409F052B74AE0356663393A68D8A" mimeType: type: string + description: MimeType of the file example: text/xml filename: type: string - example: document.xdcf + description: Filename of the file in the container. If the validated document is PAdES or standalone XAdES where filename is unknown, this attribute should be ignored. + example: form.xml unsignedObjects: type: array + description: List of files in the container that have not been referenced in any signature yet items: type: object properties: mimeType: type: string - example: text/xml + description: MimeType of the file + example: application/pdf filename: type: string - example: scam.xdcf + description: Filename of the file in the container. If the validated document is PAdES or standalone XAdES where filename is unknown, this attribute should be ignored. + example: Some_unsigned_document.pdf examples: XAdES-XML-Base64-HTML_md: From 3d9d388012ec65b1a6b85ad670eb7cadaf4de7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ce=C4=BEuch?= Date: Thu, 7 Dec 2023 13:55:25 +0100 Subject: [PATCH 12/12] add validation api xdc disclaimer and update api version --- .../resources/digital/slovensko/autogram/server/server.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/digital/slovensko/autogram/server/server.yml b/src/main/resources/digital/slovensko/autogram/server/server.yml index e6c2d0d85..6268d7642 100644 --- a/src/main/resources/digital/slovensko/autogram/server/server.yml +++ b/src/main/resources/digital/slovensko/autogram/server/server.yml @@ -13,7 +13,7 @@ info: license: name: EUPL url: https://github.com/slovensko-digital/autogram/blob/main/LICENSE - version: 1.99.9 + version: 2.1.3 servers: - url: http://localhost:37200 - url: / @@ -318,7 +318,7 @@ paths: post: tags: - Validation - summary: Get signature validation report for a signed document. Returns 422 if no signatures are present. + summary: Get signature validation report for a signed document. Returns 422 if no signatures are present. Does not validate the inner structure of XML Datacontainer files. operationId: validateDocument requestBody: content: