summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Heher <jakob.heher@iaik.tugraz.at>2022-10-04 15:02:43 +0200
committerJakob Heher <jakob.heher@iaik.tugraz.at>2022-10-04 15:02:43 +0200
commitd6f4b34eae2e977cdd0339fb17302976fdae0574 (patch)
tree3f7cb16ebb790e740698b07eb31308a47a54d6d0
parent69e427953c0a877762a1c89da266dba70195459a (diff)
downloadpdf-over-d6f4b34eae2e977cdd0339fb17302976fdae0574.tar.gz
pdf-over-d6f4b34eae2e977cdd0339fb17302976fdae0574.tar.bz2
pdf-over-d6f4b34eae2e977cdd0339fb17302976fdae0574.zip
QR code handling
-rw-r--r--pdf-over-gui/pom.xml5
-rw-r--r--pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java76
-rw-r--r--pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java12
-rw-r--r--pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java59
-rw-r--r--pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/MobileBKUQRComposite.java5
-rw-r--r--pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/MobileBKUState.java154
-rw-r--r--pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/SigningState.java12
-rw-r--r--pdf-over-signer/src/main/java/at/asit/pdfover/signer/pdfas/PdfAs4Signer.java14
8 files changed, 259 insertions, 78 deletions
diff --git a/pdf-over-gui/pom.xml b/pdf-over-gui/pom.xml
index ee0b41b2..4cb083e3 100644
--- a/pdf-over-gui/pom.xml
+++ b/pdf-over-gui/pom.xml
@@ -50,6 +50,11 @@
<version>2.9.0</version>
</dependency>
<dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20220320</version>
+ </dependency>
+ <dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java
index 6f6e5301..14971ce1 100644
--- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java
+++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java
@@ -14,6 +14,7 @@ import javax.annotation.Nonnull;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@@ -25,9 +26,12 @@ import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.NoHttpResponseException;
+import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@@ -172,13 +176,19 @@ public class MobileBKUConnector implements BkuSlConnector {
return post;
}
- private static @Nonnull ClassicHttpRequest buildFormSubmit(@Nonnull ATrustParser.HTMLResult html) {
+ private static @Nonnull ClassicHttpRequest buildFormSubmit(@Nonnull ATrustParser.HTMLResult html, @Nonnull String submitButtonId) {
HttpPost post = new HttpPost(html.formTarget);
var builder = MultipartEntityBuilder.create();
for (var pair : html.iterateFormOptions())
builder.addTextBody(pair.getKey(), pair.getValue());
-
+ if (html.submitButtons.containsKey(submitButtonId)) {
+ var submitInfo = html.submitButtons.get(submitButtonId);
+ builder.addTextBody(submitInfo.name, submitInfo.value);
+ } else {
+ log.warn("Attempted to use submit button #{} which does not exist. Omitting.", submitButtonId);
+ }
+
post.setEntity(builder.build());
return post;
}
@@ -189,11 +199,65 @@ public class MobileBKUConnector implements BkuSlConnector {
*/
private @Nonnull ClassicHttpRequest presentResponseToUserAndReturnNextRequest(@Nonnull ATrustParser.HTMLResult html) throws UserCancelledException {
if (html.usernamePasswordBlock != null) {
- while ((this.credentials.username == null) || (this.credentials.password == null)) {
- this.state.getCredentialsFromUserTo(this.credentials, null);
+ try {
+ while ((this.credentials.username == null) || (this.credentials.password == null)) {
+ this.state.getCredentialsFromUserTo(this.credentials, null); // TODO error message
+ }
+ html.usernamePasswordBlock.setUsernamePassword(this.credentials.username, this.credentials.password);
+ return buildFormSubmit(html, "Button_Identification");
+ } catch (UserCancelledException e) {
+ return buildFormSubmit(html, "Button_Cancel");
+ }
+ }
+ if (html.qrCodeBlock != null) {
+ try (final CloseableHttpClient httpClient = HttpClients.custom().disableRedirectHandling().build()) {
+ final HttpGet request = new HttpGet(html.qrCodeBlock.qrCodeURI);
+ boolean[] done = new boolean[1];
+ done[0] = false;
+ Thread longPollThread = new Thread(() -> {
+ log.debug("longPollThread hello");
+ while (!done[0]) {
+ try (final CloseableHttpResponse response = httpClient.execute(new HttpGet(html.qrCodeBlock.pollingURI))) {
+ JSONObject jsonResponse = new JSONObject(EntityUtils.toString(response.getEntity()));
+ if (jsonResponse.getBoolean("Fin"))
+ state.signalQRScanned();
+ else if (jsonResponse.getBoolean("Wait"))
+ {
+ log.debug("longPollThread continue...");
+ continue;
+ }
+ else if (jsonResponse.getBoolean("Error"))
+ state.signalQRScanned(); /* will trigger reload and find error; this is the same thing a-trust does */
+ else {
+ log.warn("Unknown long poll response:\n{}", jsonResponse.toString(2));
+ break;
+ }
+ } catch (NoHttpResponseException e) {
+ continue;
+ } catch (IOException | ParseException | IllegalStateException e) {
+ log.warn("QR code long polling exception", e);
+ /* sleep so we don't hammer a-trust too hard in case this goes wrong */
+ try { Thread.sleep(5000); } catch (InterruptedException e2) {}
+ }
+ }
+ log.debug("longPollThread goodbye");
+ });
+ try {
+ longPollThread.start();
+ MobileBKUState.QRResult result = this.state.showQRCode(html.qrCodeBlock.referenceValue, html.qrCodeBlock.qrCodeURI, null);
+ switch (result) {
+ case UPDATE: return new HttpGet(html.htmlDocument.baseUri());
+ case TO_FIDO2: throw new IllegalStateException();
+ case TO_SMS: throw new IllegalStateException();
+ }
+ } finally {
+ done[0] = true;
+ request.abort();
+ try { longPollThread.join(1000); } catch (InterruptedException e) {}
+ }
+ } catch (IOException e) {
+ log.warn("closing CloseableHttpClient threw exception", e);
}
- html.usernamePasswordBlock.setUsernamePassword(this.credentials.username, this.credentials.password);
- return buildFormSubmit(html);
}
throw new IllegalStateException("No top-level block is set? Something has gone terribly wrong.");
}
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java
index a71f37df..9703e602 100644
--- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java
+++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java
@@ -15,6 +15,9 @@
*/
package at.asit.pdfover.gui.bku;
+import java.io.IOException;
+import java.net.URISyntaxException;
+
// Imports
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,6 +28,7 @@ import at.asit.pdfover.gui.bku.OLDmobile.ATrustStatus;
import at.asit.pdfover.gui.workflow.states.MobileBKUState;
import at.asit.pdfover.signer.BkuSlConnector;
import at.asit.pdfover.signer.SignatureException;
+import at.asit.pdfover.signer.UserCancelledException;
import at.asit.pdfover.signer.pdfas.PdfAs4SLRequest;
import at.asit.pdfover.signer.pdfas.PdfAs4SigningState;
@@ -51,7 +55,7 @@ public class OLDMobileBKUConnector implements BkuSlConnector {
* @see at.asit.pdfover.signer.BkuSlConnector#handleSLRequest(java.lang.String)
*/
@Override
- public String handleSLRequest(PdfAs4SLRequest request) throws SignatureException {
+ public String handleSLRequest(PdfAs4SLRequest request) throws SignatureException, UserCancelledException {
PdfAs4SigningState signingState = this.state.getSigningState();
signingState.signatureRequest = request;
@@ -120,7 +124,11 @@ public class OLDMobileBKUConnector implements BkuSlConnector {
boolean enterTAN = true;
String responseData = null;
if (status.qrCodeURL != null) {
- this.state.showQR();
+ try {
+ this.state.OLDshowQR();
+ } catch (IOException | URISyntaxException e) {
+ throw new SignatureException(e);
+ }
if ("cancel".equals(this.state.status.errorMessage))
throw new SignatureException(new IllegalStateException());
if (status.qrCodeURL == null) {
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java
index f452202d..890ffad1 100644
--- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java
+++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java
@@ -33,9 +33,13 @@ public class ATrustParser {
log.debug("Tested for element {} -- not found.", selector);
throw new ComponentParseFailed();
}
- protected @Nonnull String getAttributeEnsureNotNull(@Nonnull String selector, @Nonnull String attribute) throws ComponentParseFailed {
+ protected @Nonnull org.jsoup.nodes.Element getElementEnsureNotNull(@Nonnull String selector) throws ComponentParseFailed {
var elm = this.htmlDocument.selectFirst(selector);
if (elm == null) { log.warn("Expected element not found in response: {}", selector); throw new ComponentParseFailed(); }
+ return elm;
+ }
+ protected @Nonnull String getAttributeEnsureNotNull(@Nonnull String selector, @Nonnull String attribute) throws ComponentParseFailed {
+ var elm = getElementEnsureNotNull(selector);
if (!elm.hasAttr(attribute)) { log.warn("Element {} is missing expected attribute '{}'.", selector, attribute); throw new ComponentParseFailed(); }
return ISNOTNULL(elm.attr(attribute));
}
@@ -55,6 +59,7 @@ public class ATrustParser {
public static class UsernamePasswordBlock extends TopLevelFormBlock {
private @Nonnull String usernameKey;
private @Nonnull String passwordKey;
+ public @CheckForNull String errorMessage;
public void setUsernamePassword(String username, String password) {
formOptions.put(usernameKey, username); formOptions.put(passwordKey, password);
@@ -65,22 +70,38 @@ public class ATrustParser {
abortIfElementMissing("#handynummer");
this.usernameKey = getAttributeEnsureNotNull("#handynummer", "name");
this.passwordKey = getAttributeEnsureNotNull("#signaturpasswort", "name");
-
- /* remove unused submit buttons */
- // TODO: we should generalize this somehow for input type="submit"
- // TODO: do we maybe want to use the actual cancel button?
- formOptions.remove(getAttributeEnsureNotNull("#Button_Cancel", "name"));
- formOptions.remove(getAttributeEnsureNotNull("#Button_localBku", "name"));
}
}
public static class QRCodeBlock extends TopLevelFormBlock {
+ public @Nonnull String referenceValue;
public @Nonnull URI qrCodeURI;
+ public @Nonnull URI pollingURI;
+ public @Nullable String errorMessage;
private QRCodeBlock(@Nonnull org.jsoup.nodes.Document htmlDocument, @Nonnull Map<String, String> formOptions) throws ComponentParseFailed {
super(htmlDocument, formOptions);
abortIfElementMissing("#qrimage");
+
+ this.referenceValue = ISNOTNULL(getElementEnsureNotNull("#vergleichswert").ownText());
this.qrCodeURI = getURIAttributeEnsureNotNull("#qrimage", "abs:src");
+
+ var pollingScriptElm = getElementEnsureNotNull("#jsLongPoll script");
+ String pollingScript = pollingScriptElm.data();
+ int startIdx = pollingScript.indexOf("qrpoll(\"");
+ if (startIdx < 0) { log.warn("Failed to find 'qrpoll(\"' in jsLongPoll script:\n{}", pollingScript); throw new ComponentParseFailed(); }
+ startIdx += 8;
+
+ int endIdx = pollingScript.indexOf("\");", startIdx);
+ if (endIdx < 0) { log.warn("Failed to find qrpoll terminator '\");' in jsLongPoll script:\n{}", pollingScript); throw new ComponentParseFailed(); }
+
+ String pollingUriString = pollingScript.substring(startIdx, endIdx);
+ try {
+ this.pollingURI = ISNOTNULL(new URI(pollingScriptElm.baseUri()).resolve(pollingUriString));
+ } catch (URISyntaxException e) {
+ log.warn("URI '{}' could not be parsed", pollingUriString);
+ throw new ComponentParseFailed();
+ }
}
}
@@ -104,6 +125,16 @@ public class ATrustParser {
public final @Nonnull URI formTarget;
public final @Nonnull Map<String, String> formOptions = new HashMap<>();
+ public static class NameValuePair {
+ public final @Nonnull String name;
+ public final @Nonnull String value;
+ public NameValuePair(@Nonnull String n, @Nonnull String v) { name = n; value = v; }
+ }
+ /**
+ * map: id -> (name, value)
+ */
+ public final @Nonnull Map<String, @CheckForNull NameValuePair> submitButtons = new HashMap<>();
+
public @Nonnull Iterable<Map.Entry<String, String>> iterateFormOptions() { return ISNOTNULL(formOptions.entrySet()); }
/* optional mode switch links (any number may or may not be null) */
@@ -182,9 +213,17 @@ public class ATrustParser {
for (var input : mainForm.select("input")) {
String name = input.attr("name");
- if (name.isEmpty())
- continue;
- this.formOptions.put(name, input.attr("value"));
+
+ /* special handling for submit inputs, they only get sent if they are "clicked" */
+ if ("submit".equals(input.attr("type"))) {
+ if (name.isEmpty())
+ this.submitButtons.put(input.attr("id"), null);
+ else
+ this.submitButtons.put(input.attr("id"), new NameValuePair(name, ISNOTNULL(input.attr("value"))));
+ } else {
+ if (!name.isEmpty())
+ this.formOptions.put(name, input.attr("value"));
+ }
}
this.fido2Link = getHrefIfExists("#FidoButton");
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/MobileBKUQRComposite.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/MobileBKUQRComposite.java
index 1a1a10ac..3f1aa04d 100644
--- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/MobileBKUQRComposite.java
+++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/MobileBKUQRComposite.java
@@ -17,6 +17,7 @@ package at.asit.pdfover.gui.composites;
// Imports
import java.awt.Desktop;
+import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
@@ -222,13 +223,13 @@ public class MobileBKUQRComposite extends StateComposite {
* @param qrcode
* the qrcode to set
*/
- public void setQR(InputStream qrcode) {
+ public void setQR(byte[] qrcode) {
if (qrcode == null) {
setErrorMessage(Messages.getString("error.FailedToLoadQRCode"));
return;
}
try {
- this.currentQRImage = new ImageData(qrcode);
+ this.currentQRImage = new ImageData(new ByteArrayInputStream(qrcode));
} catch (SWTException e) {
log.warn("Failed to load QR code", e);
setErrorMessage(Messages.getString("error.FailedToLoadQRCode"));
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/MobileBKUState.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/MobileBKUState.java
index 26be4626..9c3fc807 100644
--- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/MobileBKUState.java
+++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/MobileBKUState.java
@@ -15,8 +15,11 @@
*/
package at.asit.pdfover.gui.workflow.states;
+import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Timer;
import java.util.TimerTask;
@@ -31,6 +34,12 @@ import at.asit.pdfover.signer.SignatureException;
import at.asit.pdfover.signer.UserCancelledException;
import at.asit.pdfover.signer.pdfas.PdfAs4SigningState;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.slf4j.Logger;
@@ -220,8 +229,7 @@ public class MobileBKUState extends State {
}
public void getCredentialsFromUserTo(@Nonnull UsernameAndPassword credentials, @Nullable String errorMessage) throws UserCancelledException {
- boolean[] cancelState = new boolean[1];
- Display.getDefault().syncExec(() -> {
+ Display.getDefault().syncCall(() -> {
MobileBKUEnterNumberComposite ui = this.getMobileBKUEnterNumberComposite();
if (!ui.userAck) { // We need number and password => show UI!
@@ -252,10 +260,10 @@ public class MobileBKUState extends State {
if (!(ui.userCancel && ui.isRememberPassword())) /* don't allow "remember" to be enabled via cancel button */
getStateMachine().configProvider.setRememberMobilePasswordPersistent(ui.isRememberPassword());
- cancelState[0] = ui.userCancel;
- ui.userCancel = false;
- if (cancelState[0])
- return;
+ if (ui.userCancel) {
+ ui.userCancel = false;
+ throw new UserCancelledException();
+ }
// user hit ok
ui.userAck = false;
@@ -266,9 +274,9 @@ public class MobileBKUState extends State {
// show waiting composite
getStateMachine().display(this.getWaitingComposite());
+
+ return true; /* dummy return for lambda type deduction */
});
- if (cancelState[0])
- throw new UserCancelledException();
}
/**
@@ -342,46 +350,45 @@ public class MobileBKUState extends State {
});
}
+ public enum QRResult {
+ /* the user has pressed the FIDO2 button */
+ TO_FIDO2,
+ /* the user has pressed the SMS button */
+ TO_SMS,
+ /* signalQRScanned has been called; this indicates that we should refresh the page */
+ UPDATE
+ };
+
/**
- * Show QR code
+ * start showing the QR code at the indicated URI
+ * this method will block until the QR code state completes
+ * (due to QR code being scanned, or the user pressing a button)
+ * <p>
+ * it is the responsibility of the caller to perform AJAX long polling
+ * @return
*/
- public void showQR() {
- final ATrustStatus status = this.status;
- final ATrustHandler handler = this.handler;
-
- final Timer checkDone = new Timer();
- checkDone.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- // ping signature page to see if code has been scanned
- try {
- String resp = handler.getSignaturePage();
- if (handler.handleQRResponse(resp)) {
- log.debug("Signature page response: " + resp);
- getMobileBKUQRComposite().setDone(true);
- Display display = getStateMachine().
- getMainShell().getDisplay();
- display.wake();
- checkDone.cancel();
- }
- Display.getDefault().wake();
- } catch (Exception e) {
- log.error("Error getting signature page", e);
- }
+ public QRResult showQRCode(@Nonnull String referenceValue, @Nonnull URI qrCodeURI, @Nullable String errorMessage) throws UserCancelledException {
+ byte[] qrCode;
+ try (final CloseableHttpClient httpClient = HttpClients.createDefault()) {
+ try (final CloseableHttpResponse response = httpClient.execute(new HttpGet(qrCodeURI))) {
+ qrCode = EntityUtils.toByteArray(response.getEntity());
}
- }, 0, 5000);
-
- Display.getDefault().syncExec(() -> {
+ } catch (IOException e) {
+ log.warn("Failed to load QR code.");
+ qrCode = null;
+ }
+
+ final byte[] qrCodeCopy = qrCode; /* because java is silly */
+ return Display.getDefault().syncCall(() -> {
MobileBKUQRComposite qr = getMobileBKUQRComposite();
+ qr.setUserCancel(false);
+ qr.setUserSMS(false);
+ qr.setDone(false);
qr.setRefVal(status.refVal);
qr.setSignatureData(status.signatureDataURL);
qr.setErrorMessage(status.errorMessage);
- InputStream qrcode = handler.getQRCode();
- if (qrcode == null) {
- this.threadException = new Exception(Messages.getString("error.FailedToLoadQRCode"));
- }
- qr.setQR(qrcode);
+ qr.setQR(qrCodeCopy);
getStateMachine().display(qr);
Display display = getStateMachine().getMainShell().getDisplay();
@@ -391,29 +398,70 @@ public class MobileBKUState extends State {
}
}
- checkDone.cancel();
+ getStateMachine().display(this.getWaitingComposite());
if (qr.isUserCancel()) {
- qr.setUserCancel(false);
clearRememberedPassword();
- status.errorMessage = "cancel";
- return;
+ throw new UserCancelledException();
}
- if (qr.isUserSMS()) {
- qr.setUserSMS(false);
- status.qrCodeURL = null;
- }
+ if (qr.isUserSMS())
+ return QRResult.TO_SMS;
if (qr.isDone())
- qr.setDone(false);
-
- // show waiting composite
- getStateMachine().display(this.getWaitingComposite());
+ return QRResult.UPDATE;
+
+ throw new RuntimeException("unexpected display wake");
});
}
/**
+ * indicate that the long polling operation completed
+ * (any ongoing showQRCode call will then return)
+ */
+ public void signalQRScanned() {
+ getMobileBKUQRComposite().setDone(true);
+ Display display = getStateMachine().
+ getMainShell().getDisplay();
+ display.wake();
+ }
+
+ /**
+ * Show QR code
+ * @throws URISyntaxException
+ * @throws UserCancelledException
+ * @throws IOException
+ */
+ public void OLDshowQR() throws IOException, UserCancelledException, URISyntaxException {
+ final ATrustStatus status = this.status;
+ final ATrustHandler handler = this.handler;
+
+ final Timer checkDone = new Timer();
+ checkDone.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ // ping signature page to see if code has been scanned
+ try {
+ String resp = handler.getSignaturePage();
+ if (handler.handleQRResponse(resp)) {
+ log.debug("Signature page response: " + resp);
+ checkDone.cancel();
+ signalQRScanned();
+ }
+ Display.getDefault().wake();
+ } catch (Exception e) {
+ log.error("Error getting signature page", e);
+ }
+ }
+ }, 0, 5000);
+
+ QRResult result = showQRCode(status.refVal, new URI(status.baseURL).resolve(status.qrCodeURL), status.errorMessage);
+ checkDone.cancel();
+ if (result == QRResult.TO_SMS)
+ status.qrCodeURL = null;
+ }
+
+ /**
* This composite notifies the user to open the signature-app
*/
public void showOpenAppMessageWithSMSandCancel() throws SignatureException {
@@ -569,7 +617,7 @@ public class MobileBKUState extends State {
public void run() {
this.signingState = getStateMachine().status.signingState;
- this.signingState.bkuConnector = new OLDMobileBKUConnector(this);
+ this.signingState.bkuConnector = new MobileBKUConnector(this);
this.signingState.useBase64Request = false;
if (this.threadException != null) {
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/SigningState.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/SigningState.java
index b9bdc917..169aefb8 100644
--- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/SigningState.java
+++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/workflow/states/SigningState.java
@@ -30,6 +30,7 @@ import at.asit.pdfover.commons.Messages;
import at.asit.pdfover.gui.workflow.StateMachine;
import at.asit.pdfover.gui.workflow.Status;
import at.asit.pdfover.signer.SignatureException;
+import at.asit.pdfover.signer.UserCancelledException;
import at.asit.pdfover.signer.pdfas.PdfAs4Signer;
/**
@@ -108,11 +109,14 @@ public class SigningState extends State {
if (cause instanceof ConnectException)
message += ": " + cause.getMessage();
if (cause instanceof IllegalStateException) {
- // Dummy exception - don't display error, go back to BKU Selection
- this.setNextState(new BKUSelectionState(getStateMachine()));
- return;
+ // TODO legacy hack
+ this.threadException = new UserCancelledException();
}
-
+ }
+ if (this.threadException instanceof UserCancelledException) {
+ // don't display error, go back to BKU Selection
+ this.setNextState(new BKUSelectionState(getStateMachine()));
+ return;
}
// if we have gotten to this point, this is an actual exception
diff --git a/pdf-over-signer/src/main/java/at/asit/pdfover/signer/pdfas/PdfAs4Signer.java b/pdf-over-signer/src/main/java/at/asit/pdfover/signer/pdfas/PdfAs4Signer.java
index 80a7e14d..c7be135f 100644
--- a/pdf-over-signer/src/main/java/at/asit/pdfover/signer/pdfas/PdfAs4Signer.java
+++ b/pdf-over-signer/src/main/java/at/asit/pdfover/signer/pdfas/PdfAs4Signer.java
@@ -11,8 +11,10 @@ import at.asit.pdfover.signer.ByteArrayDocumentSource;
import at.asit.pdfover.signer.SignResult;
import at.asit.pdfover.signer.SignatureException;
import at.asit.pdfover.signer.SignaturePosition;
+import at.asit.pdfover.signer.UserCancelledException;
import at.gv.egiz.pdfas.common.exceptions.PDFASError;
import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.exceptions.SLPdfAsException;
import at.gv.egiz.pdfas.lib.api.ByteArrayDataSource;
import at.gv.egiz.pdfas.lib.api.Configuration;
import at.gv.egiz.pdfas.lib.api.IConfigurationConstants;
@@ -104,7 +106,7 @@ public class PdfAs4Signer {
}
}
- public static SignResult sign(PdfAs4SigningState state) throws SignatureException {
+ public static SignResult sign(PdfAs4SigningState state) throws SignatureException, UserCancelledException {
try {
if (state == null) {
throw new SignatureException("Incorrect SigningState!");
@@ -154,6 +156,16 @@ public class PdfAs4Signer {
return result;
}
} catch (PdfAsException | PDFASError e) {
+ { // TODO hack around pdf-as not handling this error code properly cf. #124
+ Throwable rootCause = e;
+ while (rootCause.getCause() != null)
+ rootCause = rootCause.getCause();
+ try { /* error code 6001 is user cancellation */
+ if (((SLPdfAsException)rootCause).getMessage().startsWith("6001 :"))
+ throw new UserCancelledException();
+ } catch (ClassCastException e2) { /* fall through to wrapped throw */}
+ }
+
throw new SignatureException(e);
}
}