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

feat: sign protected pdfs #498

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ff84ad3
feat: initial implementation of signing protected pdfs
xhyrom Aug 19, 2024
6d00beb
feat: allow protected pdfs in batch
xhyrom Aug 19, 2024
84a311f
fix: proxy asic original document instead casting
xhyrom Aug 19, 2024
6eed34d
fix: tests
xhyrom Aug 19, 2024
aad94b7
fix: run callback, default pass
xhyrom Aug 20, 2024
bb7c5a1
fix: check if creating signature field is possible
xhyrom Aug 20, 2024
acb36d4
refactor: change place where we're asking for password
xhyrom Aug 21, 2024
11ef8b9
fix: tests
xhyrom Aug 22, 2024
553dd41
refactor: autogram document, split passwords
xhyrom Sep 2, 2024
9d07e15
add comment explaining PDF/A encryption
xhyrom Sep 2, 2024
83bd4df
feat: show document name while unlocking
xhyrom Sep 2, 2024
e74a283
fix: tests
xhyrom Sep 3, 2024
065f674
fix: pasword controller constructor
xhyrom Sep 25, 2024
56cc58a
fix: document name can be empty
xhyrom Sep 25, 2024
2778cb2
fix: handle protected pdf in sign endpoint before validation
xhyrom Sep 25, 2024
f8a4f77
fix: password dialog
xhyrom Oct 9, 2024
a8770c1
refactor: AutogramDocument#getDSSDocument
xhyrom Oct 9, 2024
a8a2b5d
refactor: remove Autogram from buildFromRequest
xhyrom Oct 9, 2024
cf8473c
fix: PDF/A compliant check
xhyrom Oct 9, 2024
ff4eca7
refactor: wrap to hasOpenDocumentPassword
xhyrom Oct 9, 2024
3c03aef
fix: errors
xhyrom Oct 9, 2024
2940dff
style: change invalid pass subheading
xhyrom Oct 9, 2024
762b7b9
fix :tests
xhyrom Oct 13, 2024
72d3337
revert: revert some modifiers
xhyrom Oct 13, 2024
39279d1
refactor(AutogramDocument): dont use this
xhyrom Oct 26, 2024
d6660e1
mv buildSigningJobFromFile to Autogram
celuchmarek Nov 15, 2024
0a38cb2
Revert "mv buildSigningJobFromFile to Autogram"
celuchmarek Nov 15, 2024
1a51bca
Revert "Revert "mv buildSigningJobFromFile to Autogram""
celuchmarek Nov 16, 2024
d936977
lock ubuntu runner version
celuchmarek Nov 16, 2024
6f99e22
bump gh actions
celuchmarek Nov 16, 2024
a5d5899
temp rm cache from gh actions
celuchmarek Nov 16, 2024
7d94479
bump jdk minor version
celuchmarek Nov 16, 2024
1cc94b5
use system jdk in github actions
celuchmarek Nov 16, 2024
6c15d52
use debug logging in gh actions
celuchmarek Nov 16, 2024
e587086
commit classpath
celuchmarek Nov 16, 2024
5a1a71e
add full stack trace gh actions
celuchmarek Nov 16, 2024
0df85f7
ignore classpath
celuchmarek Nov 16, 2024
b846984
Merge branch 'main' into feat/sign-protected-pdfs
celuchmarek Nov 16, 2024
eebceae
Revert "use system jdk in github actions"
celuchmarek Nov 16, 2024
5266922
revert liberica version
celuchmarek Nov 16, 2024
c064992
fix compilation error
celuchmarek Nov 17, 2024
a0a9e42
make test pipeline faster
celuchmarek Nov 17, 2024
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
20 changes: 13 additions & 7 deletions src/main/java/digital/slovensko/autogram/core/Autogram.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import digital.slovensko.autogram.core.visualization.DocumentVisualizationBuilder;
import digital.slovensko.autogram.core.visualization.UnsupportedVisualization;
import digital.slovensko.autogram.drivers.TokenDriver;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import digital.slovensko.autogram.ui.BatchUiResult;
import digital.slovensko.autogram.ui.UI;
import digital.slovensko.autogram.util.Logging;
Expand Down Expand Up @@ -63,15 +64,20 @@ public void checkPDFACompliance(SigningJob job) {
});
}

public void handleProtectedPdfDocument(ProtectedDSSDocument document) {
if (!PDFUtils.isPdfAndPasswordProtected(document))
return;

var password = ui.getDocumentPassword();
document.setPassword(password);
}

public void wrapInWorkThread(Runnable callback) {
ui.onWorkThreadDo(callback);
}

public void startVisualization(SigningJob job) {
ui.onWorkThreadDo(() -> {
if (PDFUtils.isPdfAndPasswordProtected(job.getDocument())) {
ui.onUIThreadDo(() -> {
ui.showError(new AutogramException("Nastala chyba", "Dokument je chránený heslom", "Snažíte sa podpísať dokument chránený heslom, čo je funkcionalita, ktorá nie je podporovaná.\n\nOdstráňte ochranu heslom a potom budete môcť dokument podpísať."));
});
return;
}

try {
var visualization = DocumentVisualizationBuilder.fromJob(job, settings);
ui.onUIThreadDo(() -> ui.showVisualization(visualization, this));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import eu.europa.esig.dss.enumerations.ASiCContainerType;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import eu.europa.esig.dss.simplereport.SimpleReport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -24,7 +24,6 @@

import digital.slovensko.autogram.util.XMLUtils;
import eu.europa.esig.dss.enumerations.SignatureLevel;
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;
Expand Down Expand Up @@ -167,7 +166,7 @@ public static ValidationReports getSignatureCheckReport(SigningJob job) {
return new ValidationReports(validator.validateDocument(), job);
}

public static SimpleReport getSignedDocumentSimpleReport(DSSDocument document) {
public static SimpleReport getSignedDocumentSimpleReport(ProtectedDSSDocument document) {
celuchmarek marked this conversation as resolved.
Show resolved Hide resolved
var validator = createDocumentValidator(document);
if (validator == null)
return null;
Expand Down
30 changes: 17 additions & 13 deletions src/main/java/digital/slovensko/autogram/core/SigningJob.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import digital.slovensko.autogram.core.eforms.xdc.XDCBuilder;
import digital.slovensko.autogram.core.eforms.xdc.XDCValidator;
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import digital.slovensko.autogram.model.ProtectedFileDocument;
import digital.slovensko.autogram.util.Logging;
import eu.europa.esig.dss.asic.cades.signature.ASiCWithCAdESService;
import eu.europa.esig.dss.asic.xades.signature.ASiCWithXAdESService;
import eu.europa.esig.dss.cades.signature.CAdESService;
import eu.europa.esig.dss.enumerations.MimeTypeEnum;
import eu.europa.esig.dss.enumerations.SignatureLevel;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.FileDocument;
import eu.europa.esig.dss.pades.signature.PAdESService;
import eu.europa.esig.dss.spi.x509.tsp.TSPSource;
import eu.europa.esig.dss.validation.CommonCertificateVerifier;
Expand All @@ -24,16 +24,16 @@

public class SigningJob {
private final Responder responder;
private final DSSDocument document;
private final ProtectedDSSDocument document;
private final SigningParameters parameters;

private SigningJob(DSSDocument document, SigningParameters parameters, Responder responder) {
private SigningJob(ProtectedDSSDocument document, SigningParameters parameters, Responder responder) {
this.document = document;
this.parameters = parameters;
this.responder = responder;
}

public DSSDocument getDocument() {
public ProtectedDSSDocument getDocument() {
return this.document;
}

Expand All @@ -46,7 +46,6 @@ public int getVisualizationWidth() {
}

public void signWithKeyAndRespond(SigningKey key) throws InterruptedException, AutogramException {

Logging.log("Signing Job: " + this.hashCode() + " file " + getDocument().getName());
boolean isContainer = getParameters().getContainer() != null;
var doc = switch (getParameters().getSignatureType()) {
Expand Down Expand Up @@ -141,6 +140,7 @@ private DSSDocument signDocumentAsPAdeS(SigningKey key) {
signatureParameters.setSigningCertificate(key.getCertificate());
signatureParameters.setCertificateChain(key.getCertificateChain());
signatureParameters.setSignWithExpiredCertificate(true);
signatureParameters.setPasswordProtection(document.getPassword());

if (signatureParameters.getSignatureLevel().equals(SignatureLevel.PAdES_BASELINE_T)) {
service.setTspSource(getParameters().getTspSource());
Expand All @@ -153,8 +153,8 @@ private DSSDocument signDocumentAsPAdeS(SigningKey key) {
return service.signDocument(getDocument(), signatureParameters, signatureValue);
}

public static FileDocument createDSSFileDocumentFromFile(File file) {
var fileDocument = new FileDocument(file);
public static ProtectedFileDocument createDSSFileDocumentFromFile(File file) {
var fileDocument = new ProtectedFileDocument(file);

if (fileDocument.getName().endsWith(".xdcf"))
fileDocument.setMimeType(XML_DATACONTAINER_WITH_CHARSET);
Expand All @@ -168,7 +168,7 @@ else if (isTxt(fileDocument.getMimeType()))
return fileDocument;
}

private static SigningJob build(DSSDocument document, SigningParameters params, Responder responder) {
private static SigningJob build(ProtectedDSSDocument document, SigningParameters params, Responder responder) {
if (params.shouldCreateXdc() && !isXDC(document.getMimeType()) && !isAsice(document.getMimeType()))
document = XDCBuilder.transform(params, document.getName(), EFormUtils.getXmlFromDocument(document));

Expand All @@ -183,17 +183,21 @@ private static SigningJob build(DSSDocument document, SigningParameters params,
return new SigningJob(document, params, responder);
}

public static SigningJob buildFromRequest(DSSDocument document, SigningParameters params, Responder responder) {
public static SigningJob buildFromRequest(ProtectedDSSDocument document, Autogram autogram, SigningParameters params, Responder responder) {
autogram.handleProtectedPdfDocument(document);

return build(document, params, responder);
}

public static SigningJob buildFromFile(File file, Responder responder, boolean checkPDFACompliance, SignatureLevel signatureType, boolean isEn319132, TSPSource tspSource, boolean plainXmlEnabled) {
public static SigningJob buildFromFile(File file, Autogram autogram, Responder responder, boolean checkPDFACompliance, SignatureLevel signatureType, boolean isEn319132, TSPSource tspSource, boolean plainXmlEnabled) {
var document = createDSSFileDocumentFromFile(file);
autogram.handleProtectedPdfDocument(document);

var parameters = getParametersForFile(document, checkPDFACompliance, signatureType, isEn319132, tspSource, plainXmlEnabled);
return build(document, parameters, responder);
}

private static SigningParameters getParametersForFile(FileDocument document, boolean checkPDFACompliance, SignatureLevel signatureType, boolean isEn319132, TSPSource tspSource, boolean plainXmlEnabled) {
private static SigningParameters getParametersForFile(ProtectedFileDocument document, boolean checkPDFACompliance, SignatureLevel signatureType, boolean isEn319132, TSPSource tspSource, boolean plainXmlEnabled) {
var level = SignatureValidator.getSignedDocumentSignatureLevel(SignatureValidator.getSignedDocumentSimpleReport(document));
if (level != null) switch (level.getSignatureForm()) {
case PAdES:
Expand Down Expand Up @@ -221,6 +225,6 @@ private static SigningParameters getParametersForFile(FileDocument document, boo
}

public boolean shouldCheckPDFCompliance() {
return parameters.getCheckPDFACompliance() && isPDF(document.getMimeType());
return parameters.getCheckPDFACompliance() && isPDF(document.getMimeType()) && !document.isProtected();
xhyrom marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import digital.slovensko.autogram.core.errors.AutogramException;
import digital.slovensko.autogram.core.errors.SigningParametersException;
import digital.slovensko.autogram.core.errors.UnknownEformException;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import digital.slovensko.autogram.util.AsicContainerUtils;
import digital.slovensko.autogram.core.eforms.EFormUtils;
import digital.slovensko.autogram.core.eforms.xdc.XDCValidator;
Expand Down Expand Up @@ -60,7 +61,7 @@ public static SigningParameters buildParameters(
SignatureLevel level, DigestAlgorithm digestAlgorithm, ASiCContainerType container, SignaturePackaging packaging,
boolean en319132, String infoCanonicalization, String propertiesCanonicalization, String keyInfoCanonicalization,
EFormAttributes eFormAttributes, boolean autoLoadEform, String fsFormId, boolean checkPDFACompliance,
int preferredPreviewWidth, DSSDocument document, TSPSource tspSource, boolean plainXmlEnabled) throws AutogramException {
int preferredPreviewWidth, ProtectedDSSDocument document, TSPSource tspSource, boolean plainXmlEnabled) throws AutogramException {

if (level == null)
throw new SigningParametersException("Nebol zadaný typ podpisu", "Typ/level podpisu je povinný atribút");
Expand Down Expand Up @@ -107,21 +108,21 @@ public static SigningParameters buildParameters(
keyInfoCanonicalization, eFormAttributes, checkPDFACompliance, preferredPreviewWidth, tspSource);
}

public static SigningParameters buildForPDF(DSSDocument document, boolean checkPDFACompliance, boolean signAsEn319132, TSPSource tspSource) throws AutogramException {
public static SigningParameters buildForPDF(ProtectedDSSDocument document, boolean checkPDFACompliance, boolean signAsEn319132, TSPSource tspSource) throws AutogramException {
return buildParameters(
(tspSource == null) ? SignatureLevel.PAdES_BASELINE_B : SignatureLevel.PAdES_BASELINE_T, DigestAlgorithm.SHA256,
null, null, signAsEn319132, null, null, null, null, false,
null, checkPDFACompliance, 640, document, tspSource, true);
}

public static SigningParameters buildForASiCWithXAdES(DSSDocument document, boolean checkPDFACompliance, boolean signAsEn319132, TSPSource tspSource, boolean plainXmlEnabled) throws AutogramException {
public static SigningParameters buildForASiCWithXAdES(ProtectedDSSDocument document, boolean checkPDFACompliance, boolean signAsEn319132, TSPSource tspSource, boolean plainXmlEnabled) throws AutogramException {
return buildParameters(
(tspSource == null) ? SignatureLevel.XAdES_BASELINE_B : SignatureLevel.XAdES_BASELINE_T, DigestAlgorithm.SHA256,
ASiCContainerType.ASiC_E, SignaturePackaging.ENVELOPING, signAsEn319132, null, null, null, null, true,
EFormUtils.getFsFormIdFromFilename(document.getName()), checkPDFACompliance, 640, document, tspSource, plainXmlEnabled);
}

public static SigningParameters buildForASiCWithCAdES(DSSDocument document, boolean checkPDFACompliance, boolean signAsEn319132, TSPSource tspSource, boolean plainXmlEnabled) throws AutogramException {
public static SigningParameters buildForASiCWithCAdES(ProtectedDSSDocument document, boolean checkPDFACompliance, boolean signAsEn319132, TSPSource tspSource, boolean plainXmlEnabled) throws AutogramException {
return buildParameters(
(tspSource == null) ? SignatureLevel.CAdES_BASELINE_B : SignatureLevel.CAdES_BASELINE_T, DigestAlgorithm.SHA256,
ASiCContainerType.ASiC_E, SignaturePackaging.ENVELOPING, signAsEn319132, null, null, null, null, true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
import digital.slovensko.autogram.core.SigningParameters;
import digital.slovensko.autogram.core.eforms.dto.XsltParams;
import digital.slovensko.autogram.core.errors.TransformationException;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import digital.slovensko.autogram.model.ProtectedInMemoryDocument;
import digital.slovensko.autogram.util.XMLUtils;

import static digital.slovensko.autogram.core.eforms.EFormUtils.*;
import static digital.slovensko.autogram.util.DSSUtils.getXdcfFilename;
import static digital.slovensko.autogram.util.XMLUtils.getSecureDocumentBuilder;

import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.InMemoryDocument;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand All @@ -32,7 +32,7 @@
public abstract class XDCBuilder {
private static final Charset ENCODING = StandardCharsets.UTF_8;

public static DSSDocument transform(SigningParameters params, String filename, Document document) {
public static ProtectedDSSDocument transform(SigningParameters params, String filename, Document document) {
xhyrom marked this conversation as resolved.
Show resolved Hide resolved
var identifier = params.getIdentifier();
var lastSlashIndex = identifier.lastIndexOf("/");
if (lastSlashIndex == -1)
Expand All @@ -58,7 +58,7 @@ public static DSSDocument transform(SigningParameters params, String filename, D

var content = getDocumentContent(transformedDocument).getBytes(ENCODING);

return new InMemoryDocument(content, getXdcfFilename(filename), AutogramMimeType.XML_DATACONTAINER_WITH_CHARSET);
return new ProtectedInMemoryDocument(content, getXdcfFilename(filename), AutogramMimeType.XML_DATACONTAINER_WITH_CHARSET);

} catch (Exception e) {
throw new TransformationException("Nastala chyba počas transformácie dokumentu",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import javax.xml.parsers.ParserConfigurationException;

import digital.slovensko.autogram.core.UserSettings;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import eu.europa.esig.dss.model.DSSDocument;

import org.xml.sax.SAXException;
Expand All @@ -22,10 +23,10 @@

public class DocumentVisualizationBuilder {

private final DSSDocument document;
private final ProtectedDSSDocument document;
private final SigningParameters parameters;

private DocumentVisualizationBuilder(DSSDocument document, SigningParameters parameters) {
private DocumentVisualizationBuilder(ProtectedDSSDocument document, SigningParameters parameters) {
this.document = document;
this.parameters = parameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import digital.slovensko.autogram.core.SigningJob;
import digital.slovensko.autogram.core.UserSettings;
import digital.slovensko.autogram.model.ProtectedDSSDocument;
import digital.slovensko.autogram.ui.Visualizer;
import eu.europa.esig.dss.model.DSSDocument;
import org.apache.pdfbox.pdmodel.PDDocument;
Expand All @@ -15,18 +16,18 @@
import javax.imageio.ImageIO;

public class PDFVisualization extends Visualization {
private final DSSDocument document;
private final ProtectedDSSDocument document;
private final UserSettings settings;


public PDFVisualization(DSSDocument document, SigningJob job, UserSettings settings) {
public PDFVisualization(ProtectedDSSDocument document, SigningJob job, UserSettings settings) {
super(job);
this.document = document;
this.settings = settings;
}

private ArrayList<byte []> getPdfImages() throws IOException {
var pdfDocument = PDDocument.load(this.document.openStream());
var pdfDocument = PDDocument.load(this.document.openStream(), new String(this.document.getPassword()));
var pdfRenderer = new PDFRenderer(pdfDocument);
var divs = new ArrayList<byte[]>();
for (int page = 0; page < pdfDocument.getNumberOfPages(); ++page) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package digital.slovensko.autogram.model;

import eu.europa.esig.dss.model.DSSDocument;

public interface ProtectedDSSDocument extends DSSDocument {
xhyrom marked this conversation as resolved.
Show resolved Hide resolved
char[] getPassword();
celuchmarek marked this conversation as resolved.
Show resolved Hide resolved
void setPassword(char[] password);

default boolean isProtected() {
return getPassword().length != 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package digital.slovensko.autogram.model;

import eu.europa.esig.dss.model.FileDocument;

import java.io.File;

public class ProtectedFileDocument extends FileDocument implements ProtectedDSSDocument {
private char[] password = new char[0];

public ProtectedFileDocument(String path) {
super(path);
}

public ProtectedFileDocument(File file) {
super(file);
}

@Override
public char[] getPassword() {
return this.password;
}

@Override
public void setPassword(char[] password) {
this.password = password;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package digital.slovensko.autogram.model;

import eu.europa.esig.dss.enumerations.MimeType;
import eu.europa.esig.dss.model.InMemoryDocument;

import java.io.InputStream;

public class ProtectedInMemoryDocument extends InMemoryDocument implements ProtectedDSSDocument {
private char[] password = new char[0];

public ProtectedInMemoryDocument(byte[] bytes) {
super(bytes);
}

public ProtectedInMemoryDocument(byte[] bytes, String name) {
super(bytes, name);
}

public ProtectedInMemoryDocument(byte[] bytes, String name, MimeType mimeType) {
super(bytes, name, mimeType);
}

public ProtectedInMemoryDocument(InputStream inputStream) {
super(inputStream);
}

public ProtectedInMemoryDocument(InputStream inputStream, String name) {
super(inputStream, name);
}

public ProtectedInMemoryDocument(InputStream inputStream, String name, MimeType mimeType) {
super(inputStream, name, mimeType);
}

@Override
public char[] getPassword() {
return this.password;
}

@Override
public void setPassword(char[] password) {
this.password = password;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void handle(HttpExchange exchange) throws IOException {

celuchmarek marked this conversation as resolved.
Show resolved Hide resolved
var responder = body.getBatchId() == null ? new ServerResponder(exchange)
: new ResponderInBatch(new ServerResponder(exchange), autogram.getBatch(body.getBatchId()));
var job = SigningJob.buildFromRequest(body.getDocument(), body.getParameters(autogram.getTspSource(), autogram.isPlainXmlEnabled()), responder);
var job = SigningJob.buildFromRequest(body.getDocument(), autogram, body.getParameters(autogram.getTspSource(), autogram.isPlainXmlEnabled()), responder);

if (body.getBatchId() != null)
autogram.batchSign(job, body.getBatchId());
Expand Down
Loading
Loading