From 887f6727479f3ae3d89a08ba619f9382b450e4c1 Mon Sep 17 00:00:00 2001 From: mcentner Date: Fri, 12 Dec 2008 11:48:47 +0000 Subject: Updated SMCC to support non-blocking PIN entry. Added SV-Personendaten infobox implementation. git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@248 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 320 +++++++++-------- .../at/gv/egiz/smcc/AbstractSignatureCard.java | 214 +++++++----- .../src/main/java/at/gv/egiz/smcc/STARCOSCard.java | 386 +++++++++++++-------- .../smcc/SecurityStatusNotSatisfiedException.java | 38 ++ .../gv/egiz/smcc/VerificationFailedException.java | 65 ++++ .../test/java/at/gv/egiz/smcc/STARCOSCardTest.java | 40 ++- 6 files changed, 681 insertions(+), 382 deletions(-) create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/SecurityStatusNotSatisfiedException.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/VerificationFailedException.java (limited to 'smcc') diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index 2baff834..6d96599c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -30,7 +30,6 @@ package at.gv.egiz.smcc; import java.nio.charset.Charset; -import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; @@ -110,41 +109,47 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { public byte[] getCertificate(KeyboxName keyboxName) throws SignatureCardException, InterruptedException { - byte[] aid; - byte[] efc; - int maxsize; - if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { - aid = AID_SIG; - efc = EF_C_CH_DS; - maxsize = EF_C_CH_DS_MAX_SIZE; - } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { - aid = AID_DEC; - efc = EF_C_CH_EKEY; - maxsize = EF_C_CH_EKEY_MAX_SIZE; - } else { - throw new IllegalArgumentException("Keybox " + keyboxName - + " not supported."); - } - - log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" + - " (AID=" + toString(aid) + " EF=" + toString(efc) + ")."); - try { - Card card = getCardChannel().getCard(); - try { - card.beginExclusive(); - return readTLVFile(aid, efc, maxsize + 15000); - } catch (FileNotFoundException e) { - // if certificate is not present, - // the citizen card application has not been activated - throw new NotActivatedException(); - } finally { - card.endExclusive(); + + if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + + try { + getCard().beginExclusive(); + byte[] certificate = readTLVFile(AID_SIG, EF_C_CH_DS, EF_C_CH_DS_MAX_SIZE); + if (certificate == null) { + throw new NotActivatedException(); + } + return certificate; + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } finally { + getCard().endExclusive(); + } + + } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { + + try { + getCard().beginExclusive(); + byte[] certificate = readTLVFile(AID_DEC, EF_C_CH_EKEY, EF_C_CH_EKEY_MAX_SIZE); + if (certificate == null) { + throw new NotActivatedException(); + } + return certificate; + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } finally { + getCard().endExclusive(); + } + + } else { + throw new IllegalArgumentException("Keybox " + keyboxName + + " not supported."); } + } catch (CardException e) { - throw new SignatureCardException("Failed to get exclusive card access."); + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); } - } @@ -155,30 +160,47 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException, InterruptedException { - if ("IdentityLink".equals(infobox)) { - - PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); - - try { - Card card = getCardChannel().getCard(); - try { - card.beginExclusive(); - return readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider, - spec, EF_INFOBOX_MAX_SIZE); - } catch (FileNotFoundException e) { - // if certificate is not present, - // the citizen card application has not been activated - throw new NotActivatedException(); - } finally { - card.endExclusive(); - } - } catch (CardException e) { - throw new SignatureCardException("Failed to get exclusive card access."); + try { + if ("IdentityLink".equals(infobox)) { + + PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); + + int retries = -1; + String pin = null; + boolean pinRequiered = false; + + do { + if (pinRequiered) { + pin = provider.providePIN(spec, retries); + if (pin == null) { + throw new CancelledException(); + } + } + try { + getCard().beginExclusive(); + return readTLVFile(AID_DEC, EF_INFOBOX, pin, KID_PIN_INF, EF_INFOBOX_MAX_SIZE); + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (SecurityStatusNotSatisfiedException e) { + pinRequiered = true; + } catch (VerificationFailedException e) { + pinRequiered = true; + retries = e.getRetries(); + } finally { + getCard().endExclusive(); + } + } while (retries != 0); + + throw new LockedException(); + + } else { + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); } - - } else { - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); + + } catch (CardException e) { + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); } } @@ -192,68 +214,103 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } try { - Card card = getCardChannel().getCard(); - try { - card.beginExclusive(); - - if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - - // SELECT DF - selectFileFID(DF_SIG); - // VERIFY - verifyPIN(provider, new PINSpec(6, 10, "[0-9]", getResourceBundle() - .getString("sig.pin.name")), KID_PIN_SIG); - // MSE: SET DST - mseSetDST(0x81, 0xb6, DST_SIG); - // PSO: HASH - psoHash(hash); - // PSO: COMPUTE DIGITAL SIGNATURE - return psoComputDigitalSiganture(); - } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - - // SELECT DF - selectFileFID(DF_DEC); - // VERIFY - verifyPIN(provider, new PINSpec(4, 4, "[0-9]", getResourceBundle() - .getString("dec.pin.name")), KID_PIN_DEC); - // MSE: SET DST - mseSetDST(0x41, 0xa4, DST_DEC); - // INTERNAL AUTHENTICATE - return internalAuthenticate(hash); - - - // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 54 26 F0 EA AF EA F0 4E D4 A1 AD BF 66 D4 A5 9B 45 6F AF 79 00 - // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 DF 8C AB 8F E2 AD AC 7B 5A AF BE E9 44 5E 95 99 FA AF 2F 48 00 - - } else { - throw new IllegalArgumentException("KeyboxName '" + keyboxName - + "' not supported."); - } + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { + + PINSpec spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); + + int retries = -1; + String pin = null; + + do { + pin = provider.providePIN(spec, retries); + if (pin == null) { + throw new CancelledException(); + } + try { + getCard().beginExclusive(); + + // SELECT DF + selectFileFID(DF_SIG); + // VERIFY + retries = verifyPIN(pin, KID_PIN_SIG); + if (retries != -1) { + throw new VerificationFailedException(retries); + } + // MSE: SET DST + mseSetDST(0x81, 0xb6, DST_SIG); + // PSO: HASH + psoHash(hash); + // PSO: COMPUTE DIGITAL SIGNATURE + return psoComputDigitalSiganture(); + + } catch (SecurityStatusNotSatisfiedException e) { + retries = verifyPIN(null, KID_PIN_SIG); + } catch (VerificationFailedException e) { + retries = e.getRetries(); + } finally { + getCard().endExclusive(); + } + } while (retries != 0); + + throw new LockedException(); + + + } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - } catch (FileNotFoundException e) { - // if certificate is not present, - // the citizen card application has not been activated - throw new NotActivatedException(); - } finally { - card.endExclusive(); + PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("dec.pin.name")); + + int retries = -1; + String pin = null; + boolean pinRequiered = false; + + do { + if (pinRequiered) { + pin = provider.providePIN(spec, retries); + if (pin == null) { + throw new CancelledException(); + } + } + try { + getCard().beginExclusive(); + + // SELECT DF + selectFileFID(DF_DEC); + // VERIFY + retries = verifyPIN(pin, KID_PIN_DEC); + if (retries != -1) { + throw new VerificationFailedException(retries); + } + // MSE: SET DST + mseSetDST(0x41, 0xa4, DST_DEC); + // INTERNAL AUTHENTICATE + return internalAuthenticate(hash); + + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (SecurityStatusNotSatisfiedException e) { + pinRequiered = true; + retries = verifyPIN(null, KID_PIN_DEC); + } catch (VerificationFailedException e) { + pinRequiered = true; + retries = e.getRetries(); + } finally { + getCard().endExclusive(); + } + } while (retries != 0); + + throw new LockedException(); + + } else { + throw new IllegalArgumentException("KeyboxName '" + keyboxName + + "' not supported."); } + } catch (CardException e) { - throw new SignatureCardException("Failed to get exclusive card access."); - } - - } - - protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, - 0x00, fid, 256)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("Failed to select file (AID=" - + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); - } else { - return resp.getBytes(); - } + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); + } + } protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException { @@ -262,6 +319,7 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { 0x00, fid, 256)); } + @Override protected int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); @@ -290,35 +348,7 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } - /** - * - * @param pinProvider - * @param spec - * the PIN spec to be given to the pinProvider - * @param kid - * the KID (key identifier) of the PIN to be verified - * @throws CancelledException - * if the user canceld the operation - * @throws javax.smartcardio.CardException - * @throws at.gv.egiz.smcc.SignatureCardException - */ - @Override - protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid) - throws CardException, CancelledException, SignatureCardException, InterruptedException { - - int retries = -1; - do { - String pin = pinProvider.providePIN(spec, retries); - if (pin == null) { - // user canceled operation - throw new CancelledException("User canceled operation"); - } - retries = verifyPIN(pin, kid); - } while (retries > 0); - - } - - void mseSetDST(int p1, int p2, byte[] dst) throws CardException, SignatureCardException { + private void mseSetDST(int p1, int p2, byte[] dst) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, p1, p2, dst)); @@ -328,7 +358,7 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } } - void psoHash(byte[] hash) throws CardException, SignatureCardException { + private void psoHash(byte[] hash) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x90, 0x81, hash)); @@ -338,7 +368,7 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } } - byte[] psoComputDigitalSiganture() throws CardException, + private byte[] psoComputDigitalSiganture() throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E, @@ -352,7 +382,7 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } } - byte[] internalAuthenticate(byte[] hash) throws CardException, SignatureCardException { + private byte[] internalAuthenticate(byte[] hash) throws CardException, SignatureCardException { byte[] digestInfo = new byte[] { (byte) 0x30, (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2B, (byte) 0x0E, (byte) 0x03, (byte) 0x02, (byte) 0x1A, (byte) 0x05, (byte) 0x00, (byte) 0x04 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index e34c4899..633cc90d 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -28,6 +28,8 @@ // package at.gv.egiz.smcc; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Locale; import java.util.ResourceBundle; @@ -79,45 +81,56 @@ public abstract class AbstractSignatureCard implements SignatureCard { return sb.toString(); } - protected abstract byte[] selectFileAID(byte[] fid) throws CardException, - SignatureCardException; - - protected abstract ResponseAPDU selectFileFID(byte[] fid) throws CardException, - SignatureCardException; - /** - * VERIFY PIN + * Select an application using AID as DF name according to ISO/IEC 7816-4 + * section 8.2.2.2. * - *

- * Implementations of this method should call - * {@link PINProvider#providePIN(PINSpec, int)} to retrieve the PIN entered by - * the user and VERIFY PIN on the smart card until the PIN has been - * successfully verified. - *

+ * @param dfName + * AID of the application to be selected * - * @param pinProvider - * the PINProvider - * @param spec - * the PINSpec - * @param kid - * the key ID (KID) of the PIN to verify + * @return the response data of the response APDU if SW=0x9000 * * @throws CardException - * if smart card communication fails - * - * @throws CancelledException - * if the PINProvider indicated that the user canceled the PIN entry - * @throws NotActivatedException - * if the card application has not been activated - * @throws LockedException - * if the card application is locked + * if card communication fails * * @throws SignatureCardException - * if VERIFY PIN fails + * if application selection fails (e.g. an application with the + * given AID is not present on the card) */ - protected abstract void verifyPIN(PINProvider pinProvider, PINSpec spec, - byte kid) throws CardException, SignatureCardException, InterruptedException; + protected byte[] selectFileAID(byte[] dfName) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, + 0x00, dfName, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Failed to select application AID=" + + toString(dfName) + ": SW=" + Integer.toHexString(resp.getSW()) + "."); + } else { + return resp.getBytes(); + } + } + + protected abstract ResponseAPDU selectFileFID(byte[] fid) throws CardException, + SignatureCardException; + protected abstract int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException; + + + protected byte[] readRecord(int recordNumber) throws SignatureCardException, CardException { + return readRecord(getCardChannel(), recordNumber); + } + + protected byte[] readRecord(CardChannel channel, int recordNumber) throws SignatureCardException, CardException { + + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB2, + recordNumber, 0x04, 256)); + if (resp.getSW() == 0x9000) { + return resp.getData(); + } else { + throw new SignatureCardException("Failed to read records. SW=" + Integer.toHexString(resp.getSW())); + } + + } + protected byte[] readBinary(CardChannel channel, int offset, int len) throws CardException, SignatureCardException { @@ -125,6 +138,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { 0x7F & (offset >> 8), offset & 0xFF, len)); if (resp.getSW() == 0x9000) { return resp.getData(); + } else if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); } else { throw new SignatureCardException("Failed to read bytes (" + offset + "+" + len + "): SW=" + Integer.toHexString(resp.getSW())); @@ -188,43 +203,10 @@ public abstract class AbstractSignatureCard implements SignatureCard { } - /** - * Read the content of a TLV file. - * - * @param aid the application ID (AID) - * @param ef the elementary file (EF) - * @param maxLength the maximum length of the file - * - * @return the content of the file - * - * @throws SignatureCardException - */ - protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) - throws SignatureCardException, InterruptedException { - return readTLVFilePIN(aid, ef, (byte) 0, null, null, maxLength); - } - - - /** - * Read the content of a TLV file wich may require a PIN. - * - * @param aid the application ID (AID) - * @param ef the elementary file (EF) - * @param kid the key ID (KID) of the corresponding PIN - * @param provider the PINProvider - * @param spec the PINSpec - * @param maxLength the maximum length of the file - * - * @return the content of the file - * - * @throws SignatureCardException - */ - protected byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid, - PINProvider provider, PINSpec spec, int maxLength) - throws SignatureCardException, InterruptedException { - + protected byte[] readRecords(byte[] aid, byte[] ef, int start, int end) throws SignatureCardException, InterruptedException { + try { - + // SELECT FILE (AID) byte[] rb = selectFileAID(aid); if (rb[rb.length - 2] != (byte) 0x90 || rb[rb.length - 1] != (byte) 0x00) { @@ -256,37 +238,89 @@ public abstract class AbstractSignatureCard implements SignatureCard { + Integer.toHexString(resp.getSW()) + ")."); } - - // try to READ BINARY - byte[] b = new byte[1]; - int sw = readBinary(0, 1, b); - if (provider != null && sw == 0x6982) { - - // VERIFY - verifyPIN(provider, spec, kid); - - } else if (sw == 0x9000) { - // not expected type - if (b[0] != 0x30) { - throw new NotActivatedException(); - } - } else { - throw new SignatureCardException("READ BINARY failed (SW=" - + Integer.toHexString(sw) + ")."); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + for (int i = start; i <= end; i++) { + bytes.write(readRecord(i)); } - - // READ BINARY - byte[] data = readBinaryTLV(maxLength, (byte) 0x30); - - return data; - + + return bytes.toByteArray(); + } catch (CardException e) { throw new SignatureCardException("Failed to acces card.", e); + } catch (IOException e) { + throw new SignatureCardException("Failed to read records.", e); } - + + } + + /** + * Read the content of a TLV file. + * + * @param aid the application ID (AID) + * @param ef the elementary file (EF) + * @param maxLength the maximum length of the file + * + * @return the content of the file + * + * @throws SignatureCardException + * @throws CardException + */ + protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) + throws SignatureCardException, InterruptedException, CardException { + return readTLVFile(aid, ef, null, (byte) 0, maxLength); } + /** + * Read the content of a TLV file wich may require a PIN. + * + * @param aid the application ID (AID) + * @param ef the elementary file (EF) + * @param kid the key ID (KID) of the corresponding PIN + * @param provider the PINProvider + * @param spec the PINSpec + * @param maxLength the maximum length of the file + * + * @return the content of the file + * + * @throws SignatureCardException + * @throws CardException + */ + protected byte[] readTLVFile(byte[] aid, byte[] ef, String pin, byte kid, int maxLength) + throws SignatureCardException, InterruptedException, CardException { + + + // SELECT FILE (AID) + selectFileAID(aid); + + // SELECT FILE (EF) + ResponseAPDU resp = selectFileFID(ef); + if (resp.getSW() == 0x6a82) { + // EF not found + throw new FileNotFoundException("EF " + toString(ef) + " not found."); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("SELECT FILE with " + + "FID=" + + toString(ef) + + " failed (" + + "SW=" + + Integer.toHexString(resp.getSW()) + ")."); + } + + // VERIFY + if (pin != null) { + int retries = verifyPIN(pin, kid); + if (retries != -1) { + throw new VerificationFailedException(retries); + } + } + + return readBinaryTLV(maxLength, (byte) 0x30); + + + } + /** * Transmit the given command APDU using the given card channel. * diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index d6d02475..2a6e90bf 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -31,7 +31,6 @@ package at.gv.egiz.smcc; import java.math.BigInteger; import java.util.Arrays; -import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; @@ -49,6 +48,42 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + /** + * Application ID SV-Personendaten. + */ + public static final byte[] AID_SV_PERSONENDATEN = new byte[] { + (byte) 0xD0, (byte) 0x40, (byte) 0x00, (byte) 0x00, + (byte) 0x17, (byte) 0x01, (byte) 0x01, (byte) 0x01 + }; + + /** + * File ID Grunddaten ({@link #AID_SV_PERSONENDATEN}). + */ + public static final byte[] FID_GRUNDDATEN = new byte[] { + (byte) 0xEF, (byte) 0x01 + }; + + /** + * File ID EHIC ({@link #AID_SV_PERSONENDATEN}). + */ + public static final byte[] FID_EHIC = new byte[] { + (byte) 0xEF, (byte) 0x02 + }; + + /** + * File ID Status ({@link #AID_SV_PERSONENDATEN}). + */ + public static final byte[] FID_SV_PERSONENBINDUNG = new byte[] { + (byte) 0xEF, (byte) 0x03 + }; + + /** + * File ID Status ({@link #AID_SV_PERSONENDATEN}). + */ + public static final byte[] FID_STATUS = new byte[] { + (byte) 0xEF, (byte) 0x04 + }; + public static final byte[] AID_INFOBOX = new byte[] { (byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x18, (byte) 0x01 }; @@ -126,85 +161,134 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard super("at/gv/egiz/smcc/STARCOSCard"); } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName) - */ @Override public byte[] getCertificate(KeyboxName keyboxName) throws SignatureCardException, InterruptedException { - byte[] aid; - byte[] efc; - if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { - aid = AID_DF_SS; - efc = EF_C_X509_CH_DS; - } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { - aid = AID_DF_GS; - efc = EF_C_X509_CH_AUT; - } else { - throw new IllegalArgumentException("Keybox " + keyboxName - + " not supported."); - } + try { + + if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + + try { + getCard().beginExclusive(); + return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000); + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } finally { + getCard().endExclusive(); + } + + } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { - log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" + - " (AID=" + toString(aid) + " EF=" + toString(efc) + ")."); + try { + getCard().beginExclusive(); + return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000); + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } finally { + getCard().endExclusive(); + } - try { - Card card = getCardChannel().getCard(); - try { - card.beginExclusive(); - return readTLVFile(aid, efc, 2000); - } catch (FileNotFoundException e) { - // if certificate is not present, - // the citizen card application has not been activated - throw new NotActivatedException(); - } finally { - card.endExclusive(); + } else { + throw new IllegalArgumentException("Keybox " + keyboxName + + " not supported."); } + } catch (CardException e) { - throw new SignatureCardException("Failed to get exclusive card access."); + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); } - - } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String) - */ + } + @Override public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException, InterruptedException { - if ("IdentityLink".equals(infobox)) { - - PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - - try { - Card card = getCardChannel().getCard(); + try { + if ("IdentityLink".equals(infobox)) { + + PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); + + int retries = -1; + String pin = null; + boolean pinRequiered = false; + + do { + if (pinRequiered) { + pin = provider.providePIN(spec, retries); + if (pin == null) { + throw new CancelledException(); + } + } + try { + getCard().beginExclusive(); + return readTLVFile(AID_INFOBOX, EF_INFOBOX, pin, KID_PIN_CARD, 2000); + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (SecurityStatusNotSatisfiedException e) { + pinRequiered = true; + retries = verifyPIN(null, KID_PIN_CARD); + } catch (VerificationFailedException e) { + pinRequiered = true; + retries = e.getRetries(); + } finally { + getCard().endExclusive(); + } + } while (retries != 0); + + throw new LockedException(); + + + } else if ("EHIC".equals(infobox)) { + try { - card.beginExclusive(); - return readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD, - provider, spec, 2000); - } catch (FileNotFoundException e) { - // if certificate is not present, - // the citizen card application has not been activated - throw new NotActivatedException(); + getCard().beginExclusive(); + return readTLVFile(AID_SV_PERSONENDATEN, FID_EHIC, 126); } finally { - card.endExclusive(); + getCard().endExclusive(); } - } catch (CardException e) { - throw new SignatureCardException("Failed to get exclusive card access."); + + } else if ("Grunddaten".equals(infobox)) { + + try { + getCard().beginExclusive(); + return readTLVFile(AID_SV_PERSONENDATEN, FID_GRUNDDATEN, 550); + } finally { + getCard().endExclusive(); + } + + } else if ("SV-Personenbindung".equals(infobox)) { + + try { + getCard().beginExclusive(); + return readTLVFile(AID_SV_PERSONENDATEN, FID_SV_PERSONENBINDUNG, 500); + } finally { + getCard().endExclusive(); + } + + } else if ("Status".equals(infobox)) { + + try { + getCard().beginExclusive(); + return readRecords(AID_SV_PERSONENDATEN, FID_STATUS, 1, 5); + } finally { + getCard().endExclusive(); + } + + } else { + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); } - } else { - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); + } catch (CardException e) { + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); } } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#createSignature(byte[], at.gv.egiz.smcc.SignatureCard.KeyboxName, at.gv.egiz.smcc.PINProvider) - */ + @Override public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException, InterruptedException { @@ -212,72 +296,115 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard throw new IllegalArgumentException("Hash value must be of length 20."); } - byte[] aid; - byte kid; - byte[] dst; - PINSpec spec; - if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - aid = AID_DF_SS; - kid = KID_PIN_SS; - dst = DST_SS; - spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); - - } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - aid = AID_DF_GS; - kid = KID_PIN_CARD; - dst = DST_GS; - spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - - } else { - throw new IllegalArgumentException("KeyboxName '" + keyboxName - + "' not supported."); - } - try { - Card card = getCardChannel().getCard(); - try { - card.beginExclusive(); - - // SELECT MF - selectMF(); - // SELECT DF - selectFileAID(aid); - // VERIFY - verifyPIN(provider, spec, kid); - // MSE: SET DST - mseSetDST(dst); - // PSO: HASH - psoHash(hash); - // PSO: COMPUTE DIGITAL SIGNATURE - return psoComputDigitalSiganture(); - - - } catch (FileNotFoundException e) { - // if certificate is not present, - // the citizen card application has not been activated - throw new NotActivatedException(); - } finally { - card.endExclusive(); + + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { + + PINSpec spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); + + int retries = -1; + String pin = null; + + do { + try { + getCard().beginExclusive(); + selectFileAID(AID_DF_SS); + retries = verifyPIN(null, KID_PIN_SS); + } finally { + getCard().endExclusive(); + } + pin = provider.providePIN(spec, retries); + if (pin == null) { + throw new CancelledException(); + } + try { + getCard().beginExclusive(); + return createSignature(hash, AID_DF_SS, pin, KID_PIN_SS, DST_SS); + } catch (VerificationFailedException e) { + retries = e.getRetries(); + } finally { + getCard().endExclusive(); + } + } while (retries != 0); + + throw new LockedException(); + + + } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { + + PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); + + int retries = -1; + String pin = null; + boolean pinRequiered = false; + + do { + if (pinRequiered) { + pin = provider.providePIN(spec, retries); + if (pin == null) { + throw new CancelledException(); + } + } + try { + getCard().beginExclusive(); + return createSignature(hash, AID_DF_GS, pin, KID_PIN_CARD, DST_GS); + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (SecurityStatusNotSatisfiedException e) { + pinRequiered = true; + retries = verifyPIN(null, KID_PIN_CARD); + } catch (VerificationFailedException e) { + pinRequiered = true; + retries = e.getRetries(); + } finally { + getCard().endExclusive(); + } + } while (retries != 0); + + throw new LockedException(); + + } else { + throw new IllegalArgumentException("KeyboxName '" + keyboxName + + "' not supported."); } + } catch (CardException e) { - throw new SignatureCardException("Failed to get exclusive card access."); + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); } } - protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { + protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, + return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02, 0x04, fid, 256)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("Failed to select file (AID=" - + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); - } else { - return resp.getBytes(); + } + + private byte[] createSignature(byte[] hash, byte[] aid, String pin, byte kid, + byte[] dst) throws CardException, SignatureCardException { + + // SELECT MF + selectMF(); + // SELECT DF + selectFileAID(aid); + // VERIFY + int retries = verifyPIN(pin, kid); + if (retries != -1) { + throw new VerificationFailedException(retries); } + // MSE: SET DST + mseSetDST(dst); + // PSO: HASH + psoHash(hash); + // PSO: COMPUTE DIGITAL SIGNATURE + return psoComputDigitalSiganture(); + + } - void selectMF() throws CardException, SignatureCardException { + + private void selectMF() throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00, 0x0C)); @@ -287,13 +414,7 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } } - protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException { - CardChannel channel = getCardChannel(); - return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02, - 0x04, fid, 256)); - } - - void mseSetDST(byte[] dst) throws CardException, SignatureCardException { + private void mseSetDST(byte[] dst) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x41, 0xB6, dst)); @@ -303,7 +424,7 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } } - void psoHash(byte[] hash) throws CardException, SignatureCardException { + private void psoHash(byte[] hash) throws CardException, SignatureCardException { byte[] data = new byte[hash.length + 2]; data[0] = (byte) 0x90; // tag data[1] = (byte) (hash.length); // length @@ -318,7 +439,7 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } } - byte[] psoComputDigitalSiganture() throws CardException, + private byte[] psoComputDigitalSiganture() throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E, @@ -353,7 +474,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard * @throws SignatureCardException * if VERIFY PIN fails */ - private int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException { + @Override + protected int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); @@ -385,6 +507,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { // return number of possible retries return resp.getSW2() & 0x0f; + } else if (resp.getSW() == 0x6983) { + throw new LockedException(); } else if (resp.getSW() == 0x6984) { // PIN LCS = "Initialized" (-> not activated) throw new NotActivatedException("PIN not set."); @@ -397,26 +521,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINProvider, at.gv.egiz.smcc.PINSpec, byte, int) - */ - protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid) - throws CardException, SignatureCardException, InterruptedException { - - int retries = verifyPIN(null, kid); - do { - String pin = pinProvider.providePIN(spec, retries); - if (pin == null) { - // user canceled operation - throw new CancelledException("User canceld operation."); - } - retries = verifyPIN(pin, kid); - } while (retries > 0); - - } - public String toString() { - return "eCard"; + return "e-card"; } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SecurityStatusNotSatisfiedException.java b/smcc/src/main/java/at/gv/egiz/smcc/SecurityStatusNotSatisfiedException.java new file mode 100644 index 00000000..bf0af76c --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SecurityStatusNotSatisfiedException.java @@ -0,0 +1,38 @@ +/* +* 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; + +public class SecurityStatusNotSatisfiedException extends SignatureCardException { + + private static final long serialVersionUID = 1L; + + public SecurityStatusNotSatisfiedException() { + } + + public SecurityStatusNotSatisfiedException(String message, Throwable cause) { + super(message, cause); + } + + public SecurityStatusNotSatisfiedException(String message) { + super(message); + } + + public SecurityStatusNotSatisfiedException(Throwable cause) { + super(cause); + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/VerificationFailedException.java b/smcc/src/main/java/at/gv/egiz/smcc/VerificationFailedException.java new file mode 100644 index 00000000..fa066ff9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/VerificationFailedException.java @@ -0,0 +1,65 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.smcc; + +public class VerificationFailedException extends SignatureCardException { + + private static final long serialVersionUID = 1L; + + public static final int UNKNOWN = -1; + + private int retries = UNKNOWN; + + public VerificationFailedException() { + } + + public VerificationFailedException(String message, Throwable cause) { + super(message, cause); + } + + public VerificationFailedException(String message) { + super(message); + } + + public VerificationFailedException(Throwable cause) { + super(cause); + } + + public VerificationFailedException(int retries) { + this.retries = retries; + } + + public VerificationFailedException(int retries, String message, Throwable cause) { + super(message, cause); + this.retries = retries; + } + + public VerificationFailedException(int retries, String message) { + super(message); + this.retries = retries; + } + + public VerificationFailedException(int retries, Throwable cause) { + super(cause); + this.retries = retries; + } + + public int getRetries() { + return retries; + } + +} diff --git a/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java index 13210540..090e1181 100644 --- a/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java +++ b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java @@ -19,6 +19,8 @@ package at.gv.egiz.smcc; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Locale; @@ -27,6 +29,8 @@ import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; +import sun.misc.HexDumpEncoder; + import at.gv.egiz.smcc.SignatureCard.KeyboxName; import at.gv.egiz.smcc.util.SMCCHelper; @@ -34,10 +38,9 @@ public class STARCOSCardTest { /** * @param args - * @throws CardException - * @throws NoSuchAlgorithmException + * @throws Exception */ - public static void main(String[] args) throws CardException, NoSuchAlgorithmException, InterruptedException { + public static void main(String[] args) throws Exception { SMCCHelper helper = new SMCCHelper(); while (helper.getResultCode() != SMCCHelper.CARD_FOUND) { @@ -55,18 +58,41 @@ public class STARCOSCardTest { System.out.println("Found '" + signatureCard + "'."); try { -// signatureCard.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR); -// signatureCard.getCertificate(KeyboxName.CERITIFIED_KEYPAIR); -// signatureCard.getInfobox("IdentityLink", new CommandLinePINProvider(), null); +// printJavaByteArray( +// signatureCard.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR), System.out); +// printJavaByteArray( +// signatureCard.getCertificate(KeyboxName.CERITIFIED_KEYPAIR), System.out); +// System.out. println(new String(signatureCard.getInfobox("IdentityLink", new CommandLinePINProvider(), null))); +// byte[] infobox = signatureCard.getInfobox("Status", new CommandLinePINProvider(), null); +// printJavaByteArray(infobox, System.out); MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); byte[] digest = messageDigest.digest("test".getBytes()); - signatureCard.createSignature(digest, KeyboxName.CERITIFIED_KEYPAIR, new CommandLinePINProvider()); + byte[] signature = signatureCard.createSignature(digest, KeyboxName.SECURE_SIGNATURE_KEYPAIR, new CommandLinePINProvider()); + printJavaByteArray(signature, System.out); } catch (SignatureCardException e) { e.printStackTrace(); } } + public static void printJavaByteArray(byte[] bytes, OutputStream os) { + + PrintWriter w = new PrintWriter(os); + + w.write("new byte[] {"); + for (int i = 0; i < bytes.length;) { + if (i % 8 == 0) { + w.write("\n "); + } + w.write("(byte) 0x" + Integer.toHexString(0x0F & (bytes[i] >> 4)) + Integer.toHexString(0x0F & bytes[i])); + if (++i < bytes.length) { + w.write(", "); + } + } + w.write("\n};"); + w.flush(); + } + private static class CommandLinePINProvider implements PINProvider { @Override -- cgit v1.2.3