diff options
Diffstat (limited to 'smccSTAL/src')
19 files changed, 1760 insertions, 0 deletions
diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/AbstractPINProvider.java b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/AbstractPINProvider.java new file mode 100644 index 00000000..00738188 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/AbstractPINProvider.java @@ -0,0 +1,60 @@ +/* + * 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.pin.gui; + +import at.gv.egiz.smcc.pin.gui.PINProvider; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * common super class providing action listener for all PIN GUIs + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public abstract class AbstractPINProvider implements ActionListener { + + protected static final Log log = LogFactory.getLog(AbstractPINProvider.class); + + protected String action; + protected boolean actionPerformed; + + protected synchronized void waitForAction() throws InterruptedException { + try { + while (!actionPerformed) { + this.wait(); + } + } catch (InterruptedException e) { + log.error("[" + Thread.currentThread().getName() + "] interrupt in waitForAction"); + throw e; + } + actionPerformed = false; + } + + private synchronized void actionPerformed() { + actionPerformed = true; + notify();//All(); + } + + @Override + public void actionPerformed(ActionEvent e) { + log.debug("[" + Thread.currentThread().getName() + "] action performed - " + e.getActionCommand()); + action = e.getActionCommand(); + actionPerformed(); + } +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/SignPINGUI.java b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/SignPINGUI.java new file mode 100644 index 00000000..81db0e90 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/SignPINGUI.java @@ -0,0 +1,136 @@ +/* + * 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.pin.gui; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.bku.smccstal.SecureViewer; +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.pin.gui.PINGUI; +import at.gv.egiz.stal.signedinfo.SignedInfoType; +import java.security.DigestException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The number of retries is not fixed and there is no way (?) to obtain this value. + * A PINProvider should therefore maintain an internal retry counter or flag + * to decide whether or not to warn the user (num retries passed in providePIN). + * + * Therefore PINProvider objects should not be reused. + * + * (ACOS: reload counter: between 0 and 15, where 15 meens deactivated) + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class SignPINGUI extends SignPINProvider implements PINGUI { + + protected static final Log log = LogFactory.getLog(SignPINGUI.class); + + private boolean retry = false; + + public SignPINGUI(BKUGUIFacade gui, SecureViewer viewer, SignedInfoType signedInfo) { + super(gui, viewer, signedInfo); + } + + @Override + public void enterPINDirect(PINSpec spec, int retries) + throws CancelledException, InterruptedException { + if (retry) { + gui.showEnterPINDirect(spec, retries); + } else { + showSignatureData(spec); + gui.showEnterPINDirect(spec, -1); + retry = true; + } + } + + @Override + public void enterPIN(PINSpec spec, int retries) + throws CancelledException, InterruptedException { + if (retry) { + gui.showEnterPIN(spec, retries); + } else { + showSignatureData(spec); + gui.showEnterPIN(spec, -1); + retry = true; + } + } + + private void showSignatureData(PINSpec spec) + throws CancelledException, InterruptedException { + + gui.showSignatureDataDialog(spec, + this, "enterPIN", + this, "cancel", + this, "secureViewer"); + + do { + log.trace("[" + Thread.currentThread().getName() + "] wait for action"); + waitForAction(); + log.trace("[" + Thread.currentThread().getName() + "] received action " + action); + + if ("secureViewer".equals(action)) { + try { + viewer.displayDataToBeSigned(signedInfo, this, "signatureData"); + } catch (DigestException ex) { + log.error("Bad digest value: " + ex.getMessage()); + gui.showErrorDialog(BKUGUIFacade.ERR_INVALID_HASH, + new Object[]{ex.getMessage()}, + this, "error"); + } catch (Exception ex) { + log.error("Could not display hashdata inputs: " + + ex.getMessage()); + gui.showErrorDialog(BKUGUIFacade.ERR_DISPLAY_HASHDATA, + new Object[]{ex.getMessage()}, + this, "error"); + } + } else if ("signatureData".equals(action)) { + gui.showSignatureDataDialog(spec, + this, "enterPIN", + this, "cancel", + this, "secureViewer"); + } else if ("enterPIN".equals(action)) { + return; + } 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); + } + + @Override + public void validKeyPressed() { + gui.validKeyPressed(); + } + + @Override + public void correctionButtonPressed() { + gui.correctionButtonPressed(); + } + + @Override + public void allKeysCleared() { + gui.allKeysCleared(); + } + +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/SignPINProvider.java b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/SignPINProvider.java new file mode 100644 index 00000000..fc1d39af --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/SignPINProvider.java @@ -0,0 +1,105 @@ +/* + * 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.pin.gui; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.bku.smccstal.SecureViewer; +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.pin.gui.PINProvider; +import at.gv.egiz.stal.signedinfo.SignedInfoType; +import java.security.DigestException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The number of retries is not fixed and there is no way (?) to obtain this value. + * A PINProvider should therefore maintain an internal retry counter or flag + * to decide whether or not to warn the user (num retries passed in providePIN). + * + * Therefore PINProvider objects should not be reused. + * + * (ACOS: reload counter: between 0 and 15, where 15 meens deactivated) + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class SignPINProvider extends AbstractPINProvider implements PINProvider { + + protected static final Log log = LogFactory.getLog(SignPINProvider.class); + + protected BKUGUIFacade gui; + protected SecureViewer viewer; + protected SignedInfoType signedInfo; + private boolean retry = false; + + public SignPINProvider(BKUGUIFacade gui, SecureViewer viewer, SignedInfoType signedInfo) { + this.gui = gui; + this.viewer = viewer; + this.signedInfo = signedInfo; + } + + @Override + public char[] providePIN(PINSpec spec, int retries) + throws CancelledException, InterruptedException { + + gui.showSignaturePINDialog(spec, (retry) ? retries : -1, + this, "sign", + this, "cancel", + this, "secureViewer"); + + do { + log.trace("[" + Thread.currentThread().getName() + "] wait for action"); + waitForAction(); + log.trace("[" + Thread.currentThread().getName() + "] received action " + action); + + if ("secureViewer".equals(action)) { + try { + viewer.displayDataToBeSigned(signedInfo, this, "pinEntry"); + } catch (DigestException ex) { + log.error("Bad digest value: " + ex.getMessage()); + gui.showErrorDialog(BKUGUIFacade.ERR_INVALID_HASH, + new Object[]{ex.getMessage()}, + this, "error"); + } catch (Exception ex) { + log.error("Could not display hashdata inputs: " + + ex.getMessage()); + gui.showErrorDialog(BKUGUIFacade.ERR_DISPLAY_HASHDATA, + new Object[]{ex.getMessage()}, + this, "error"); + } + } else if ("sign".equals(action)) { + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + retry = true; + return gui.getPin(); + } else if ("pinEntry".equals(action)) { + gui.showSignaturePINDialog(spec, (retry) ? retries : -1, + this, "sign", + this, "cancel", + 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/main/java/at/gv/egiz/bku/pin/gui/VerifyPINGUI.java b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/VerifyPINGUI.java new file mode 100644 index 00000000..dc21492e --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/VerifyPINGUI.java @@ -0,0 +1,76 @@ +/* + * 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.pin.gui; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.pin.gui.PINGUI; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The number of retries is not fixed and there is no way (?) to obtain this value. + * A PINProvider should therefore maintain an internal retry counter or flag + * to decide whether or not to warn the user (num retries passed in providePIN). + * + * Therefore PINProvider objects should not be reused. + * + * (ACOS: reload counter: between 0 and 15, where 15 meens deactivated) + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class VerifyPINGUI extends VerifyPINProvider implements PINGUI { + + protected static final Log log = LogFactory.getLog(VerifyPINGUI.class); + + private boolean retry = false; + + public VerifyPINGUI(BKUGUIFacade gui) { + super(gui); + } + + @Override + public void enterPINDirect(PINSpec spec, int retries) + throws CancelledException, InterruptedException { + gui.showEnterPINDirect(spec, (retry) ? retries : -1); + retry = true; + } + + @Override + public void enterPIN(PINSpec spec, int retries) { + gui.showEnterPIN(spec, (retry) ? retries : -1); + retry = true; + } + + + @Override + public void validKeyPressed() { + gui.validKeyPressed(); + } + + @Override + public void correctionButtonPressed() { + gui.correctionButtonPressed(); + } + + @Override + public void allKeysCleared() { + gui.allKeysCleared(); + } + +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/VerifyPINProvider.java b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/VerifyPINProvider.java new file mode 100644 index 00000000..fda1e402 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/pin/gui/VerifyPINProvider.java @@ -0,0 +1,72 @@ +/* + * 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.pin.gui; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.pin.gui.PINProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The number of retries is not fixed and there is no way (?) to obtain this value. + * A PINProvider should therefore maintain an internal retry counter or flag + * to decide whether or not to warn the user (num retries passed in providePIN). + * + * Therefore PINProvider objects should not be reused. + * + * (ACOS: reload counter: between 0 and 15, where 15 meens deactivated) + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class VerifyPINProvider extends AbstractPINProvider implements PINProvider { + + protected static final Log log = LogFactory.getLog(VerifyPINProvider.class); + + protected BKUGUIFacade gui; + private boolean retry = false; + + public VerifyPINProvider(BKUGUIFacade gui) { + this.gui = gui; + } + + @Override + public char[] providePIN(PINSpec spec, int retries) + throws CancelledException, InterruptedException { + + gui.showVerifyPINDialog(spec, (retry) ? retries : -1, + this, "verify", + this, "cancel"); + + log.trace("[" + Thread.currentThread().getName() + "] wait for action"); + waitForAction(); + log.trace("[" + Thread.currentThread().getName() + "] received action " + action); + + if ("cancel".equals(action)) { + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + throw new CancelledException(spec.getLocalizedName() + + " entry cancelled"); + } + + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + retry = true; + return gui.getPin(); + } +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java new file mode 100644 index 00000000..14b36e28 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java @@ -0,0 +1,206 @@ +/* + * 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.smccstal; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.SignatureCard; +import at.gv.egiz.smcc.util.SMCCHelper; +import at.gv.egiz.stal.QuitRequest; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; + +/** + * Abstract base class for AppletBKUWorker and LocalBKUWorker, + * providing card specific functionality not implemented by AbstractSMCCSTAL + * as well as common GUI functionality (action event handling). + * <br/> + * This class implements SMCCSTALRequestHandler and registers itself as QUIT handler. + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public abstract class AbstractBKUWorker extends AbstractSMCCSTAL implements ActionListener, SMCCSTALRequestHandler { + + protected static Log log = LogFactory.getLog(AbstractBKUWorker.class); + protected BKUGUIFacade gui; + protected List<String> actionCommandList = new ArrayList<String>(); + protected Boolean actionPerformed = false; + protected boolean finished = false; + + public AbstractBKUWorker(BKUGUIFacade gui) { + if (gui == null) { + throw new NullPointerException("No BKU GUI provided"); + } + this.gui = gui; +// this.locale = gui.getLocale(); + addRequestHandler(QuitRequest.class, this); + } + + /////////////////////////////////////////////////////////////////// + // Common action event handling // + /////////////////////////////////////////////////////////////////// + + /** + * notifies all registered handlers that an event occured + * @param e + */ + @Override + public void actionPerformed(ActionEvent e) { + log.info("Action: " + e); + if (actionCommandList != null) { + if (actionCommandList.contains(e.getActionCommand())) { + actionOccured(); + } + } else { + actionOccured(); + } + } + + /** + * register for notification on action event + * @throws java.lang.InterruptedException + */ + protected synchronized void waitForAction() throws InterruptedException { + log.info("Waiting for Action"); + while (!actionPerformed) { + wait(); + } + actionPerformed = false; + } + + protected synchronized void actionOccured() { + log.info("Received Action"); + actionPerformed = true; + notifyAll(); + } + + /////////////////////////////////////////////////////////////////// + // card specific implementations of AbstractSMCCSTAL // + /////////////////////////////////////////////////////////////////// + + @Override + protected boolean waitForCard() { + if (signatureCard != null) { + return false; + } + SMCCHelper smccHelper = new SMCCHelper(); + actionCommandList.clear(); + actionCommandList.add("cancel"); + // while no sigcard found or cancel button pressed + int oldValue = SMCCHelper.PC_SC_NOT_SUPPORTED; // this is a save default + while ((signatureCard == null) && (!actionPerformed)) { + switch (smccHelper.getResultCode()) { + case SMCCHelper.PC_SC_NOT_SUPPORTED: + actionCommandList.clear(); + actionCommandList.add("ok"); + gui.showErrorDialog(BKUGUIFacade.ERR_NO_PCSC, null, this, "ok"); + try { + waitForAction(); + } catch (InterruptedException e) { + log.error(e); + } + return true; + case SMCCHelper.TERMINAL_NOT_PRESENT: + actionCommandList.clear(); + actionCommandList.add("ok"); + gui.showErrorDialog(BKUGUIFacade.ERR_NO_CARDTERMINAL, null, this, "ok"); + try { + waitForAction(); + } catch (InterruptedException e) { + log.error(e); + } + return true; + case SMCCHelper.CARD_NOT_SUPPORTED: + if (oldValue != SMCCHelper.CARD_NOT_SUPPORTED) { + actionCommandList.clear(); + actionCommandList.add("cancel"); + gui.showMessageDialog(BKUGUIFacade.TITLE_CARD_NOT_SUPPORTED, + BKUGUIFacade.MESSAGE_CARD_NOT_SUPPORTED, null, + BKUGUIFacade.BUTTON_CANCEL, this, "cancel"); + oldValue = SMCCHelper.CARD_NOT_SUPPORTED; + } + break; + case SMCCHelper.NO_CARD: + if (oldValue != SMCCHelper.NO_CARD) { + actionCommandList.clear(); + actionCommandList.add("cancel"); + gui.showMessageDialog(BKUGUIFacade.TITLE_INSERTCARD, + BKUGUIFacade.MESSAGE_INSERTCARD, null, + BKUGUIFacade.BUTTON_CANCEL, this, "cancel"); + oldValue = SMCCHelper.NO_CARD; + } + break; + case SMCCHelper.CARD_FOUND: + signatureCard = smccHelper.getSignatureCard(gui.getLocale()); + return false; + } + smccHelper.update(3000); + } + return signatureCard == null; + } + + @Override + protected BKUGUIFacade getGUI() { + return gui; + } + + /////////////////////////////////////////////////////////////////// + // SMCCSTALRequestHandler for QUIT requests // + /////////////////////////////////////////////////////////////////// + + /** + * Handle QUIT requests: set finished true. + * @param request a QUIT request + * @return null (no response on QUIT) + */ + @Override + public STALResponse handleRequest(STALRequest request) { + if (request instanceof QuitRequest) { + log.info("Setting state to: finished for BKUWorker " + this); + finished = true; + } else { + log.error("Unexpected request to handle: " + request); + } + return null; + } + + /** + * No initialization required for QUIT request handlers. + * @param sc + * @param gui + */ + @Override + public void init(SignatureCard sc, BKUGUIFacade gui) { + } + + /** + * QUIT request handlers do not require a card. + * @return false + */ + @Override + public boolean requireCard() { + return false; + } +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractRequestHandler.java new file mode 100644 index 00000000..98b21f79 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractRequestHandler.java @@ -0,0 +1,86 @@ +/* +* 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.smccstal; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.SignatureCard; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; + +public abstract class AbstractRequestHandler implements SMCCSTALRequestHandler, + ActionListener { + private final static Log log = LogFactory + .getLog(AbstractRequestHandler.class); + + protected SignatureCard card; + protected BKUGUIFacade gui; + protected static STALMessageConsumer messageConsumer = null; + protected String actionCommand; + protected boolean actionPerformed = false; + + @Override + public abstract STALResponse handleRequest(STALRequest request) throws InterruptedException; + + @Override + public void init(SignatureCard sc, BKUGUIFacade gui) { + if ((sc == null) || (gui == null)) { + throw new NullPointerException("Parameter must not be set to null"); + } + this.card = sc; + this.gui = gui; + } + + public static void setMessageConsumer(STALMessageConsumer messageConsumer) { + AbstractRequestHandler.messageConsumer = messageConsumer; + } + + protected static void newSTALMessage(String caption, String message) { + if (messageConsumer != null) { + messageConsumer.consumeNewSTALMessage(caption, message); + } + } + + protected synchronized void waitForAction() throws InterruptedException { + try { + while (!actionPerformed) { + wait(); + } + } catch (InterruptedException e) { + log.error("interrupt in waitForAction"); + throw e; + } + actionPerformed = false; + } + + private synchronized void actionPerformed() { + actionPerformed = true; + notifyAll(); + } + + @Override + public void actionPerformed(ActionEvent e) { + actionCommand = e.getActionCommand(); + actionPerformed(); + } + +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractSMCCSTAL.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractSMCCSTAL.java new file mode 100644 index 00000000..d0762da9 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractSMCCSTAL.java @@ -0,0 +1,192 @@ +/* + * 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.smccstal; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +//import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.SignatureCard; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.InfoboxReadRequest; +import at.gv.egiz.stal.STAL; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.StatusRequest; + +public abstract class AbstractSMCCSTAL implements STAL { + private static Log log = LogFactory.getLog(AbstractSMCCSTAL.class); + + public final static int DEFAULT_MAX_RETRIES = 1; + +// protected Locale locale = Locale.getDefault(); + protected SignatureCard signatureCard = null; + protected Map<String, SMCCSTALRequestHandler> handlerMap = new HashMap<String, SMCCSTALRequestHandler>(); + + protected int maxRetries = DEFAULT_MAX_RETRIES; + protected Set<Integer> unrecoverableErrors = new HashSet<Integer>(); + + protected AbstractSMCCSTAL() { + addRequestHandler(InfoboxReadRequest.class, new InfoBoxReadRequestHandler()); + addRequestHandler(StatusRequest.class, new StatusRequestHandler()); + unrecoverableErrors.add(6001); + } + + /** + * Implementations must assign the signature card within this method. + * + * @return if the user canceled + */ + protected abstract boolean waitForCard(); + + protected abstract BKUGUIFacade getGUI(); + + private STALResponse getResponse(STALRequest request) throws InterruptedException { + int retryCounter = 0; + while (retryCounter < maxRetries) { + log.info("Retry #" + retryCounter + " of " + maxRetries); + SMCCSTALRequestHandler handler = null; + handler = handlerMap.get(request.getClass().getSimpleName()); + if (handler != null) { + if (handler.requireCard()) { + if (waitForCard()) { + return new ErrorResponse(6001); + } + } + try { + handler.init(signatureCard, getGUI()); + STALResponse response = handler.handleRequest(request); + if (response != null) { + if (response instanceof ErrorResponse) { + log.info("Got an error response"); + ErrorResponse err = (ErrorResponse) response; + if (unrecoverableErrors.contains(err.getErrorCode())) { + return response; + } + if ((++retryCounter < maxRetries) && (handler.requireCard())) { + signatureCard.disconnect(true); + signatureCard = null; + } else { + log.info("Exceeded max retries, returning error " + + err.getErrorMessage()); + return response; + } + } else { + return response; + } + } else { + log.info("Got null response from handler, assuming quit"); + return null; + } + } catch (InterruptedException e) { + log.info("Interrupt during request handling, do not retry"); + throw e; + } catch (Exception e) { + log.info("Error while handling STAL request:", e); + if (++retryCounter < maxRetries) { + signatureCard.disconnect(true); + signatureCard = null; + } else { + log.info("Exceeded max retries, returning error."); + return new ErrorResponse(6000); + } + } + } else { + log.error("Cannot find a handler for STAL request: " + request); + return new ErrorResponse(); + } + } + return new ErrorResponse(6000); + } + + /** + * + * @param requestList + * @return + * @throws RuntimeException with cause InterruptedException if interrupted + */ + @Override + public List<STALResponse> handleRequest(List<? extends STALRequest> requestList) { + log.debug("Got request list containing " + requestList.size() + + " STAL requests"); + List<STALResponse> responseList = new ArrayList<STALResponse>(requestList + .size()); + for (STALRequest request : requestList) { + log.info("Processing: " + request.getClass()); + STALResponse response; + try { + response = getResponse(request); + if (response != null) { + responseList.add(response); + if (response instanceof ErrorResponse) { + log.info("Got an error response, don't process remaining requests"); + break; + } + } + } catch (InterruptedException ex) { + log.error("interrupted during request handling"); + throw new RuntimeException("interrupted during request handling", ex); + } + + } + return responseList; + } + + public void addRequestHandler(Class<? extends STALRequest> id, + SMCCSTALRequestHandler handler) { + log.debug("Registering STAL request handler: " + id.getSimpleName()); + handlerMap.put(id.getSimpleName(), handler); + } + + public void removeRequestHandler(Class<? extends STALRequest> id) { + log.debug("De-registering STAL request handler: " + id.getSimpleName()); + handlerMap.remove(id.getSimpleName()); + } + + public SMCCSTALRequestHandler getRequestHandler( + Class<? extends STALRequest> request) { + return handlerMap.get(request.getSimpleName()); + } + +// @Override +// public void setLocale(Locale locale) { +// if (locale == null) { +// throw new NullPointerException("Locale must not be set to null"); +// } +// this.locale = locale; +// } + + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + public Set<Integer> getUnrecoverableErrors() { + return unrecoverableErrors; + } + + public void setUnrecoverableErrors(Set<Integer> unrecoverableErrors) { + this.unrecoverableErrors = unrecoverableErrors; + } +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/DomainIdConverter.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/DomainIdConverter.java new file mode 100644 index 00000000..3a564b91 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/DomainIdConverter.java @@ -0,0 +1,85 @@ +/* +* 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.smccstal;
+
+import iaik.me.asn1.ASN1;
+import iaik.me.utils.Base64;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Vector;
+
+public class DomainIdConverter {
+
+ private static String getBaseId(ASN1 identityLink) throws IOException {
+
+ if (identityLink.getType() == ASN1.TYPE_SEQUENCE) {
+ ASN1 personData = identityLink.getElementAt(4);
+ if (personData.getType() == ASN1.TAG_CONTEXT_SPECIFIC) {
+ ASN1 physicalPersonData = personData.gvASN1();
+ ASN1 baseId = physicalPersonData.getElementAt(0);
+ return baseId.gvString();
+ }
+ throw new IOException("Invalid structure.");
+
+ }
+ throw new IOException("Invalid structure.");
+
+ }
+
+ private static ASN1 replaceBaseId(ASN1 identityLink, String newBaseId)
+ throws IOException {
+
+ ASN1 newIdentityLink = new ASN1(ASN1.TYPE_SEQUENCE, new Vector<ASN1>());
+ for (int i = 0; i < identityLink.getSize(); i++) {
+ ASN1 asn1 = identityLink.getElementAt(i);
+ if (i == 4 && asn1.getType() == ASN1.TAG_CONTEXT_SPECIFIC) {
+ ASN1 physicalPersonData = asn1.gvASN1();
+ ASN1 newPhysicalPersonData = new ASN1(ASN1.TYPE_SEQUENCE,
+ new Vector<ASN1>());
+ newPhysicalPersonData.addElement(new ASN1(ASN1.TYPE_UTF8_STRING,
+ newBaseId));
+ for (int j = 1; j < physicalPersonData.getSize(); j++) {
+ newPhysicalPersonData.addElement(physicalPersonData.getElementAt(j));
+ }
+ asn1 = new ASN1(ASN1.TAG_CONTEXT_SPECIFIC, newPhysicalPersonData);
+ }
+ newIdentityLink.addElement(asn1);
+ }
+ return newIdentityLink;
+
+ }
+
+ public static byte[] convertDomainId(byte[] data, String domainId)
+ throws IOException, NoSuchAlgorithmException {
+ if (domainId == null) {
+ return data;
+ }
+ ASN1 identityLink = new ASN1(data);
+ MessageDigest sha = null;
+ sha = MessageDigest.getInstance("SHA");
+ String base = getBaseId(identityLink);
+ sha.update((base + "+" + domainId).getBytes());
+ String bpkStr = new String(Base64.encode(sha.digest()));
+ bpkStr = bpkStr.trim();
+ identityLink = replaceBaseId(identityLink, bpkStr);
+ System.out.println(getBaseId(identityLink));
+ return identityLink.getEncoded();
+ }
+
+}
diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/HashDataInputDisplay.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/HashDataInputDisplay.java new file mode 100644 index 00000000..59700d4a --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/HashDataInputDisplay.java @@ -0,0 +1,46 @@ +/* + * 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.smccstal; + +import at.gv.egiz.stal.signedinfo.ReferenceType; +import java.security.DigestException; +import java.util.List; + +/** + * + * @author clemens + */ +public interface HashDataInputDisplay { + + + /** + * TODO: (see AbstractHelpListener) + * + * + * Displays the hashdata inputs for all provided dsig:SignedReferences. + * Implementations may verify the digest value if necessary. + * (LocalSignRequestHandler operates on DataObjectHashDataInput, + * other SignRequestHandlers should cache the HashDataInputs obtained by webservice calls, + * or simply forward to a HashDataInputServlet.) + * @param signedReferences The caller may select a subset of the references in SignedInfo to be displayed. + * @throws java.security.DigestException if digest values are verified and do not correspond + * (or any other digest computation error occurs) + * @throws java.lang.Exception + */ + void displayHashDataInputs(List<ReferenceType> signedReferences) throws DigestException, Exception; + +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java new file mode 100644 index 00000000..b34ab862 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/InfoBoxReadRequestHandler.java @@ -0,0 +1,141 @@ +/* +* 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.smccstal; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.bku.pin.gui.VerifyPINGUI; +import org.apache.commons.logging.Log; +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.SignatureCard; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.InfoboxReadRequest; +import at.gv.egiz.stal.InfoboxReadResponse; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; + +public class InfoBoxReadRequestHandler extends AbstractRequestHandler { + + private static Log log = LogFactory.getLog(InfoBoxReadRequestHandler.class); + +// protected PINProviderFactory pinProviderFactory; + + @Override + public STALResponse handleRequest(STALRequest request) throws InterruptedException { + if (request instanceof InfoboxReadRequest) { + InfoboxReadRequest infoBox = (InfoboxReadRequest) request; + + try { + if (infoBox.getInfoboxIdentifier().equals("IdentityLink")) { + newSTALMessage("Message.RequestCaption", "Message.IdentityLink"); + log.debug("Handling identitylink infobox"); + byte[] resp = card.getInfobox(infoBox.getInfoboxIdentifier(), + new VerifyPINGUI(gui), + infoBox.getDomainIdentifier()); + if (resp == null) { + log.info("Infobox doesn't contain any data. Assume card is not activated."); + throw new NotActivatedException(); + } else { + try { + resp = DomainIdConverter.convertDomainId(resp, infoBox + .getDomainIdentifier()); + } catch (Exception e) { + log.error("Cannot convert domain specific id", e); + return new ErrorResponse(1000); + } + } + InfoboxReadResponse stalResp = new InfoboxReadResponse(); + stalResp.setInfoboxValue(resp); + return stalResp; + } else if (SignatureCard.KeyboxName.CERITIFIED_KEYPAIR.equals(infoBox + .getInfoboxIdentifier())) { + newSTALMessage("Message.RequestCaption", "Message.CertifiedKeypair"); + log.debug("Handling certified keypair infobox"); + byte[] resp = card + .getCertificate(SignatureCard.KeyboxName.CERITIFIED_KEYPAIR); + if (resp == null) { + return new ErrorResponse(6001); + } + InfoboxReadResponse stalResp = new InfoboxReadResponse(); + stalResp.setInfoboxValue(resp); + return stalResp; + } else if (SignatureCard.KeyboxName.SECURE_SIGNATURE_KEYPAIR + .equals(infoBox.getInfoboxIdentifier())) { + newSTALMessage("Message.RequestCaption", + "Message.SecureSignatureKeypair"); + log.debug("Handling secure signature keypair infobox"); + byte[] resp = card + .getCertificate(SignatureCard.KeyboxName.SECURE_SIGNATURE_KEYPAIR); + if (resp == null) { + return new ErrorResponse(6001); + } + InfoboxReadResponse stalResp = new InfoboxReadResponse(); + stalResp.setInfoboxValue(resp); + return stalResp; + } else { + newSTALMessage("Message.RequestCaption", "Message.InfoboxReadRequest"); + log.warn("Unknown infobox identifier: " + + infoBox.getInfoboxIdentifier() + " trying generic request"); + byte[] resp = card.getInfobox(infoBox.getInfoboxIdentifier(), + new VerifyPINGUI(gui), + infoBox.getDomainIdentifier()); + if (resp == null) { + return new ErrorResponse(6001); + } + InfoboxReadResponse stalResp = new InfoboxReadResponse(); + stalResp.setInfoboxValue(resp); + return stalResp; + } + } catch (IllegalArgumentException e) { + log.info("Infobox " + infoBox.getInfoboxIdentifier() + " not supported."); + return new ErrorResponse(4002); + } catch (NotActivatedException e) { + log.info("Citizen card not activated.", e); + gui.showErrorDialog(BKUGUIFacade.ERR_CARD_NOTACTIVATED, null, this, null); + waitForAction(); + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + return new ErrorResponse(6001); + } catch (LockedException e) { + log.info("Citizen card locked.", e); + gui.showErrorDialog(BKUGUIFacade.ERR_CARD_LOCKED, null, this, null); + waitForAction(); + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + return new ErrorResponse(6001); + } catch (CancelledException cx) { + log.debug("User cancelled request", cx); + return new ErrorResponse(6001); + } catch (SignatureCardException e) { + log.info("Error while reading infobox: " + e); + return new ErrorResponse(4000); + } + } else { + log.fatal("Got unexpected STAL request: " + request); + return new ErrorResponse(1000); + } + } + + @Override + public boolean requireCard() { + return true; + } +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SMCCSTALRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SMCCSTALRequestHandler.java new file mode 100644 index 00000000..f19b3ef3 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SMCCSTALRequestHandler.java @@ -0,0 +1,33 @@ +/* +* 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.smccstal; + + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.SignatureCard; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; + +public interface SMCCSTALRequestHandler { + + public void init(SignatureCard sc, BKUGUIFacade gui); + + public STALResponse handleRequest(STALRequest request) throws InterruptedException; + + public boolean requireCard(); + +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/STALMessageConsumer.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/STALMessageConsumer.java new file mode 100644 index 00000000..7d7a6ec0 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/STALMessageConsumer.java @@ -0,0 +1,21 @@ +/* +* 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.smccstal;
+
+public interface STALMessageConsumer {
+ public void consumeNewSTALMessage(String captionId, String messageId);
+}
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 new file mode 100644 index 00000000..2ee37dc1 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SecureViewer.java @@ -0,0 +1,43 @@ +/* + * 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.smccstal; + +import at.gv.egiz.stal.signedinfo.SignedInfoType; +import java.awt.event.ActionListener; +import java.security.DigestException; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public interface SecureViewer { + + /** + * Displays the hashdata inputs for all provided dsig:SignedReferences. + * Implementations may verify the digest value if necessary. + * (LocalSignRequestHandler operates on DataObjectHashDataInput, + * other SignRequestHandlers should cache the HashDataInputs obtained by webservice calls, + * or simply forward to a HashDataInputServlet.) + * @param signedReferences The caller may select a subset of the references in SignedInfo to be displayed. + * @throws java.security.DigestException if digest values are verified and do not correspond + * (or any other digest computation error occurs) + * @throws java.lang.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 new file mode 100644 index 00000000..5b436d16 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/SignRequestHandler.java @@ -0,0 +1,136 @@ +/* + * 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.smccstal; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.bku.pin.gui.SignPINGUI; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.apache.commons.logging.Log; +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.SignatureCard; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.SignatureCard.KeyboxName; +import at.gv.egiz.smcc.TimeoutException; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.SignRequest; +import at.gv.egiz.stal.SignResponse; +import at.gv.egiz.stal.signedinfo.ObjectFactory; +import at.gv.egiz.stal.signedinfo.SignedInfoType; + +public class SignRequestHandler extends AbstractRequestHandler { + + private static Log log = LogFactory.getLog(SignRequestHandler.class); + private static JAXBContext jaxbContext; + + static { + try { + jaxbContext = JAXBContext.newInstance(ObjectFactory.class.getPackage().getName()); + } catch (JAXBException e) { + log.fatal("Cannot init jaxbContext", e); + } + } + + protected SecureViewer secureViewer; + + public SignRequestHandler(SecureViewer secureViewer) { + this.secureViewer = secureViewer; + } + + @SuppressWarnings("unchecked") + @Override + public STALResponse handleRequest(STALRequest request) throws InterruptedException { + if (request instanceof SignRequest) { + SignRequest signReq = (SignRequest) request; + newSTALMessage("Message.RequestCaption", "Message.SignRequest"); + try { + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + InputStream is = new ByteArrayInputStream(signReq.getSignedInfo()); + JAXBElement<SignedInfoType> si = (JAXBElement<SignedInfoType>) unmarshaller.unmarshal(is); + String signatureMethod = si.getValue().getSignatureMethod().getAlgorithm(); + log.debug("Found signature method: " + signatureMethod); + KeyboxName kb = SignatureCard.KeyboxName.getKeyboxName(signReq.getKeyIdentifier()); + + byte[] resp = card.createSignature(new ByteArrayInputStream(signReq.getSignedInfo()), kb, + new SignPINGUI(gui, secureViewer, si.getValue()), signatureMethod); + if (resp == null) { + return new ErrorResponse(6001); + } + SignResponse stalResp = new SignResponse(); + stalResp.setSignatureValue(resp); + return stalResp; + } catch (NotActivatedException e) { + log.info("Citizen card not activated.", e); + gui.showErrorDialog(BKUGUIFacade.ERR_CARD_NOTACTIVATED, null, this, null); + waitForAction(); + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + return new ErrorResponse(6001); + } catch (LockedException e) { + log.info("Citizen card locked.", e); + gui.showErrorDialog(BKUGUIFacade.ERR_CARD_LOCKED, null, this, null); + waitForAction(); + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + return new ErrorResponse(6001); + } catch (CancelledException cx) { + log.debug("User cancelled request"); + return new ErrorResponse(6001); + } catch (TimeoutException ex) { + log.error("Timeout during pin entry"); + gui.showMessageDialog(BKUGUIFacade.TITLE_ENTRY_TIMEOUT, + BKUGUIFacade.ERR_PIN_TIMEOUT, null, + BKUGUIFacade.BUTTON_CANCEL, this, null); + waitForAction(); + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + return new ErrorResponse(6001); + } catch (SignatureCardException e) { + log.error("Error while creating signature: " + e); + return new ErrorResponse(4000); + } catch (JAXBException e) { + log.error("Cannot unmarshall signed info", e); + return new ErrorResponse(1000); + } catch (IOException e) { + log.error("Error while creating signature: " + e); + return new ErrorResponse(4000); + } + } else { + log.fatal("Got unexpected STAL request: " + request); + return new ErrorResponse(1000); + } + } + + @Override + public boolean requireCard() { + return true; + } + +} diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/StatusRequestHandler.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/StatusRequestHandler.java new file mode 100644 index 00000000..56b24920 --- /dev/null +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/StatusRequestHandler.java @@ -0,0 +1,72 @@ +/* + * 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.smccstal; + +import at.gv.egiz.bku.gui.BKUGUIFacade; +import at.gv.egiz.smcc.SignatureCard; +import at.gv.egiz.smcc.util.SMCCHelper; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.StatusRequest; +import at.gv.egiz.stal.StatusResponse; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class StatusRequestHandler extends AbstractRequestHandler { + + protected static final Log log = LogFactory.getLog(StatusRequestHandler.class); + + @Override + public void init(SignatureCard sc, BKUGUIFacade gui) { + //nothing, avoid NullPointerEx since requireCard==false => sc==null + } + + + @Override + public STALResponse handleRequest(STALRequest request) throws InterruptedException { + + if (request instanceof StatusRequest) { + log.info("handling STATUS request"); + SMCCHelper smccHelper = new SMCCHelper(); + StatusResponse response = new StatusResponse(); + if (log.isTraceEnabled()) { + log.trace("SMCC result code: " + smccHelper.getResultCode() + + ", cardReady: " + + (smccHelper.getResultCode() == SMCCHelper.CARD_FOUND)); + } + if (smccHelper.getResultCode() == SMCCHelper.CARD_FOUND) { + response.setCardReady(Boolean.TRUE); + } else { + response.setCardReady(Boolean.FALSE); + } + return response; + } else { + log.error("Got unexpected STAL request: " + request); + return new ErrorResponse(1000); + } + } + + @Override + public boolean requireCard() { + return false; + } +} diff --git a/smccSTAL/src/test/java/at/gv/egiz/smcc/ASN1Test.java b/smccSTAL/src/test/java/at/gv/egiz/smcc/ASN1Test.java new file mode 100644 index 00000000..f2d61e7f --- /dev/null +++ b/smccSTAL/src/test/java/at/gv/egiz/smcc/ASN1Test.java @@ -0,0 +1,105 @@ +/* +* 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;
+
+import iaik.me.asn1.ASN1;
+import iaik.me.utils.Base64;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Vector;
+
+import org.junit.Ignore;
+
+@Ignore
+public class ASN1Test {
+
+ public static void main(String[] args) throws IOException {
+
+ ClassLoader classLoader = ASN1Test.class.getClassLoader();
+
+ InputStream stream = classLoader.getResourceAsStream("IdentityLink.bin");
+
+ if (stream != null) {
+
+ ASN1 identityLink = new ASN1(stream);
+ System.out.println("BaseId:" + getBaseId(identityLink));
+ identityLink = replaceBaseId(identityLink, "test");
+ System.out.println("BaseId:" + getBaseId(identityLink));
+
+ String bpk = "hansi";
+ MessageDigest sha = null;
+ try {
+ sha = MessageDigest.getInstance("SHA");
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ sha.update((identityLink + "+" + bpk).getBytes());
+ String bpkStr = new String(Base64.encode(sha.digest()));
+ System.out.println("bpk: "+bpkStr);
+ identityLink = replaceBaseId(identityLink, bpkStr);
+ System.out.println(getBaseId(identityLink));
+
+ } else {
+ System.out.println("Not found.");
+ }
+
+ }
+
+ private static String getBaseId(ASN1 identityLink) throws IOException {
+
+ if (identityLink.getType() == ASN1.TYPE_SEQUENCE) {
+ ASN1 personData = identityLink.getElementAt(4);
+ if (personData.getType() == ASN1.TAG_CONTEXT_SPECIFIC) {
+ ASN1 physicalPersonData = personData.gvASN1();
+ ASN1 baseId = physicalPersonData.getElementAt(0);
+ return baseId.gvString();
+ }
+ throw new IOException("Invalid structure.");
+
+ }
+ throw new IOException("Invalid structure.");
+
+ }
+
+ private static ASN1 replaceBaseId(ASN1 identityLink, String newBaseId)
+ throws IOException {
+
+ ASN1 newIdentityLink = new ASN1(ASN1.TYPE_SEQUENCE, new Vector<ASN1>());
+ for (int i = 0; i < identityLink.getSize(); i++) {
+ ASN1 asn1 = identityLink.getElementAt(i);
+ if (i == 4 && asn1.getType() == ASN1.TAG_CONTEXT_SPECIFIC) {
+ ASN1 physicalPersonData = asn1.gvASN1();
+ ASN1 newPhysicalPersonData = new ASN1(ASN1.TYPE_SEQUENCE,
+ new Vector<ASN1>());
+ newPhysicalPersonData.addElement(new ASN1(ASN1.TYPE_UTF8_STRING,
+ newBaseId));
+ for (int j = 1; j < physicalPersonData.getSize(); j++) {
+ newPhysicalPersonData.addElement(physicalPersonData.getElementAt(j));
+ }
+ asn1 = new ASN1(ASN1.TAG_CONTEXT_SPECIFIC, newPhysicalPersonData);
+ }
+ newIdentityLink.addElement(asn1);
+ }
+ return newIdentityLink;
+
+ }
+
+}
diff --git a/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java b/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java new file mode 100644 index 00000000..bf57b0a6 --- /dev/null +++ b/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java @@ -0,0 +1,126 @@ +package at.gv.egiz.smcc;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardTerminal;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import at.gv.egiz.bku.gui.BKUGUIFacade;
+import at.gv.egiz.bku.smccstal.AbstractSMCCSTAL;
+import at.gv.egiz.bku.smccstal.SMCCSTALRequestHandler;
+import at.gv.egiz.smcc.pin.gui.PINGUI;
+import at.gv.egiz.stal.ErrorResponse;
+import at.gv.egiz.stal.InfoboxReadRequest;
+import at.gv.egiz.stal.InfoboxReadResponse;
+import at.gv.egiz.stal.STALRequest;
+import at.gv.egiz.stal.STALResponse;
+
+public class AbstractSMCCSTALTest extends AbstractSMCCSTAL implements
+ SMCCSTALRequestHandler {
+ private int errorConter;
+
+ @Override
+ protected BKUGUIFacade getGUI() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected boolean waitForCard() {
+ signatureCard = new SignatureCard() {
+
+ @Override
+ public byte[] createSignature(InputStream input, KeyboxName keyboxName,
+ PINGUI provider, String alg) throws SignatureCardException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void disconnect(boolean reset) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public byte[] getInfobox(String infobox, PINGUI provider,
+ String domainId) throws SignatureCardException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void init(Card card, CardTerminal cardTerminal) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setLocale(Locale locale) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Card getCard() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void reset() throws SignatureCardException {
+ // TODO Auto-generated method stub
+
+ }
+ };
+ return false;
+ }
+
+ @Before
+ public void setUp() {
+ addRequestHandler(InfoboxReadRequest.class, this);
+ }
+
+ @Test
+ @Ignore
+ public void testRetry() {
+ InfoboxReadRequest irr = new InfoboxReadRequest();
+ List<STALRequest> irrl = new ArrayList<STALRequest>();
+ irrl.add(irr);
+ List<STALResponse> list = handleRequest(irrl);
+ Assert.assertFalse(list.get(0) instanceof ErrorResponse);
+ }
+
+ @Override
+ public STALResponse handleRequest(STALRequest request) {
+ if (++errorConter < 3) {
+ return new ErrorResponse(400);
+ }
+ return new InfoboxReadResponse();
+ }
+
+ @Override
+ public void init(SignatureCard sc, BKUGUIFacade gui) {
+ }
+
+ @Override
+ public boolean requireCard() {
+ return true;
+ }
+
+}
diff --git a/smccSTAL/src/test/resources/log4j.properties b/smccSTAL/src/test/resources/log4j.properties new file mode 100644 index 00000000..053eac17 --- /dev/null +++ b/smccSTAL/src/test/resources/log4j.properties @@ -0,0 +1,19 @@ +# loglever DEBUG, appender STDOUT +log4j.rootLogger=TRACE, STDOUT +#log4j.logger.at.gv.egiz.slbinding.RedirectEventFilter=DEBUG, STDOUT + +# STDOUT appender +log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout +#log4j.appender.STDOUT.layout.ConversionPattern=%5p | %d{dd HH:mm:ss,SSS} | %20c | %10t | %m%n +#log4j.appender.STDOUT.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n +log4j.appender.STDOUT.layout.ConversionPattern=%-5p |%d | %t | %c %x- %m%n + +### FILE appender +#log4j.appender.file=org.apache.log4j.RollingFileAppender +#log4j.appender.file.maxFileSize=100KB +#log4j.appender.file.maxBackupIndex=9 +#log4j.appender.file.File=egovbus_ca.log +#log4j.appender.file.threshold=info +#log4j.appender.file.layout=org.apache.log4j.PatternLayout +#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
\ No newline at end of file |