diff options
author | Jakob Heher <jakob.heher@iaik.tugraz.at> | 2022-09-29 13:36:57 +0200 |
---|---|---|
committer | Jakob Heher <jakob.heher@iaik.tugraz.at> | 2022-09-29 13:36:57 +0200 |
commit | fd24a56578a323715b844b610ba91a3bfd400342 (patch) | |
tree | ab3fefdf5595068b5245b078a0727bbe7f74c52c | |
parent | 93d1862956c3d7eca281d22b74a31c72391ed951 (diff) | |
download | pdf-over-fd24a56578a323715b844b610ba91a3bfd400342.tar.gz pdf-over-fd24a56578a323715b844b610ba91a3bfd400342.tar.bz2 pdf-over-fd24a56578a323715b844b610ba91a3bfd400342.zip |
fido2 proof of concept
5 files changed, 150 insertions, 18 deletions
diff --git a/pdf-over-gui/pom.xml b/pdf-over-gui/pom.xml index 671f2967..b856e881 100644 --- a/pdf-over-gui/pom.xml +++ b/pdf-over-gui/pom.xml @@ -65,6 +65,11 @@ <version>${project.parent.version}</version> <scope>compile</scope> </dependency> + <dependency> + <groupId>at.a-sit</groupId> + <artifactId>webauthn-java</artifactId> + <version>0.0.1-SNAPSHOT</version> + </dependency> </dependencies> <dependencyManagement> <dependencies> 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 34b53173..dae4d007 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 @@ -15,10 +15,15 @@ */
package at.asit.pdfover.gui.bku;
+import java.util.Base64;
+
// Imports
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+
import at.asit.pdfover.gui.bku.mobile.ATrustHandler;
import at.asit.pdfover.gui.bku.mobile.ATrustStatus;
import at.asit.pdfover.gui.bku.mobile.MobileBKUHandler;
@@ -29,6 +34,11 @@ import at.asit.pdfover.signator.SLRequest; import at.asit.pdfover.signator.SLResponse;
import at.asit.pdfover.signator.SignatureException;
import at.asit.pdfover.signer.pdfas.PdfAs4SigningState;
+import at.asit.webauthn.PublicKeyCredential;
+import at.asit.webauthn.PublicKeyCredentialRequestOptions;
+import at.asit.webauthn.WebAuthN;
+import at.asit.webauthn.exceptions.WebAuthNOperationFailed;
+import at.asit.webauthn.responsefields.AuthenticatorAssertionResponse;
/**
*
@@ -125,6 +135,51 @@ public class MobileBKUConnector implements BkuSlConnector { if (status instanceof ATrustStatus) {
ATrustStatus aStatus = (ATrustStatus) status;
ATrustHandler aHandler = (ATrustHandler) handler;
+ if (aStatus.fido2OptionAvailable && (aStatus.fido2FormOptions == null)) {
+ try {
+ handler.handleCredentialsResponse(aHandler.postFIDO2Request());
+ } catch (Exception ex) {
+ log.error("Error in PostCredentialsThread", ex);
+ this.state.threadException = ex;
+ throw new SignatureException(ex);
+ }
+ }
+ if (aStatus.fido2FormOptions != null) {
+ log.info("Fido2 credentials GET!");
+ if (WebAuthN.isAvailable())
+ {
+ log.info("Authenticating with WebAuthn!");
+ enterTAN = false;
+ try {
+ PublicKeyCredential<AuthenticatorAssertionResponse> credential =
+ PublicKeyCredentialRequestOptions.FromJSONString(aStatus.fido2FormOptions.get(aStatus.fido2OptionsKey)).get("https://service.a-trust.at");
+
+ Base64.Encoder base64 = Base64.getEncoder();
+ JsonObject aTrustCredential = new JsonObject();
+ aTrustCredential.addProperty("id", credential.id);
+ aTrustCredential.addProperty("rawId", base64.encodeToString(credential.rawId));
+ aTrustCredential.addProperty("type", credential.type);
+ aTrustCredential.add("extensions", new JsonObject()); // TODO fix getClientExtensionResults() in library
+
+ JsonObject aTrustCredentialResponse = new JsonObject();
+ aTrustCredential.add("response", aTrustCredentialResponse);
+ aTrustCredentialResponse.addProperty("authenticatorData", base64.encodeToString(credential.response.authenticatorData));
+ aTrustCredentialResponse.addProperty("clientDataJson", base64.encodeToString(credential.response.clientDataJSON));
+ aTrustCredentialResponse.addProperty("signature", base64.encodeToString(credential.response.signature));
+ if (credential.response.userHandle != null)
+ aTrustCredentialResponse.addProperty("userHandle", base64.encodeToString(credential.response.userHandle));
+ else
+ aTrustCredentialResponse.add("userHandle", JsonNull.INSTANCE);
+
+ aStatus.fido2FormOptions.put(aStatus.fido2ResultKey, aTrustCredential.toString());
+ handler.handleTANResponse(aHandler.postFIDO2Result()); // TODO dedicated response
+ } catch (WebAuthNOperationFailed e) {
+ log.error("WebAuthN failed", e);
+ } catch (Exception e) {
+ log.error("generic failure", e);
+ }
+ }
+ }
if (aStatus.qrCodeURL != null) {
this.state.showQR();
if ("cancel".equals(this.state.status.errorMessage))
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustHandler.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustHandler.java index 18f93b22..5e4975e3 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustHandler.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustHandler.java @@ -21,16 +21,22 @@ import java.io.BufferedInputStream; import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringEscapeUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -149,7 +155,7 @@ public class ATrustHandler extends MobileBKUHandler { * @see at.asit.pdfover.gui.workflow.states.mobilebku.MobileBKUHandler#handleCredentialsResponse(java.lang.String)
*/
@Override
- public void handleCredentialsResponse(String responseData) throws Exception {
+ public void handleCredentialsResponse(final String responseData) throws Exception {
ATrustStatus status = getStatus();
String viewState = status.viewState;
String eventValidation = status.eventValidation;
@@ -160,6 +166,8 @@ public class ATrustHandler extends MobileBKUHandler { status.errorMessage = null;
+ final Document responseDocument = Jsoup.parse(responseData);
+
if (responseData.contains("ExpiresInfo.aspx?sid=")) {
// Certificate expiration interstitial - skip
if (!expiryNoticeDisplayed) {
@@ -196,8 +204,8 @@ public class ATrustHandler extends MobileBKUHandler { post.addParameter("__EVENTVALIDATION", t_eventValidation);
post.addParameter("Button_Next", "Weiter");
- responseData = executePost(client, post);
- log.trace("Response from mobile BKU: " + responseData);
+ handleCredentialsResponse(executePost(client, post));
+ return;
} else if (responseData.contains("tanAppInfo.aspx?sid=")) {
// App info interstitial - skip
log.info("Skipping tan app interstitial");
@@ -216,8 +224,8 @@ public class ATrustHandler extends MobileBKUHandler { post.addParameter("__EVENTVALIDATION", t_eventValidation);
post.addParameter("NextBtn", "Weiter");
- responseData = executePost(client, post);
- log.trace("Response from mobile BKU: " + responseData);
+ handleCredentialsResponse(executePost(client, post));
+ return;
}
if (responseData.contains("signature.aspx?sid=")) {
@@ -227,7 +235,12 @@ public class ATrustHandler extends MobileBKUHandler { sessionID = MobileBKUHelper.extractSubstring(responseData, "signature.aspx?sid=", "\"");
viewState = MobileBKUHelper.extractValueFromTagWithParam(responseData, "", "id", "__VIEWSTATE", "value");
eventValidation = MobileBKUHelper.extractValueFromTagWithParam(responseData, "", "id", "__EVENTVALIDATION", "value");
- refVal = MobileBKUHelper.extractSubstring(responseData, "id='vergleichswert'><b>Vergleichswert:</b>", "</div>");
+ try {
+ refVal = MobileBKUHelper.extractSubstring(responseData, "id='vergleichswert'><b>Vergleichswert:</b>", "</div>");
+ } catch (Exception e) {
+ refVal = null;
+ log.debug("No reference value");
+ }
signatureDataURL = status.baseURL + "/ShowSigobj.aspx" +
MobileBKUHelper.extractSubstring(responseData, "ShowSigobj.aspx", "'");
try {
@@ -253,17 +266,27 @@ public class ATrustHandler extends MobileBKUHandler { }catch (Exception e) {
log.debug("No text_tan tag");
}
- try {
- String webauthnLink = MobileBKUHelper.extractValueFromTagWithParam(responseData, "a", "id", "FidoButton", "href");
- log.info("Webauthn link: {}", webauthnLink);
- } catch (Exception e) {
- log.info("No webauthnLink");
- }
- try {
- String webauthnData = MobileBKUHelper.extractValueFromTagWithParam(responseData, "input", "id", "credentialOptions", "value");
- log.info("Fido credential options: {}", webauthnData);
- } catch (Exception e) {
- log.info("No webauthnData");
+
+ status.fido2OptionAvailable = (responseDocument.selectFirst("#FidoButton") != null);
+ {
+ Element fidoBlock = responseDocument.selectFirst("#fidoBlock");
+
+ if (fidoBlock != null) {
+ Map<String,String> options = new HashMap<>();
+ for (Element field : fidoBlock.select("input"))
+ {
+ if (!field.hasAttr("name"))
+ continue;
+ options.put(field.attr("name"), field.attr("value"));
+ if ("credentialOptions".equals(field.attr("id")))
+ status.fido2OptionsKey = field.attr("name");
+ if ("credentialResult".equals(field.attr("id")))
+ status.fido2ResultKey = field.attr("name");
+ }
+ log.info("Fido credential options: {}", options);
+ status.fido2FormOptions = options;
+ status.qrCodeURL = null;
+ }
}
} else if (responseData.contains("sl:InfoboxReadResponse")) {
@@ -341,6 +364,7 @@ public class ATrustHandler extends MobileBKUHandler { return executePost(client, post);
}
+
/* (non-Javadoc)
* @see at.asit.pdfover.gui.workflow.states.mobilebku.MobileBKUHandler#handleTANResponse(java.lang.String)
*/
@@ -402,6 +426,40 @@ public class ATrustHandler extends MobileBKUHandler { }
/**
+ * Cancel QR process, request FIDO2 authentication
+ * @return the response
+ * @throws IOException Error during posting
+ */
+
+ public String postFIDO2Request() throws IOException {
+ ATrustStatus status = getStatus();
+
+ MobileBKUHelper.registerTrustedSocketFactory();
+ HttpClient client = MobileBKUHelper.getHttpClient(status);
+ GetMethod get = new GetMethod(status.baseURL + "/usefido.aspx?sid=" + status.sessionID);
+ get.getParams().setContentCharset("utf-8");
+
+ return executeGet(client, get);
+ }
+
+ public String postFIDO2Result() throws IOException {
+ ATrustStatus status = getStatus();
+
+ MobileBKUHelper.registerTrustedSocketFactory();
+ HttpClient client = MobileBKUHelper.getHttpClient(status);
+
+ PostMethod post = new PostMethod(status.baseURL + "/signature.aspx?sid=" + status.sessionID);
+ post.getParams().setContentCharset("utf-8");
+ post.addParameter("__VIEWSTATE", status.viewState);
+ post.addParameter("__VIEWSTATEGENERATOR", status.viewStateGenerator);
+ post.addParameter("__EVENTVALIDATION", status.eventValidation);
+ for (Map.Entry<String, String> entry : status.fido2FormOptions.entrySet())
+ post.addParameter(entry.getKey(), entry.getValue());
+
+ return executePost(client, post);
+ }
+
+ /**
* Get the QR code image
* @return the QR code image as a String
*/
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustStatus.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustStatus.java index b61b3a8b..6258b4ce 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustStatus.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustStatus.java @@ -15,6 +15,8 @@ */
package at.asit.pdfover.gui.bku.mobile;
+import java.util.Map;
+
// Imports
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,6 +49,11 @@ public class ATrustStatus extends MobileBKUStatus { public String dynAttrSignButton;
public boolean isSMSTan = false;
+ public boolean fido2OptionAvailable = false;
+ public String fido2OptionsKey;
+ public String fido2ResultKey;
+ public Map<String,String> fido2FormOptions;
+
/**
* Constructor
* @param provider the ConfigProvider
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 121d8c71..1a1a10ac 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 @@ -21,6 +21,7 @@ import java.io.InputStream; import java.net.URI;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
@@ -226,7 +227,13 @@ public class MobileBKUQRComposite extends StateComposite { setErrorMessage(Messages.getString("error.FailedToLoadQRCode"));
return;
}
- this.currentQRImage = new ImageData(qrcode);
+ try {
+ this.currentQRImage = new ImageData(qrcode);
+ } catch (SWTException e) {
+ log.warn("Failed to load QR code", e);
+ setErrorMessage(Messages.getString("error.FailedToLoadQRCode"));
+ return;
+ }
updateQRImage();
}
|