From e126bd7af77b30710d9eec39cd08d1d63d605cec Mon Sep 17 00:00:00 2001 From: tkellner Date: Tue, 10 Jul 2012 12:17:07 +0000 Subject: Cypriotic EID implementation working git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1094 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../java/at/gv/egiz/bku/gui/PINManagementGUI.java | 429 ++++++++++++++++++ .../at/gv/egiz/bku/gui/PINManagementGUIFacade.java | 7 + smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java | 478 +++++++++++++-------- .../main/java/at/gv/egiz/smcc/LogCardChannel.java | 1 + .../at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java | 2 + .../at/gv/egiz/smcc/CypriotEID.properties | 5 + .../gv/egiz/bku/pin/gui/ManagementPINProvider.java | 23 + 7 files changed, 766 insertions(+), 179 deletions(-) create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/CypriotEID.properties diff --git a/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java b/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java index 32fe207c..e3e0ec83 100644 --- a/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java +++ b/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java @@ -886,7 +886,436 @@ public class PINManagementGUI extends CardMgmtGUI implements } }); } + + @Override + public void showPUKDialog(DIALOG type, PinInfo pinSpec, PinInfo pukSpec, int retries, + ActionListener okListener, String okCommand, + ActionListener cancelListener, String cancelCommand) { + showPUKDialog(type, pinSpec, pukSpec, retries, false, okListener, okCommand, + cancelListener, cancelCommand); + } + + private void showPUKDialog(final DIALOG type, final PinInfo pinSpec, final PinInfo pukSpec, + final int retries, final boolean pinpad, + final ActionListener okListener, final String okCommand, + final ActionListener cancelListener, final String cancelCommand) { + + log.debug("Scheduling puk dialog."); + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + String HELP_TOPIC, TITLE, MESSAGE_MGMT, MESSAGE_MGMT_PARAM; + HELP_TOPIC = HELP_PINMGMT; + + if (retries < 0) { + if (type == DIALOG.CHANGE) { + log.debug("Show change pin dialog."); + TITLE = TITLE_CHANGE_PIN; + MESSAGE_MGMT = MESSAGE_UNBLOCK_PIN; + } else if(type == DIALOG.UNBLOCK) { + log.debug("Show unblock pin dialog."); + TITLE = TITLE_UNBLOCK_PIN; + MESSAGE_MGMT = MESSAGE_UNBLOCK_PIN; + } else { + log.info("PUK Dialog may only be used for change and unblocking!"); + return; + } + if (shortText) { + MESSAGE_MGMT_PARAM = "PIN"; + } else { + MESSAGE_MGMT_PARAM = pinSpec.getLocalizedName(); + } + } else { + log.debug("Show retry pin dialog."); + TITLE = TITLE_RETRY; + MESSAGE_MGMT = (retries < 2) ? MESSAGE_LAST_RETRY + : MESSAGE_RETRIES; + MESSAGE_MGMT_PARAM = String.valueOf(retries); + } + + mainPanel.removeAll(); + buttonPanel.removeAll(); + + helpListener.setHelpTopic(HELP_TOPIC); + + mgmtLabel = new JLabel(); + if (retries < 0) { + mgmtLabel.setFont(mgmtLabel.getFont().deriveFont( + mgmtLabel.getFont().getStyle() & ~Font.BOLD)); + } else { + mgmtLabel.setFont(mgmtLabel.getFont().deriveFont( + mgmtLabel.getFont().getStyle() | Font.BOLD)); + mgmtLabel.setForeground(ERROR_COLOR); + helpListener.setHelpTopic(HELP_RETRY); + } + + if (renderHeaderPanel) { + titleLabel.setText(getMessage(TITLE)); + String mgmtPattern = getMessage(MESSAGE_MGMT); + mgmtLabel.setText(MessageFormat.format(mgmtPattern, + MESSAGE_MGMT_PARAM)); + } else { + mgmtLabel.setText(getMessage(TITLE)); + } + + // ////////////////////////////////////////////////////////////// + // COMMON LAYOUT SECTION + // ////////////////////////////////////////////////////////////// + + GroupLayout mainPanelLayout = new GroupLayout(mainPanel); + mainPanel.setLayout(mainPanelLayout); + + GroupLayout.SequentialGroup infoHorizontal = mainPanelLayout + .createSequentialGroup().addComponent(mgmtLabel); + GroupLayout.ParallelGroup infoVertical = mainPanelLayout + .createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(mgmtLabel); + + if (!renderHeaderPanel) { + infoHorizontal.addPreferredGap( + LayoutStyle.ComponentPlacement.UNRELATED, 0, + Short.MAX_VALUE) + .addComponent(switchFocusDummyLabel).addComponent( + helpLabel); + infoVertical.addComponent(switchFocusDummyLabel) + .addComponent(helpLabel); + } + + GroupLayout.ParallelGroup pinHorizontal; + GroupLayout.SequentialGroup pinVertical; +/* Currently not supporting pinpads!! + if (pinpad) { + pinpadLabel = new JLabel(); + pinpadLabel.setFont(mgmtLabel.getFont().deriveFont( + mgmtLabel.getFont().getStyle() & ~Font.BOLD)); + String pinpadPattern = getMessage(MESSAGE_ENTERPIN_PINPAD); + pinpadLabel.setText(MessageFormat.format(pinpadPattern, + new Object[] { pinSpec.getLocalizedName(), + pinSpec.getLocalizedLength() })); + + pinHorizontal = mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.LEADING).addComponent( + pinpadLabel); + pinVertical = mainPanelLayout.createSequentialGroup() + .addComponent(pinpadLabel); + } else { +*/ + okButton = new JButton(); + okButton.setFont(okButton.getFont().deriveFont( + okButton.getFont().getStyle() & ~Font.BOLD)); + okButton.setText(getMessage(BUTTON_OK)); + okButton.setEnabled(pinSpec.getMinLength() <= 0); + okButton.setActionCommand(okCommand); + okButton.addActionListener(okListener); + + pinLabel = new JLabel(); + pinLabel.setFont(pinLabel.getFont().deriveFont( + pinLabel.getFont().getStyle() & ~Font.BOLD)); + String pinLabelPattern = getMessage(LABEL_NEW_PIN); + + pinLabel.setText(MessageFormat.format(pinLabelPattern, + new Object[] { pinSpec.getLocalizedName() })); + + repeatPinField = new JPasswordField(); + pinField = new JPasswordField(); + pinField.setText(""); + pinField.setActionCommand(okCommand); + pinField.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (pinField.getPassword().length >= pinSpec + .getMinLength()) { + if (type == DIALOG.VERIFY) { + okListener.actionPerformed(e); + } else { + repeatPinField.requestFocusInWindow(); + } + } + } + }); + + repeatPinLabel = new JLabel(); + repeatPinLabel.setFont(pinLabel.getFont()); + String repeatPinLabelPattern = getMessage(LABEL_REPEAT_PIN); + repeatPinLabel.setText(MessageFormat.format( + repeatPinLabelPattern, new Object[] { pinSpec + .getLocalizedName() })); + + repeatPinField.setText(""); + repeatPinField.setActionCommand(okCommand); + repeatPinField.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (okButton.isEnabled()) { + okListener.actionPerformed(e); + } + } + }); + + oldPinLabel = new JLabel(); + oldPinLabel.setFont(oldPinLabel.getFont() + .deriveFont( + oldPinLabel.getFont().getStyle() + & ~java.awt.Font.BOLD)); + String oldPinLabelPattern = getMessage(LABEL_PUK); + oldPinLabel.setText(MessageFormat.format( + oldPinLabelPattern, new Object[] { pinSpec + .getLocalizedName() })); + + oldPinField = new JPasswordField(); + oldPinField.setText(""); + oldPinField.setActionCommand(okCommand); + oldPinField.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (oldPinField.getPassword().length >= pinSpec + .getMinLength()) { + pinField.requestFocusInWindow(); + } + } + }); + + ExtendedPinDocument oldPinDocument = new ExtendedPinDocument( + pukSpec.getMinLength(), pukSpec + .getMaxLength(), pukSpec + .getRegexpPattern(), okButton); + ComparePinDocument newPinDocument = new ComparePinDocument( + pinSpec.getRecMinLength(), pinSpec + .getRecMaxLength(), pinSpec + .getRegexpPattern(), okButton); + ComparePinDocument confirmPinDocument = new ComparePinDocument( + pinSpec.getRecMinLength(), pinSpec + .getRecMaxLength(), pinSpec + .getRegexpPattern(), okButton); + + oldPinDocument.newPIN = newPinDocument; + oldPinDocument.confirmPIN = confirmPinDocument; + + newPinDocument.compareTo = confirmPinDocument; + newPinDocument.currentPIN = oldPinDocument; + confirmPinDocument.compareTo = newPinDocument; + confirmPinDocument.currentPIN = oldPinDocument; + oldPinField.setDocument(oldPinDocument); + pinField.setDocument(newPinDocument); + repeatPinField.setDocument(confirmPinDocument); + + primaryFocusHolder = oldPinField; + + + + + pinsizeLabel = new JLabel(); + pinsizeLabel.setFont(pinsizeLabel.getFont().deriveFont( + pinsizeLabel.getFont().getStyle() & ~Font.BOLD, + pinsizeLabel.getFont().getSize() - 2)); + String pinsizePattern = getMessage(LABEL_PINSIZE); + pinsizeLabel.setText(MessageFormat.format(pinsizePattern, + pinSpec.getLocalizedLength())); + + // ////////////////////////////////////////////////////////////// + // NON-PINPAD SPECIFIC LAYOUT SECTION + // ////////////////////////////////////////////////////////////// + + pinHorizontal = mainPanelLayout + .createParallelGroup(GroupLayout.Alignment.LEADING); + pinVertical = mainPanelLayout.createSequentialGroup(); + + if (type == DIALOG.CHANGE || type == DIALOG.UNBLOCK) { + pinHorizontal + .addGroup(mainPanelLayout + .createSequentialGroup() + .addGroup( + mainPanelLayout + .createParallelGroup( + GroupLayout.Alignment.LEADING) + .addComponent( + oldPinLabel, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE) + .addComponent( + pinLabel, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE) + .addComponent( + repeatPinLabel, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE)) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + mainPanelLayout + .createParallelGroup( + GroupLayout.Alignment.LEADING) + .addComponent( + oldPinField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + pinField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + repeatPinField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); +//>>>>>>> .r684 + + pinVertical.addGroup( + mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.BASELINE) + .addComponent(oldPinLabel) + .addComponent(oldPinField)) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.BASELINE) + .addComponent(pinLabel) + .addComponent(pinField)) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.BASELINE) + .addComponent(repeatPinLabel) + .addComponent(repeatPinField)) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED); + } else if (type == DIALOG.ACTIVATE) { + pinHorizontal + .addGroup(mainPanelLayout + .createSequentialGroup() + .addGroup( + mainPanelLayout + .createParallelGroup( + GroupLayout.Alignment.LEADING) + .addComponent( + pinLabel, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE) + .addComponent( + repeatPinLabel, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE)) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + mainPanelLayout + .createParallelGroup( + GroupLayout.Alignment.LEADING) + .addComponent( + pinField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + repeatPinField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + pinVertical.addGroup( + mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.BASELINE) + .addComponent(pinLabel).addComponent( + pinField)).addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.BASELINE) + .addComponent(repeatPinLabel) + .addComponent(repeatPinField)) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED); + } else { // VERIFY + pinHorizontal.addGroup(mainPanelLayout + .createSequentialGroup().addComponent(pinLabel, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)); + + pinVertical.addGroup( + mainPanelLayout.createParallelGroup( + GroupLayout.Alignment.BASELINE) + .addComponent(pinLabel).addComponent( + pinField)).addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED); + } + pinHorizontal.addGroup(mainPanelLayout + .createSequentialGroup().addPreferredGap( + LayoutStyle.ComponentPlacement.UNRELATED, + 0, Short.MAX_VALUE).addComponent( + pinsizeLabel, GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE)); + pinVertical.addComponent(pinsizeLabel); + + 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); + + + updateMethodToRunAtResize("at.gv.egiz.bku.gui.PINManagementGUI", "renderPINDialogButtonPanel"); + + renderPINDialogButtonPanel(); + + if (oldPinField != null) { + oldPinField.requestFocusInWindow(); + } else { + pinField.requestFocusInWindow(); + } + + //} // END NON-PINPAD SECTION + + mainPanelLayout.setHorizontalGroup(mainPanelLayout + .createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(infoHorizontal).addGroup(pinHorizontal)); + + mainPanelLayout + .setVerticalGroup(mainPanelLayout + .createSequentialGroup().addGroup(infoVertical) + .addPreferredGap( + LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pinVertical)); + + contentPanel.validate(); + + if (windowCloseAdapter != null) { + windowCloseAdapter.registerListener(cancelListener, cancelCommand); + } + + resize(); + + } + }); + } + + public void renderPINDialogButtonPanel() { GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); diff --git a/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java b/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java index f1bf57ab..6f429456 100644 --- a/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java +++ b/BKUGuiExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java @@ -116,6 +116,13 @@ public interface PINManagementGUIFacade extends BKUGUIFacade { ActionListener okListener, String okCmd, ActionListener cancelListener, String cancelCmd); + /** + * "software" puk and pin-entry dialog (change, unblock) + */ + public void showPUKDialog(DIALOG type, PinInfo pinSpec, PinInfo pukSpec, int retries, + ActionListener okListener, String okCmd, + ActionListener cancelListener, String cancelCmd); + /** * direct pinpad pin-entry dialog */ diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java b/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java index 35556049..c1695278 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/CypriotEID.java @@ -26,6 +26,7 @@ package at.gv.egiz.smcc; import iaik.me.asn1.*; import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -34,6 +35,7 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Iterator; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; @@ -51,6 +53,8 @@ import at.gv.egiz.smcc.pin.gui.ModifyPINGUI; import at.gv.egiz.smcc.pin.gui.PINGUI; import at.gv.egiz.smcc.util.ISO7816Utils; import at.gv.egiz.smcc.util.SMCCHelper; +import at.gv.egiz.smcc.util.TLV; +import at.gv.egiz.smcc.util.TLVSequence; import at.gv.egiz.smcc.util.TransparentFileInputStream; public class CypriotEID extends AbstractSignatureCard implements @@ -63,7 +67,7 @@ public class CypriotEID extends AbstractSignatureCard implements public static final byte KID_PIN_SIG = (byte) 0x01; public static final byte[] CD_ID = new byte[] { (byte) 0x70, (byte) 0x05 }; - + public static final byte[] MF_ID = new byte[] { (byte) 0x3f, (byte) 0x00 }; public static final byte[] ADF_AWP_ID = new byte[] { (byte) 0xad, @@ -79,32 +83,32 @@ public class CypriotEID extends AbstractSignatureCard implements ObjectDirectory od; protected byte[] cert_id; - + @Override public void init(Card card, CardTerminal cardTerminal) { super.init(card, cardTerminal); log.info("Cypriot EID found"); - pinPinInfo = new PinInfo(4, 8, "[0-9]", "at/gv/egiz/smcc/CypriotEID", + pinPinInfo = new PinInfo(4, 64, "[0-9]", "at/gv/egiz/smcc/CypriotEID", "sig.pin", KID_PIN_SIG, AID_SIG, 3); - //pinPinInfo.setActive(3); - - pukPinInfo = new PinInfo(4, 8, "[0-9]", "at/gv/egiz/smcc/CypriotEID", + // pinPinInfo.setActive(3); + + pukPinInfo = new PinInfo(4, 64, "[0-9]", "at/gv/egiz/smcc/CypriotEID", "sig.puk", KID_PUK_SIG, AID_SIG, 3); - + try { this.exec_readcd(getCardChannel()); } catch (CardException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.warn("Failed to read the certificate ID", e); + cert_id = null; } catch (SignatureCardException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.warn("Failed to read the certificate ID", e); + cert_id = null; } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.warn("Failed to read the certificate ID", e); + cert_id = null; } } @@ -116,14 +120,14 @@ public class CypriotEID extends AbstractSignatureCard implements try { return exec_readcert(channel); } catch (CardException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.info("Failed to get the certificate.", e); + throw new SignatureCardException("Failed to get the certificate.", + e); } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + log.info("Failed to get the certificate.", e); + throw new SignatureCardException("Failed to get the certificate.", + e); } - - return null; } @Override @@ -137,58 +141,92 @@ public class CypriotEID extends AbstractSignatureCard implements public byte[] createSignature(InputStream input, KeyboxName keyboxName, PINGUI pinGUI, String alg) throws SignatureCardException, InterruptedException, IOException { - + byte AlgID = 0; - - MessageDigest md; - try { - if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) - && (alg == null || "http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg))) { - AlgID = (byte) 0x12; // SHA-1 with padding according to PKCS#1 block type 01 - md = MessageDigest.getInstance("SHA-1"); - } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) - && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256".equals(alg)) { - AlgID = (byte) 0x41; // SHA-256 with padding according to PKCS#1 - md = MessageDigest.getInstance("SHA256"); - } else { - throw new SignatureCardException("Card does not support signature algorithm " + alg + "."); - } - } catch (NoSuchAlgorithmException e) { - log.error("Failed to get MessageDigest.", e); - throw new SignatureCardException(e); - } - - byte[] digest = new byte[md.getDigestLength()]; - for (int l; (l = input.read(digest)) != -1;) { - md.update(digest, 0, l); - } - digest = md.digest(); - - try - { - CardChannel channel = getCardChannel(); - - // SELECT application - exec_selectADF(channel); - - // MANAGE SECURITY ENVIRONMENT : SET DST - exec_MSE(channel, AlgID); - // VERIFY - verifyPINLoop(channel, pinPinInfo, pinGUI); - - // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE - return exec_sign(channel, digest); - } - catch (Exception e) { - e.printStackTrace(); - return null; + + MessageDigest md; + try { + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) + && (alg == null || "http://www.w3.org/2000/09/xmldsig#rsa-sha1" + .equals(alg))) { + AlgID = (byte) 0x12; // SHA-1 with padding according to PKCS#1 + // block type 01 + md = MessageDigest.getInstance("SHA-1"); + } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) + && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" + .equals(alg)) { + AlgID = (byte) 0x41; // SHA-256 with padding according to PKCS#1 + md = MessageDigest.getInstance("SHA256"); + } else { + throw new SignatureCardException( + "Card does not support signature algorithm " + alg + + "."); + } + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get MessageDigest.", e); + throw new SignatureCardException(e); + } + + byte[] digest = new byte[md.getDigestLength()]; + for (int l; (l = input.read(digest)) != -1;) { + md.update(digest, 0, l); } + digest = md.digest(); + + CardChannel channel = getCardChannel(); + + try { + try { + // SELECT application + exec_selectADF(channel); + + // MANAGE SECURITY ENVIRONMENT : SET DST + exec_MSE(channel, AlgID); + + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE + return exec_sign(channel, digest); + } catch (SecurityStatusNotSatisfiedException e) { + // NEED to provide PIN code ... + + // SELECT application + exec_selectADF(channel); + + // MANAGE SECURITY ENVIRONMENT : SET DST + exec_MSE(channel, AlgID); + + // VERIFY + verifyPINLoop(channel, pinPinInfo, pinGUI); + + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE + return exec_sign(channel, digest); + } + } catch (CardException e) { + log.info("Failed to create digital signature", e); + throw new SignatureCardException("Failed to create digital signature", e); + } + } @Override public PinInfo[] getPinInfos() throws SignatureCardException { - // TODO Auto-generated method stub - return new PinInfo[] { pinPinInfo }; + PinInfo[] pinInfos = new PinInfo[] { pinPinInfo, pukPinInfo }; + + CardChannel channel = getCardChannel(); + for (PinInfo pinInfo : pinInfos) { + if (pinInfo.getState() == PinInfo.STATE.UNKNOWN ) { + try { + log.debug("Query pin status for {}.", pinInfo.getLocalizedName()); + testPIN(channel, pinInfo); + } catch (Exception e) { + log.trace("Failed to execute command.", e); + // status already set by verifyPIN + } + } else if (log.isTraceEnabled()) { + log.trace("assume pin status {} to be up to date", pinInfo.getState()); + } + } + + return pinInfos; } @Override @@ -208,13 +246,24 @@ public class CypriotEID extends AbstractSignatureCard implements } } + @Override + public String toString() { + return ("Oberthur Thechnologies ID-ONE Token SLIM"); + } + @Override public void changePIN(PinInfo pinInfo, ModifyPINGUI changePINGUI) throws LockedException, NotActivatedException, CancelledException, PINFormatException, SignatureCardException, InterruptedException { + CardChannel channel = getCardChannel(); - exec_unblockPIN(channel, changePINGUI); + try { + unblockPINLoop(channel, changePINGUI, pinInfo); + } catch (CardException e) { + log.info("Failed to change PIN.", e); + throw new SignatureCardException("Failed to change PIN.", e); + } } @Override @@ -222,7 +271,8 @@ public class CypriotEID extends AbstractSignatureCard implements throws CancelledException, SignatureCardException, InterruptedException { log.error("ACTIVATE PIN not supported by Cypriotic EID"); - throw new SignatureCardException("PIN activation not supported by this card."); + throw new SignatureCardException( + "PIN activation not supported by this card."); } @Override @@ -231,7 +281,12 @@ public class CypriotEID extends AbstractSignatureCard implements InterruptedException { CardChannel channel = getCardChannel(); - exec_unblockPIN(channel, pukGUI); + try { + unblockPINLoop(channel, pukGUI, pinInfo); + } catch (CardException e) { + log.info("Failed to unblock PIN.", e); + throw new SignatureCardException("Failed to unblock PIN.", e); + } } // ////////////////////////////////////////////////////////////////////// @@ -248,16 +303,28 @@ public class CypriotEID extends AbstractSignatureCard implements } while (retries > 0); } + protected void unblockPINLoop(CardChannel channel, + ModifyPINGUI provider, PinInfo pin) throws InterruptedException, CardException, SignatureCardException{ + + int retries = -1; + do { + retries = exec_unblockPIN(channel, provider, pin); + } while (retries > 0); + } + + /* + * Verify PIN/PUK entry + */ protected int verifyPIN(CardChannel channel, PinInfo pinInfo, PINGUI provider, int retries) throws InterruptedException, CardException, SignatureCardException { char[] pin = provider.providePIN(pinInfo, pinInfo.retries); - + byte[] ascii_pin = encodePIN(pin); exec_selectADF(channel); - ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, pinInfo.getKID(), ascii_pin)); if (resp.getSW() == 0x9000) { @@ -284,99 +351,149 @@ public class CypriotEID extends AbstractSignatureCard implements } } + + protected int testPIN(CardChannel channel, PinInfo pinInfo) throws InterruptedException, + CardException, SignatureCardException { + + exec_selectADF(channel); + + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, + pinInfo.getKID())); + + if (resp.getSW() == 0x9000) { + pinInfo.setActive(pinInfo.maxRetries); + return -1; + } + if (resp.getSW() >> 4 == 0x63c) { + pinInfo.setActive(0x0f & resp.getSW()); + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + pinInfo.setBlocked(); + throw new LockedException(); + + default: + String msg = "VERIFY failed. SW=" + + Integer.toHexString(resp.getSW()); + log.info(msg); + pinInfo.setUnknown(); + throw new SignatureCardException(msg); + } + + } - private byte[] encodePIN(char[] pin) - { - return Charset.forName("ASCII").encode( - CharBuffer.wrap(pin)).array(); + private byte[] encodePIN(char[] pin) { + return Charset.forName("ASCII").encode(CharBuffer.wrap(pin)).array(); } - - protected void exec_unblockPIN(CardChannel channel, ModifyPINGUI changePINGUI) throws CancelledException, InterruptedException - { - char[] PUK = changePINGUI.provideCurrentPIN(pukPinInfo, pukPinInfo.retries); - char[] newPIN = changePINGUI.provideNewPIN(pinPinInfo); + + /* + * Unblock PIN with PUK code + */ + protected int exec_unblockPIN(CardChannel channel, ModifyPINGUI changePINGUI, PinInfo pin) + throws InterruptedException, CardException, SignatureCardException { - byte[] ascii_puk = encodePIN(PUK); - byte[] ascii_pin = encodePIN(newPIN); + char[] PUK = changePINGUI.providePUK(pin, pukPinInfo, + pukPinInfo.retries); - try { - log.debug("PUK: " + new String(PUK) + "(" + getHexString(ascii_puk) + ") NEW PIN: " + - new String(newPIN) + "(" + getHexString(ascii_pin) + ")"); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + char[] newPIN = changePINGUI.provideNewPIN(pin); - // TODO: INPUT checking PIN SIZES etc. - /* - try { - exec_selectADF(channel); + byte[] ascii_puk = encodePIN(PUK); - ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, - pukPinInfo.getKID(), ascii_puk)); - - if (resp.getSW() == 0x9000) { - pukPinInfo.setActive(pukPinInfo.maxRetries); - } - else - { - log.debug("WRONG PUK CODE!! SW=" + resp.getSW()); - return; - } - - resp = channel.transmit(new CommandAPDU(0x00, 0x2C, 0x02, - pinPinInfo.getKID(), ascii_pin)); - - if (resp.getSW() == 0x9000) { - pinPinInfo.setActive(pinPinInfo.maxRetries); - } - else - { - log.debug("FAILED TO SET PIN! SW=" + resp.getSW()); - } - - } catch (CardException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (SignatureCardException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + byte[] ascii_pin = encodePIN(newPIN); + + exec_selectADF(channel); + + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, + pukPinInfo.getKID(), ascii_puk)); + + if (resp.getSW() == 0x9000) { + pukPinInfo.setActive(pukPinInfo.maxRetries); + } else if (resp.getSW() >> 4 == 0x63c) { + pukPinInfo.setActive(0x0f & resp.getSW()); + return 0x0f & resp.getSW(); + } else if (resp.getSW() == 0x6983) { + // authentication method blocked + pukPinInfo.setBlocked(); + throw new LockedException(); + } else { + String msg = "VERIFY failed. SW=" + + Integer.toHexString(resp.getSW()); + log.info(msg); + pukPinInfo.setUnknown(); + throw new SignatureCardException(msg); + } + + resp = channel.transmit(new CommandAPDU(0x00, 0x2C, 0x02, pin + .getKID(), ascii_pin)); + + if (resp.getSW() == 0x9000) { + pin.setActive(pin.maxRetries); + return -1; + } else { + String msg = "SET PIN failed. SW=" + + Integer.toHexString(resp.getSW()); + log.info(msg); + pin.setUnknown(); + throw new SignatureCardException(msg); } - */ + } - + + /* + * Read certificate based on certificate ID + */ protected byte[] exec_readcert(CardChannel channel) throws CardException, - SignatureCardException, IOException { - if(cert_id == null) - { + SignatureCardException, IOException { + if (cert_id == null) { exec_readcd(channel); } - - /*if(cert_id == null) - { - throw CardException("Failed to read the certificate id"); - }*/ - + + if (cert_id == null) { + throw new CardException("Failed to read the certificate id"); + } + exec_selectADF(channel); exec_selectFILE(channel, cert_id); - + return exec_readBinary(channel); } - + + /* + * Read and parse CD file to determine certificate ID + */ protected void exec_readcd(CardChannel channel) throws CardException, - SignatureCardException, IOException - { + SignatureCardException, IOException { exec_selectADF(channel); exec_selectFILE(channel, CD_ID); - + byte[] cd_buffer = exec_readBinary(channel); - - // TODO interpret CD => get CERT ID - - cert_id = new byte[] { (byte) 0x34, (byte) 0x01 }; + + ASN1 asn = new ASN1(cd_buffer); + + for (int i = 0; i < asn.getSize(); i++) { + + ASN1 element = asn.getElementAt(i); + + if (element.getTagClass() == ASN1.TAG_CONTEXT_SPECIFIC + && element.getTypeOnly() == ASN1.TYPE_BOOLEAN) { + ASN1 ele = element.gvASN1().getElementAt(0).getElementAt(0); + if (ele.getTypeOnly() == ASN1.TYPE_OCTET_STRING) { + cert_id = ele.gvByteArray(); + return; + } + } + } + cert_id = null; + throw new CardException("Failed to read the certificate ID."); } - + + /* + * Select the ADF application + */ protected void exec_selectADF(CardChannel channel) throws CardException, SignatureCardException { @@ -385,8 +502,9 @@ public class CypriotEID extends AbstractSignatureCard implements } - - + /* + * Select a file in the current context by its id + */ protected byte[] exec_selectFILE(CardChannel channel, byte[] file_id) throws CardException, SignatureCardException { ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x01, @@ -403,23 +521,43 @@ public class CypriotEID extends AbstractSignatureCard implements return resp.getBytes(); } - protected void exec_MSE(CardChannel channel, byte algoID) throws CardException - { - byte[] secure_setup = new byte[] { (byte) 0x80, (byte) 0x01, algoID, // Algorithm setup + /* + * Setup Manage Security Environment (MSE) for cryptographic signatur !fixed + * key id + */ + protected void exec_MSE(CardChannel channel, byte algoID) + throws CardException { + byte[] secure_setup = new byte[] { (byte) 0x80, (byte) 0x01, algoID, // Algorithm + // setup (byte) 0x84, (byte) 0x01, (byte) 0x81 }; // Key setup - - ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x22, - 0x41, 0xB6, secure_setup)); + + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x22, 0x41, + 0xB6, secure_setup)); } - - protected byte[] exec_sign(CardChannel channel, byte[] hash) throws CardException - { - ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A, - 0x9E, 0x9A, hash)); - - return resp.getData(); + + /* + * Execute signature command + */ + protected byte[] exec_sign(CardChannel channel, byte[] hash) + throws CardException, SignatureCardException { + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A, 0x9E, + 0x9A, hash)); + + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } + if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO - COMPUTE DIGITAL SIGNATRE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } } + /* + * Read current binary information + */ protected byte[] exec_readBinary(CardChannel channel) throws CardException, IOException, SignatureCardException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); @@ -428,8 +566,8 @@ public class CypriotEID extends AbstractSignatureCard implements int offset = 0; do { - int offset_lo = offset % 0xFF; - int offset_hi = offset / 0xFF; + int offset_lo = (byte) offset; + int offset_hi = (byte) (offset >> 8); ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xB0, offset_hi, offset_lo, 0x00)); @@ -451,26 +589,8 @@ public class CypriotEID extends AbstractSignatureCard implements } while (repeat); byte[] buf = buffer.toByteArray(); - - log.debug("BINARY READ: "); - - try { - log.debug(getHexString(buf)); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - + return buf; } - - - public static String getHexString(byte[] b) throws Exception { - String result = ""; - for (int i=0; i < b.length; i++) { - result += ":" + - Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 ); - } - return result; - } + } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java index c06c3296..1f0f5c4c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java @@ -69,6 +69,7 @@ public class LogCardChannel extends CardChannel { switch (command.getINS()) { case 0x20: // VERIFY case 0x21: // VERIFY + case 0x2C: // RESET RETRY COUNTER case 0x24: { // CHANGE REFERENCE DATA // Don't log possibly sensitive command data StringBuilder sb = new StringBuilder(); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java index 5a29e6ce..2d1ec970 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/pin/gui/ModifyPINProvider.java @@ -53,4 +53,6 @@ public interface ModifyPINProvider { public char[] provideNewPIN(PinInfo pinInfo) throws CancelledException, InterruptedException; + public char[] providePUK(PinInfo pinInfo, PinInfo pukInfo, int retries) + throws CancelledException, InterruptedException; } diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/CypriotEID.properties b/smcc/src/main/resources/at/gv/egiz/smcc/CypriotEID.properties new file mode 100644 index 00000000..df5f05cc --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/CypriotEID.properties @@ -0,0 +1,5 @@ +sig.pin.name=PIN +sig.pin.length=4-64 + +sig.puk.name=PUK +sig.puk.length=4-64 \ No newline at end of file diff --git a/smccSTALExt/src/main/java/at/gv/egiz/bku/pin/gui/ManagementPINProvider.java b/smccSTALExt/src/main/java/at/gv/egiz/bku/pin/gui/ManagementPINProvider.java index 89a349b4..fb571495 100644 --- a/smccSTALExt/src/main/java/at/gv/egiz/bku/pin/gui/ManagementPINProvider.java +++ b/smccSTALExt/src/main/java/at/gv/egiz/bku/pin/gui/ManagementPINProvider.java @@ -94,4 +94,27 @@ public class ManagementPINProvider extends AbstractPINProvider implements Modify } return gui.getPin(); } + +@Override +public char[] providePUK(PinInfo pinInfo, PinInfo pukInfo, int retries) + throws CancelledException, InterruptedException { + gui.showPUKDialog(type, pinInfo, pukInfo, (retry) ? retries : -1, + this, "change", + this, "cancel"); + + log.trace("[{}] wait for action.", Thread.currentThread().getName()); + waitForAction(); + log.trace("[{}] received action {}.", Thread.currentThread().getName(), action); + + gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT, + BKUGUIFacade.MESSAGE_WAIT); + + if ("cancel".equals(action)) { + throw new CancelledException(pukInfo.getLocalizedName() + + " entry cancelled"); + } + retry = true; + return gui.getOldPin(); +} + } -- cgit v1.2.3