Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signature validation #214

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
b10b052
wip validation
celuchmarek Jun 28, 2023
3989444
wip validation
celuchmarek Jul 4, 2023
8d1b71b
add wip signature check and validation visualization
celuchmarek Jul 12, 2023
3ebcfdc
gui major update
celuchmarek Jul 14, 2023
e92c66b
rm unintentedly commited files
celuchmarek Jul 14, 2023
1c85504
fix ocsp header and add lotl certs
celuchmarek Jul 19, 2023
0ec71a1
minor refactor
celuchmarek Jul 19, 2023
7f8719a
refactor and speed up
celuchmarek Jul 26, 2023
f2fb6fd
Merge remote-tracking branch 'slovensko-digital/main' into AG-85/sign…
celuchmarek Jul 26, 2023
ced89e3
make SignatureValidation Singleton
celuchmarek Jul 26, 2023
279fd7c
detect signature qualification and render the results
celuchmarek Aug 1, 2023
8e4ac87
merge main
celuchmarek Aug 1, 2023
0a250ba
add ts qualification check
celuchmarek Aug 2, 2023
61e98f1
validate api wip
celuchmarek Aug 3, 2023
3124176
revert addition of validaiton api
celuchmarek Aug 3, 2023
b263d31
refactor
celuchmarek Aug 10, 2023
f30af12
rm unwanted file
celuchmarek Aug 10, 2023
841863e
fix resource packaging and small fixes
celuchmarek Aug 17, 2023
313e6c7
refactor
celuchmarek Aug 17, 2023
291bae8
rm server.yml from PR
celuchmarek Aug 18, 2023
ef9f753
refactor
celuchmarek Aug 18, 2023
2f1409d
use separate template for signature details html
celuchmarek Aug 18, 2023
85aabd6
change certs cache dir to users home
celuchmarek Aug 18, 2023
1b72673
fix application closing with System.exit 0
celuchmarek Aug 18, 2023
8bd7dac
Merge branch 'main' into AG-85/signature-validation
celuchmarek Aug 18, 2023
da2751b
use executor services for validator and server; fix visualization to …
celuchmarek Aug 18, 2023
f57efb9
update .gitignore
celuchmarek Aug 18, 2023
16f8436
merge main
celuchmarek Aug 18, 2023
53706b1
validaiton gui without nice badges
celuchmarek Aug 24, 2023
76c4e97
WIP signature list in signing dialog
celuchmarek Aug 25, 2023
6a169e3
lenghtier validation GUI update
celuchmarek Sep 6, 2023
a47071a
Merge branch 'main' into AG-85/signature-validation
celuchmarek Sep 12, 2023
ce00801
add signature table to invaldi signatures dialog and refactor css
celuchmarek Sep 12, 2023
1b47fe8
move static component builders to GUIValidationUtils from controllers
celuchmarek Sep 12, 2023
00a0c12
rm magic numbers from styles in code and refactor styles
celuchmarek Sep 12, 2023
0059573
refactor GUIApp::stop
celuchmarek Sep 12, 2023
1d8de25
minor refactor gui classes
celuchmarek Sep 12, 2023
add857a
rm forgotten print
celuchmarek Sep 13, 2023
720f90b
reword main menu label
celuchmarek Sep 13, 2023
a07bad3
mention signature validation in about autogram
celuchmarek Sep 13, 2023
2a73536
fix signature type in present signatures
celuchmarek Sep 13, 2023
de6b120
Merge branch 'main' into AG-85/signature-validation
celuchmarek Sep 14, 2023
58b7858
cancel singing on validation modals cancel
celuchmarek Sep 14, 2023
786e1e9
dont cnacel signing on validation warning close
celuchmarek Sep 14, 2023
a8d0473
do not wait for validator initialization on unsigned documents
celuchmarek Sep 14, 2023
12e0e20
use document name in signing dialog and review the unsupported vis me…
celuchmarek Sep 14, 2023
89f46b6
reword signature table heading in signing dialog
celuchmarek Sep 14, 2023
ee060cc
wrap name text in signature table
celuchmarek Sep 14, 2023
29fdd7e
mv scrollbar out of simple report iframe and make timestamp details u…
celuchmarek Sep 14, 2023
63915fe
do not consider unknown signature as invalid
celuchmarek Sep 19, 2023
b3cdf79
make timestamp details unfolded by default
celuchmarek Sep 19, 2023
2ea0ad3
check TS validity
celuchmarek Sep 20, 2023
2a80afb
unknow signature yellow badge
celuchmarek Sep 21, 2023
cc0c4bc
fix multiple badges in signature and make left column wider
celuchmarek Sep 21, 2023
80fca42
consider offline validaiton cases
celuchmarek Sep 21, 2023
514e1b5
minor refactor and cleanup
celuchmarek Sep 21, 2023
3310fd0
mv link from whoSigned to a new link button
celuchmarek Sep 21, 2023
19e37cc
only show validation section when signatures are present
celuchmarek Sep 22, 2023
4b00f88
wip TableView
celuchmarek Sep 27, 2023
340f8fb
max N signatures in table
celuchmarek Sep 28, 2023
2ffd3bb
refactor hidden signatures and make tags wrap text
celuchmarek Sep 28, 2023
6420582
do not set multiple badge wrapping width in signing dialog signature …
celuchmarek Sep 28, 2023
8b28106
Minor refactorings, text tuning a bugfixes
jsuchal Sep 29, 2023
ea1b206
Minor wording fix
jsuchal Sep 29, 2023
a1b7f6a
fix present signatures screen width
celuchmarek Sep 29, 2023
e2855d2
fix signatures controller rendering
celuchmarek Sep 29, 2023
e743554
Readme tuning
jsuchal Sep 29, 2023
74a857e
Merge remote-tracking branch 'origin/AG-85/signature-validation' into…
jsuchal Sep 29, 2023
4dcf4e6
rm debug label
celuchmarek Sep 29, 2023
2fc3a0d
add signature validation to release checklist
celuchmarek Sep 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/mvnw @slovensko-digital/autogram-release-team
/mvnw.cmd @slovensko-digital/autogram-release-team
/.mvn @slovensko-digital/autogram-release-team
/src/main/resources/digital/slovensko/autogram/core/lotlKeyStore.p12 @slovensko-digital/autogram-release-team
15 changes: 12 additions & 3 deletions .github/ISSUE_TEMPLATE/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,30 @@ labels: release
- [ ] funguje spustenie v GUI móde
- [ ] funguje URL handler [autogram://go](autogram://go)
- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
- [ ] funguje CLI `autogram --help`
- [ ] podpísaný súbor otvorím v autograme a pod náhľadom dokumentu je zobrazený môj podpis
- [ ] kliknem na "Zobraziť detail podpisov" a otvorí sa detail podpisov
- [ ] kliknem na "Zobraziť technické detaily" a otvorí sa report
- [ ] funguje CLI `autogram --help`

## Linux
- [ ] funguje inštalácia na Linux (Debian-based) cez stiahnutý .deb
- [ ] funguje inštalácia na Linux cez stiahnutý .rpm
- [ ] funguje spustenie v GUI móde
- [ ] funguje URL handler [autogram://go](autogram://go)
- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
- [ ] funguje CLI `autogram --help`
- [ ] podpísaný súbor otvorím v autograme a pod náhľadom dokumentu je zobrazený môj podpis
- [ ] kliknem na "Zobraziť detail podpisov" a otvorí sa detail podpisov
- [ ] kliknem na "Zobraziť technické detaily" a otvorí sa report
- [ ] funguje CLI `autogram --help`

## MacOS
- [ ] funguje inštalácia na MacOS cez stiahnutý .pkg
- [ ] funguje spustenie v GUI móde
- [ ] funguje URL handler [autogram://go](autogram://go)
- [ ] funguje GUI otvoriť jeden súbor, ten sa zobrazí, viem ho podpísať, vytvorí sa podpísaný súbor
- [ ] podpísaný súbor otvorím v autograme a pod náhľadom dokumentu je zobrazený môj podpis
- [ ] kliknem na "Zobraziť detail podpisov" a otvorí sa detail podpisov
- [ ] kliknem na "Zobraziť technické detaily" a otvorí sa report
- [ ] funguje CLI `/Applications/Autogram.app/Contents/MacOS/AutogramApp --help`


Expand All @@ -39,4 +48,4 @@ labels: release
- [ ] funguje API info request
- [ ] funguje API docs request
- [ ] funguje API sign request
- [ ] funguje s [extension](https://github.com/slovensko-digital/autogram-extension)
- [ ] funguje s [extension](https://github.com/slovensko-digital/autogram-extension)
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ whitelabel.iml
.java-version
secret

/fakeTokenDriver
.autogram

/fakeTokenDriver
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogram

Autogram je multi-platformová (Windows, MacOS, Linux) desktopová JavaFX aplikácia, ktorá slúži na podpisovanie dokumentov v súlade s európskym nariadením eIDAS. Používateľ ňou môže podpisovať súbory priamo alebo je možné aplikáciu jednoducho zaintegrovať do vlastného (webového) informačného systému pomocou HTTP API. Podpisovanie je možné spúšať aj z príkazového riadku, čo je vhodné pre hromadné podpisovanie veľkého množstva súborov naraz.
Autogram je multi-platformová (Windows, MacOS, Linux) desktopová JavaFX aplikácia, ktorá slúži na podpisovanie a overovanie dokumentov v súlade s európskym nariadením eIDAS. Používateľ ňou môže podpisovať súbory priamo alebo je možné aplikáciu jednoducho zaintegrovať do vlastného (webového) informačného systému pomocou HTTP API. Podpisovanie je možné spúšať aj z príkazového riadku, čo je vhodné pre hromadné podpisovanie veľkého množstva súborov naraz.

**Inštalačné balíky pre Windows, MacOS a Linux sú dostupné v časti [Releases](https://github.com/slovensko-digital/autogram/releases).** Na použitie na existujúcich štátnych weboch bude potrebné doinštalovať aj [rozšírenie do prehliadača](https://github.com/slovensko-digital/autogram-extension#readme).

Expand Down
Binary file modified assets/autogram-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 28 additions & 1 deletion src/main/java/digital/slovensko/autogram/core/Autogram.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import eu.europa.esig.dss.pdfa.PDFAStructureValidator;

import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;

public class Autogram {
Expand Down Expand Up @@ -42,7 +44,23 @@ public Autogram(UI ui, DriverDetector driverDetector, Integer slotId) {
}

public void sign(SigningJob job) {
ui.onUIThreadDo(() -> ui.startSigning(job, this));
ui.onUIThreadDo(()
-> ui.startSigning(job, this));
}

public void checkAndValidateSignatures(SigningJob job) {
checkSignatures(job);

var reports = SignatureValidator.getInstance().getSignatureValidationReport(job);
if (!reports.haveSignatures())
return;

ui.onUIThreadDo(() -> ui.onSignatureValidationCompleted(reports));
}

private void checkSignatures(SigningJob job) {
var reports = SignatureValidator.getSignatureCheckReport(job);
ui.onUIThreadDo(() -> ui.onSignatureCheckCompleted(reports));
}

public void checkPDFACompliance(SigningJob job) {
Expand Down Expand Up @@ -189,4 +207,13 @@ public void onDocumentBatchSaved(BatchUiResult result) {
public void onSigningFailed(AutogramException e) {
ui.onUIThreadDo(() -> ui.onSigningFailed(e));
}

public void initializeSignatureValidator(ScheduledExecutorService scheduledExecutorService, ExecutorService cachedExecutorService) {
ui.onWorkThreadDo(() -> {
SignatureValidator.getInstance().initialize(cachedExecutorService);
});

scheduledExecutorService.scheduleAtFixedRate(() -> SignatureValidator.getInstance().refresh(),
480, 480, java.util.concurrent.TimeUnit.MINUTES);
}
}
178 changes: 178 additions & 0 deletions src/main/java/digital/slovensko/autogram/core/SignatureValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package digital.slovensko.autogram.core;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.service.crl.OnlineCRLSource;
import eu.europa.esig.dss.service.http.commons.CommonsDataLoader;
import eu.europa.esig.dss.service.http.commons.FileCacheDataLoader;
import eu.europa.esig.dss.service.ocsp.OnlineOCSPSource;
import eu.europa.esig.dss.spi.tsl.TrustedListsCertificateSource;
import eu.europa.esig.dss.spi.x509.CertificateSource;
import eu.europa.esig.dss.spi.x509.KeyStoreCertificateSource;
import eu.europa.esig.dss.tsl.function.OfficialJournalSchemeInformationURI;
import eu.europa.esig.dss.tsl.function.TLPredicateFactory;
import eu.europa.esig.dss.tsl.job.TLValidationJob;
import eu.europa.esig.dss.tsl.source.LOTLSource;
import eu.europa.esig.dss.tsl.sync.ExpirationAndSignatureCheckStrategy;
import eu.europa.esig.dss.validation.CertificateVerifier;
import eu.europa.esig.dss.validation.CommonCertificateVerifier;
import eu.europa.esig.dss.validation.SignedDocumentValidator;
import eu.europa.esig.dss.validation.reports.Reports;

import static digital.slovensko.autogram.util.DSSUtils.*;

public class SignatureValidator {
private static final String LOTL_URL = "https://ec.europa.eu/tools/lotl/eu-lotl.xml";
private static final String OJ_URL = "https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv:OJ.C_.2019.276.01.0001.01.ENG";
private CertificateVerifier verifier;
private TLValidationJob validationJob;
private static Logger logger = LoggerFactory.getLogger(SignatureValidator.class);

// Singleton
private static SignatureValidator instance;

private SignatureValidator() {
}

public synchronized static SignatureValidator getInstance() {
if (instance == null)
instance = new SignatureValidator();

return instance;
}

public synchronized Reports validate(SignedDocumentValidator docValidator) {
docValidator.setCertificateVerifier(verifier);

// TODO: do not print stack trace inside DSS
return docValidator.validateDocument();
}

public synchronized void refresh() {
validationJob.offlineRefresh();
}

public synchronized void initialize(ExecutorService executorService) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
logger.debug("Initializing signature validator at {}", formatter.format(new Date()));

validationJob = new TLValidationJob();

var lotlSource = new LOTLSource();
lotlSource.setCertificateSource(getJournalCertificateSource());
lotlSource.setSigningCertificatesAnnouncementPredicate(new OfficialJournalSchemeInformationURI(OJ_URL));
lotlSource.setUrl(LOTL_URL);
lotlSource.setPivotSupport(true);
lotlSource.setTlPredicate(TLPredicateFactory.createEUTLCountryCodePredicate("SK", "CZ", "AT", "HU", "PL"));

var targetLocation = Path.of(System.getProperty("user.dir"), ".autogram", "cache", "certs").toFile();
targetLocation.mkdirs();

var offlineFileLoader = new FileCacheDataLoader();
offlineFileLoader.setCacheExpirationTime(21600000);
offlineFileLoader.setDataLoader(new CommonsDataLoader());
offlineFileLoader.setFileCacheDirectory(targetLocation);
validationJob.setOfflineDataLoader(offlineFileLoader);

var onlineFileLoader = new FileCacheDataLoader();
onlineFileLoader.setCacheExpirationTime(0);
onlineFileLoader.setDataLoader(new CommonsDataLoader());
onlineFileLoader.setFileCacheDirectory(targetLocation);
validationJob.setOnlineDataLoader(onlineFileLoader);

var trustedListCertificateSource = new TrustedListsCertificateSource();
validationJob.setTrustedListCertificateSource(trustedListCertificateSource);
validationJob.setListOfTrustedListSources(lotlSource);
validationJob.setSynchronizationStrategy(new ExpirationAndSignatureCheckStrategy());
validationJob.setExecutorService(executorService);
validationJob.setDebug(false);

logger.debug("Starting signature validator offline refresh");
validationJob.offlineRefresh();

verifier = new CommonCertificateVerifier();
verifier.setTrustedCertSources(trustedListCertificateSource);
verifier.setCrlSource(new OnlineCRLSource());
verifier.setOcspSource(new OnlineOCSPSource());

logger.debug("Signature validator initialized at {}", formatter.format(new Date()));
}

private CertificateSource getJournalCertificateSource() throws AssertionError {
try {
var keystore = getClass().getResourceAsStream("lotlKeyStore.p12");
return new KeyStoreCertificateSource(keystore, "PKCS12", "dss-password");

} catch (DSSException | NullPointerException e) {
throw new AssertionError("Cannot load LOTL keystore", e);
}
}

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);
}

public static String getSignatureValidationReportHTML(Reports signatureValidationReport) {
var builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);

try {
var document = builderFactory.newDocumentBuilder().parse(new InputSource(new StringReader(signatureValidationReport.getXmlSimpleReport())));
var xmlSource = new DOMSource(document);

var xsltFile = SignatureValidator.class.getResourceAsStream("simple-report-bootstrap4.xslt");
var xsltSource = new StreamSource(xsltFile);

var outputTarget = new StreamResult(new StringWriter());
var transformer = TransformerFactory.newInstance().newTransformer(xsltSource);
transformer.transform(xmlSource, outputTarget);

var r = outputTarget.getWriter().toString().trim();

var templateFile = SignatureValidator.class.getResourceAsStream("simple-report-template.html");
var templateString = new String(templateFile.readAllBytes());
return templateString.replace("{{content}}", r);

} catch (SAXException | IOException | ParserConfigurationException | TransformerException e) {
return "Error transforming validation report";
}
}

public static ValidationReports getSignatureCheckReport(SigningJob job) {
var validator = createDocumentValidator(job.getDocument());
if (validator == null)
return new ValidationReports(null, job);

validator.setCertificateVerifier(new CommonCertificateVerifier());
return new ValidationReports(validator.validateDocument(), job);
}

public synchronized boolean areTLsLoaded() {
// TODO: consider validation turned off as well
return validationJob.getSummary().getNumberOfProcessedTLs() > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package digital.slovensko.autogram.core;

import eu.europa.esig.dss.validation.reports.Reports;

public class ValidationReports {
private final Reports reports;
private final SigningJob signingJob;

public ValidationReports(Reports reports, SigningJob signingJob) {
this.reports = reports;
this.signingJob = signingJob;
}

public Reports getReports() {
return reports;
}

public SigningJob getSigningJob() {
return signingJob;
}

public boolean haveSignatures() {
return reports != null && reports.getSimpleReport().getSignaturesCount() > 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.security.KeyStore;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
Expand All @@ -25,9 +24,10 @@ public class AutogramServer {
private final HttpServer server;
private final Autogram autogram;

public AutogramServer(Autogram autogram, String hostname, int port, boolean isHttps) {
public AutogramServer(Autogram autogram, String hostname, int port, boolean isHttps, ExecutorService executorService) {
this.autogram = autogram;
this.server = buildServer(hostname, port, isHttps);
this.server.setExecutor(executorService);
}

public void start() {
Expand All @@ -47,7 +47,6 @@ public void start() {
.add(new AutogramCorsFilter(List.of("POST", "DELETE")));

// Start server
server.setExecutor(Executors.newCachedThreadPool());
server.start();
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/digital/slovensko/autogram/ui/UI.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public interface UI {

void onPDFAComplianceCheckFailed(SigningJob job);

public void onSignatureValidationCompleted(ValidationReports reports);

public void onSignatureCheckCompleted(ValidationReports reports);

void showIgnorableExceptionDialog(IgnorableException exception);

void showError(AutogramException exception);
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/digital/slovensko/autogram/ui/cli/CliUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import digital.slovensko.autogram.core.SigningJob;
import digital.slovensko.autogram.core.SigningKey;
import digital.slovensko.autogram.core.Updater;
import digital.slovensko.autogram.core.ValidationReports;
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.core.errors.FunctionCanceledException;
import digital.slovensko.autogram.core.errors.InitializationFailedException;
Expand Down Expand Up @@ -210,6 +211,15 @@ public void showVisualization(Visualization visualization, Autogram autogram) {
}

@Override
public void onSignatureValidationCompleted(ValidationReports reports) {

}

@Override
public void onSignatureCheckCompleted(ValidationReports reports) {

}

public void showIgnorableExceptionDialog(IgnorableException exception) {
throw exception;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public void hideVisualization() {
}

public void disableKeyPicking() {
chooseKeyButton.setText("Načítavam certifikáty...");
chooseKeyButton.setText("Načítavam certifikáty");
chooseKeyButton.setDisable(true);
}

Expand Down
Loading