From 6cb4a071eab9a3b8cf78b8ec7e407aa148f2d038 Mon Sep 17 00:00:00 2001 From: mcentner Date: Wed, 1 Jul 2009 13:03:41 +0000 Subject: Major refactoring of SMCC git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@381 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 1089 +++++++++++----------- 1 file changed, 566 insertions(+), 523 deletions(-) (limited to 'smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java') 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 d064b821..9825978c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -1,48 +1,47 @@ -//Copyright (C) 2002 IAIK -//http://jce.iaik.at -// -//Copyright (C) 2003 Stiftung Secure Information and -// Communication Technologies SIC -//http://www.sic.st -// -//All rights reserved. -// -//This source is provided for inspection purposes and recompilation only, -//unless specified differently in a contract with IAIK. This source has to -//be kept in strict confidence and must not be disclosed to any third party -//under any circumstances. Redistribution in source and binary forms, with -//or without modification, are permitted in any case! -// -//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -//SUCH DAMAGE. -// -// +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ package at.gv.egiz.smcc; -import at.gv.egiz.smcc.ccid.CCID; -import at.gv.egiz.smcc.util.SMCCHelper; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; - +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class ACOSCard extends AbstractSignatureCard { +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; +import at.gv.egiz.smcc.util.TransparentFileInputStream; + +public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureCard { private static Log log = LogFactory.getLog(ACOSCard.class); @@ -69,6 +68,8 @@ public class ACOSCard extends AbstractSignatureCard { (byte) 0x01 }; public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xc0, (byte) 0x02 }; + + public static final byte[] EF_INFO = new byte[] { (byte) 0xd0, (byte) 0x02 }; public static final int EF_INFOBOX_MAX_SIZE = 1500; @@ -90,7 +91,7 @@ public class ACOSCard extends AbstractSignatureCard { (byte) 0x14 // ECDSA }; - public static final byte[] DST_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag + public static final byte[] AT_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag // , // length // ( @@ -102,123 +103,278 @@ public class ACOSCard extends AbstractSignatureCard { (byte) 0x01 // RSA // TODO: Not verified yet }; - protected static final int PINSPEC_INF = 0; - protected static final int PINSPEC_DEC = 1; - protected static final int PINSPEC_SIG = 2; + private static final PINSpec DEC_PIN_SPEC = new PINSpec(0, 8, "[0-9]", + "at/gv/egiz/smcc/ACOSCard", "dec.pin.name", KID_PIN_DEC, AID_DEC); + + private static final PINSpec SIG_PIN_SPEC = new PINSpec(0, 8, "[0-9]", + "at/gv/egiz/smcc/ACOSCard", "sig.pin.name", KID_PIN_SIG, AID_SIG); + + private static final PINSpec INF_PIN_SPEC = new PINSpec(0, 8, "[0-9]", + "at/gv/egiz/smcc/ACOSCard", "inf.pin.name", KID_PIN_INF, AID_DEC); + + /** + * The version of the card's digital signature application. + */ + protected int appVersion = -1; public ACOSCard() { super("at/gv/egiz/smcc/ACOSCard"); - pinSpecs.add(PINSPEC_INF, - new PINSpec(0, 8, "[0-9]", getResourceBundle().getString("inf.pin.name"), KID_PIN_INF, AID_DEC)); - pinSpecs.add(PINSPEC_DEC, - new PINSpec(0, 8, "[0-9]", getResourceBundle().getString("dec.pin.name"), KID_PIN_DEC, AID_DEC)); - pinSpecs.add(PINSPEC_SIG, - new PINSpec(0, 8, "[0-9]", getResourceBundle().getString("sig.pin.name"), KID_PIN_SIG, AID_SIG)); } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName) - */ @Override + public void init(Card card, CardTerminal cardTerminal) { + super.init(card, cardTerminal); + + // determine application version + try { + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_SIG); + // SELECT file + execSELECT_FID(channel, EF_INFO); + // READ BINARY + TransparentFileInputStream is = ISO7816Utils.openTransparentFileInputStream(channel, 8); + appVersion = is.read(); + log.info("a-sign premium application version = " + appVersion); + } catch (FileNotFoundException e) { + appVersion = 1; + log.info("a-sign premium application version = " + appVersion); + } catch (SignatureCardException e) { + log.warn(e); + appVersion = 0; + } catch (IOException e) { + log.warn(e); + appVersion = 0; + } catch (CardException e) { + log.warn(e); + appVersion = 0; + } + + pinSpecs.add(DEC_PIN_SPEC); + pinSpecs.add(SIG_PIN_SPEC); + if (appVersion < 2) { + pinSpecs.add(INF_PIN_SPEC); + } + + } + + @Override + @Exclusive public byte[] getCertificate(KeyboxName keyboxName) throws SignatureCardException, InterruptedException { - - try { - + + byte[] aid; + byte[] fid; 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(); - } - + aid = AID_SIG; + fid = EF_C_CH_DS; } 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(); - } - + aid = AID_DEC; + fid = EF_C_CH_EKEY; } else { throw new IllegalArgumentException("Keybox " + keyboxName + " not supported."); } - } catch (CardException e) { - log.warn(e); - throw new SignatureCardException("Failed to access card.", e); - } - + try { + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, aid); + // SELECT file + byte[] fcx = execSELECT_FID(channel, fid); + int maxSize = -1; + if (getAppVersion() < 2) { + maxSize = ISO7816Utils.getLengthFromFCx(fcx); + log.debug("Size of selected file = " + maxSize); + } + // READ BINARY + byte[] certificate = ISO7816Utils.readTransparentFileTLV(channel, maxSize, (byte) 0x30); + if (certificate == null) { + throw new NotActivatedException(); + } + return certificate; + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (CardException e) { + log.info("Failed to get certificate.", e); + throw new SignatureCardException(e); + } + + } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String) - */ @Override + @Exclusive public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException, InterruptedException { + + if ("IdentityLink".equals(infobox)) { + if (getAppVersion() < 2) { + return getIdentityLinkV1(provider, domainId); + } else { + return getIdentityLinkV2(provider, domainId); + } + } else { + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + } + + protected byte[] getIdentityLinkV1(PINProvider provider, String domainId) + throws SignatureCardException, InterruptedException { + + try { + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_DEC); + // SELECT file + byte[] fcx = execSELECT_FID(channel, EF_INFOBOX); + int maxSize = ISO7816Utils.getLengthFromFCx(fcx); + log.debug("Size of selected file = " + maxSize); + // READ BINARY + while(true) { + try { + return ISO7816Utils.readTransparentFileTLV(channel, maxSize, (byte) 0x30); + } catch (SecurityStatusNotSatisfiedException e) { + verifyPINLoop(channel, INF_PIN_SPEC, provider); + } + } + + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (CardException e) { + log.info("Faild to get infobox.", e); + throw new SignatureCardException(e); + } + + } + protected byte[] getIdentityLinkV2(PINProvider provider, String domainId) + throws SignatureCardException, InterruptedException { + try { - if ("IdentityLink".equals(infobox)) { - - PINSpec spec = pinSpecs.get(PINSPEC_INF); - //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_DEC); + // SELECT file + execSELECT_FID(channel, EF_INFOBOX); + + // READ BINARY + TransparentFileInputStream is = ISO7816Utils.openTransparentFileInputStream(channel, -1); + + int b = is.read(); + if (b == 0x00) { + return null; + } + if (b != 0x41 || is.read() != 0x49 || is.read() != 0x4b) { + String msg = "Infobox structure invalid."; + log.info(msg); + throw new SignatureCardException(msg); + } - int retries = -1; - boolean pinRequired = false; + b = is.read(); + if (b != 0x01) { + String msg = "Infobox structure v" + b + " not supported."; + log.info(msg); + throw new SignatureCardException(msg); + } + + while ((b = is.read()) != 0x01 && b != 00) { + is.read(); // modifiers + is.skip(is.read() + (is.read() << 8)); // length + } + + if (b != 0x01) { + return null; + } + + int modifiers = is.read(); + int length = is.read() + (is.read() << 8); - do { - try { - getCard().beginExclusive(); - if (pinRequired) { - char[] pin = provider.providePIN(spec, retries); - return readTLVFile(AID_DEC, EF_INFOBOX, pin, spec.getKID(), EF_INFOBOX_MAX_SIZE); - } else { - return readTLVFile(AID_DEC, EF_INFOBOX, EF_INFOBOX_MAX_SIZE); - } - } catch (FileNotFoundException e) { - throw new NotActivatedException(); - } catch (SecurityStatusNotSatisfiedException e) { - pinRequired = true; - } catch (VerificationFailedException e) { - pinRequired = true; - retries = e.getRetries(); - } finally { - getCard().endExclusive(); - } - } while (retries != 0); + byte[] bytes; + byte[] key = null; + + switch (modifiers) { + case 0x00: + bytes = new byte[length]; + break; + case 0x01: + key = new byte[is.read() + (is.read() << 8)]; + is.read(key); + bytes = new byte[length - key.length - 2]; + break; + default: + String msg = "Compressed infobox structure not yet supported."; + log.info(msg); + throw new SignatureCardException(msg); + } + + is.read(bytes); + + if (key == null) { + return bytes; + } - throw new LockedException(); + execMSE(channel, 0x41, 0xb8, new byte[] { + (byte) 0x84, (byte) 0x01, (byte) 0x88, (byte) 0x80, (byte) 0x01, + (byte) 0x02 }); - } else { - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); - } + byte[] plainKey = null; + + while (true) { + try { + plainKey = execPSO_DECIPHER(channel, key); + break; + } catch(SecurityStatusNotSatisfiedException e) { + verifyPINLoop(channel, DEC_PIN_SPEC, provider); + } + } + + try { + Cipher cipher = Cipher + .getInstance("DESede/CBC/PKCS5Padding"); + byte[] iv = new byte[8]; + Arrays.fill(iv, (byte) 0x00); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + AlgorithmParameters parameters = AlgorithmParameters + .getInstance("DESede"); + parameters.init(ivParameterSpec); + + DESedeKeySpec keySpec = new DESedeKeySpec(plainKey); + SecretKeyFactory keyFactory = SecretKeyFactory + .getInstance("DESede"); + SecretKey secretKey = keyFactory.generateSecret(keySpec); + + cipher.init(Cipher.DECRYPT_MODE, secretKey, parameters); + + return cipher.doFinal(bytes); + + } catch (GeneralSecurityException e) { + String msg = "Failed to decrypt infobox."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + + } catch (FileNotFoundException e) { + throw new NotActivatedException(); } catch (CardException e) { - log.warn(e); - throw new SignatureCardException("Failed to access card.", e); + log.info("Faild to get infobox.", e); + throw new SignatureCardException(e); + } catch (IOException e) { + if (e.getCause() instanceof SignatureCardException) { + throw (SignatureCardException) e.getCause(); + } else { + throw new SignatureCardException(e); + } } - + } - + @Override + @Exclusive public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException, InterruptedException { @@ -228,87 +384,40 @@ public class ACOSCard extends AbstractSignatureCard { try { - if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - - PINSpec spec = pinSpecs.get(PINSPEC_SIG); - //new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); - - int retries = -1; - char[] pin = null; - - do { - pin = provider.providePIN(spec, retries); - try { - getCard().beginExclusive(); - - // SELECT DF - selectFileFID(DF_SIG); - // VERIFY - retries = verifyPIN(KID_PIN_SIG, pin); - 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(); + CardChannel channel = getCardChannel(); - } catch (SecurityStatusNotSatisfiedException e) { - retries = verifyPIN(KID_PIN_SIG); - } catch (VerificationFailedException e) { - retries = e.getRetries(); - } finally { - getCard().endExclusive(); - } - } while (retries != 0); + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - throw new LockedException(); + PINSpec spec = SIG_PIN_SPEC; + // SELECT application + execSELECT_AID(channel, AID_SIG); + // MANAGE SECURITY ENVIRONMENT : SET DST + execMSE(channel, 0x41, 0xb6, DST_SIG); + // VERIFY + verifyPINLoop(channel, spec, provider); + // PERFORM SECURITY OPERATION : HASH + execPSO_HASH(channel, hash); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - PINSpec spec = pinSpecs.get(PINSPEC_DEC); - //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("dec.pin.name")); - - int retries = -1; - char[] pin = null; - boolean pinRequired = false; + PINSpec spec = DEC_PIN_SPEC; - do { - if (pinRequired) { - pin = provider.providePIN(spec, retries); - } + // SELECT application + execSELECT_AID(channel, AID_DEC); + // MANAGE SECURITY ENVIRONMENT : SET AT + execMSE(channel, 0x41, 0xa4, AT_DEC); + + while (true) { try { - getCard().beginExclusive(); - - // SELECT DF - selectFileFID(DF_DEC); - // VERIFY - retries = verifyPIN(KID_PIN_DEC, pin); - 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(); + return execINTERNAL_AUTHENTICATE(channel, hash); } catch (SecurityStatusNotSatisfiedException e) { - pinRequired = true; - retries = verifyPIN(KID_PIN_DEC); - } catch (VerificationFailedException e) { - pinRequired = true; - retries = e.getRetries(); - } finally { - getCard().endExclusive(); + verifyPINLoop(channel, spec, provider); } - } while (retries != 0); - - throw new LockedException(); + } } else { throw new IllegalArgumentException("KeyboxName '" + keyboxName @@ -321,377 +430,311 @@ public class ACOSCard extends AbstractSignatureCard { } } + + public int getAppVersion() { + return appVersion; + } - //////////////////////////////////////////////////////////////////////// - // PROTECTED METHODS (assume exclusive card access) - //////////////////////////////////////////////////////////////////////// + /* (non-Javadoc) + * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider) + */ + @Override + public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { - protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); - return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00, - 0x00, fid, 256)); + + try { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + // VERIFY + verifyPIN(channel, pinSpec, pinProvider, -1); + } catch (CardException e) { + log.info("Failed to verify PIN.", e); + throw new SignatureCardException("Failed to verify PIN.", e); + } + } + + /* (non-Javadoc) + * @see at.gv.egiz.smcc.AbstractSignatureCard#changePIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.ChangePINProvider) + */ + @Override + public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { - 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)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("MSE:SET DST failed: SW=" - + Integer.toHexString(resp.getSW())); + + try { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + // CHANGE REFERENCE DATA + changePIN(channel, pinSpec, pinProvider, -1); + } catch (CardException e) { + log.info("Failed to change PIN.", e); + throw new SignatureCardException("Failed to change PIN.", e); } + } - private void psoHash(byte[] hash) throws CardException, SignatureCardException { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x90, - 0x81, hash)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("PSO:HASH failed: SW=" - + Integer.toHexString(resp.getSW())); - } + @Override + public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) + throws CancelledException, SignatureCardException, CancelledException, + TimeoutException, InterruptedException { + log.error("ACTIVATE PIN not supported by ACOS"); + throw new SignatureCardException("PIN activation not supported by this card."); } - private byte[] psoComputDigitalSiganture() throws CardException, - SignatureCardException { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E, - 0x9A, 256)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException( - "PSO: COMPUTE DIGITAL SIGNATRE failed: SW=" - + Integer.toHexString(resp.getSW())); - } else { - return resp.getData(); - } + @Override + public void unblockPIN(PINSpec pinSpec, PINProvider pinProvider) + throws CancelledException, SignatureCardException, InterruptedException { + throw new SignatureCardException("Unblock PIN not supported."); } - 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 - }; - - byte[] data = new byte[digestInfo.length + hash.length + 1]; - - System.arraycopy(digestInfo, 0, data, 0, digestInfo.length); - data[digestInfo.length] = (byte) hash.length; - System.arraycopy(hash, 0, data, digestInfo.length + 1, hash.length); - - CardChannel channel = getCardChannel(); - - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x88, 0x10, 0x00, data, 256)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("INTERNAL AUTHENTICATE failed: SW=" + Integer.toHexString(resp.getSW())); + /* (non-Javadoc) + * @see at.gv.egiz.smcc.PINMgmtSignatureCard#getPINSpecs() + */ + @Override + public List getPINSpecs() { + if (getAppVersion() < 2) { + return Arrays.asList(new PINSpec[] {DEC_PIN_SPEC, SIG_PIN_SPEC, INF_PIN_SPEC}); } else { - return resp.getData(); + return Arrays.asList(new PINSpec[] {DEC_PIN_SPEC, SIG_PIN_SPEC}); } } - /** - * - * @param kid - * @return -1 + /* (non-Javadoc) + * @see at.gv.egiz.smcc.PINMgmtSignatureCard#getPINStatus(at.gv.egiz.smcc.PINSpec) */ @Override - protected int verifyPIN(byte kid) { - log.debug("VERIFY PIN without PIN BLOCK not supported by ACOS"); - return -1; + public PIN_STATE getPINState(PINSpec pinSpec) throws SignatureCardException { + return PIN_STATE.UNKNOWN; } @Override - protected int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, PINFormatException, PINOperationAbortedException, SignatureCardException { - try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { - log.debug("verify pin on cardreader"); - sw = reader.verifyPinDirect(getPINVerifyStructure(kid)); -// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; - } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) { - log.debug("verify pin on cardreader"); - sw = reader.verifyPin(getPINVerifyStructure(kid)); - } else { - byte[] pinBlock = encodePINBlock(pin); - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock), false); - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - } + public String toString() { + return "a-sign premium"; + } - //6A 00 (falshe P1/P2) nicht in contextAID - //69 85 (nutzungsbedingungen nicht erfüllt) in DF_Sig und nicht sigpin - - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return -1; - } else if (sw[0] == (byte) 0x63 && sw[1] == (byte) 0xc0) { - throw new LockedException("[63:c0]"); - } else if (sw[0] == (byte) 0x63 && (sw[1] & 0xf0) >> 4 == 0xc) { - return sw[1] & 0x0f; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - throw new NotActivatedException("[69:83]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { -// //referenzierte Daten sind reversibel gesperrt (invalidated) -// throw new NotActivatedException("[69:84]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { -// //Benutzungsbedingungen nicht erfüllt -// throw new NotActivatedException("[69:85]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); - } - log.error("Failed to verify pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); + //////////////////////////////////////////////////////////////////////// + // PROTECTED METHODS (assume exclusive card access) + //////////////////////////////////////////////////////////////////////// - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); - } + protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINProvider provider) + throws InterruptedException, LockedException, NotActivatedException, + TimeoutException, PINFormatException, PINOperationAbortedException, + SignatureCardException, CardException { + + int retries = -1; + do { + retries = verifyPIN(channel, spec, provider, retries); + } while (retries > 0); + } - /** - * SCARD_E_NOT_TRANSACTED inf/dec PIN not active (pcsc crash) - * @param kid - * @param oldPin - * @param newPin - * @return - * @throws at.gv.egiz.smcc.LockedException - * @throws at.gv.egiz.smcc.NotActivatedException - * @throws at.gv.egiz.smcc.SignatureCardException - */ - @Override - protected int changePIN(byte kid, char[] oldPin, char[] newPin) - throws LockedException, NotActivatedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { - try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("modify pin on cardreader"); - sw = reader.modifyPinDirect(getPINModifyStructure(kid)); - } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { - log.debug("modify pin on cardreader"); - sw = reader.modifyPin(getPINModifyStructure(kid)); - } else { - byte[] cmd = new byte[16]; - System.arraycopy(encodePINBlock(oldPin), 0, cmd, 0, 8); - System.arraycopy(encodePINBlock(newPin), 0, cmd, 8, 8); - - CardChannel channel = getCardChannel(); + protected int verifyPIN(CardChannel channel, PINSpec pinSpec, + PINProvider provider, int retries) throws InterruptedException, CardException, SignatureCardException { + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }, + 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); + + ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + + if (resp.getSW() == 0x9000) { + return -1; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x24, 0x00, kid, cmd), false); + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + throw new LockedException(); + + default: + String msg = "VERIFY failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - } + } - // activates pin (newPIN) if not active - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return -1; - } else if (sw[0] == (byte) 0x63 && sw[1] == (byte) 0xc0) { - throw new LockedException("[63:c0]"); - } else if (sw[0] == (byte) 0x63 && (sw[1] & 0xf0) >> 4 == 0xc) { - return sw[1] & 0x0f; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - // sig-pin only (card not transacted for inf/dec pin) - throw new NotActivatedException("[69:83]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { - throw new PINConfirmationException("[64:02]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); - } else if (sw[0] == (byte) 0x6a && sw[1] == (byte) 0x80) { - log.info("invalid parameter, assume wrong pin size"); - throw new PINFormatException("[6a:80]"); - } - log.error("Failed to change pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); + protected int changePIN(CardChannel channel, PINSpec pinSpec, + ChangePINProvider pinProvider, int retries) throws CancelledException, InterruptedException, CardException, SignatureCardException { + + ChangeReferenceDataAPDUSpec apduSpec = new ChangeReferenceDataAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x24, (byte) 0x00, pinSpec.getKID(), (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }, + 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); + + + + ResponseAPDU resp = reader.modify(channel, apduSpec, pinSpec, pinProvider, retries); + + if (resp.getSW() == 0x9000) { + return -1; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + throw new LockedException(); + + default: + String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + + } - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); + protected byte[] execSELECT_AID(CardChannel channel, byte[] aid) + throws SignatureCardException, CardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0xA4, 0x04, 0x00, aid, 256)); + + if (resp.getSW() == 0x6A82) { + String msg = "File or application not found AID=" + + SMCCHelper.toString(aid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.info(msg); + throw new FileNotFoundException(msg); + } else if (resp.getSW() != 0x9000) { + String msg = "Failed to select application AID=" + + SMCCHelper.toString(aid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.info(msg); + throw new SignatureCardException(msg); + } else { + return resp.getBytes(); } + } + + protected byte[] execSELECT_FID(CardChannel channel, byte[] fid) + throws SignatureCardException, CardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0xA4, 0x00, 0x00, fid, 256)); + + if (resp.getSW() == 0x6A82) { + String msg = "File or application not found FID=" + + SMCCHelper.toString(fid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.info(msg); + throw new FileNotFoundException(msg); + } else if (resp.getSW() != 0x9000) { + String msg = "Failed to select application FID=" + + SMCCHelper.toString(fid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.error(msg); + throw new SignatureCardException(msg); + } else { + return resp.getBytes(); + } - /** - * throws SignatureCardException (PIN activation not supported by ACOS) - * @throws at.gv.egiz.smcc.SignatureCardException - */ - @Override - public void activatePIN(byte kid, char[] pin) - throws SignatureCardException { - log.error("ACTIVATE PIN not supported by ACOS"); - throw new SignatureCardException("PIN activation not supported by this card"); + } + + protected void execMSE(CardChannel channel, int p1, + int p2, byte[] data) throws SignatureCardException, CardException { - /** - * ASCII encoded pin, padded with 0x00 - * @param pin - * @return a 8 byte pin block - */ - @Override - protected byte[] encodePINBlock(char[] pin) { -// byte[] asciiPIN = new String(pin).getBytes(Charset.forName("ASCII")); - CharBuffer chars = CharBuffer.wrap(pin); - ByteBuffer bytes = Charset.forName("ASCII").encode(chars); - byte[] asciiPIN = bytes.array(); - byte[] encodedPIN = new byte[8]; - System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length, - encodedPIN.length)); -// System.out.println("ASCII encoded PIN block: " + SMCCHelper.toString(encodedPIN)); - return encodedPIN; + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0x22, p1, p2, data)); + + if (resp.getSW() != 0x9000) { + String msg = "MSE failed: SW=" + + Integer.toHexString(resp.getSW()); + log.error(msg); + throw new SignatureCardException(msg); + } + } - private byte[] getPINVerifyStructure(byte kid) { - - byte bTimeOut = reader.getbTimeOut(); - byte bTimeOut2 = reader.getbTimeOut2(); - byte bmFormatString = (byte) 0x82; // 1 0000 0 10 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0000 1000 - // ^^^^--------- PIN length size: 0 bits - // ^^^^---- Length PIN = 8 bytes - byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 - // ^-------- System bit units is bit - // ^^^^--- no PIN length - byte wPINMaxExtraDigitL = //TODO compare ints, not bytes - (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x08; - byte wPINMaxExtraDigitH = - (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x00; - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; - byte wLangIdH = (byte) 0x04; - byte bMsgIndex = (byte) 0x00; - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }; - - int offset = 0; - byte[] pinVerifyStructure = new byte[offset + 19 + apdu.length]; - pinVerifyStructure[offset++] = bTimeOut; - pinVerifyStructure[offset++] = bTimeOut2; - pinVerifyStructure[offset++] = bmFormatString; - pinVerifyStructure[offset++] = bmPINBlockString; - pinVerifyStructure[offset++] = bmPINLengthFormat; - pinVerifyStructure[offset++] = wPINMaxExtraDigitL; - pinVerifyStructure[offset++] = wPINMaxExtraDigitH; - pinVerifyStructure[offset++] = bEntryValidationCondition; - pinVerifyStructure[offset++] = bNumberMessage; - pinVerifyStructure[offset++] = wLangIdL; - pinVerifyStructure[offset++] = wLangIdH; - pinVerifyStructure[offset++] = bMsgIndex; - - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - - pinVerifyStructure[offset++] = (byte) apdu.length; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinVerifyStructure, offset, apdu.length); - - return pinVerifyStructure; + protected byte[] execPSO_DECIPHER(CardChannel channel, byte [] cipher) throws CardException, SignatureCardException { + + byte[] data = new byte[cipher.length + 1]; + data[0] = 0x00; + System.arraycopy(cipher, 0, data, 1, cipher.length); + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A, 0x80, 0x86, data, 256)); + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO - DECIPHER failed: SW=" + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + } - public byte[] getPINModifyStructure(byte kid) { - - byte bTimeOut = reader.getbTimeOut(); - byte bTimeOut2 = reader.getbTimeOut2(); - byte bmFormatString = (byte) 0x82; // 1 0000 0 10 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0000 1000 - // ^^^^--------- PIN length size: 0 bits - // ^^^^---- Length PIN = 8 bytes - byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 - // ^-------- System bit units is bit - // ^^^^--- no PIN length - byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x08; - byte wPINMaxExtraDigitL = - (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x08; - byte wPINMaxExtraDigitH = - (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x00; - byte bConfirmPIN = (byte) 0x03; - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x03; - byte wLangIdL = (byte) 0x0C; - byte wLangIdH = (byte) 0x04; - byte bMsgIndex1 = (byte) 0x00; - byte bMsgIndex2 = (byte) 0x01; - byte bMsgIndex3 = (byte) 0x02; - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }; - - int offset = 0; - byte[] pinModifyStructure = new byte[offset + 24 + apdu.length]; - pinModifyStructure[offset++] = bTimeOut; - pinModifyStructure[offset++] = bTimeOut2; - pinModifyStructure[offset++] = bmFormatString; - pinModifyStructure[offset++] = bmPINBlockString; - pinModifyStructure[offset++] = bmPINLengthFormat; - pinModifyStructure[offset++] = bInsertionOffsetOld; - pinModifyStructure[offset++] = bInsertionOffsetNew; - pinModifyStructure[offset++] = wPINMaxExtraDigitL; - pinModifyStructure[offset++] = wPINMaxExtraDigitH; - pinModifyStructure[offset++] = bConfirmPIN; - pinModifyStructure[offset++] = bEntryValidationCondition; - pinModifyStructure[offset++] = bNumberMessage; - pinModifyStructure[offset++] = wLangIdL; - pinModifyStructure[offset++] = wLangIdH; - pinModifyStructure[offset++] = bMsgIndex1; - pinModifyStructure[offset++] = bMsgIndex2; - pinModifyStructure[offset++] = bMsgIndex3; - - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - - pinModifyStructure[offset++] = (byte) apdu.length; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinModifyStructure, offset, apdu.length); - - return pinModifyStructure; + protected void execPSO_HASH(CardChannel channel, byte[] hash) throws CardException, SignatureCardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0x2A, 0x90, 0x81, hash)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("PSO - HASH failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel) throws CardException, + SignatureCardException { - @Override - public String toString() { - return "a-sign premium"; + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO - COMPUTE DIGITAL SIGNATRE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } + + } + + protected byte[] execINTERNAL_AUTHENTICATE(CardChannel channel, 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 }; + + byte[] data = new byte[digestInfo.length + hash.length + 1]; + + System.arraycopy(digestInfo, 0, data, 0, digestInfo.length); + data[digestInfo.length] = (byte) hash.length; + System.arraycopy(hash, 0, data, digestInfo.length + 1, hash.length); + + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x88, 0x10, 0x00, data, 256)); + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } else if (resp.getSW() == 0x6983) { + throw new LockedException(); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("INTERNAL AUTHENTICATE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } } } -- cgit v1.2.3