diff options
45 files changed, 2364 insertions, 1177 deletions
diff --git a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java index 9b9735f6..e8d8976d 100644 --- a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java +++ b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java @@ -18,6 +18,7 @@ package at.gv.egiz.bku.online.applet; import at.gv.egiz.bku.smccstal.AbstractBKUWorker; import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.bku.smccstal.SignRequestHandler; import at.gv.egiz.stal.STALRequest; import at.gv.egiz.stal.STALResponse; import at.gv.egiz.stal.SignRequest; @@ -67,8 +68,10 @@ public class AppletBKUWorker extends AbstractBKUWorker implements Runnable { STALPortType stalPort = applet.getSTALPort(); STALTranslator stalTranslator = applet.getSTALTranslator(); + AppletSecureViewer secureViewer = + new AppletSecureViewer(gui, stalPort, sessionId); addRequestHandler(SignRequest.class, - new AppletSecureViewer(stalPort, sessionId)); + new SignRequestHandler(secureViewer)); GetNextRequestResponseType nextRequestResp = stalPort.connect(sessionId); diff --git a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletSecureViewer.java b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletSecureViewer.java index e2551e2d..929cecb1 100644 --- a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletSecureViewer.java +++ b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletSecureViewer.java @@ -16,17 +16,8 @@ */ package at.gv.egiz.bku.online.applet; +import at.gv.egiz.bku.gui.BKUGUIFacade; import at.gv.egiz.bku.smccstal.SecureViewer; -import java.security.DigestException; -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import at.gv.egiz.bku.smccstal.SignRequestHandler; import at.gv.egiz.stal.HashDataInput; import at.gv.egiz.stal.impl.ByteArrayHashDataInput; import at.gv.egiz.stal.service.GetHashDataInputFault; @@ -34,49 +25,73 @@ import at.gv.egiz.stal.service.STALPortType; import at.gv.egiz.stal.service.types.GetHashDataInputResponseType; import at.gv.egiz.stal.service.types.GetHashDataInputType; import at.gv.egiz.stal.signedinfo.ReferenceType; +import at.gv.egiz.stal.signedinfo.SignedInfoType; +import java.awt.event.ActionListener; +import java.security.DigestException; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** - * A SignRequesthandler that obtains hashdata inputs from a STAL webservice and - * displays these either within the applet or in a separate frame. - * The internal viewer displays plaintext data only, other mimetypes can be saved to disk. - * The standalone (frame) viewer displays all mimetypes. - * - * (This class depends on STALService and therefore is not part of BKUCommonGUI.) - * + * * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> */ -public class AppletSecureViewer extends SignRequestHandler { +public class AppletSecureViewer implements SecureViewer { private static final Log log = LogFactory.getLog(AppletSecureViewer.class); + + protected BKUGUIFacade gui; protected STALPortType stalPort; protected String sessId; + protected List<HashDataInput> verifiedDataToBeSigned; - public AppletSecureViewer(STALPortType stalPort, String sessId) { - if (stalPort == null || sessId == null) { + public AppletSecureViewer(BKUGUIFacade gui, STALPortType stalPort, + String sessId) { + if (gui == null) { + throw new NullPointerException("GUI must not be null"); + } + if (stalPort == null) { throw new NullPointerException("STAL port must not be null"); } - this.sessId = sessId; + if (sessId == null) { + throw new NullPointerException("session id must not be null"); + } + this.gui = gui; this.stalPort = stalPort; + this.sessId = sessId; } /** - * TODO don't throw exceptions + * retrieves the data to be signed for * @param signedReferences + * @param okListener + * @param okCommand + * @param cancelListener + * @param cancelCommand * @throws java.security.DigestException * @throws java.lang.Exception */ @Override - public void displayDataToBeSigned(List<ReferenceType> signedReferences) + public void displayDataToBeSigned(SignedInfoType signedInfo, + ActionListener okListener, String okCommand) throws DigestException, Exception { - - List<GetHashDataInputResponseType.Reference> hdi = getHashDataInput(signedReferences); - List<HashDataInput> verifiedHashDataInputs = verifyHashDataInput(signedReferences, hdi); - - if (verifiedHashDataInputs.size() > 0) { - gui.showSecureViewer(verifiedHashDataInputs, this, "hashDataDone"); + + if (verifiedDataToBeSigned == null) { + log.info("retrieve data to be signed for dsig:SignedInfo " + + signedInfo.getId()); + List<GetHashDataInputResponseType.Reference> hdi = + getHashDataInput(signedInfo.getReference()); + verifiedDataToBeSigned = verifyHashDataInput(signedInfo.getReference(), + hdi); + } + if (verifiedDataToBeSigned.size() > 0) { + gui.showSecureViewer(verifiedDataToBeSigned, okListener, okCommand); } else { - throw new Exception("No signature data (apart from any QualifyingProperties or a Manifest)"); + throw new Exception("No data to be signed (apart from any QualifyingProperties or a Manifest)"); } } @@ -110,7 +125,7 @@ public class AppletSecureViewer extends SignRequestHandler { } } } - + if (request.getReference().size() < 1) { log.error("No signature data (apart from any QualifyingProperties or a Manifest) for session " + sessId); throw new Exception("No signature data (apart from any QualifyingProperties or a Manifest)"); @@ -187,7 +202,7 @@ public class AppletSecureViewer extends SignRequestHandler { verifiedHashDataInputs.add(new ByteArrayHashDataInput(hdi, signedRefId, mimeType, encoding)); } } - + return verifiedHashDataInputs; } diff --git a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/BKUApplet.java b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/BKUApplet.java index a4337bbd..0ddb6dc6 100644 --- a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/BKUApplet.java +++ b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/BKUApplet.java @@ -34,6 +34,7 @@ import at.gv.egiz.bku.gui.BKUGUIFacade; import at.gv.egiz.bku.gui.BKUGUIImpl; import at.gv.egiz.stal.service.STALPortType; import at.gv.egiz.stal.service.STALService; +import java.applet.AppletContext; import java.awt.Container; import javax.xml.namespace.QName; @@ -207,14 +208,19 @@ public class BKUApplet extends JApplet { */ protected void sendRedirect(String sessionId) { try { + AppletContext ctx = getAppletContext(); + if (ctx == null) { + log.error("no applet context (applet might already have been destroyed)"); + return; + } URL redirectURL = getURLParameter(REDIRECT_URL, sessionId); String redirectTarget = getParameter(REDIRECT_TARGET); if (redirectTarget == null) { log.info("Done. Redirecting to " + redirectURL + " ..."); - getAppletContext().showDocument(redirectURL); + ctx.showDocument(redirectURL); } else { log.info("Done. Redirecting to " + redirectURL + " (target=" + redirectTarget + ") ..."); - getAppletContext().showDocument(redirectURL, redirectTarget); + ctx.showDocument(redirectURL, redirectTarget); } } catch (MalformedURLException ex) { log.warn("Failed to redirect: " + ex.getMessage(), ex); diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java index 159dd29d..d1ca6c00 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java @@ -118,18 +118,18 @@ public class PINManagementGUI extends CardMgmtGUI implements PINManagementGUIFac pinStatusTable.setDefaultRenderer(PINSpec.class, new PINSpecRenderer()); pinStatusTable.setDefaultRenderer(STATUS.class, new PINStatusRenderer(cardmgmtMessages)); pinStatusTable.setTableHeader(null); - - pinStatusTable.addMouseMotionListener(new MouseMotionAdapter() { - - @Override - public void mouseMoved(MouseEvent e) { - if (pinStatusTable.columnAtPoint(e.getPoint()) == 0) { - pinStatusTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } else { - pinStatusTable.setCursor(Cursor.getDefaultCursor()); - } - } - }); + pinStatusTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); +// pinStatusTable.addMouseMotionListener(new MouseMotionAdapter() { +// +// @Override +// public void mouseMoved(MouseEvent e) { +// if (pinStatusTable.columnAtPoint(e.getPoint()) == 0) { +// pinStatusTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); +// } else { +// pinStatusTable.setCursor(Cursor.getDefaultCursor()); +// } +// } +// }); final JButton activateButton = new JButton(); activateButton.setFont(activateButton.getFont().deriveFont(activateButton.getFont().getStyle() & ~java.awt.Font.BOLD)); @@ -392,7 +392,7 @@ public class PINManagementGUI extends CardMgmtGUI implements PINManagementGUIFac if (pinpad) { JLabel pinpadLabel = new JLabel(); pinpadLabel.setFont(mgmtLabel.getFont().deriveFont(mgmtLabel.getFont().getStyle() & ~Font.BOLD)); - String pinpadPattern = getMessage(MESSAGE_PINPAD); + String pinpadPattern = getMessage(MESSAGE_VERIFYPIN_PINPAD); pinpadLabel.setText(MessageFormat.format(pinpadPattern, new Object[] { pinSpec.getLocalizedName(), pinSize })); diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java index 45313f42..f0cc0a27 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java @@ -40,13 +40,16 @@ public interface PINManagementGUIFacade extends BKUGUIFacade { public static final String MESSAGE_ACTIVATE_SUCCESS = "activate.success"; public static final String MESSAGE_CHANGE_SUCCESS = "change.success"; public static final String MESSAGE_PINMGMT = "pin.mgmt"; - public static final String MESSAGE_PINPAD = "pinpad"; - public static final String MESSAGE_CHANGEPIN_PINPAD = "pinpad.change"; +// public static final String MESSAGE_PINPAD = "pinpad"; public static final String MESSAGE_ACTIVATE_PIN = "activate.pin"; public static final String MESSAGE_CHANGE_PIN = "change.pin"; public static final String MESSAGE_VERIFY_PIN = "verify.pin"; public static final String MESSAGE_UNBLOCK_PIN = "unblock.pin"; - + public static final String MESSAGE_ACTIVATEPIN_PINPAD = "activate.pinpad"; + public static final String MESSAGE_CHANGEPIN_PINPAD = "change.pinpad"; + public static final String MESSAGE_VERIFYPIN_PINPAD = "verify.pinpad"; + public static final String MESSAGE_UNBLOCKPIN_PINPAD = "unblock.pinpad"; + public static final String LABEL_OLD_PIN = "label.old.pin"; public static final String LABEL_NEW_PIN = "label.new.pin"; public static final String LABEL_REPEAT_PIN = "label.repeat.pin"; diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java index 4cb84b77..83ff74f2 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java @@ -22,8 +22,6 @@ import java.awt.Color; import java.awt.Font; import java.util.ResourceBundle; import javax.swing.table.DefaultTableCellRenderer; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; /** * diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/ManagementPINProviderFactory.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/ManagementPINProviderFactory.java index b0dd8766..d635b8df 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/ManagementPINProviderFactory.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/ManagementPINProviderFactory.java @@ -19,6 +19,7 @@ package at.gv.egiz.bku.smccstal.ext; import at.gv.egiz.smcc.ChangePINProvider; import at.gv.egiz.bku.gui.PINManagementGUIFacade; +import at.gv.egiz.smcc.ccid.CCID; import at.gv.egiz.smcc.PINProvider; import at.gv.egiz.smcc.SignatureCard; @@ -33,13 +34,13 @@ public abstract class ManagementPINProviderFactory { public static ManagementPINProviderFactory getInstance(SignatureCard forCard, PINManagementGUIFacade gui) { -// if (forCard.ifdSupportsFeature(SignatureCard.FEATURE_VERIFY_PIN_DIRECT)) { -//// forCard.ifdSupportsFeature(SignatureCard.FEATURE_MODIFY_PIN_DIRECT) -// return new PinpadPINProviderFactory(gui); -// -// } else { + if (forCard.getReader().hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { +// forCard.ifdSupportsFeature(SignatureCard.FEATURE_MODIFY_PIN_DIRECT) + return new PinpadPINProviderFactory(gui); + + } else { return new SoftwarePINProviderFactory(gui); -// } + } } public abstract PINProvider getVerifyPINProvider(); diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PinpadPINProviderFactory.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PinpadPINProviderFactory.java index 4176e0a9..a9ad5ef8 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PinpadPINProviderFactory.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PinpadPINProviderFactory.java @@ -73,23 +73,6 @@ public class PinpadPINProviderFactory extends ManagementPINProviderFactory { showPinpadPINDialog(retries, spec); retry = true; return null; - -// gui.showPINDialog(type, spec, (retry) ? retries : -1, -// this, "exec", -// this, "back"); -// -// waitForAction(); -// -// if ("exec".equals(action)) { -// gui.showWaitDialog(null); -// retry = true; -// return gui.getPin(); -// } else if ("back".equals(action)) { -// throw new CancelledException(); -// } else { -// log.error("unsupported command " + action); -// throw new CancelledException(); -// } } /** @@ -111,14 +94,38 @@ public class PinpadPINProviderFactory extends ManagementPINProviderFactory { title = BKUGUIFacade.TITLE_RETRY; message = BKUGUIFacade.MESSAGE_RETRIES; params = new Object[]{String.valueOf(retries)}; - } else { - title = BKUGUIFacade.TITLE_SIGN; + } else if (type == DIALOG.VERIFY) { + title = PINManagementGUIFacade.TITLE_VERIFY_PIN; message = BKUGUIFacade.MESSAGE_ENTERPIN_PINPAD; String pinSize = String.valueOf(pinSpec.getMinLength()); if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { pinSize += "-" + pinSpec.getMaxLength(); } params = new Object[]{pinSpec.getLocalizedName(), pinSize}; + } else if (type == DIALOG.ACTIVATE) { + title = PINManagementGUIFacade.TITLE_ACTIVATE_PIN; + message = PINManagementGUIFacade.MESSAGE_ACTIVATEPIN_PINPAD; + String pinSize = String.valueOf(pinSpec.getMinLength()); + if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { + pinSize += "-" + pinSpec.getMaxLength(); + } + params = new Object[]{pinSpec.getLocalizedName(), pinSize}; + } else if (type == DIALOG.CHANGE) { + title = PINManagementGUIFacade.TITLE_CHANGE_PIN; + message = PINManagementGUIFacade.MESSAGE_CHANGEPIN_PINPAD; + String pinSize = String.valueOf(pinSpec.getMinLength()); + if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { + pinSize += "-" + pinSpec.getMaxLength(); + } + params = new Object[]{pinSpec.getLocalizedName(), pinSize}; + } else { //if (type == DIALOG.UNBLOCK) { + title = PINManagementGUIFacade.TITLE_UNBLOCK_PIN; + message = PINManagementGUIFacade.MESSAGE_UNBLOCKPIN_PINPAD; + String pinSize = String.valueOf(pinSpec.getMinLength()); + if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { + pinSize += "-" + pinSpec.getMaxLength(); + } + params = new Object[]{pinSpec.getLocalizedName(), pinSize}; } gui.showMessageDialog(title, message, params); } diff --git a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties index 4ceacb21..c6d219d4 100644 --- a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties +++ b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties @@ -24,12 +24,14 @@ title.change.success=<html>Erfolg</html> # removed message.* prefix to reuse keys as help keys pin.mgmt=<html>Die Karte verf\u00FCgt \u00FCber {0} PINs</html> -pinpad=<html>{0} ({1} stellig) am Kartenleser eingeben und best\u00E4tigen.</html> -pinpad.change=<html>{0} ({1} stellig) am Kartenleser eingeben und best\u00E4tigen.</html> activate.pin=<html>{0} eingeben und best\u00E4tigen</html> change.pin=<html>{0} eingeben und best\u00E4tigen</html> unblock.pin=<html>PUK zu {0} eingeben</html> verify.pin=<html>{0} eingeben (TODO: Warning not activated)</html> +verify.pinpad=<html>{0} ({1} stellig) am Kartenleser eingeben (und best\u00E4tigen).</html> +activate.pinpad=<html>{0} ({1} stellig) am Kartenleser eingeben und wiederholen (jeweils best\u00E4tigen).</html> +change.pinpad=<html>Alte {0} ({1} stellig) am Kartenleser eingeben, danach neue {0} eingeben und wiederholen (jeweils best\u00E4tigen). </html> +unblock.pinpad=<html>{0} ({1} stellig) am Kartenleser eingeben (und best\u00E4tigen).</html> activate.success=<html>{0} wurde erfolgreich aktiviert.</html> change.success=<html>{0} wurde erfolgreich ge\u00E4ndert.</html> diff --git a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties index 9178d65c..b4bededf 100644 --- a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties +++ b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties @@ -23,11 +23,13 @@ title.change.success=<html>Success</html> # removed message.* prefix to reuse keys as help keys pin.mgmt=<html>The smartcard has {0} PINs</html> -pinpad=<html>Enter {0} ({1} digits) on pinpad and confirm.</html> -pinpad.change=<html>Enter {0} ({1} digits) on pinpad and confirm.</html> activate.pin=<html>Enter and confirm {0}</html> change.pin=<html>Enter and confirm {0}</html> unblock.pin=<html>Enter PUK for {0}</html> +verify.pinpad=<html>Enter {0} ({1} digits) on cardreader (and confirm).</html> +activate.pinpad=<html>Enter {0} ({1} digits) on cardreader and repeat (confirm in each case).</html> +change.pinpad=<html>Enter old {0} ({1} digits) on cardreader, then enter new {0} and repeat (confirm in each case).</html> +unblock.pinpad=<html>Enter {0} ({1} digits) on cardreader (and confirm).</html> activate.success=<html>{0} successfully activated</html> change.success=<html>{0} successfully changed</html> diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java index 1043b6a1..4b079428 100644 --- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java +++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java @@ -43,6 +43,7 @@ public interface BKUGUIFacade { public static final String MESSAGES_BUNDLE = "at/gv/egiz/bku/gui/Messages"; public static final String DEFAULT_BACKGROUND = "/images/BackgroundChipperling.png"; + public static final String DEFAULT_ICON = "/images/ChipperlingLogo.png"; public static final String HELP_IMG = "/images/help.png"; public static final String HASHDATA_FONT = "Monospaced"; public static final Color ERROR_COLOR = Color.RED; @@ -58,6 +59,7 @@ public interface BKUGUIFacade { public static final String TITLE_WAIT = "title.wait"; public static final String TITLE_HASHDATA = "title.hashdata"; public static final String WINDOWTITLE_SAVE = "windowtitle.save"; + public static final String WINDOWTITLE_ERROR = "windowtitle.error"; public static final String WINDOWTITLE_SAVEDIR = "windowtitle.savedir"; public static final String WINDOWTITLE_OVERWRITE = "windowtitle.overwrite"; public static final String WINDOWTITLE_VIEWER = "windowtitle.viewer"; @@ -102,42 +104,36 @@ public interface BKUGUIFacade { public enum Style { tiny, simple, advanced }; -// public void init(Container contentPane, Locale locale, Style guiStyle, URL background, ActionListener helpListener); - /** * BKUWorker needs to init signature card with locale * @return */ public Locale getLocale(); -// public void showWelcomeDialog(); - - /** - * - * @param waitMessage if null, a simple 'please wait' text is displayed - */ -// public void showWaitDialog(String waitMessage); - -// public void showInsertCardDialog(ActionListener cancelListener, String actionCommand); - -// public void showCardNotSupportedDialog(ActionListener cancelListener, String actionCommand); - public void showCardPINDialog(PINSpec pinSpec, int numRetries, ActionListener okListener, String okCommand, ActionListener cancelListener, String cancelCommand); -// public void showCardPINRetryDialog(PINSpec pinSpec, int numRetries, ActionListener okListener, String okCommand, ActionListener cancelListener, String cancelCommand); - - public void showSignaturePINDialog(PINSpec pinSpec, int numRetries, ActionListener signListener, String signCommand, ActionListener cancelListener, String cancelCommand, ActionListener hashdataListener, String hashdataCommand); - -// public void showSignaturePINRetryDialog(PINSpec pinSpec, int numRetries, ActionListener okListener, String okCommand, ActionListener cancelListener, String cancelCommand, ActionListener hashdataListener, String hashdataCommand); + public void showSignaturePINDialog(PINSpec pinSpec, int numRetries, + ActionListener signListener, String signCommand, + ActionListener cancelListener, String cancelCommand, + ActionListener viewerListener, String viewerCommand); -// public void showPinpadSignaturePINDialog(PINSpec pinSpec, int retries); + public void showPinpadSignaturePINDialog(PINSpec pinSpec, int numRetries, + ActionListener viewerListener, String viewerCommand); public char[] getPin(); - public void showSecureViewer(List<HashDataInput> signedReferences, - ActionListener okListener, String okCommand); + /** + * + * @param dataToBeSigned + * @param backListener if list of references hides pin dialog, backListener + * receives an action when user hits 'back' button (i.e. whenever the pin-dialog + * needs to be re-paint) + * @param backCommand + */ + public void showSecureViewer(List<HashDataInput> dataToBeSigned, + ActionListener backListener, String backCommand); public void showErrorDialog(String errorMsgKey, Object[] errorMsgParams, ActionListener okListener, String okCommand); diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java index 928be249..a7eebbfd 100644 --- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java +++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java @@ -71,6 +71,7 @@ public class BKUGUIImpl implements BKUGUIFacade { } protected HelpMouseListener helpListener; + protected SecureViewerDialog secureViewer; protected Container contentPane; protected ResourceBundle messages; @@ -145,13 +146,16 @@ public class BKUGUIImpl implements BKUGUIFacade { @Override public void run() { - log.debug("initializing gui"); + log.debug("initializing gui [" + Thread.currentThread().getName() + "]"); if (renderIconPanel) { initIconPanel(background); initContentPanel(null); } else { - initContentPanel(background); + initContentPanel((background == null) ? + getClass().getResource(DEFAULT_BACKGROUND) : + background + ); } GroupLayout layout = new GroupLayout(contentPane); @@ -159,15 +163,27 @@ public class BKUGUIImpl implements BKUGUIFacade { if (renderIconPanel) { layout.setHorizontalGroup(layout.createSequentialGroup() - .addComponent(iconPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(contentPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)); - layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addComponent(iconPanel, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(contentPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)); + .addContainerGap() + .addComponent(iconPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(contentPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()); + layout.setVerticalGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(iconPanel, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(contentPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()); } else { - layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(contentPanel)); - layout.setVerticalGroup(layout.createSequentialGroup().addComponent(contentPanel)); + layout.setHorizontalGroup(layout.createSequentialGroup() + // left border + .addContainerGap() + .addComponent(contentPanel) + .addContainerGap()); + layout.setVerticalGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(contentPanel) + .addContainerGap()); } } }); @@ -178,11 +194,12 @@ public class BKUGUIImpl implements BKUGUIFacade { protected void initIconPanel(URL background) { if (background == null) { - background = getClass().getResource(DEFAULT_BACKGROUND); + background = getClass().getResource(DEFAULT_ICON); } if ("file".equals(background.getProtocol())) { - log.warn("file:// background images not permitted: " + background); - background = getClass().getResource(DEFAULT_BACKGROUND); + log.warn("file:// background images not permitted: " + background + + ", loading default background"); + background = getClass().getResource(DEFAULT_ICON); } log.debug("loading icon panel background " + background); @@ -194,26 +211,24 @@ public class BKUGUIImpl implements BKUGUIFacade { iconPanel.setLayout(iconPanelLayout); iconPanelLayout.setHorizontalGroup( iconPanelLayout.createSequentialGroup() - .addContainerGap() .addComponent(iconLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)); - // no gap here (contentPanel has containerGap) iconPanelLayout.setVerticalGroup( iconPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(iconLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)); + .addComponent(iconLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)); } protected void initContentPanel(URL background) { +// if (background == null) { +// background = getClass().getResource(DEFAULT_BACKGROUND); +// } if (background == null) { - background = getClass().getResource(DEFAULT_BACKGROUND); - } - if (background == null) { + log.debug("no background image set"); contentPanel = new JPanel(); } else { if ("file".equals(background.getProtocol())) { - log.warn("file:// background images not permitted: " + background); + log.warn("file:// background images not permitted: " + background + + ", loading default background"); background = getClass().getResource(DEFAULT_BACKGROUND); } log.debug("loading background " + background); @@ -257,34 +272,29 @@ public class BKUGUIImpl implements BKUGUIFacade { GroupLayout contentPanelLayout = new GroupLayout(contentPanel); contentPanel.setLayout(contentPanelLayout); - GroupLayout.ParallelGroup horizontalContentInner = contentPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING); + // align header, main and button to the right + GroupLayout.ParallelGroup horizontalContent = + contentPanelLayout.createParallelGroup(GroupLayout.Alignment.TRAILING); //LEADING); + GroupLayout.SequentialGroup verticalContent = + contentPanelLayout.createSequentialGroup(); + if (renderHeaderPanel) { - horizontalContentInner + horizontalContent .addComponent(headerPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE); + verticalContent + .addComponent(headerPanel, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED); + } - horizontalContentInner + horizontalContent .addComponent(mainPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(buttonPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE); - GroupLayout.SequentialGroup horizontalContentOuter = contentPanelLayout.createSequentialGroup(); - if (!renderIconPanel) { - horizontalContentOuter - .addContainerGap(); - } - horizontalContentOuter - .addGroup(horizontalContentInner) - .addContainerGap(); - contentPanelLayout.setHorizontalGroup(horizontalContentOuter); - - GroupLayout.SequentialGroup verticalContent = contentPanelLayout.createSequentialGroup(); - verticalContent.addContainerGap(); - if (renderHeaderPanel) { - verticalContent.addComponent(headerPanel, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED); - } - verticalContent.addComponent(mainPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(buttonPanel, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap(); + .addComponent(buttonPanel, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE); //Short.MAX_VALUE); + verticalContent + .addComponent(mainPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(buttonPanel, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE); + + contentPanelLayout.setHorizontalGroup(horizontalContent); //Outer); contentPanelLayout.setVerticalGroup(verticalContent); } @@ -521,7 +531,7 @@ public class BKUGUIImpl implements BKUGUIFacade { @Override public void run() { - log.debug("show card-pin dialog"); + log.debug("show card-pin dialog [" + Thread.currentThread().getName() + "]"); mainPanel.removeAll(); buttonPanel.removeAll(); @@ -653,7 +663,6 @@ public class BKUGUIImpl implements BKUGUIFacade { buttonPanel.setLayout(buttonPanelLayout); GroupLayout.SequentialGroup buttonHorizontal = buttonPanelLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(okButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); GroupLayout.Group buttonVertical; @@ -701,6 +710,128 @@ public class BKUGUIImpl implements BKUGUIFacade { // } @Override + public void showPinpadSignaturePINDialog(final PINSpec pinSpec, final int numRetries, +// final ActionListener cancelListener, final String cancelCommand, + final ActionListener hashdataListener, final String hashdataCommand) { + + log.debug("scheduling pinpad signature-pin dialog"); + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + log.debug("show pinpad signature-pin dialog [" + Thread.currentThread().getName() + "]"); + + mainPanel.removeAll(); + buttonPanel.removeAll(); + + if (renderHeaderPanel) { + if (numRetries < 0) { + titleLabel.setText(getMessage(TITLE_SIGN)); + } else { + titleLabel.setText(getMessage(TITLE_RETRY)); + } + } + + JLabel infoLabel = new JLabel(); + if (numRetries < 0) { + infoLabel.setFont(infoLabel.getFont().deriveFont(infoLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); + if (shortText) { + infoLabel.setText(getMessage(MESSAGE_HASHDATALINK_TINY)); + } else { + infoLabel.setText(getMessage(MESSAGE_HASHDATALINK)); + } + infoLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + infoLabel.setForeground(HYPERLINK_COLOR); + infoLabel.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent me) { + ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, hashdataCommand); + hashdataListener.actionPerformed(e); + } + }); + helpListener.setHelpTopic(HELP_SIGNPIN); + } else { + String retryPattern; + if (numRetries < 2) { + retryPattern = getMessage(MESSAGE_LAST_RETRY); + } else { + retryPattern = getMessage(MESSAGE_RETRIES); + } + infoLabel.setText(MessageFormat.format(retryPattern, new Object[]{String.valueOf(numRetries)})); + infoLabel.setFont(infoLabel.getFont().deriveFont(infoLabel.getFont().getStyle() | java.awt.Font.BOLD)); + infoLabel.setForeground(ERROR_COLOR); + helpListener.setHelpTopic(HELP_RETRY); + } + + String pinSize = String.valueOf(pinSpec.getMinLength()); + if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { + pinSize += "-" + pinSpec.getMaxLength(); + } + + String msgPattern = getMessage(MESSAGE_ENTERPIN_PINPAD); + String msg = MessageFormat.format(msgPattern, new Object[] { + pinSpec.getLocalizedName(), pinSize }); + + JLabel msgLabel = new JLabel(); + msgLabel.setFont(msgLabel.getFont().deriveFont(msgLabel.getFont().getStyle() & ~Font.BOLD)); + msgLabel.setText(msg); + + GroupLayout mainPanelLayout = new GroupLayout(mainPanel); + mainPanel.setLayout(mainPanelLayout); + + GroupLayout.SequentialGroup infoHorizontal = mainPanelLayout.createSequentialGroup() + .addComponent(infoLabel); + GroupLayout.ParallelGroup infoVertical = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(infoLabel); + + if (!renderHeaderPanel) { + infoHorizontal + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) + .addComponent(helpLabel); + infoVertical + .addComponent(helpLabel); + } + + mainPanelLayout.setHorizontalGroup( + mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(infoHorizontal) + .addComponent(msgLabel)); + + mainPanelLayout.setVerticalGroup( + mainPanelLayout.createSequentialGroup() + .addGroup(infoVertical) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(msgLabel)); + + //no cancel button (cancel via pinpad) +// if (renderCancelButton) { +// JButton cancelButton = new JButton(); +// cancelButton.setFont(cancelButton.getFont().deriveFont(cancelButton.getFont().getStyle() & ~java.awt.Font.BOLD)); +// cancelButton.setText(getMessage(BUTTON_CANCEL)); +// cancelButton.setActionCommand(cancelCommand); +// cancelButton.addActionListener(cancelListener); +// +// GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); +// buttonPanel.setLayout(buttonPanelLayout); +// +// GroupLayout.SequentialGroup buttonHorizontal = buttonPanelLayout.createSequentialGroup() +// .addComponent(cancelButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); +// GroupLayout.SequentialGroup buttonVertical = buttonPanelLayout.createSequentialGroup() +// .addComponent(cancelButton); +// +// buttonPanelLayout.setHorizontalGroup(buttonHorizontal); +// buttonPanelLayout.setVerticalGroup(buttonVertical); +// } + + contentPanel.validate(); + } + }); + } + + @Override public void showSignaturePINDialog(final PINSpec pinSpec, final int numRetries, final ActionListener signListener, final String signCommand, final ActionListener cancelListener, final String cancelCommand, @@ -717,7 +848,7 @@ public class BKUGUIImpl implements BKUGUIFacade { @Override public void run() { - log.debug("show signature-pin dialog"); + log.debug("show signature-pin dialog [" + Thread.currentThread().getName() + "]"); mainPanel.removeAll(); buttonPanel.removeAll(); @@ -730,6 +861,38 @@ public class BKUGUIImpl implements BKUGUIFacade { } } + JLabel infoLabel = new JLabel(); + if (numRetries < 0) { + infoLabel.setFont(infoLabel.getFont().deriveFont(infoLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); + if (shortText) { + infoLabel.setText(getMessage(MESSAGE_HASHDATALINK_TINY)); + } else { + infoLabel.setText(getMessage(MESSAGE_HASHDATALINK)); + } + infoLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + infoLabel.setForeground(HYPERLINK_COLOR); + infoLabel.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent me) { + ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, hashdataCommand); + hashdataListener.actionPerformed(e); + } + }); + helpListener.setHelpTopic(HELP_SIGNPIN); + } else { + String retryPattern; + if (numRetries < 2) { + retryPattern = getMessage(MESSAGE_LAST_RETRY); + } else { + retryPattern = getMessage(MESSAGE_RETRIES); + } + infoLabel.setText(MessageFormat.format(retryPattern, new Object[]{String.valueOf(numRetries)})); + infoLabel.setFont(infoLabel.getFont().deriveFont(infoLabel.getFont().getStyle() | java.awt.Font.BOLD)); + infoLabel.setForeground(ERROR_COLOR); + helpListener.setHelpTopic(HELP_RETRY); + } + JButton signButton = new JButton(); signButton.setFont(signButton.getFont().deriveFont(signButton.getFont().getStyle() & ~java.awt.Font.BOLD)); signButton.setText(getMessage(BUTTON_SIGN)); @@ -765,46 +928,14 @@ public class BKUGUIImpl implements BKUGUIFacade { } pinsizeLabel.setText(MessageFormat.format(pinsizePattern, new Object[]{pinSize})); - JLabel infoLabel = new JLabel(); - if (numRetries < 0) { - infoLabel.setFont(infoLabel.getFont().deriveFont(infoLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); - if (shortText) { - infoLabel.setText(getMessage(MESSAGE_HASHDATALINK_TINY)); - } else { - infoLabel.setText(getMessage(MESSAGE_HASHDATALINK)); - } - infoLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - infoLabel.setForeground(HYPERLINK_COLOR); - infoLabel.addMouseListener(new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent me) { - ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, hashdataCommand); - hashdataListener.actionPerformed(e); - } - }); - helpListener.setHelpTopic(HELP_SIGNPIN); - } else { - String retryPattern; - if (numRetries < 2) { - retryPattern = getMessage(MESSAGE_LAST_RETRY); - } else { - retryPattern = getMessage(MESSAGE_RETRIES); - } - infoLabel.setText(MessageFormat.format(retryPattern, new Object[]{String.valueOf(numRetries)})); - infoLabel.setFont(infoLabel.getFont().deriveFont(infoLabel.getFont().getStyle() | java.awt.Font.BOLD)); - infoLabel.setForeground(ERROR_COLOR); - helpListener.setHelpTopic(HELP_RETRY); - } - GroupLayout mainPanelLayout = new GroupLayout(mainPanel); mainPanel.setLayout(mainPanelLayout); - + GroupLayout.SequentialGroup infoHorizontal = mainPanelLayout.createSequentialGroup() .addComponent(infoLabel); GroupLayout.ParallelGroup infoVertical = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(infoLabel); - + if (!renderHeaderPanel) { infoHorizontal .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) @@ -814,8 +945,8 @@ public class BKUGUIImpl implements BKUGUIFacade { } // align pinfield and pinsize to the right - GroupLayout.ParallelGroup pinHorizontal = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.TRAILING); - GroupLayout.Group pinVertical; + GroupLayout.Group pinHorizontal = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.TRAILING); + GroupLayout.SequentialGroup pinVertical = mainPanelLayout.createSequentialGroup(); if (pinLabelPos == PinLabelPosition.ABOVE) { pinHorizontal @@ -823,20 +954,25 @@ public class BKUGUIImpl implements BKUGUIFacade { .addComponent(signPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(pinsizeLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE); - pinVertical = mainPanelLayout.createSequentialGroup() + pinVertical .addComponent(signPinLabel) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE); - } else { + .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinsizeLabel); + } else { // PinLabelPosition.LEFT pinHorizontal .addGroup(mainPanelLayout.createSequentialGroup() .addComponent(signPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(pinsizeLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE); - pinVertical = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(signPinLabel) - .addComponent(pinField); + pinVertical + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(signPinLabel) + .addComponent(pinField)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinsizeLabel); } mainPanelLayout.setHorizontalGroup( @@ -848,18 +984,14 @@ public class BKUGUIImpl implements BKUGUIFacade { mainPanelLayout.createSequentialGroup() .addGroup(infoVertical) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addGroup(pinVertical) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pinsizeLabel)); + .addGroup(pinVertical)); GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); buttonPanel.setLayout(buttonPanelLayout); - GroupLayout.SequentialGroup buttonHorizontal = buttonPanelLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(signButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); + GroupLayout.SequentialGroup buttonHorizontal = buttonPanelLayout.createSequentialGroup(); GroupLayout.Group buttonVertical; - + if (renderCancelButton) { JButton cancelButton = new JButton(); cancelButton.setFont(cancelButton.getFont().deriveFont(cancelButton.getFont().getStyle() & ~java.awt.Font.BOLD)); @@ -868,17 +1000,19 @@ public class BKUGUIImpl implements BKUGUIFacade { cancelButton.addActionListener(cancelListener); buttonHorizontal + .addComponent(signButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); - buttonVertical = buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(signButton) - .addComponent(cancelButton); + .addComponent(cancelButton); } else { + buttonHorizontal + .addComponent(signButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); buttonVertical = buttonPanelLayout.createSequentialGroup() .addComponent(signButton); } - + buttonPanelLayout.setHorizontalGroup(buttonHorizontal); buttonPanelLayout.setVerticalGroup(buttonVertical); @@ -951,7 +1085,7 @@ public class BKUGUIImpl implements BKUGUIFacade { @Override public void run() { - log.debug("show message dialog"); + log.debug("show message dialog [" + Thread.currentThread().getName() + "]"); mainPanel.removeAll(); buttonPanel.removeAll(); @@ -1011,7 +1145,6 @@ public class BKUGUIImpl implements BKUGUIFacade { buttonPanelLayout.setHorizontalGroup( buttonPanelLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(okButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)); buttonPanelLayout.setVerticalGroup( buttonPanelLayout.createSequentialGroup() @@ -1084,68 +1217,69 @@ public class BKUGUIImpl implements BKUGUIFacade { } return null; } + + + //////////////////////////////////////////////////////////////////////////// + // SECURE VIEWER + //////////////////////////////////////////////////////////////////////////// + /** - * TODO handle multiple references in HashDataViewer * @param signedReferences - * @param okListener + * @param backListener gets notified if pin-dialog has to be redrawn + * (signedRefencesList returns via BACK button) * @param okCommand */ @Override - public void showSecureViewer(final List<HashDataInput> signedReferences, - final ActionListener okListener, - final String okCommand) { - - if (signedReferences == null) { - showErrorDialog(getMessage(ERR_NO_HASHDATA), new Object[] {"No SignedReferences provided"}, okListener, okCommand); - return; - } + public void showSecureViewer(final List<HashDataInput> dataToBeSigned, + final ActionListener backListener, final String backCommand) { - if (signedReferences.size() == 1) { + if (dataToBeSigned == null) { + showErrorDialog(getMessage(ERR_NO_HASHDATA), + new Object[] {"no signature data provided"}, + backListener, backCommand); + } else if (dataToBeSigned.size() == 1) { try { - log.debug("scheduling hashdata viewer"); + log.debug("scheduling secure viewer"); - SwingUtilities.invokeAndWait(new Runnable() { + SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - ActionListener saveHashDataListener = new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - HashDataInput hdi = signedReferences.get(0); - showSaveHashDataInputDialog(Collections.singletonList(hdi), okListener, okCommand); - } - }; - showHashDataViewer(signedReferences.get(0), saveHashDataListener, "save"); + showSecureViewer(dataToBeSigned.get(0)); } }); - } catch (InterruptedException ex) { - log.error("Failed to display HashDataViewer: " + ex.getMessage()); - } catch (InvocationTargetException ex) { - log.error("Failed to display HashDataViewer: " + ex.getMessage()); + } catch (Exception ex) { //InterruptedException InvocationTargetException + log.error("Failed to display secure viewer: " + ex.getMessage()); + log.trace(ex); + showErrorDialog(ERR_UNKNOWN, null, backListener, backCommand); } } else { - showSignedReferencesListDialog(signedReferences, okListener, okCommand); + showSignedReferencesListDialog(dataToBeSigned, backListener, backCommand); } } /** * has to be called from event dispatcher thread + * This method blocks until the dialog's close button is pressed. * @param hashDataText * @param saveListener * @param saveCommand */ - private void showHashDataViewer(final HashDataInput hashDataInput, final ActionListener saveListener, final String saveCommand) { + private void showSecureViewer(HashDataInput dataToBeSigned) { - log.debug("show hashdata viewer"); - - ActionListener l = helpListener.getActionListener(); - HashDataViewer.showHashDataInput(contentPane, hashDataInput, messages, saveListener, saveCommand, l); + log.debug("show secure viewer [" + Thread.currentThread().getName() + "]"); + if (secureViewer == null) { + secureViewer = new SecureViewerDialog(null, messages, + helpListener.getActionListener()); + } + secureViewer.setContent(dataToBeSigned); + log.trace("show secure viewer returned"); } - private void showSignedReferencesListDialog(final List<HashDataInput> signedReferences, final ActionListener backListener, final String backCommand) { + private void showSignedReferencesListDialog(final List<HashDataInput> signedReferences, + final ActionListener backListener, final String backCommand) { log.debug("scheduling signed references list dialog"); @@ -1154,7 +1288,7 @@ public class BKUGUIImpl implements BKUGUIFacade { @Override public void run() { - log.debug("show signed references list dialog"); + log.debug("show signed references list dialog [" + Thread.currentThread().getName() + "]"); mainPanel.removeAll(); buttonPanel.removeAll(); @@ -1202,13 +1336,7 @@ public class BKUGUIImpl implements BKUGUIFacade { int selectionIdx = lsm.getMinSelectionIndex(); if (selectionIdx >= 0) { final HashDataInput selection = signedReferences.get(selectionIdx); - showHashDataViewer(selection, new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - showSaveHashDataInputDialog(Collections.singletonList(selection), null, null); - } - }, "save"); + showSecureViewer(selection); } } }); @@ -1255,9 +1383,8 @@ public class BKUGUIImpl implements BKUGUIFacade { buttonPanel.setLayout(buttonPanelLayout); buttonPanelLayout.setHorizontalGroup(buttonPanelLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(backButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)); - buttonPanelLayout.setVerticalGroup(buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + buttonPanelLayout.setVerticalGroup(buttonPanelLayout.createSequentialGroup() .addComponent(backButton)); contentPanel.validate(); @@ -1266,96 +1393,101 @@ public class BKUGUIImpl implements BKUGUIFacade { } /** - * - * @param signedRefs * @param okListener may be null - * @param okCommand */ - private void showSaveHashDataInputDialog(final List<HashDataInput> signedRefs, final ActionListener okListener, final String okCommand) { - - log.debug("scheduling save hashdatainput dialog"); - - SwingUtilities.invokeLater(new Runnable() { +// private void showSaveDialog(final List<HashDataInput> signedRefs, +// final ActionListener okListener, final String okCommand) { +// +// log.debug("scheduling save dialog"); +// +// SwingUtilities.invokeLater(new Runnable() { +// +// @Override +// public void run() { +// +// log.debug("show save dialog"); +// +// String userHome = System.getProperty("user.home"); +// +// JFileChooser fileDialog = new JFileChooser(userHome); +// fileDialog.setMultiSelectionEnabled(false); +// fileDialog.setDialogType(JFileChooser.SAVE_DIALOG); +// fileDialog.setFileHidingEnabled(true); +// if (signedRefs.size() == 1) { +// fileDialog.setDialogTitle(getMessage(WINDOWTITLE_SAVE)); +// fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY); +// String mimeType = signedRefs.get(0).getMimeType(); +// MimeFilter mimeFilter = new MimeFilter(mimeType, messages); +// fileDialog.setFileFilter(mimeFilter); +// String filename = getMessage(SAVE_HASHDATAINPUT_PREFIX) + MimeFilter.getExtension(mimeType); +// fileDialog.setSelectedFile(new File(userHome, filename)); +// } else { +// fileDialog.setDialogTitle(getMessage(WINDOWTITLE_SAVEDIR)); +// fileDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); +// } +// +// //parent contentPane -> placed over applet +// switch (fileDialog.showSaveDialog(fileDialog)) { +// case JFileChooser.APPROVE_OPTION: +// File f = fileDialog.getSelectedFile(); +// for (HashDataInput hashDataInput : signedRefs) { +// String mimeType = hashDataInput.getMimeType(); +// String id = hashDataInput.getReferenceId(); +// File file; +// if (f.isDirectory()) { +// String filename = getMessage(SAVE_HASHDATAINPUT_PREFIX) + '_' + id + MimeFilter.getExtension(mimeType); +// file = new File(f, filename); +// } else { +// file = f; +// } +// if (file.exists()) { +// String ovrwrt = getMessage(MESSAGE_OVERWRITE); +// int overwrite = JOptionPane.showConfirmDialog(fileDialog, MessageFormat.format(ovrwrt, file), getMessage(WINDOWTITLE_OVERWRITE), JOptionPane.OK_CANCEL_OPTION); +// if (overwrite != JOptionPane.OK_OPTION) { +// continue; +// } +// } +// if (log.isDebugEnabled()) { +// log.debug("writing hashdata input " + id + " (" + mimeType + ") to file " + file); +// } +// FileOutputStream fos = null; +// try { +// fos = new FileOutputStream(file); +// BufferedOutputStream bos = new BufferedOutputStream(fos); +// InputStream hdi = hashDataInput.getHashDataInput(); +// int b; +// while ((b = hdi.read()) != -1) { +// bos.write(b); +// } +// bos.flush(); +// bos.close(); +// } catch (IOException ex) { +// log.error("Failed to write " + file + ": " + ex.getMessage()); +// showErrorDialog(ERR_WRITE_HASHDATA, new Object[] {ex.getMessage()}, null, null); +// ex.printStackTrace(); +// } finally { +// try { +// fos.close(); +// } catch (IOException ex) { +// } +// } +// } +// break; +// case JFileChooser.CANCEL_OPTION : +// log.debug("cancelled save dialog"); +// break; +// } +// if (okListener != null) { +// okListener.actionPerformed(new ActionEvent(fileDialog, ActionEvent.ACTION_PERFORMED, okCommand)); +// } +// } +// }); +// } + + //////////////////////////////////////////////////////////////////////////// + // UTILITY METHODS + //////////////////////////////////////////////////////////////////////////// - @Override - public void run() { - - log.debug("show save hashdatainput dialog"); - - String userHome = System.getProperty("user.home"); - - JFileChooser fileDialog = new JFileChooser(userHome); - fileDialog.setMultiSelectionEnabled(false); - fileDialog.setDialogType(JFileChooser.SAVE_DIALOG); - fileDialog.setFileHidingEnabled(true); - if (signedRefs.size() == 1) { - fileDialog.setDialogTitle(getMessage(WINDOWTITLE_SAVE)); - fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY); - String mimeType = signedRefs.get(0).getMimeType(); - MimeFilter mimeFilter = new MimeFilter(mimeType, messages); - fileDialog.setFileFilter(mimeFilter); - String filename = getMessage(SAVE_HASHDATAINPUT_PREFIX) + MimeFilter.getExtension(mimeType); - fileDialog.setSelectedFile(new File(userHome, filename)); - } else { - fileDialog.setDialogTitle(getMessage(WINDOWTITLE_SAVEDIR)); - fileDialog.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - } - - //parent contentPane -> placed over applet - switch (fileDialog.showSaveDialog(fileDialog)) { - case JFileChooser.APPROVE_OPTION: - File f = fileDialog.getSelectedFile(); - for (HashDataInput hashDataInput : signedRefs) { - String mimeType = hashDataInput.getMimeType(); - String id = hashDataInput.getReferenceId(); - File file; - if (f.isDirectory()) { - String filename = getMessage(SAVE_HASHDATAINPUT_PREFIX) + '_' + id + MimeFilter.getExtension(mimeType); - file = new File(f, filename); - } else { - file = f; - } - if (file.exists()) { - String ovrwrt = getMessage(MESSAGE_OVERWRITE); - int overwrite = JOptionPane.showConfirmDialog(fileDialog, MessageFormat.format(ovrwrt, file), getMessage(WINDOWTITLE_OVERWRITE), JOptionPane.OK_CANCEL_OPTION); - if (overwrite != JOptionPane.OK_OPTION) { - continue; - } - } - if (log.isDebugEnabled()) { - log.debug("Writing HashDataInput " + id + " (" + mimeType + ") to file " + file); - } - FileOutputStream fos = null; - try { - fos = new FileOutputStream(file); - BufferedOutputStream bos = new BufferedOutputStream(fos); - InputStream hdi = hashDataInput.getHashDataInput(); - int b; - while ((b = hdi.read()) != -1) { - bos.write(b); - } - bos.flush(); - bos.close(); - } catch (IOException ex) { - log.error("Failed to write HashDataInput to file " + file + ": " + ex.getMessage()); - showErrorDialog(ERR_WRITE_HASHDATA, new Object[] {ex.getMessage()}, null, null); - ex.printStackTrace(); - } finally { - try { - fos.close(); - } catch (IOException ex) { - } - } - } - } - log.debug("done saving hashdatainput"); - if (okListener != null) { - okListener.actionPerformed(new ActionEvent(fileDialog, ActionEvent.ACTION_PERFORMED, okCommand)); - } - } - }); - } - private void registerHelpListener(ActionListener helpListener) { if (helpListener != null) { this.helpListener = new HelpMouseListener(helpListener); @@ -1371,6 +1503,11 @@ public class BKUGUIImpl implements BKUGUIFacade { } } + + //////////////////////////////////////////////////////////////////////////// + // INITIALIZERS (MAY BE OVERRIDDEN BY SUBCLASSES) + //////////////////////////////////////////////////////////////////////////// + /** * Called from constructor. * Subclasses may override this method to ensure the message bundle is loaded diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/HashDataViewer.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/HashDataViewer.java deleted file mode 100644 index 6c097b2a..00000000 --- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/HashDataViewer.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright 2008 Federal Chancellery Austria and - * Graz University of Technology - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package at.gv.egiz.bku.gui; - -import at.gv.egiz.bku.gui.html.RestrictedHTMLEditorKit; -import at.gv.egiz.stal.HashDataInput; -import java.awt.Component; -import java.awt.Container; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Frame; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.Charset; -import java.text.MessageFormat; -import java.util.ResourceBundle; -import javax.swing.GroupLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JEditorPane; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.LayoutStyle; -import javax.swing.text.Document; -import javax.swing.text.EditorKit; -import javax.swing.text.StyledEditorKit; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * - * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> - */ -public class HashDataViewer extends JDialog - implements ActionListener { - - public static final String PLAINTEXT_FONT = "Monospaced"; - protected static final Log log = LogFactory.getLog(HashDataViewer.class); - - private static HashDataViewer dialog; - - protected ResourceBundle messages; - - /** - * - * @param signedReferences currently, only one hashdata input (the first in the list) is displayed - */ - public static void showHashDataInput(HashDataInput hashDataInput, - ResourceBundle messages, - ActionListener saveListener, - String saveCommand, - ActionListener helpListener) { - showHashDataInput(null, hashDataInput, messages, saveListener, saveCommand, helpListener); - } - - /** - * - * @param frameComp owner - */ - public static void showHashDataInput(Component frameComp, - HashDataInput hashDataInput, - ResourceBundle messages, - ActionListener saveListener, - String saveCommand, - ActionListener helpListener) { - - Frame frame = null; - if (frameComp != null) { - JOptionPane.getFrameForComponent(frameComp); - } - dialog = new HashDataViewer(frame, - messages, - hashDataInput, - saveListener, - saveCommand, - helpListener); - dialog.setVisible(true); - } - - private HashDataViewer(Frame frame, - ResourceBundle messages, - HashDataInput hashDataInput, - ActionListener saveListener, - String saveCommand, - ActionListener helpListener) { - super(frame, messages.getString(BKUGUIFacade.WINDOWTITLE_VIEWER), true); - this.messages = messages; - - Charset cs; - if (hashDataInput.getEncoding() == null) { - cs = Charset.forName("UTF-8"); - } else { - try { - cs = Charset.forName(hashDataInput.getEncoding()); - } catch (Exception ex) { - log.debug("charset " + hashDataInput.getEncoding() + " not supported, assuming UTF-8: " + ex.getMessage()); - cs = Charset.forName("UTF-8"); - } - } - - - InputStreamReader isr = new InputStreamReader(hashDataInput.getHashDataInput(), cs); - Reader content = new BufferedReader(isr); - - JPanel hashDataPanel = createViewerPanel(content, - hashDataInput.getMimeType(), - helpListener); - JPanel buttonPanel = createButtonPanel(saveListener, saveCommand); - initContentPane(new Dimension(600, 400), hashDataPanel, buttonPanel); - - pack(); - if (frame != null) { - setLocationRelativeTo(frame); - } else { - setLocationByPlatform(true); - } - } - - private void initContentPane(Dimension preferredSize, JPanel viewerPanel, JPanel buttonPanel) { - Container contentPane = getContentPane(); - contentPane.setPreferredSize(preferredSize); - - GroupLayout mainLayout = new GroupLayout(contentPane); - contentPane.setLayout(mainLayout); - - mainLayout.setHorizontalGroup( - mainLayout.createSequentialGroup().addContainerGap().addGroup( - mainLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(viewerPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(buttonPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)).addContainerGap()); - mainLayout.setVerticalGroup( - mainLayout.createSequentialGroup() - .addContainerGap() - .addComponent(viewerPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap()); - } - - /** - * - * @param messages - * @param content - * @param mimeType defaults to text/plain if null - * @param encoding must be null if document contains charset declaration (e.g. HTML page), otherwise the parser crashes - * @param helpListener may be null - * @return - */ - private JPanel createViewerPanel(Reader content, - String mimeType, - final ActionListener helpListener) { - - if (mimeType == null) { - mimeType = "text/plain"; - } - log.debug("viewer dialog: " + mimeType); - - JEditorPane viewer = new JEditorPane(); - viewer.setEditable(false); - viewer.setContentType(mimeType); - - if ("text/plain".equals(mimeType)) { - viewer.setEditorKit(new StyledEditorKit()); - viewer.setFont(new Font(PLAINTEXT_FONT, viewer.getFont().getStyle(), viewer.getFont().getSize())); -// } else if ("text/html".equals(mimeType)) { -// viewer.setEditorKit(new RestrictedHTMLEditorKit()); - } else if ("application/xhtml+xml".equals(mimeType)) { - viewer.setContentType("text/html"); - } - - EditorKit editorKit = viewer.getEditorKit(); - Document document = editorKit.createDefaultDocument(); -// document.putProperty("IgnoreCharsetDirective", new Boolean(true)); - - try { - viewer.read(content, document); - content.close(); - } catch (Exception ex) { - log.error(ex.getMessage(), ex); - String p = messages.getString(BKUGUIFacade.ERR_VIEWER); - viewer.setText(MessageFormat.format(p, ex.getMessage())); - } - - JScrollPane scrollPane = new JScrollPane(viewer); - scrollPane.setPreferredSize(viewer.getPreferredSize()); - scrollPane.setAlignmentX(LEFT_ALIGNMENT); - viewer.setCaretPosition(0); - - JPanel viewerPanel = new JPanel(); - GroupLayout viewerPanelLayout = new GroupLayout(viewerPanel); - viewerPanel.setLayout(viewerPanelLayout); - - GroupLayout.SequentialGroup infoHorizontal = viewerPanelLayout.createSequentialGroup(); - GroupLayout.ParallelGroup infoVertical = viewerPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING); - - if ("application/xhtml+xml".equals(mimeType)) { - JLabel viewerLabel = new JLabel(); - viewerLabel.setText(messages.getString(BKUGUIFacade.WARNING_XHTML)); - viewerLabel.setFont(viewerLabel.getFont().deriveFont(viewerLabel.getFont().getStyle() | java.awt.Font.BOLD)); - viewerLabel.setLabelFor(viewer); - - infoHorizontal.addComponent(viewerLabel); - infoVertical.addComponent(viewerLabel); - } - - if (helpListener != null) { - JLabel helpLabel = new JLabel(); - helpLabel.setIcon(new ImageIcon(getClass().getResource(BKUGUIFacade.HELP_IMG))); - helpLabel.getAccessibleContext().setAccessibleName(messages.getString(BKUGUIFacade.ALT_HELP)); - helpLabel.addMouseListener(new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent arg0) { - ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, BKUGUIFacade.HELP_HASHDATAVIEWER); - helpListener.actionPerformed(e); - } - }); - helpLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - - infoHorizontal - .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) - .addComponent(helpLabel); - infoVertical - .addComponent(helpLabel); - } - - viewerPanelLayout.setHorizontalGroup( - viewerPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup(infoHorizontal) - .addComponent(scrollPane)); - viewerPanelLayout.setVerticalGroup( - viewerPanelLayout.createSequentialGroup() - .addGroup(infoVertical) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(scrollPane)); - - - return viewerPanel; - } - - private JPanel createButtonPanel(ActionListener saveListener, String saveCommand) { - JButton closeButton = new JButton(); - closeButton.setText(messages.getString(BKUGUIFacade.BUTTON_CLOSE)); - closeButton.addActionListener(this); - - JButton saveButton = new JButton(); - saveButton.setText(messages.getString(BKUGUIFacade.BUTTON_SAVE)); - saveButton.setActionCommand(saveCommand); - saveButton.addActionListener(saveListener); - - int buttonSize = closeButton.getPreferredSize().width; - if (saveButton.getPreferredSize().width > buttonSize) { - buttonSize = saveButton.getPreferredSize().width; - } - - JPanel buttonPanel = new JPanel(); - GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); - buttonPanel.setLayout(buttonPanelLayout); - - buttonPanelLayout.setHorizontalGroup( - buttonPanelLayout.createSequentialGroup().addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(saveButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED).addComponent(closeButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)); - buttonPanelLayout.setVerticalGroup( - buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE).addComponent(saveButton).addComponent(closeButton)); - - return buttonPanel; - } - - @Override - public void actionPerformed(ActionEvent e) { - HashDataViewer.dialog.setVisible(false); - } -} diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/SecureViewerDialog.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/SecureViewerDialog.java new file mode 100644 index 00000000..6a16306b --- /dev/null +++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/SecureViewerDialog.java @@ -0,0 +1,371 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.gui; + +import at.gv.egiz.stal.HashDataInput; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.text.MessageFormat; +import java.util.ResourceBundle; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.LayoutStyle; +import javax.swing.SwingUtilities; +import javax.swing.text.Document; +import javax.swing.text.EditorKit; +import javax.swing.text.StyledEditorKit; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class SecureViewerDialog extends JDialog implements ActionListener { + + public static final String PLAINTEXT_FONT = "Monospaced"; + public static final Dimension VIEWER_DIMENSION = new Dimension(600, 400); + protected static final Log log = LogFactory.getLog(SecureViewerDialog.class); + +// private static SecureViewerDialog dialog; + protected ResourceBundle messages; + protected JEditorPane viewer; + protected JLabel viewerLabel; + protected JScrollPane scrollPane; + protected HashDataInput content; //remember for save dialog + + /** + * Create and display a modal SecureViewer dialog. + * This method blocks until the dialog's close button is pressed. + * + * @param owner, dialog is positioned relative to its owner + * (if null, at default location of native windowing system) + */ +// public static void showDataToBeSigned(HashDataInput dataToBeSigned, +// ResourceBundle messages, +// ActionListener saveListener, String saveCommand, +// ActionListener helpListener) { +// +//// Frame ownerFrame = (owner != null) ? +//// JOptionPane.getFrameForComponent(owner) : +//// null; +// dialog = new SecureViewerDialog(null, messages, +// saveListener, saveCommand, helpListener); +// dialog.setContent(dataToBeSigned); +// dialog.setVisible(true); +// } + public SecureViewerDialog(Frame owner, ResourceBundle messages, +// ActionListener saveListener, String saveCommand, + ActionListener helpListener) { + super(owner, messages.getString(BKUGUIFacade.WINDOWTITLE_VIEWER), true); + this.messages = messages; + + initContentPane(VIEWER_DIMENSION, + createViewerPanel(helpListener), + createButtonPanel()); //saveListener, saveCommand)); + + pack(); + if (owner != null) { + setLocationRelativeTo(owner); + } else { + setLocationByPlatform(true); + } + } + + private void initContentPane(Dimension preferredSize, + JPanel viewerPanel, JPanel buttonPanel) { + Container contentPane = getContentPane(); + contentPane.setPreferredSize(preferredSize); + + GroupLayout mainLayout = new GroupLayout(contentPane); + contentPane.setLayout(mainLayout); + + mainLayout.setHorizontalGroup( + mainLayout.createSequentialGroup().addContainerGap().addGroup( + mainLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(viewerPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(buttonPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)).addContainerGap()); + mainLayout.setVerticalGroup( + mainLayout.createSequentialGroup().addContainerGap().addComponent(viewerPanel, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED).addComponent(buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE).addContainerGap()); + } + + /** + * @param helpListener may be null + */ + private JPanel createViewerPanel(final ActionListener helpListener) { + viewer = new JEditorPane(); + viewer.setEditable(false); + + scrollPane = new JScrollPane(); + + JPanel viewerPanel = new JPanel(); + GroupLayout viewerPanelLayout = new GroupLayout(viewerPanel); + viewerPanel.setLayout(viewerPanelLayout); + + GroupLayout.SequentialGroup infoHorizontal = viewerPanelLayout.createSequentialGroup(); + GroupLayout.ParallelGroup infoVertical = viewerPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING); + + viewerLabel = new JLabel(); + viewerLabel.setFont(viewerLabel.getFont().deriveFont(viewerLabel.getFont().getStyle() | java.awt.Font.BOLD)); +// viewerLabel.setLabelFor(viewer); + + infoHorizontal.addComponent(viewerLabel); + infoVertical.addComponent(viewerLabel); + + if (helpListener != null) { + JLabel helpLabel = new JLabel(); + helpLabel.setIcon(new ImageIcon(getClass().getResource(BKUGUIFacade.HELP_IMG))); + helpLabel.getAccessibleContext().setAccessibleName(messages.getString(BKUGUIFacade.ALT_HELP)); + helpLabel.addMouseListener(new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent arg0) { + ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, BKUGUIFacade.HELP_HASHDATAVIEWER); + helpListener.actionPerformed(e); + } + }); + helpLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + infoHorizontal.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE).addComponent(helpLabel); + infoVertical.addComponent(helpLabel); + } + + viewerPanelLayout.setHorizontalGroup( + viewerPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING).addGroup(infoHorizontal).addComponent(scrollPane)); + viewerPanelLayout.setVerticalGroup( + viewerPanelLayout.createSequentialGroup().addGroup(infoVertical).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED).addComponent(scrollPane)); + + return viewerPanel; + } + + /** + * Sets the hashdataInput to be displayed and makes the dialog visible. + * This method blocks until the dialog's close button is pressed. + * + * @param mimeType defaults to text/plain if null + * @param encoding must be null if document contains charset declaration (e.g. HTML page), otherwise the parser crashes + + * @param hashDataInput + */ + public void setContent(HashDataInput hashDataInput) { + + this.content = null; + + String mimeType = hashDataInput.getMimeType(); + if (mimeType == null) { + mimeType = "text/plain"; + } + log.debug("secure viewer mime type: " + mimeType); + // loads editorkit for text/plain if unrecognized + viewer.setContentType(mimeType); + + if ("text/plain".equals(mimeType)) { + viewer.setEditorKit(new StyledEditorKit()); + viewer.setFont(new Font(PLAINTEXT_FONT, viewer.getFont().getStyle(), viewer.getFont().getSize())); +// } else if ("text/html".equals(mimeType)) { +// viewer.setEditorKit(new RestrictedHTMLEditorKit()); + } else if ("application/xhtml+xml".equals(mimeType)) { + viewer.setContentType("text/html"); + } + + EditorKit editorKit = viewer.getEditorKit(); + Document document = editorKit.createDefaultDocument(); +// document.putProperty("IgnoreCharsetDirective", new Boolean(true)); + + try { + Charset cs = (hashDataInput.getEncoding() == null) ? Charset.forName("UTF-8") : Charset.forName(hashDataInput.getEncoding()); + log.debug("secure viewer encoding: " + cs.toString()); + + InputStreamReader isr = new InputStreamReader(hashDataInput.getHashDataInput(), cs); + Reader contentReader = new BufferedReader(isr); + viewer.read(contentReader, document); + contentReader.close(); + + this.content = hashDataInput; + +// } catch (IllegalCharsetNameException ex) { +// } catch (UnsupportedCharsetException ex) { + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + String p = messages.getString(BKUGUIFacade.ERR_VIEWER); + viewer.setText(MessageFormat.format(p, ex.getMessage())); + } + viewer.setCaretPosition(0); + + scrollPane.setViewportView(viewer); + scrollPane.setPreferredSize(viewer.getPreferredSize()); + scrollPane.setAlignmentX(LEFT_ALIGNMENT); + + if ("application/xhtml+xml".equals(mimeType)) { + viewerLabel.setText(messages.getString(BKUGUIFacade.WARNING_XHTML)); + } else { + viewerLabel.setText(""); + } + + setVisible(true); + } + + private JPanel createButtonPanel() { //ActionListener saveListener, String saveCommand) { + JButton closeButton = new JButton(); + closeButton.setText(messages.getString(BKUGUIFacade.BUTTON_CLOSE)); + closeButton.setActionCommand("close"); + closeButton.addActionListener(this); + + JButton saveButton = new JButton(); + saveButton.setText(messages.getString(BKUGUIFacade.BUTTON_SAVE)); + saveButton.setActionCommand("save"); + saveButton.addActionListener(this); + + int buttonSize = closeButton.getPreferredSize().width; + if (saveButton.getPreferredSize().width > buttonSize) { + buttonSize = saveButton.getPreferredSize().width; + } + + JPanel buttonPanel = new JPanel(); + GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); + buttonPanel.setLayout(buttonPanelLayout); + + buttonPanelLayout.setHorizontalGroup( + buttonPanelLayout.createSequentialGroup().addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(saveButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE).addPreferredGap(LayoutStyle.ComponentPlacement.RELATED).addComponent(closeButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)); + buttonPanelLayout.setVerticalGroup( + buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE).addComponent(saveButton).addComponent(closeButton)); + + return buttonPanel; + } + + @Override + public void actionPerformed(ActionEvent e) { + if ("close".equals(e.getActionCommand())) { +// SecureViewerDialog.dialog.setVisible(false); + log.trace("closing secure viewer"); + setVisible(false); + log.trace("secure viewer closed"); + } else if ("save".equals(e.getActionCommand())) { + log.trace("display secure viewer save dialog"); + showSaveDialog(content, null, null); + log.trace("done secure viewer save"); + } else { + log.warn("unknown action command " + e.getActionCommand()); + } + } + + private void showSaveDialog(final HashDataInput hashDataInput, + final ActionListener okListener, final String okCommand) { + + log.debug("scheduling save dialog [" + Thread.currentThread().getName() + "]"); + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + log.debug("show save dialog [" + Thread.currentThread().getName() + "]"); + + String userHome = System.getProperty("user.home"); + + JFileChooser fileDialog = new JFileChooser(userHome); + fileDialog.setMultiSelectionEnabled(false); + fileDialog.setDialogType(JFileChooser.SAVE_DIALOG); + fileDialog.setFileHidingEnabled(true); + fileDialog.setDialogTitle(messages.getString(BKUGUIFacade.WINDOWTITLE_SAVE)); + fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY); + String mimeType = hashDataInput.getMimeType(); + MimeFilter mimeFilter = new MimeFilter(mimeType, messages); + fileDialog.setFileFilter(mimeFilter); + String filename = messages.getString(BKUGUIFacade.SAVE_HASHDATAINPUT_PREFIX) + + MimeFilter.getExtension(mimeType); + fileDialog.setSelectedFile(new File(userHome, filename)); + + //parent contentPane -> placed over applet + switch (fileDialog.showSaveDialog(fileDialog)) { + case JFileChooser.APPROVE_OPTION: + File file = fileDialog.getSelectedFile(); + String id = hashDataInput.getReferenceId(); + if (file.exists()) { + String msgPattern = messages.getString(BKUGUIFacade.MESSAGE_OVERWRITE); + int overwrite = JOptionPane.showConfirmDialog(fileDialog, + MessageFormat.format(msgPattern, file), + messages.getString(BKUGUIFacade.WINDOWTITLE_OVERWRITE), + JOptionPane.OK_CANCEL_OPTION); + if (overwrite != JOptionPane.OK_OPTION) { + return; + } + } + if (log.isDebugEnabled()) { + log.debug("writing hashdata input " + id + " (" + mimeType + ") to file " + file); + } + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + InputStream hdi = hashDataInput.getHashDataInput(); + int b; + while ((b = hdi.read()) != -1) { + bos.write(b); + } + bos.flush(); + bos.close(); + } catch (IOException ex) { + log.error("Failed to write " + file + ": " + ex.getMessage()); + log.debug(ex); + String errPattern = messages.getString(BKUGUIFacade.ERR_WRITE_HASHDATA); + JOptionPane.showMessageDialog(fileDialog, + MessageFormat.format(errPattern, ex.getMessage()), + messages.getString(BKUGUIFacade.WINDOWTITLE_ERROR), + JOptionPane.ERROR_MESSAGE); + } finally { + try { + if (fos != null) { + fos.close(); + } + } catch (IOException ex) { + } + } + break; + case JFileChooser.CANCEL_OPTION: + log.debug("cancelled save dialog"); + break; + } + if (okListener != null) { + okListener.actionPerformed(new ActionEvent(fileDialog, ActionEvent.ACTION_PERFORMED, okCommand)); + } + } + }); + } +} diff --git a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties index 6d651b2d..9bfe8fb1 100644 --- a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties +++ b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties @@ -25,6 +25,7 @@ title.retry=<html>Falsche PIN</html> title.wait=<html>Bitte warten</html> title.hashdata=<html>Signaturdaten</html> windowtitle.save=Signaturdaten speichern +windowtitle.error=Fehler windowtitle.savedir=Signaturdaten in Verzeichnis speichern windowtitle.overwrite=Datei \u00FCberschreiben? windowtitle.viewer=Signaturdaten @@ -79,7 +80,7 @@ error.unknown.param=<html>Ein Fehler trat auf: {0}</html> error.unknown=<html>Ein Fehler trat auf</html> error.test=<html>Fehler1 {0} - Fehler2 {1}</html> error.card.locked=<html>B\u00FCrgerkarte ist gesperrt</html> -error.card.notactivated=<html>B\u00FCrgerkartenfunktion ist nicht aktiviert</html> +error.card.notactivated=<html>Die B\u00FCrgerkarte ist nicht aktiviert</html> error.pin.timeout=<html>Zeit\u00FCberschreitung bei Eingabe der PIN</html> error.viewer=Der Inhalt kann nicht dargestellt werden: {0} error.external.link=<html>Externer Link {0} wird nicht ge\u00F6ffnet</html> diff --git a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_en.properties b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_en.properties index 2fb66969..a36f9b83 100644 --- a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_en.properties +++ b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_en.properties @@ -25,6 +25,7 @@ title.retry=<html>Wrong PIN</html> title.wait=<html>Please wait</html> title.hashdata=<html>Signature data</html> windowtitle.save=Save signature data +windowtitle.error=Error windowtitle.savedir=Save signature data to directory windowtitle.overwrite=Overwrite file? windowtitle.viewer=Signature data diff --git a/BKUCommonGUI/src/main/resources/images/ChipperlingLogo.png b/BKUCommonGUI/src/main/resources/images/ChipperlingLogo.png Binary files differnew file mode 100644 index 00000000..eee4be4f --- /dev/null +++ b/BKUCommonGUI/src/main/resources/images/ChipperlingLogo.png diff --git a/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUITest.java b/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUITest.java index c8cff617..b3eaf8c7 100644 --- a/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUITest.java +++ b/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUITest.java @@ -39,8 +39,9 @@ public class BKUGUITest { public void testBKUGUI() { JFrame testFrame = new JFrame("BKUGUITest"); Container contentPane = testFrame.getContentPane(); - contentPane.setPreferredSize(new Dimension(170, 150)); - BKUGUIFacade gui = new BKUGUIImpl(contentPane, null, BKUGUIFacade.Style.tiny, null, null); +// contentPane.setPreferredSize(new Dimension(170, 150)); + contentPane.setPreferredSize(new Dimension(290, 190)); + BKUGUIFacade gui = new BKUGUIImpl(contentPane, null, BKUGUIFacade.Style.advanced, null, null); BKUGUIWorker worker = new BKUGUIWorker(); worker.init(gui); testFrame.pack(); diff --git a/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java b/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java index 194e18b0..5475a45b 100644 --- a/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java +++ b/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java @@ -149,7 +149,9 @@ public class BKUGUIWorker implements Runnable { // // Thread.sleep(2000); // - gui.showSignaturePINDialog(signPinSpec, -1, signListener, "sign", cancelListener, "cancel", hashdataListener, "hashdata"); +// gui.showSignaturePINDialog(signPinSpec, -1, signListener, "sign", cancelListener, "cancel", hashdataListener, "hashdata"); + + gui.showPinpadSignaturePINDialog(signPinSpec, -1, hashdataListener, "hashdata"); // // Thread.sleep(4000); // diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/BKUGuiProxy.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/BKUGuiProxy.java index 5a0ba84a..3f560967 100644 --- a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/BKUGuiProxy.java +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/BKUGuiProxy.java @@ -144,4 +144,12 @@ public class BKUGuiProxy implements BKUGUIFacade { showDialog();
delegate.showMessageDialog(titleKey, msgKey);
}
+
+ @Override
+ public void showPinpadSignaturePINDialog(PINSpec pinSpec, int numRetries,
+ ActionListener viewerListener, String viewerCommand) {
+ showDialog();
+ delegate.showPinpadSignaturePINDialog(pinSpec, numRetries,
+ viewerListener, viewerCommand);
+ }
}
diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java index 61cc7c4c..a782de1a 100644 --- a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalBKUWorker.java @@ -37,7 +37,8 @@ public class LocalBKUWorker extends AbstractBKUWorker { public LocalBKUWorker(BKUGUIFacade gui, JDialog container) { super(gui); this.container = container; - addRequestHandler(SignRequest.class, new LocalSignRequestHandler()); + addRequestHandler(SignRequest.class, + new LocalSignRequestHandler(new LocalSecureViewer(gui))); } @Override diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java new file mode 100644 index 00000000..cbe5af7a --- /dev/null +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSecureViewer.java @@ -0,0 +1,109 @@ + +package at.gv.egiz.bku.local.stal; + +import at.gv.egiz.bku.slcommands.impl.DataObjectHashDataInput; +import at.gv.egiz.bku.smccstal.SecureViewer; +import java.io.IOException; +import java.util.ArrayList; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.stal.HashDataInput; +import at.gv.egiz.stal.impl.ByteArrayHashDataInput; +import at.gv.egiz.stal.signedinfo.ReferenceType; +import at.gv.egiz.stal.signedinfo.SignedInfoType; +import java.awt.event.ActionListener; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class LocalSecureViewer implements SecureViewer { + + private static final Log log = LogFactory.getLog(LocalSignRequestHandler.class); + private List<HashDataInput> hashDataInputs = Collections.EMPTY_LIST; + + protected BKUGUIFacade gui; + + public LocalSecureViewer(BKUGUIFacade gui) { + this.gui = gui; + } + + public void setDataToBeSigned(List<HashDataInput> dataToBeSigned) { + this.hashDataInputs = dataToBeSigned; + } + + /** + * + * @param dsigReferences + * @throws java.lang.Exception + */ + @Override + public void displayDataToBeSigned(SignedInfoType signedInfo, + ActionListener okListener, String okCommand) + throws Exception { + if (signedInfo.getReference().size() == 0) { + log.error("No hashdata input selected to be displayed: null"); + throw new Exception("No HashData Input selected to be displayed"); + } + + ArrayList<HashDataInput> selectedHashDataInputs = new ArrayList<HashDataInput>(); + for (ReferenceType dsigRef : signedInfo.getReference()) { + // don't get Manifest, QualifyingProperties, ... + if (dsigRef.getType() == null) { + String dsigRefId = dsigRef.getId(); + if (dsigRefId != null) { + boolean hdiAvailable = false; + for (HashDataInput hashDataInput : hashDataInputs) { + if (dsigRefId.equals(hashDataInput.getReferenceId())) { + log.debug("display hashdata input for dsig:SignedReference " + + dsigRefId); + selectedHashDataInputs.add( + ensureCachedHashDataInput(hashDataInput)); + hdiAvailable = true; + break; + } + } + if (!hdiAvailable) { + log.error("no hashdata input for dsig:SignedReference " + dsigRefId); + throw new Exception( + "No HashDataInput available for dsig:SignedReference " + dsigRefId); + } + } else { + throw new Exception( + "Cannot get HashDataInput for dsig:Reference without Id attribute"); + } + } + } + + if (selectedHashDataInputs.size() < 1) { + log.error("dsig:SignedInfo does not contain a data reference"); + throw new Exception("dsig:SignedInfo does not contain a data reference"); + } + gui.showSecureViewer(selectedHashDataInputs, okListener, okCommand); + } + + + private HashDataInput ensureCachedHashDataInput(HashDataInput hashDataInput) + throws IOException { + if (!(hashDataInput instanceof DataObjectHashDataInput)) { + + log.warn("expected DataObjectHashDataInput for LocalSignRequestHandler, got " + + hashDataInput.getClass().getName()); + + InputStream hdIs = hashDataInput.getHashDataInput(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(hdIs.available()); + int b; + while ((b = hdIs.read()) != -1) { + baos.write(b); + } + hashDataInput = new ByteArrayHashDataInput(baos.toByteArray(), + hashDataInput.getReferenceId(), + hashDataInput.getMimeType(), + hashDataInput.getEncoding()); + } + return hashDataInput; + } + +} diff --git a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSignRequestHandler.java b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSignRequestHandler.java index 531e6591..492b8a05 100644 --- a/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSignRequestHandler.java +++ b/BKULocal/src/main/java/at/gv/egiz/bku/local/stal/LocalSignRequestHandler.java @@ -16,9 +16,7 @@ */ package at.gv.egiz.bku.local.stal; -import at.gv.egiz.bku.slcommands.impl.DataObjectHashDataInput; -import java.io.IOException; -import java.util.ArrayList; +import at.gv.egiz.bku.smccstal.SecureViewer; import java.util.Collections; import java.util.List; @@ -40,9 +38,16 @@ import java.io.InputStream; * @author clemens */ public class LocalSignRequestHandler extends SignRequestHandler { +// implements SecureViewer { private static final Log log = LogFactory.getLog(LocalSignRequestHandler.class); - private List<HashDataInput> hashDataInputs = Collections.EMPTY_LIST; + + protected LocalSecureViewer secureViewer; + + public LocalSignRequestHandler(LocalSecureViewer secureViewer) { + super(secureViewer); + } + /** * If the request is a SIGN request, it contains a list of DataObjectHashDataInput @@ -53,75 +58,13 @@ public class LocalSignRequestHandler extends SignRequestHandler { */ @SuppressWarnings("unchecked") @Override - public STALResponse handleRequest(STALRequest request) throws InterruptedException { + public STALResponse handleRequest(STALRequest request) + throws InterruptedException { + if (request instanceof SignRequest) { SignRequest signReq = (SignRequest) request; - hashDataInputs = signReq.getHashDataInput(); + secureViewer.setDataToBeSigned(signReq.getHashDataInput()); } return super.handleRequest(request); } - - /** - * - * @param dsigReferences - * @throws java.lang.Exception - */ - @Override - public void displayDataToBeSigned(List<ReferenceType> dsigReferences) throws Exception { - if (dsigReferences == null || dsigReferences.size() < 1) { - log.error("No hashdata input selected to be displayed: null"); - throw new Exception("No HashData Input selected to be displayed"); - } - - ArrayList<HashDataInput> selectedHashDataInputs = new ArrayList<HashDataInput>(); - for (ReferenceType dsigRef : dsigReferences) { - // don't get Manifest, QualifyingProperties, ... - if (dsigRef.getType() == null) { - String dsigRefId = dsigRef.getId(); - if (dsigRefId != null) { - boolean hdiAvailable = false; - for (HashDataInput hashDataInput : hashDataInputs) { - if (dsigRefId.equals(hashDataInput.getReferenceId())) { - log.debug("display hashdata input for dsig:SignedReference " + dsigRefId); - if (!(hashDataInput instanceof DataObjectHashDataInput)) { - log.warn( - "expected DataObjectHashDataInput for LocalSignRequestHandler, got " + hashDataInput.getClass().getName()); - hashDataInput = getByteArrayHashDataInput(hashDataInput); - } - selectedHashDataInputs.add(hashDataInput); - hdiAvailable = true; - break; - } - } - if (!hdiAvailable) { - log.error("no hashdata input for dsig:SignedReference " + dsigRefId); - throw new Exception( - "No HashDataInput available for dsig:SignedReference " + dsigRefId); - } - } else { - throw new Exception( - "Cannot get HashDataInput for dsig:Reference without Id attribute"); - } - } - } - - if (selectedHashDataInputs.size() < 1) { - log.error("dsig:SignedInfo does not contain a data reference"); - throw new Exception("dsig:SignedInfo does not contain a data reference"); - } - gui.showSecureViewer(selectedHashDataInputs, this, "hashDataDone"); - } - - private ByteArrayHashDataInput getByteArrayHashDataInput(HashDataInput hashDataInput) throws IOException { - - InputStream hdIs = hashDataInput.getHashDataInput(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(hdIs.available()); - int b; - while ((b = hdIs.read()) != -1) { - baos.write(b); - } - ByteArrayHashDataInput hdi = new ByteArrayHashDataInput(baos.toByteArray(), hashDataInput.getReferenceId(), hashDataInput.getMimeType(), hashDataInput.getEncoding()); - - return hdi; - } } diff --git a/BKUOnline/src/main/resources/at/gv/egiz/bku/online/conf/defaultConf.properties b/BKUOnline/src/main/resources/at/gv/egiz/bku/online/conf/defaultConf.properties index e2f07481..04c9c7bf 100644 --- a/BKUOnline/src/main/resources/at/gv/egiz/bku/online/conf/defaultConf.properties +++ b/BKUOnline/src/main/resources/at/gv/egiz/bku/online/conf/defaultConf.properties @@ -40,9 +40,12 @@ SSL.sslProtocol=TLS # warning do not set the following property to true
# its intended for debugging and testing only
SSL.disableAllChecks=false
+#SSL.disableHostnameVerification=true
# ------------ END SSL Config --------------------
+#UserAgent=citizen-card-environment/1.2 MOCCA/1.0
+
ValidateHashDataInputs=true
AppletTimeout=300000
diff --git a/BKUOnline/src/main/resources/log4j.properties b/BKUOnline/src/main/resources/log4j.properties index f608c83d..25ea9faa 100644 --- a/BKUOnline/src/main/resources/log4j.properties +++ b/BKUOnline/src/main/resources/log4j.properties @@ -30,7 +30,7 @@ log4j.appender.STDOUT.layout.ConversionPattern=%-5p | %t | %c %x - %m%n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.maxFileSize=500KB log4j.appender.file.maxBackupIndex=9 -log4j.appender.file.File=${catalina.home}/logs/bkuonline.log +log4j.appender.file.File=${catalina.base}/logs/bkuonline.log log4j.appender.file.threshold=trace log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p | %t | %c{1}:%L - %m%n
\ No newline at end of file diff --git a/BKUOnline/src/main/webapp/SLRequestForm.html b/BKUOnline/src/main/webapp/SLRequestForm.html index 9ff7b68a..4714e82f 100644 --- a/BKUOnline/src/main/webapp/SLRequestForm.html +++ b/BKUOnline/src/main/webapp/SLRequestForm.html @@ -103,6 +103,33 @@ </sl:InfoboxReadRequest> --> <!-- +<?xml version="1.0" encoding="UTF-8"?> +<sl:CreateXMLSignatureRequest + xmlns:sl="http://www.buergerkarte.at/namespaces/securitylayer/1.2#"> + <sl:KeyboxIdentifier>SecureSignatureKeypair</sl:KeyboxIdentifier> + <sl:DataObjectInfo Structure="enveloping"> + <sl:DataObject> + <sl:XMLContent>Ich bin ein einfacher Text.</sl:XMLContent> + </sl:DataObject> + <sl:TransformsInfo> + <sl:FinalDataMetaInfo> + <sl:MimeType>text/plain</sl:MimeType> + </sl:FinalDataMetaInfo> + </sl:TransformsInfo> + </sl:DataObjectInfo> + <sl:DataObjectInfo Structure="enveloping"> + <sl:DataObject> + <sl:XMLContent><html xmlns="http://www.w3.org/1999/xhtml"><head><title>TestXHTML</title><style/></head><body><p>Ich bin ein einfacher Text.</p></body></html></sl:XMLContent> + </sl:DataObject> + <sl:TransformsInfo> + <sl:FinalDataMetaInfo> + <sl:MimeType>application/xhtml+xml</sl:MimeType> + </sl:FinalDataMetaInfo> + </sl:TransformsInfo> + </sl:DataObjectInfo> +</sl:CreateXMLSignatureRequest> +--> +<!-- <?xml version='1.0' encoding='UTF-8'?> <sl10:InfoboxUpdateRequest xmlns:sl10='http://www.buergerkarte.at/namespaces/securitylayer/1.2#'> <sl10:InfoboxIdentifier>CardChannel</sl10:InfoboxIdentifier> diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/binding/ExpiryRemoverTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/binding/ExpiryRemoverTest.java index 61729567..0e64e7a2 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/binding/ExpiryRemoverTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/binding/ExpiryRemoverTest.java @@ -14,54 +14,57 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package at.gv.egiz.bku.binding;
-
+package at.gv.egiz.bku.binding; + import java.net.MalformedURLException; -import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class ExpiryRemoverTest {
-
- @Test
- public void testMe() throws InterruptedException, MalformedURLException {
- BindingProcessorManager manager = new BindingProcessorManagerImpl(new DummyStalFactory(),
- new SLCommandInvokerImpl());
- BindingProcessor bp = manager.createBindingProcessor("http://www.at", null);
- ExpiryRemover remover = new ExpiryRemover();
- remover.setBindingProcessorManager(manager);
- remover.execute();
- manager.process(bp);
- remover.execute();
- assertTrue(manager.getManagedIds().size() == 1);
- remover.setMaxAcceptedAge(1000);
- Thread.sleep(500);
- remover.execute();
- assertTrue(manager.getManagedIds().size() == 1);
- Thread.sleep(510);
- remover.execute();
- assertTrue(manager.getManagedIds().size() == 0);
- }
-
- @Test
- public void testMe2() throws InterruptedException, MalformedURLException {
- BindingProcessorManager manager = new BindingProcessorManagerImpl(new DummyStalFactory(),
- new SLCommandInvokerImpl());
- BindingProcessor bp = manager.createBindingProcessor("http://www.iaik.at", null);
- ExpiryRemover remover = new ExpiryRemover();
- remover.setBindingProcessorManager(manager);
- remover.execute();
- manager.process(bp);
- remover.execute();
- assertTrue(manager.getManagedIds().size() == 1);
- remover.setMaxAcceptedAge(1000);
- Thread.sleep(500);
- remover.execute();
- assertTrue(manager.getManagedIds().size() == 1);
- bp.updateLastAccessTime();
- Thread.sleep(510);
- remover.execute();
- assertTrue(manager.getManagedIds().size() == 1);
- }
-
-}
+import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class ExpiryRemoverTest { + + @Test + @Ignore + public void testMe() throws InterruptedException, MalformedURLException { + BindingProcessorManager manager = new BindingProcessorManagerImpl(new DummyStalFactory(), + new SLCommandInvokerImpl()); + BindingProcessor bp = manager.createBindingProcessor("http://www.at", null); + ExpiryRemover remover = new ExpiryRemover(); + remover.setBindingProcessorManager(manager); + remover.execute(); + manager.process(bp); + remover.execute(); + assertTrue(manager.getManagedIds().size() == 1); + remover.setMaxAcceptedAge(1000); + Thread.sleep(500); + remover.execute(); + assertTrue(manager.getManagedIds().size() == 1); + Thread.sleep(510); + remover.execute(); + assertTrue(manager.getManagedIds().size() == 0); + } + + @Test + @Ignore + public void testMe2() throws InterruptedException, MalformedURLException { + BindingProcessorManager manager = new BindingProcessorManagerImpl(new DummyStalFactory(), + new SLCommandInvokerImpl()); + BindingProcessor bp = manager.createBindingProcessor("http://www.iaik.at", null); + ExpiryRemover remover = new ExpiryRemover(); + remover.setBindingProcessorManager(manager); + remover.execute(); + manager.process(bp); + remover.execute(); + assertTrue(manager.getManagedIds().size() == 1); + remover.setMaxAcceptedAge(1000); + Thread.sleep(500); + remover.execute(); + assertTrue(manager.getManagedIds().size() == 1); + bp.updateLastAccessTime(); + Thread.sleep(510); + remover.execute(); + assertTrue(manager.getManagedIds().size() == 1); + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index 01b9155b..06e4a018 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -28,6 +28,7 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; import at.gv.egiz.smcc.util.SMCCHelper; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -41,7 +42,7 @@ import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class ACOSCard extends AbstractSignatureCard implements SignatureCard { +public class ACOSCard extends AbstractSignatureCard { private static Log log = LogFactory.getLog(ACOSCard.class); @@ -180,22 +181,23 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); int retries = -1; - char[] pin = null; - boolean pinRequiered = false; + boolean pinRequired = false; do { - if (pinRequiered) { - pin = provider.providePIN(spec, retries); - } try { getCard().beginExclusive(); - return readTLVFile(AID_DEC, EF_INFOBOX, pin, KID_PIN_INF, EF_INFOBOX_MAX_SIZE); + if (pinRequired) { + char[] pin = provider.providePIN(spec, retries); + return readTLVFile(AID_DEC, EF_INFOBOX, pin, spec.getKID(), EF_INFOBOX_MAX_SIZE); + } else { + return readTLVFile(AID_DEC, EF_INFOBOX, EF_INFOBOX_MAX_SIZE); + } } catch (FileNotFoundException e) { throw new NotActivatedException(); } catch (SecurityStatusNotSatisfiedException e) { - pinRequiered = true; + pinRequired = true; } catch (VerificationFailedException e) { - pinRequiered = true; + pinRequired = true; retries = e.getRetries(); } finally { getCard().endExclusive(); @@ -402,10 +404,10 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { + if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { log.debug("verify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_VERIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + CCID.FEATURE_VERIFY_PIN_DIRECT, getPINVerifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -466,10 +468,10 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_MODIFY_PIN_DIRECT)) { + if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { log.debug("modify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_MODIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + CCID.FEATURE_MODIFY_PIN_DIRECT, getPINModifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -543,34 +545,37 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } private byte[] getPINVerifyStructure(byte kid) { - - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out - byte bmFormatString = (byte) 0x82; // 1 0001 0 01 + + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); + byte bmFormatString = (byte) 0x82; // 1 0000 0 10 // ^------------ System unit = byte // ^^^^------- PIN position in the frame = 1 byte // ^----- PIN justification left - // ^^-- BCD format - // 1 0000 0 10 // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 + byte bmPINBlockString = (byte) 0x08; // 0000 1000 + // ^^^^--------- PIN length size: 0 bits + // ^^^^---- Length PIN = 8 bytes + byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit - byte wPINMaxExtraDigitL = (byte) 0x04; // Max=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Min=4 digits - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed + // ^^^^--- no PIN length + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x08; + byte wPINMaxExtraDigitH = + (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x00; + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex = (byte) 0x00; // Default Msg + byte wLangIdL = (byte) 0x0C; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex = (byte) 0x00; byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, // CLA INS P1 P2 LC - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Data - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 // Data + (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; int offset = 0; @@ -603,40 +608,43 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { public byte[] getPINModifyStructure(byte kid) { - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out - byte bmFormatString = (byte) 0x82; // 1 0001 0 01 + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); + byte bmFormatString = (byte) 0x82; // 1 0000 0 10 // ^------------ System unit = byte // ^^^^------- PIN position in the frame = 1 byte // ^----- PIN justification left - // ^^-- BCD format - // 1 0000 0 10 // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x00; // 000 0 0100 + byte bmPINBlockString = (byte) 0x08; // 0000 1000 + // ^^^^--------- PIN length size: 0 bits + // ^^^^---- Length PIN = 8 bytes + byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit + // ^^^^--- no PIN length byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x00; // insertion position offset in bytes - byte wPINMaxExtraDigitL = (byte) 0x04; // Min=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Max=12 digits - byte bConfirmPIN = (byte) 0x00; // ??? need for confirm pin - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex1 = (byte) 0x00; // Default Msg - byte bMsgIndex2 = (byte) 0x00; // Default Msg - byte bMsgIndex3 = (byte) 0x00; // Default Msg + byte bInsertionOffsetNew = (byte) 0x08; + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x08; + byte wPINMaxExtraDigitH = + (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x00; + byte bConfirmPIN = (byte) 0x03; + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); + byte bNumberMessage = (byte) 0x03; + byte wLangIdL = (byte) 0x0C; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex1 = (byte) 0x00; + byte bMsgIndex2 = (byte) 0x01; + byte bMsgIndex3 = (byte) 0x02; byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, // CLA INS P1 P2 LC - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // ... - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff // ... + (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; int offset = 0; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index 6587aaf9..47c27369 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -28,6 +28,9 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; +import at.gv.egiz.smcc.ccid.DefaultReader; +import at.gv.egiz.smcc.ccid.ReaderFactory; import at.gv.egiz.smcc.util.SMCCHelper; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -39,6 +42,8 @@ import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.smartcardio.ATR; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; @@ -54,14 +59,6 @@ public abstract class AbstractSignatureCard implements SignatureCard { private static Log log = LogFactory.getLog(AbstractSignatureCard.class); - static final short GET_FEATURE_REQUEST = 3400; - - private static int getCtrlCode(short function) { - return 0x310000 | ((0xFFFF & function) << 2); - } - - protected Map<Byte, Long> ifdCtrlCmds; - protected List<PINSpec> pinSpecs = new ArrayList<PINSpec>(); private ResourceBundle i18n; @@ -76,7 +73,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { /** * The card terminal that connects the {@link #card_}. */ - private CardTerminal cardTerminal; +// private CardTerminal cardTerminal; + protected CCID reader; protected AbstractSignatureCard(String resourceBundleName) { this.resourceBundleName = resourceBundleName; @@ -341,16 +339,34 @@ public abstract class AbstractSignatureCard implements SignatureCard { */ protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) throws SignatureCardException, InterruptedException, CardException { - return readTLVFile(aid, ef, null, (byte) 0, maxLength); + // SELECT FILE (AID) + selectFileAID(aid); + + // SELECT FILE (EF) + ResponseAPDU resp = selectFileFID(ef); + if (resp.getSW() == 0x6a82) { + // EF not found + throw new FileNotFoundException("EF " + toString(ef) + " not found."); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("SELECT FILE with " + + "FID=" + + toString(ef) + + " failed (" + + "SW=" + + Integer.toHexString(resp.getSW()) + ")."); + } + + return readBinaryTLV(maxLength, (byte) 0x30); +// return readTLVFile(aid, ef, null, (byte) 0, maxLength); } /** - * Read the content of a TLV file wich may require a PIN. + * Read the content of a TLV file wich requires a PIN. * * @param aid the application ID (AID) * @param ef the elementary file (EF) * @param kid the key ID (KID) of the corresponding PIN - * @param provider the PINProvider + * @param pin the pin or null if VERIFY on pinpad * @param spec the PINSpec * @param maxLength the maximum length of the file * @@ -381,12 +397,10 @@ public abstract class AbstractSignatureCard implements SignatureCard { } // VERIFY - if (pin != null) { int retries = verifyPIN(kid, pin); if (retries != -1) { throw new VerificationFailedException(retries); } - } return readBinaryTLV(maxLength, (byte) 0x30); @@ -443,16 +457,16 @@ public abstract class AbstractSignatureCard implements SignatureCard { } + @Override public void init(Card card, CardTerminal cardTerminal) { - card_ = card; - this.cardTerminal = cardTerminal; + this.card_ = card; + this.reader = ReaderFactory.getReader(card, cardTerminal); ATR atr = card.getATR(); byte[] atrBytes = atr.getBytes(); if (atrBytes.length >= 6) { ifs_ = 0xFF & atr.getBytes()[6]; log.trace("Setting IFS (information field size) to " + ifs_); } - ifdCtrlCmds = queryIFDFeatures(); } @Override @@ -465,6 +479,11 @@ public abstract class AbstractSignatureCard implements SignatureCard { } @Override + public CCID getReader() { + return reader; + } + + @Override public void setLocale(Locale locale) { if (locale == null) { throw new NullPointerException("Locale must not be set to null"); @@ -497,9 +516,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { log.debug("Disconnect and reset smart card."); card_.disconnect(true); log.debug("Reconnect smart card."); - if (cardTerminal != null) { - card_ = cardTerminal.connect("*"); - } + card_ = reader.connect(); } catch (CardException e) { throw new SignatureCardException("Failed to reset card.", e); } @@ -520,6 +537,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { selectFileAID(pinSpec.getContextAID()); } + // -1 if ok or unknown int retries = verifyPIN(pinSpec.getKID()); do { char[] pin = pinProvider.providePIN(pinSpec, retries); @@ -611,166 +629,4 @@ public abstract class AbstractSignatureCard implements SignatureCard { throws CancelledException, SignatureCardException, InterruptedException { throw new SignatureCardException("Unblock not supported yet"); } - - ///////////////////////////////////////////////////////////////////////////// - // IFD related code - ///////////////////////////////////////////////////////////////////////////// - - /** - * TODO implement VERIFY_PIN_START/FINISH (feature 0x01/0x02) - * @return - */ - @Override - public boolean ifdSupportsFeature(byte feature) { - if (ifdCtrlCmds != null) { - return ifdCtrlCmds.containsKey(feature); - } - return false; - } - - protected Map<Byte, Long> queryIFDFeatures() { - - if (card_ == null) { - throw new NullPointerException("Need connected smart card to query IFD features"); - } - - Map<Byte, Long> ifdFeatures = new HashMap<Byte, Long>(); - - try { - if (log.isTraceEnabled()) { - log.trace("GET_FEATURE_REQUEST CtrlCode " + Integer.toHexString(getCtrlCode(GET_FEATURE_REQUEST))); - } - byte[] resp = card_.transmitControlCommand(getCtrlCode(GET_FEATURE_REQUEST), new byte[]{}); - - if (log.isTraceEnabled()) { - log.trace("GET_FEATURE_REQUEST Response " + SMCCHelper.toString(resp)); - } - - for (int i = 0; i + 5 < resp.length; i += 6) { - Byte feature = new Byte(resp[i]); - Long ctrlCode = new Long( - ((0xFF & resp[i + 2]) << 24) | - ((0xFF & resp[i + 3]) << 16) | - ((0xFF & resp[i + 4]) << 8) | - (0xFF & resp[i + 5])); - if (log.isInfoEnabled()) { - log.info("IFD supports feature " + Integer.toHexString(feature.byteValue()) + - ": " + Long.toHexString(ctrlCode.longValue())); - } - ifdFeatures.put(feature, ctrlCode); - } - - } catch (CardException ex) { - log.debug("Failed to query IFD features: " + ex.getMessage()); - log.trace(ex); - log.info("IFD does not support PINPad"); - return null; - } - return ifdFeatures; - } - - - protected byte ifdGetKeyPressed() throws CardException { - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { - - Long controlCode = (Long) ifdCtrlCmds.get(new Byte((byte) 0x05)); - - byte key = 0x00; - while (key == 0x00) { - - byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); - - if (resp != null && resp.length > 0) { - key = resp[0]; - } - } - - System.out.println("Key: " + key); - - } - - return 0x00; - } - - protected byte[] ifdVerifyPINFinish() throws CardException { - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { - - Long controlCode = (Long) ifdCtrlCmds.get(new Byte((byte) 0x02)); - - byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); - - System.out.println("CommandResp: " + toString(resp)); - - return resp; - - } - - return null; - } - - - /** - * assumes ifdSupportsVerifyPIN() == true - * @param pinVerifyStructure - * @return - * @throws javax.smartcardio.CardException - */ -// protected byte[] ifdVerifyPIN(byte[] pinVerifyStructure) throws CardException { -// -//// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_IFD_PIN_PROPERTIES); -//// if (ctrlCode != null) { -//// if (log.isTraceEnabled()) { -//// log.trace("PIN_PROPERTIES CtrlCode " + Integer.toHexString(ctrlCode.intValue())); -//// } -//// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), new byte[] {}); -//// -//// if (log.isTraceEnabled()) { -//// log.trace("PIN_PROPERTIES Response " + SMCCHelper.toString(resp)); -//// } -//// } -// -// -// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_VERIFY_PIN_DIRECT); -// if (ctrlCode == null) { -// throw new NullPointerException("no CtrlCode for FEATURE_VERIFY_PIN_DIRECT"); -// } -// -// if (log.isTraceEnabled()) { -// log.trace("VERIFY_PIN_DIRECT CtrlCode " + Integer.toHexString(ctrlCode.intValue()) + -// ", PIN_VERIFY_STRUCTURE " + SMCCHelper.toString(pinVerifyStructure)); -// } -// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), pinVerifyStructure); -// -// if (log.isTraceEnabled()) { -// log.trace("VERIFY_PIN_DIRECT Response " + SMCCHelper.toString(resp)); -// } -// return resp; -// } - -// protected Long getControlCode(Byte feature) { -// if (ifdFeatures != null) { -// return ifdFeatures.get(feature); -// } -// return null; -// } - - protected byte[] transmitControlCommand(Long ctrlCode, byte[] ctrlCommand) - throws CardException { -// Long ctrlCode = (Long) ifdFeatures.get(feature); - if (ctrlCode == null) { - throw new NullPointerException("ControlCode " + - Integer.toHexString(ctrlCode.intValue()) + " not supported"); - } - if (log.isTraceEnabled()) { - log.trace("CtrlCommand (" + Integer.toHexString(ctrlCode.intValue()) + - ") " + SMCCHelper.toString(ctrlCommand)); - } - byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), ctrlCommand); - - if (log.isTraceEnabled()) { - log.trace("CtrlCommand Response " + SMCCHelper.toString(resp)); - } - return resp; - } - } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index 91245c50..bc6a2316 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -28,6 +28,7 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.DefaultReader; import at.gv.egiz.smcc.util.SMCCHelper; import java.util.Arrays; import javax.smartcardio.CardChannel; @@ -38,7 +39,7 @@ import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class STARCOSCard extends AbstractSignatureCard implements SignatureCard { +public class STARCOSCard extends AbstractSignatureCard { /** * Logging facility. @@ -153,8 +154,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard public static final byte KID_PIN_CARD = (byte) 0x01; - private static final int PINSPEC_CARD = 0; - private static final int PINSPEC_SS = 1; + public static final int PINSPEC_CARD = 0; + public static final int PINSPEC_SS = 1; /** * Creates an new instance. @@ -217,28 +218,29 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard try { if ("IdentityLink".equals(infobox)) { - + PINSpec spec = pinSpecs.get(PINSPEC_CARD); //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - + int retries = -1; - char[] pin = null; - boolean pinRequiered = false; + boolean pinRequired = false; do { - if (pinRequiered) { - pin = provider.providePIN(spec, retries); - } try { getCard().beginExclusive(); - return readTLVFile(AID_INFOBOX, EF_INFOBOX, pin, KID_PIN_CARD, 2000); + if (pinRequired) { + char[] pin = provider.providePIN(spec, retries); + return readTLVFile(AID_INFOBOX, EF_INFOBOX, pin, spec.getKID(), 2000); + } else { + return readTLVFile(AID_INFOBOX, EF_INFOBOX, 2000); + } } catch (FileNotFoundException e) { throw new NotActivatedException(); } catch (SecurityStatusNotSatisfiedException e) { - pinRequiered = true; + pinRequired = true; retries = verifyPIN(KID_PIN_CARD); } catch (VerificationFailedException e) { - pinRequiered = true; + pinRequired = true; retries = e.getRetries(); } finally { getCard().endExclusive(); @@ -246,54 +248,43 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } while (retries != 0); throw new LockedException(); - } else if ("EHIC".equals(infobox)) { - try { getCard().beginExclusive(); return readTLVFile(AID_SV_PERSONENDATEN, FID_EHIC, 126); } finally { getCard().endExclusive(); } - } else if ("Grunddaten".equals(infobox)) { - try { getCard().beginExclusive(); return readTLVFile(AID_SV_PERSONENDATEN, FID_GRUNDDATEN, 550); } finally { getCard().endExclusive(); } - } else if ("SV-Personenbindung".equals(infobox)) { - try { getCard().beginExclusive(); return readTLVFile(AID_SV_PERSONENDATEN, FID_SV_PERSONENBINDUNG, 500); } finally { getCard().endExclusive(); } - } else if ("Status".equals(infobox)) { - try { getCard().beginExclusive(); return readRecords(AID_SV_PERSONENDATEN, FID_STATUS, 1, 5); } finally { getCard().endExclusive(); } - } else { throw new IllegalArgumentException("Infobox '" + infobox + "' not supported."); } - } catch (CardException e) { log.warn(e); throw new SignatureCardException("Failed to access card.", e); } - } @Override @@ -466,10 +457,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard throws LockedException, NotActivatedException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { + if (reader.hasFeature(DefaultReader.FEATURE_VERIFY_PIN_DIRECT)) { log.debug("verify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_VERIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + DefaultReader.FEATURE_VERIFY_PIN_DIRECT, getPINVerifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -551,10 +542,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_MODIFY_PIN_DIRECT)) { + if (reader.hasFeature(DefaultReader.FEATURE_MODIFY_PIN_DIRECT)) { log.debug("modify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_MODIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + DefaultReader.FEATURE_MODIFY_PIN_DIRECT, getPINModifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -606,31 +597,43 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard protected void activatePIN(byte kid, char[] pin) throws CancelledException, TimeoutException, SignatureCardException { try { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x24, 0x01, kid, encodePINBlock(pin)), false); + byte[] sw; + if (reader.hasFeature(DefaultReader.FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("activate PIN on IFD"); + sw = reader.transmitControlCommand( + DefaultReader.FEATURE_MODIFY_PIN_DIRECT, + getActivatePINModifyStructure(kid)); +// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + } else { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, + new CommandAPDU(0x00, 0x24, 0x01, kid, encodePINBlock(pin)), false); - log.trace("activate pin returned SW=" + Integer.toHexString(resp.getSW())); + sw = new byte[2]; + sw[0] = (byte) resp.getSW1(); + sw[1] = (byte) resp.getSW2(); + log.trace("activate pin returned SW=" + Integer.toHexString(resp.getSW())); + } - if (resp.getSW1() == 0x9000) { + if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { return; - } else if (resp.getSW() == 0x6983) { + } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { //Authentisierungsmethode gesperrt throw new LockedException("[69:83]"); - } else if (resp.getSW() == 0x6984) { - //referenzierte Daten sind reversibel gesperrt (invalidated) - throw new NotActivatedException("[69:84]"); - } else if (resp.getSW() == 0x6985) { - //Benutzungsbedingungen nicht erfüllt - throw new NotActivatedException("[69:85]"); - } else if (resp.getSW() == 0x6400) { +// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { +// //referenzierte Daten sind reversibel gesperrt (invalidated) +// throw new NotActivatedException("[69:84]"); +// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { +// //Benutzungsbedingungen nicht erfüllt +// throw new NotActivatedException("[69:85]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { throw new TimeoutException("[64:00]"); - } else if (resp.getSW() == 0x6401) { + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); } - log.error("Failed to activate pin: SW=" + - Integer.toHexString(resp.getSW())); - throw new SignatureCardException("[" + Integer.toHexString(resp.getSW()) + "]"); + log.error("Failed to activate pin: SW=" + + SMCCHelper.toString(sw)); + throw new SignatureCardException(SMCCHelper.toString(sw)); } catch (CardException ex) { log.error("smart card communication failed: " + ex.getMessage()); @@ -667,9 +670,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } private byte[] getPINVerifyStructure(byte kid) { - - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); // time out after first entry byte bmFormatString = (byte) 0x89; // 1 0001 0 01 // ^------------ System unit = byte // ^^^^------- PIN position in the frame = 1 byte @@ -681,9 +683,14 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 // ^-------- System bit units is bit // ^^^^--- PIN length is at the 4th position bit - byte wPINMaxExtraDigitL = (byte) 0x04; // Max=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Min=4 digits - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed + byte wPINMaxExtraDigitL = // Max=12 digits (Gemplus support max 8) + (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x12; + byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) + (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x04; + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); byte bNumberMessage = (byte) 0x00; // No message byte wLangIdL = (byte) 0x0C; // - English? byte wLangIdH = (byte) 0x04; // \ @@ -725,38 +732,99 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard private byte[] getPINModifyStructure(byte kid) { - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out - byte bmFormatString = (byte) 0x89; // 1 0001 0 01 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- BCD format - byte bmPINBlockString = (byte) 0x47; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 - // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit - byte bInsertionOffsetOld = (byte) 0x01; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x08; // insertion position offset in bytes - byte wPINMaxExtraDigitL = (byte) 0x04; // Min=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Max=12 digits - byte bConfirmPIN = (byte) 0x00; // ??? need for confirm pin - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex1 = (byte) 0x00; // Default Msg - byte bMsgIndex2 = (byte) 0x00; // Default Msg - byte bMsgIndex3 = (byte) 0x00; // Default Msg + byte bTimeOut = reader.getbTimeOut(); // s.o. + byte bTimeOut2 = reader.getbTimeOut2(); // s.o. + byte bmFormatString = (byte) 0x89; // s.o. + byte bmPINBlockString = (byte) 0x47; // s.o. + byte bmPINLengthFormat = (byte) 0x04; // s.o. + byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes + byte bInsertionOffsetNew = (byte) 0x08; // (add 1 from bmFormatString b3) + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x12; + byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) + (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x04; + byte bConfirmPIN = (byte) 0x03; // current pin entry + confirmation + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); + byte bNumberMessage = (byte) 0x03; // 3 messages + byte wLangIdL = (byte) 0x0C; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex1 = (byte) 0x00; // insertion + byte bMsgIndex2 = (byte) 0x01; // modification + byte bMsgIndex3 = (byte) 0x02; // confirmation byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, // CLA INS P1 P2 LC - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // ... - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff // ... + (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff + }; + + int offset = 0; + byte[] pinModifyStructure = new byte[offset + 24 + apdu.length]; + pinModifyStructure[offset++] = bTimeOut; + pinModifyStructure[offset++] = bTimeOut2; + pinModifyStructure[offset++] = bmFormatString; + pinModifyStructure[offset++] = bmPINBlockString; + pinModifyStructure[offset++] = bmPINLengthFormat; + pinModifyStructure[offset++] = bInsertionOffsetOld; + pinModifyStructure[offset++] = bInsertionOffsetNew; + pinModifyStructure[offset++] = wPINMaxExtraDigitL; + pinModifyStructure[offset++] = wPINMaxExtraDigitH; + pinModifyStructure[offset++] = bConfirmPIN; + pinModifyStructure[offset++] = bEntryValidationCondition; + pinModifyStructure[offset++] = bNumberMessage; + pinModifyStructure[offset++] = wLangIdL; + pinModifyStructure[offset++] = wLangIdH; + pinModifyStructure[offset++] = bMsgIndex1; + pinModifyStructure[offset++] = bMsgIndex2; + pinModifyStructure[offset++] = bMsgIndex3; + + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + + pinModifyStructure[offset++] = (byte) apdu.length; + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + System.arraycopy(apdu, 0, pinModifyStructure, offset, apdu.length); + +// log.debug("PIN MODIFY " + SMCCHelper.toString(pinModifyStructure)); + return pinModifyStructure; + } + private byte[] getActivatePINModifyStructure(byte kid) { + + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); + byte bmFormatString = (byte) 0x89; + byte bmPINBlockString = (byte) 0x47; + byte bmPINLengthFormat = (byte) 0x04; + byte bInsertionOffsetOld = (byte) 0x00; // ignored + byte bInsertionOffsetNew = (byte) 0x00; + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x12; + byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) + (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x04; + byte bConfirmPIN = (byte) 0x01; // confirm, no current pin entry + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); + byte bNumberMessage = (byte) 0x02; // 2 messages + byte wLangIdL = (byte) 0x0c; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex1 = (byte) 0x01; // modification prompt + byte bMsgIndex2 = (byte) 0x02; // confirmation prompt + byte bMsgIndex3 = (byte) 0x00; + + byte[] apdu = new byte[] { + (byte) 0x00, (byte) 0x24, (byte) 0x01, kid, (byte) 0x08, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; int offset = 0; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java index 293b9c71..253ac7a0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -17,6 +17,7 @@ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -44,6 +45,7 @@ import java.util.Locale; import java.util.Map; import javax.smartcardio.Card; +import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; import org.apache.commons.logging.Log; @@ -419,7 +421,50 @@ public class SWCard implements SignatureCard { } @Override - public boolean ifdSupportsFeature(byte feature) { - return false; + public CCID getReader() { + return new CCID() { + + @Override + public boolean hasFeature(Byte feature) { + return false; + } + + @Override + public byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) + throws SignatureCardException { + throw new SignatureCardException(CCID.FEATURES[feature.intValue()] + + " not supported"); + } + + @Override + public byte getbTimeOut() { + return 0; + } + + @Override + public byte getbTimeOut2() { + return 0; + } + + @Override + public byte getwPINMaxExtraDigitL() { + return 0x12; + } + + @Override + public byte getwPINMaxExtraDigitH() { + return 0x00; + } + + @Override + public byte getbEntryValidationCondition() { + return 0x02; + } + + @Override + public Card connect() { + return null; + } + }; } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java index 2097e6d3..ad530ad5 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -28,6 +28,7 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; import java.util.List; import java.util.Locale; @@ -36,14 +37,6 @@ import javax.smartcardio.CardTerminal; public interface SignatureCard { - /** - * IFD FEATURES - */ - static final Byte FEATURE_VERIFY_PIN_DIRECT = new Byte((byte) 0x06); - static final Byte FEATURE_MODIFY_PIN_DIRECT = new Byte((byte) 0x07); - static final Byte FEATURE_MCT_READER_DIRECT = new Byte((byte) 0x08); - static final Byte FEATURE_IFD_PIN_PROPERTIES = new Byte((byte) 0x0a); - public static class KeyboxName { public static KeyboxName SECURE_SIGNATURE_KEYPAIR = new KeyboxName( @@ -143,11 +136,7 @@ public interface SignatureCard { public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) throws CancelledException, SignatureCardException, InterruptedException; - /** - * TODO - * @return - */ - public boolean ifdSupportsFeature(byte feature); + public CCID getReader(); /** * Sets the local for evtl. required callbacks (e.g. PINSpec) diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java new file mode 100644 index 00000000..2c56ce98 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java @@ -0,0 +1,77 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.smcc.ccid; + +import at.gv.egiz.smcc.*; +import javax.smartcardio.Card; +import javax.smartcardio.CardException; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public interface CCID { + + + String[] FEATURES = new String[]{"NO_FEATURE", + "FEATURE_VERIFY_PIN_START", + "FEATURE_VERIFY_PIN_FINISH", + "FEATURE_MODIFY_PIN_START", + "FEATURE_MODIFY_PIN_FINISH", + "FEATURE_GET_KEY_PRESSED", + "FEATURE_VERIFY_PIN_DIRECT", + "FEATURE_MODIFY_PIN_DIRECT", + "FEATURE_MCT_READER_DIRECT", + "FEATURE_MCT_UNIVERSAL", + "FEATURE_IFD_PIN_PROPERTIES", + "FEATURE_ABORT", + "FEATURE_SET_SPE_MESSAGE", + "FEATURE_VERIFY_PIN_DIRECT_APP_ID", + "FEATURE_MODIFY_PIN_DIRECT_APP_ID", + "FEATURE_WRITE_DISPLAY", + "FEATURE_GET_KEY", + "FEATURE_IFD_DISPLAY_PROPERTIES"}; + + Byte FEATURE_IFD_PIN_PROPERTIES = new Byte((byte) 10); + Byte FEATURE_MCT_READER_DIRECT = new Byte((byte) 8); + Byte FEATURE_MODIFY_PIN_DIRECT = new Byte((byte) 7); + Byte FEATURE_VERIFY_PIN_DIRECT = new Byte((byte) 6); + + Card connect() throws CardException; + + boolean hasFeature(Byte feature); + + /** + * + * @param feature the corresponding control code will be transmitted + * @param ctrlCommand + * @return + * @throws at.gv.egiz.smcc.SignatureCardException if feature is not supported + * or card communication fails + */ + byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) throws SignatureCardException; + + /** + * allow subclasses to override default (deal with reader bugs) + * @return + */ + byte getbTimeOut(); + byte getbTimeOut2(); + byte getwPINMaxExtraDigitL(); + byte getwPINMaxExtraDigitH(); + byte getbEntryValidationCondition(); +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java new file mode 100644 index 00000000..2cc77dc9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java @@ -0,0 +1,264 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.smcc.ccid; + +import at.gv.egiz.smcc.*; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.HashMap; +import java.util.Map; +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class DefaultReader implements CCID { + + protected final static Log log = LogFactory.getLog(DefaultReader.class); + + private static int CTL_CODE(int code) { + return 0x42000000 + code; + } + + int IOCTL_GET_FEATURE_REQUEST = CTL_CODE(3400); + + + protected Card icc; + protected CardTerminal ct; + + /** + * supported features and respective control codes + */ + protected Map<Byte, Integer> features; + + public DefaultReader(Card icc, CardTerminal ct) { + if (icc == null || ct == null) { + throw new NullPointerException("no card or card terminal provided"); + } + this.icc = icc; + this.ct = ct; + features = queryFeatures(); + } + + public Card connect() throws CardException { //SignatureCardException { +// try { + icc = ct.connect("*"); + return icc; +// } catch (CardException ex) { +// log.error(ex.getMessage(), ex); +// throw new SignatureCardException("Failed to connect to card: " + ex.getMessage()); +// } + } + + Map<Byte, Integer> queryFeatures() { + Map<Byte, Integer> features = new HashMap<Byte, Integer>(); + + if (icc == null) { + log.warn("invalid card handle, cannot query ifd features"); + } else { + try { + if (log.isTraceEnabled()) { + log.trace("GET_FEATURE_REQUEST " + + Integer.toHexString(IOCTL_GET_FEATURE_REQUEST) + + " on " + ct.getName()); + } + byte[] resp = icc.transmitControlCommand(IOCTL_GET_FEATURE_REQUEST, + new byte[]{}); + + if (log.isTraceEnabled()) { + log.trace("Response TLV " + SMCCHelper.toString(resp)); + } + // tag + // length in bytes (always 4) + // control code value for supported feature (in big endian) + for (int i = 0; i < resp.length; i += 6) { + Byte feature = new Byte(resp[i]); + int ioctlBigEndian = (resp[i + 2] << 24) | + (resp[i + 3] << 16) | (resp[i + 4] << 8) | resp[i + 5]; + Integer ioctl = new Integer(ioctlBigEndian); + if (log.isInfoEnabled()) { + log.info("CCID supports " + FEATURES[feature.intValue()] + + ": " + Integer.toHexString(ioctl.intValue())); + } + features.put(feature, ioctl); + } + } catch (CardException ex) { + log.debug("Failed to query CCID features: " + ex.getMessage()); + log.trace(ex); + log.info("CCID does not support PINPad"); + } + } + return features; + } + + @Override + public boolean hasFeature(Byte feature) { + if (features != null) { + return features.containsKey(feature); + } + return false; + } + +// public Integer getIOCTL(Byte feature) { +// if (features != null) { +// return features.get(feature); +// } +// return null; +// } + + @Override + public byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) + throws SignatureCardException { + try { + if (!features.containsKey(feature)) { + throw new SignatureCardException(FEATURES[feature.intValue()] + " not supported"); + } + int ioctl = features.get(feature); + if (log.isTraceEnabled()) { + log.trace("CtrlCommand (" + Integer.toHexString(ioctl) + + ") " + SMCCHelper.toString(ctrlCommand)); + } + byte[] resp = icc.transmitControlCommand(ioctl, ctrlCommand); + if (log.isTraceEnabled()) { + log.trace("CtrlCommand Response " + SMCCHelper.toString(resp)); + } + return resp; + } catch (CardException ex) { + log.error(ex.getMessage()); + throw new SignatureCardException("Failed to transmit CtrlCommand for " + + FEATURES[feature.intValue()]); + } + } + + + @Override + public byte getbTimeOut() { + return (byte) 0x3c; // (max 1min on ReinerSCT), + // 0x00=default, 0x1e = 30sec + } + + @Override + public byte getbTimeOut2() { + return (byte) 0x00; // default + } + + @Override + public byte getwPINMaxExtraDigitL() { + return (byte) 0x12; + } + + @Override + public byte getwPINMaxExtraDigitH() { + return (byte) 0x00; + } + + @Override + public byte getbEntryValidationCondition() { + return (byte) 0x02; // validation key pressed + } + + + // protected byte ifdGetKeyPressed() throws CardException { +// if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { +// +// Long controlCode = (Long) IFD_IOCTL.get(new Byte((byte) 0x05)); +// +// byte key = 0x00; +// while (key == 0x00) { +// +// byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); +// +// if (resp != null && resp.length > 0) { +// key = resp[0]; +// } +// } +// +// System.out.println("Key: " + key); +// +// } +// +// return 0x00; +// } +// +// protected byte[] ifdVerifyPINFinish() throws CardException { +// if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { +// +// Long controlCode = (Long) IFD_IOCTL.get(new Byte((byte) 0x02)); +// +// byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); +// +// System.out.println("CommandResp: " + toString(resp)); +// +// return resp; +// +// } +// +// return null; +// } + + + /** + * assumes ifdSupportsVerifyPIN() == true + * @param pinVerifyStructure + * @return + * @throws javax.smartcardio.CardException + */ +// protected byte[] ifdVerifyPIN(byte[] pinVerifyStructure) throws CardException { +// +//// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_IFD_PIN_PROPERTIES); +//// if (ctrlCode != null) { +//// if (log.isTraceEnabled()) { +//// log.trace("PIN_PROPERTIES CtrlCode " + Integer.toHexString(ctrlCode.intValue())); +//// } +//// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), new byte[] {}); +//// +//// if (log.isTraceEnabled()) { +//// log.trace("PIN_PROPERTIES Response " + SMCCHelper.toString(resp)); +//// } +//// } +// +// +// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_VERIFY_PIN_DIRECT); +// if (ctrlCode == null) { +// throw new NullPointerException("no CtrlCode for FEATURE_VERIFY_PIN_DIRECT"); +// } +// +// if (log.isTraceEnabled()) { +// log.trace("VERIFY_PIN_DIRECT CtrlCode " + Integer.toHexString(ctrlCode.intValue()) + +// ", PIN_VERIFY_STRUCTURE " + SMCCHelper.toString(pinVerifyStructure)); +// } +// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), pinVerifyStructure); +// +// if (log.isTraceEnabled()) { +// log.trace("VERIFY_PIN_DIRECT Response " + SMCCHelper.toString(resp)); +// } +// return resp; +// } + +// protected Long getControlCode(Byte feature) { +// if (ifdFeatures != null) { +// return ifdFeatures.get(feature); +// } +// return null; +// } + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java new file mode 100644 index 00000000..903b11fc --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java @@ -0,0 +1,65 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.smcc.ccid; + +import javax.smartcardio.Card; +import javax.smartcardio.CardTerminal; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class GemplusGemPCPinpad extends DefaultReader { + + protected final static Log log = LogFactory.getLog(GemplusGemPCPinpad.class); + + public GemplusGemPCPinpad(Card icc, CardTerminal ct) { + super(icc, ct); + log.info("Initializing Gemplus GemPC Pinpad reader"); + log.info("Gemplus GemPC Pinpad allows PINs to have 4-8 digits"); + + } + + @Override + public byte getbTimeOut() { + return (byte) 0x3c; // 0x00 default = 15sec + // max 40sec (?) + } + + @Override + public byte getbTimeOut2() { + return (byte) 0x00; // 0x00 default = 15sec + } + + @Override + public byte getwPINMaxExtraDigitL() { + return (byte) 0x08; + } + + @Override + public byte getwPINMaxExtraDigitH() { + return (byte) 0x04; + } + + @Override + public byte getbEntryValidationCondition() { + return (byte) 0x02; // validation key pressed + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java new file mode 100644 index 00000000..2cfcef19 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package at.gv.egiz.smcc.ccid; + +import javax.smartcardio.Card; +import javax.smartcardio.CardTerminal; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class ReaderFactory { + + public static CCID getReader(Card icc, CardTerminal ct) { + if ("Gemplus GemPC Pinpad 00 00".equals(ct.getName())) { + return new GemplusGemPCPinpad(icc, ct); + } + return new DefaultReader(icc, ct); + } +} diff --git a/smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java new file mode 100644 index 00000000..5839d14a --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java @@ -0,0 +1,135 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import at.gv.egiz.smcc.SignatureCard.KeyboxName; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.List; +import java.util.Locale; +import javax.smartcardio.ResponseAPDU; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author clemens + */ +@Ignore +public class ACOSCardTest { + + static ACOSCard card; + static PINSpec infPin, decPin, sigPin; + + public ACOSCardTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + SMCCHelper smccHelper = new SMCCHelper(); + switch (smccHelper.getResultCode()) { + case SMCCHelper.CARD_FOUND: + SignatureCard sigCard = smccHelper.getSignatureCard(Locale.GERMAN); + if (sigCard instanceof ACOSCard) { + System.out.println("ACOS card found"); + card = (ACOSCard) sigCard; + List<PINSpec> pinSpecs = card.getPINSpecs(); + infPin = pinSpecs.get(ACOSCard.PINSPEC_INF); + decPin = pinSpecs.get(ACOSCard.PINSPEC_DEC); + sigPin = pinSpecs.get(ACOSCard.PINSPEC_SIG); + } else { + throw new Exception("not STARCOS card: " + sigCard.toString()); + } + break; + default: + throw new Exception("no card found"); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_pinpad() throws Exception { + System.out.println("verifyPIN (pinpad)"); + assertNotNull(card); + + card.verifyPIN(decPin, new PINProvider() { + + @Override + public char[] providePIN(PINSpec spec, int retries) { + return null; + } + }); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_internal() throws Exception { + System.out.println("verifyPIN (internal)"); + assertNotNull(card); + + card.reset(); + + card.getCard().beginExclusive(); + + // 0x6700 without sending an APDU prior to send CtrlCmd + System.out.println("WARNING: this command will fail if no card " + + "communication took place prior to sending the CtrlCommand"); + int retries = card.verifyPIN(decPin.getKID(), null); //"1397".toCharArray()); + + System.out.println("VERIFY PIN returned " + retries); + card.getCard().endExclusive(); + } + + /** + * Test of changePIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testChangePIN() throws Exception { + System.out.println("changePIN"); + assertNotNull(card); + + card.reset(); + int retries = card.changePIN(decPin.getKID(), null, null); + + System.out.println("CHANGE PIN returned " + retries); + } + + /** + * Test of reset method, of class STARCOSCard. + */ + @Test + public void testReset() throws Exception { + System.out.println("reset"); + assertNotNull(card); + card.reset(); + } + +}
\ No newline at end of file diff --git a/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java new file mode 100644 index 00000000..9be8db00 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java @@ -0,0 +1,267 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import at.gv.egiz.smcc.SignatureCard.KeyboxName; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.List; +import java.util.Locale; +import javax.smartcardio.ResponseAPDU; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author clemens + */ +@Ignore +public class STARCOSCardTest { + + static STARCOSCard card; + static PINSpec cardPin, ssPin; + + public STARCOSCardTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + SMCCHelper smccHelper = new SMCCHelper(); + switch (smccHelper.getResultCode()) { + case SMCCHelper.CARD_FOUND: + SignatureCard sigCard = smccHelper.getSignatureCard(Locale.GERMAN); + if (sigCard instanceof STARCOSCard) { + System.out.println("STARCOS card found"); + card = (STARCOSCard) sigCard; + List<PINSpec> pinSpecs = card.getPINSpecs(); + cardPin = pinSpecs.get(STARCOSCard.PINSPEC_CARD); + ssPin = pinSpecs.get(STARCOSCard.PINSPEC_SS); + + } else { + throw new Exception("not STARCOS card: " + sigCard.toString()); + } + break; + default: + throw new Exception("no card found"); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getCertificate method, of class STARCOSCard. + */ + @Test + @Ignore + public void testGetCertificate() throws Exception { + System.out.println("getCertificate"); + KeyboxName keyboxName = null; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.getCertificate(keyboxName); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of getInfobox method, of class STARCOSCard. + */ + @Test + @Ignore + public void testGetInfobox() throws Exception { + System.out.println("getInfobox"); + String infobox = ""; + PINProvider provider = null; + String domainId = ""; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.getInfobox(infobox, provider, domainId); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of createSignature method, of class STARCOSCard. + */ + @Test + @Ignore + public void testCreateSignature() throws Exception { + System.out.println("createSignature"); + byte[] hash = null; + KeyboxName keyboxName = null; + PINProvider provider = null; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.createSignature(hash, keyboxName, provider); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of selectFileFID method, of class STARCOSCard. + */ + @Test + @Ignore + public void testSelectFileFID() throws Exception { + System.out.println("selectFileFID"); + byte[] fid = null; + STARCOSCard instance = new STARCOSCard(); + ResponseAPDU expResult = null; + ResponseAPDU result = instance.selectFileFID(fid); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_pinpad() throws Exception { + System.out.println("verifyPIN (pinpad)"); + assertNotNull(card); + + card.verifyPIN(cardPin, new PINProvider() { + + @Override + public char[] providePIN(PINSpec spec, int retries) { + return null; + } + }); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_internal() throws Exception { + System.out.println("verifyPIN (internal)"); + assertNotNull(card); + + card.reset(); + + card.getCard().beginExclusive(); + + // 0x6700 without sending an APDU prior to send CtrlCmd + System.out.println("WARNING: this command will fail if no card " + + "communication took place prior to sending the CtrlCommand"); + int retries = card.verifyPIN(cardPin.getKID(), null); //"1397".toCharArray()); + + System.out.println("VERIFY PIN returned " + retries); + card.getCard().endExclusive(); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_byte() throws Exception { + System.out.println("verifyPIN"); + byte kid = 0; + STARCOSCard instance = new STARCOSCard(); + int expResult = 0; + int result = instance.verifyPIN(kid); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of changePIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testChangePIN() throws Exception { + System.out.println("changePIN"); + assertNotNull(card); + + card.reset(); + int retries = card.changePIN(cardPin.getKID(), null, null); + + System.out.println("CHANGE PIN returned " + retries); + } + + /** + * Test of activatePIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testActivatePIN() throws Exception { + System.out.println("activatePIN"); + assertNotNull(card); + + card.reset(); + card.activatePIN(cardPin, new PINProvider() { + + @Override + public char[] providePIN(PINSpec spec, int retries) throws CancelledException, InterruptedException { + return null; + } + }); + } + + /** + * Test of encodePINBlock method, of class STARCOSCard. + */ + @Test + @Ignore + public void testEncodePINBlock() throws Exception { + System.out.println("encodePINBlock"); + char[] pin = null; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.encodePINBlock(pin); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of reset method, of class STARCOSCard. + */ + @Test + public void testReset() throws Exception { + System.out.println("reset"); + assertNotNull(card); + card.reset(); + } + + /** + * Test of toString method, of class STARCOSCard. + */ + @Test + @Ignore + public void testToString() { + System.out.println("toString"); + STARCOSCard instance = new STARCOSCard(); + String expResult = ""; + String result = instance.toString(); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + +}
\ No newline at end of file diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractPINProvider.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractPINProvider.java index e32f08d4..e2499023 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractPINProvider.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractPINProvider.java @@ -35,7 +35,7 @@ public abstract class AbstractPINProvider implements PINProvider, ActionListener protected String action; - private boolean actionPerformed; + protected boolean actionPerformed; // protected void waitForAction() throws InterruptedException { // super.wait(); diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PINProviderFactory.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PINProviderFactory.java index 670b71dc..ce1b2d00 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PINProviderFactory.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PINProviderFactory.java @@ -18,6 +18,7 @@ package at.gv.egiz.bku.smccstal; import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.ccid.CCID; import at.gv.egiz.smcc.PINProvider; import at.gv.egiz.smcc.SignatureCard; import at.gv.egiz.stal.signedinfo.SignedInfoType; @@ -32,7 +33,7 @@ public abstract class PINProviderFactory { public static PINProviderFactory getInstance(SignatureCard forCard, BKUGUIFacade gui) { - if (forCard.ifdSupportsFeature(SignatureCard.FEATURE_VERIFY_PIN_DIRECT)) { + if (forCard.getReader().hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { return new PinpadPINProviderFactory(gui); } else { return new SoftwarePINProviderFactory(gui); diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PinpadPINProviderFactory.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PinpadPINProviderFactory.java index 55321b72..c109ceba 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PinpadPINProviderFactory.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/PinpadPINProviderFactory.java @@ -21,9 +21,8 @@ import at.gv.egiz.bku.gui.BKUGUIFacade; import at.gv.egiz.smcc.CancelledException; import at.gv.egiz.smcc.PINProvider; import at.gv.egiz.smcc.PINSpec; -import at.gv.egiz.stal.HashDataInput; import at.gv.egiz.stal.signedinfo.SignedInfoType; -import java.util.List; +import java.security.DigestException; /** * @@ -51,8 +50,9 @@ public class PinpadPINProviderFactory extends PINProviderFactory { // protected BKUGUIFacade gui; protected SecureViewer viewer; + protected ViewerThread viewerThread; protected SignedInfoType signedInfo; - protected List<HashDataInput> hashDataInputs; + private SignaturePinProvider(SecureViewer viewer, SignedInfoType signedInfo) { @@ -60,61 +60,92 @@ public class PinpadPINProviderFactory extends PINProviderFactory { this.signedInfo = signedInfo; } + protected class ViewerThread extends Thread { + + PINSpec pinSpec; + int retries; + + public ViewerThread(PINSpec pinSpec, int retries) { + this.pinSpec = pinSpec; + this.retries = retries; + } + + @Override + public void run() { + + try { + + gui.showPinpadSignaturePINDialog(pinSpec, retries, + SignaturePinProvider.this, "secureViewer"); + + while (true) { + waitForAction(); + + if ("secureViewer".equals(action)) { + viewer.displayDataToBeSigned(signedInfo, + SignaturePinProvider.this, "pinEntry"); + } else if ("pinEntry".equals(action)) { + gui.showPinpadSignaturePINDialog(pinSpec, retries, + SignaturePinProvider.this, "secureViewer"); + } else { + log.error("unsupported action command: " + action); + } + } + + } catch (DigestException ex) { + log.error("Bad digest value: " + ex.getMessage()); + gui.showErrorDialog(BKUGUIFacade.ERR_INVALID_HASH, + new Object[]{ex.getMessage()}); + } catch (InterruptedException ex) { + log.info("pinpad secure viewer thread interrupted"); + } catch (Exception ex) { + log.error("Could not display hashdata inputs: " + + ex.getMessage()); + gui.showErrorDialog(BKUGUIFacade.ERR_DISPLAY_HASHDATA, + new Object[]{ex.getMessage()}); + } + } + } + @Override public char[] providePIN(PINSpec spec, int retries) throws CancelledException, InterruptedException { - showPinpadPINDialog(retries, spec); + if (viewerThread != null) { + updateViewerThread(retries); + } else { + viewerThread = new ViewerThread(spec, -1); + viewerThread.start(); + } +// if (viewerThread != null) { +// log.trace("interrupt old secure viewer thread"); +// viewerThread.interrupt(); +// } +// viewerThread = new ViewerThread(spec, (retry) ? retries : -1); +// log.trace("start new secure viewer thread"); +// viewerThread.start(); + retry = true; return null; - -// do { -// waitForAction(); -// gui.showWaitDialog(null); -// -// if ("hashData".equals(action)) { -// // show pin dialog in background -// gui.showSignaturePINDialog(spec, (retry) ? retries : -1, -// this, "sign", -// this, "cancel", -// this, "hashData"); -// -// viewer.displayDataToBeSigned(signedInfo.getReference()); -// -// } else if ("sign".equals(action)) { -// retry = true; -// return gui.getPin(); -// } else if ("hashDataDone".equals(action)) { -// gui.showSignaturePINDialog(spec, (retry) ? retries : -1, -// this, "sign", -// this, "cancel", -// this, "hashData"); -// } else if ("cancel".equals(action) || -// "error".equals(action)) { -// throw new CancelledException(spec.getLocalizedName() + -// " entry cancelled"); -// } -// } while (true); } - private void showPinpadPINDialog(int retries, PINSpec pinSpec) { - String title, message; - Object[] params; - if (retry) { - title = BKUGUIFacade.TITLE_RETRY; - message = BKUGUIFacade.MESSAGE_RETRIES; - params = new Object[]{String.valueOf(retries)}; - } else { - title = BKUGUIFacade.TITLE_SIGN; - message = BKUGUIFacade.MESSAGE_ENTERPIN_PINPAD; - String pinSize = String.valueOf(pinSpec.getMinLength()); - if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { - pinSize += "-" + pinSpec.getMaxLength(); - } - params = new Object[]{pinSpec.getLocalizedName(), pinSize}; - } - gui.showMessageDialog(title, message, params); + private synchronized void updateViewerThread(int retries) { + log.trace("update viewer thread"); + viewerThread.retries = retries; + action = "pinEntry"; + actionPerformed = true; + notify(); } + + +// @Override +// protected void finalize() throws Throwable { +// if (viewerThread != null) { +// viewerThread.interrupt(); +// } +// log.info("finalizing Pinpad SignaturePinProvider"); +// super.finalize(); +// } } class CardPinProvider extends AbstractPINProvider { @@ -151,5 +182,5 @@ public class PinpadPINProviderFactory extends PINProviderFactory { gui.showMessageDialog(title, message, params); } } -} + } diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SecureViewer.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SecureViewer.java index c395679a..2ee37dc1 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SecureViewer.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SecureViewer.java @@ -14,12 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package at.gv.egiz.bku.smccstal; -import at.gv.egiz.stal.signedinfo.ReferenceType; +import at.gv.egiz.stal.signedinfo.SignedInfoType; +import java.awt.event.ActionListener; import java.security.DigestException; -import java.util.List; /** * @@ -38,7 +37,7 @@ public interface SecureViewer { * (or any other digest computation error occurs) * @throws java.lang.Exception */ - void displayDataToBeSigned(List<ReferenceType> signedReferences) - throws DigestException, Exception; - + void displayDataToBeSigned(SignedInfoType signedInfo, + ActionListener okListener, String okCommand) + throws DigestException, Exception; } diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java index ac510f38..7a4f6572 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java @@ -33,7 +33,6 @@ import org.apache.commons.logging.LogFactory; import at.gv.egiz.smcc.CancelledException; import at.gv.egiz.smcc.LockedException; import at.gv.egiz.smcc.NotActivatedException; -import at.gv.egiz.smcc.PINProvider; import at.gv.egiz.smcc.SignatureCard; import at.gv.egiz.smcc.SignatureCardException; import at.gv.egiz.smcc.SignatureCard.KeyboxName; @@ -47,11 +46,12 @@ import at.gv.egiz.stal.signedinfo.ObjectFactory; import at.gv.egiz.stal.signedinfo.SignedInfoType; import at.gv.egiz.stal.util.JCEAlgorithmNames; -public abstract class SignRequestHandler extends AbstractRequestHandler implements SecureViewer { +public class SignRequestHandler extends AbstractRequestHandler { private static Log log = LogFactory.getLog(SignRequestHandler.class); private static JAXBContext jaxbContext; private PINProviderFactory pinProviderFactory; + private SecureViewer secureViewer; static { try { @@ -61,6 +61,10 @@ public abstract class SignRequestHandler extends AbstractRequestHandler implemen } } + public SignRequestHandler(SecureViewer secureViewer) { + this.secureViewer = secureViewer; + } + @SuppressWarnings("unchecked") @Override public STALResponse handleRequest(STALRequest request) throws InterruptedException { @@ -85,10 +89,8 @@ public abstract class SignRequestHandler extends AbstractRequestHandler implemen if (pinProviderFactory == null) { pinProviderFactory = PINProviderFactory.getInstance(card, gui); } - PINProvider pinProvider = pinProviderFactory. - getSignaturePINProvider(this, si.getValue()); - - byte[] resp = card.createSignature(md.digest(), kb, pinProvider); + byte[] resp = card.createSignature(md.digest(), kb, + pinProviderFactory.getSignaturePINProvider(secureViewer, si.getValue())); if (resp == null) { return new ErrorResponse(6001); } diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SoftwarePINProviderFactory.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SoftwarePINProviderFactory.java index 54a34280..7d36c2c3 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SoftwarePINProviderFactory.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SoftwarePINProviderFactory.java @@ -49,7 +49,6 @@ public class SoftwarePINProviderFactory extends PINProviderFactory { // protected BKUGUIFacade gui; protected SecureViewer viewer; protected SignedInfoType signedInfo; - protected List<HashDataInput> hashDataInputs; private SignaturePinProvider(SecureViewer viewer, SignedInfoType signedInfo) { @@ -64,22 +63,14 @@ public class SoftwarePINProviderFactory extends PINProviderFactory { gui.showSignaturePINDialog(spec, (retry) ? retries : -1, this, "sign", this, "cancel", - this, "hashData"); + this, "secureViewer"); do { waitForAction(); - gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, - BKUGUIFacade.MESSAGE_WAIT); - - if ("hashData".equals(action)) { - // show pin dialog in background - gui.showSignaturePINDialog(spec, (retry) ? retries : -1, - this, "sign", - this, "cancel", - this, "hashData"); + if ("secureViewer".equals(action)) { try { - viewer.displayDataToBeSigned(signedInfo.getReference()); + viewer.displayDataToBeSigned(signedInfo, this, "pinEntry"); } catch (DigestException ex) { log.error("Bad digest value: " + ex.getMessage()); gui.showErrorDialog(BKUGUIFacade.ERR_INVALID_HASH, @@ -93,17 +84,23 @@ public class SoftwarePINProviderFactory extends PINProviderFactory { this, "error"); } } else if ("sign".equals(action)) { + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); retry = true; return gui.getPin(); - } else if ("hashDataDone".equals(action)) { + } else if ("pinEntry".equals(action)) { gui.showSignaturePINDialog(spec, (retry) ? retries : -1, this, "sign", this, "cancel", - this, "hashData"); + this, "secureViewer"); } else if ("cancel".equals(action) || "error".equals(action)) { + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); throw new CancelledException(spec.getLocalizedName() + " entry cancelled"); + } else { + log.error("unknown action command " + action); } } while (true); } diff --git a/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java b/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java index 51dfe0da..1c1cb833 100644 --- a/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java +++ b/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java @@ -1,5 +1,6 @@ package at.gv.egiz.smcc;
+import at.gv.egiz.smcc.ccid.CCID;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -109,10 +110,10 @@ public class AbstractSMCCSTALTest extends AbstractSMCCSTAL implements }
@Override
- public boolean ifdSupportsFeature(byte feature) {
- return false;
+ public CCID getReader() {
+ throw new UnsupportedOperationException("Not supported yet.");
}
-
+
};
return false;
}
|