-
Notifications
You must be signed in to change notification settings - Fork 20
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
Signature validation #214
Changes from 19 commits
b10b052
3989444
8d1b71b
3ebcfdc
e92c66b
1c85504
0ec71a1
7f8719a
f2fb6fd
ced89e3
279fd7c
8e4ac87
0a250ba
61e98f1
3124176
b263d31
f30af12
841863e
313e6c7
291bae8
ef9f753
2f1409d
85aabd6
1b72673
8bd7dac
da2751b
f57efb9
16f8436
53706b1
76c4e97
6a169e3
a47071a
ce00801
1b47fe8
00a0c12
0059573
1d8de25
add857a
720f90b
a07bad3
2a73536
de6b120
58b7858
786e1e9
a8d0473
12e0e20
89f46b6
ee060cc
29fdd7e
63915fe
b3cdf79
2ea0ad3
2a80afb
cc0c4bc
80fca42
514e1b5
3310fd0
19e37cc
4b00f88
340f8fb
2ffd3bb
6420582
8b28106
ea1b206
a1b7f6a
e2855d2
e743554
74a857e
4dcf4e6
2fc3a0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,4 +25,6 @@ whitelabel.iml | |
.java-version | ||
secret | ||
|
||
/fakeTokenDriver | ||
cache/ | ||
|
||
/fakeTokenDriver |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
|
@@ -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()); | ||
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) { | ||
|
@@ -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 | ||
*/ | ||
|
@@ -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 | ||
*/ | ||
|
@@ -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) { | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Toto robi co? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Každých 8 hodín behu Autogramu refreshne Trusted Listy |
||
} | ||
} |
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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()); | ||
|
||
|
@@ -31,6 +32,8 @@ public void start(Stage windowStage) throws Exception { | |
|
||
windowStage.setOnCloseRequest(event -> { | ||
new Thread(server::stop).start(); | ||
timer.cancel(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||
}); | ||
|
There was a problem hiding this comment.
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)