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 19 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
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
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
cache/

/fakeTokenDriver
43 changes: 39 additions & 4 deletions src/main/java/digital/slovensko/autogram/core/Autogram.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import eu.europa.esig.dss.pdfa.PDFAStructureValidator;

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Consumer;

public class Autogram {
Expand All @@ -31,7 +33,23 @@ public Autogram(UI ui, DriverDetector driverDetector) {
}

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

public void checkAndValidateSignatures(SigningJob job) {
ui.onWorkThreadDo(() -> checkSignatures(job));

if (!SignatureValidator.hasSignatures(job.getDocument()))
return;

var reports = SignatureValidator.getInstance().getSignatureValidationReport(job.getDocument());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nedava zmysel, aby nas validator dostal job a vratil uz ten ValidationReports? (Wrapper by som dropol, ved to je nas namespace)

ui.onUIThreadDo(() -> ui.onSignatureValidationCompleted(new ValidationReportsWrapper(reports, job)));
}

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

public void checkPDFACompliance(SigningJob job) {
Expand Down Expand Up @@ -77,7 +95,7 @@ public void sign(SigningJob job, SigningKey signingKey) {

/**
* Starts a batch - ask user - get signing key - start batch - return batch ID
*
*
* @param totalNumberOfDocuments - expected number of documents to be signed
* @param responder - callback for http response
*/
Expand All @@ -95,7 +113,7 @@ public void batchStart(int totalNumberOfDocuments, BatchResponder responder) {

/**
* Sign a single document
*
*
* @param job
* @param batchId - current batch ID, used to authenticate the request
*/
Expand All @@ -111,7 +129,7 @@ public void batchSign(SigningJob job, String batchId) {

/**
* End the batch
*
*
* @param batchId - current batch ID, used to authenticate the request
*/
public boolean batchEnd(String batchId) {
Expand Down Expand Up @@ -171,4 +189,21 @@ public void onDocumentBatchSaved(BatchUiResult result) {
public void onSigningFailed(AutogramException e) {
ui.onUIThreadDo(() -> ui.onSigningFailed(e));
}

public Timer initializeSignatureValidator() {
ui.onWorkThreadDo(() -> {
SignatureValidator.getInstance().initialize();
});

var timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
SignatureValidator.getInstance().refresh();
}
},
3600000L,
3600000L);

return timer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Toto robi co?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Každých 8 hodín behu Autogramu refreshne Trusted Listy

}
}
175 changes: 175 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,175 @@
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.Executors;

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.DSSDocument;
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);
return docValidator.validateDocument();
}

public synchronized void refresh() {
System.out.println("Refreshing signature validator...");
validationJob.offlineRefresh();
System.out.println("Signature validator refreshed");
}

public synchronized void initialize() {
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"), "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(Executors.newFixedThreadPool(4));
validationJob.setDebug(true);

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 Reports getSignatureValidationReport(DSSDocument document) {
return validate(createDocumentValidator(document));
}

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 Reports getSignatureCheckReport(DSSDocument document) {
var validator = createDocumentValidator(document);
if (validator == null)
return null;

validator.setCertificateVerifier(new CommonCertificateVerifier());
return validator.validateDocument();
}

public static boolean hasSignatures(DSSDocument document) {
var signatureCheckReport = getSignatureCheckReport(document);
return signatureCheckReport != null && signatureCheckReport.getSimpleReport().getSignaturesCount() > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package digital.slovensko.autogram.core;

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

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

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

public Reports getReports() {
return reports;
}

public SigningJob getSigningJob() {
return signingJob;
}
}
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,5 +48,9 @@ public interface UI {

void onPDFAComplianceCheckFailed(SigningJob job);

public void onSignatureValidationCompleted(ValidationReportsWrapper wrapper);

public void onSignatureCheckCompleted(ValidationReportsWrapper wrapper);

void showIgnorableExceptionDialog(IgnorableException exception);
}
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.ValidationReportsWrapper;
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 @@ -209,6 +210,15 @@ public void showVisualization(Visualization visualization, Autogram autogram) {
}

@Override
public void onSignatureValidationCompleted(ValidationReportsWrapper wrapper) {

}

@Override
public void onSignatureCheckCompleted(ValidationReportsWrapper wrapper) {

}

public void showIgnorableExceptionDialog(IgnorableException exception) {
throw exception;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package digital.slovensko.autogram.ui.gui;

import java.io.PrintWriter;
import java.io.StringWriter;

import digital.slovensko.autogram.core.errors.AutogramException;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/digital/slovensko/autogram/ui/gui/GUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import digital.slovensko.autogram.core.Batch;
import digital.slovensko.autogram.core.SigningJob;
import digital.slovensko.autogram.core.SigningKey;
import digital.slovensko.autogram.core.ValidationReportsWrapper;
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.core.errors.NoDriversDetectedException;
import digital.slovensko.autogram.core.errors.NoKeysDetectedException;
Expand Down Expand Up @@ -47,6 +48,9 @@ public GUI(HostServices hostServices) {

@Override
public void startSigning(SigningJob job, Autogram autogram) {
onWorkThreadDo(()
-> autogram.checkAndValidateSignatures(job));

autogram.startVisualization(job);
}

Expand Down Expand Up @@ -236,6 +240,17 @@ public void onPDFAComplianceCheckFailed(SigningJob job) {
}

@Override
public void onSignatureValidationCompleted(ValidationReportsWrapper wrapper) {
var controller = jobControllers.get(wrapper.getSigningJob());
controller.onSignatureValidationCompleted(wrapper.getReports());
}

@Override
public void onSignatureCheckCompleted(ValidationReportsWrapper wrapper) {
var controller = jobControllers.get(wrapper.getSigningJob());
controller.onSignatureCheckCompleted(wrapper.getReports());
}

public void showVisualization(Visualization visualization, Autogram autogram) {
var controller = new SigningDialogController(visualization, autogram, this);
jobControllers.put(visualization.getJob(), controller);
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/digital/slovensko/autogram/ui/gui/GUIApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public void start(Stage windowStage) throws Exception {

Platform.setImplicitExit(false);
autogram.checkForUpdate();
var timer = autogram.initializeSignatureValidator();

setUserAgentStylesheet(getClass().getResource("idsk.css").toExternalForm());

Expand All @@ -31,6 +32,8 @@ public void start(Stage windowStage) throws Exception {

windowStage.setOnCloseRequest(event -> {
new Thread(server::stop).start();
timer.cancel();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Toto mi pride ako prilisna vnutornost. Nemal by mat mat autogram len nejaky close kde sa toto udeje?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tomuto nerozumiem. Vytvára a zastavuje sa to na rovnakom mieste ako server. To nie je ok?

System.out.println("Closing application");
celuchmarek marked this conversation as resolved.
Show resolved Hide resolved

Platform.exit();
});
Expand Down
Loading