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 --- 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 + 4 files changed, 307 insertions(+), 179 deletions(-) create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/CypriotEID.properties (limited to 'smcc/src') 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 -- cgit v1.2.3