From 50512519e5dea2405ceaead5ad111e3e827888b2 Mon Sep 17 00:00:00 2001 From: tzefferer Date: Mon, 29 Nov 2010 10:02:31 +0000 Subject: Bugfix DNIE FINEID Support git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@848 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../at/gv/egiz/smcc/CIOCertificateDirectory.java | 22 +- .../java/at/gv/egiz/smcc/DNIeSecuredChannel.java | 247 ++++++++------ .../java/at/gv/egiz/smcc/EFObjectDirectory.java | 308 +++++++++-------- smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java | 31 +- .../java/at/gv/egiz/smcc/FINEIDAODirectory.java | 143 ++++++++ .../gv/egiz/smcc/FINEIDAuthenticationObject.java | 57 ++++ .../egiz/smcc/FINEIDCIOCertificateDirectory.java | 56 ++++ .../at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java | 33 ++ smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java | 364 +++++++++++++++++++++ .../at/gv/egiz/smcc/FINEIDEFObjectDirectory.java | 44 +++ smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java | 37 +++ .../java/at/gv/egiz/smcc/LIEZertifikatCard.java | 2 +- .../java/at/gv/egiz/smcc/SignatureCardFactory.java | 41 ++- .../main/java/at/gv/egiz/smcc/T0CardChannel.java | 2 +- .../java/at/gv/egiz/smcc/util/ISO7816Utils.java | 8 +- .../at/gv/egiz/smcc/FINEIDCard.properties | 3 + smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java | 9 +- smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java | 354 ++++++++++++++++++++ .../at/gv/egiz/smcc/test/es_dnie/es_cds.der | Bin 0 -> 1296 bytes .../at/gv/egiz/smcc/test/es_dnie/es_prkd.der | Bin 0 -> 256 bytes .../at/gv/egiz/smcc/test/fineid/fin_cds.der | Bin 0 -> 224 bytes .../at/gv/egiz/smcc/test/fineid/fin_prkd.der | Bin 0 -> 288 bytes 22 files changed, 1485 insertions(+), 276 deletions(-) create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties create mode 100644 smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java create mode 100644 smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.der create mode 100644 smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.der create mode 100644 smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.der create mode 100644 smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.der diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java index 33dd99bb..a9886e80 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java @@ -59,12 +59,8 @@ public class CIOCertificateDirectory { */ public void selectAndRead(CardChannel channel) throws CardException, SignatureCardException, IOException { - CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, ISO7816Utils.P2_FCP, fid, 256); - ResponseAPDU resp = channel.transmit(cmd); - - byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCP); - byte[] fd = new TLVSequence(fcx).getValue(0x82); - + byte[] fd = executeSelect(channel); + if ((fd[0] & 0x04) > 0) { readCIOCertificatesFromRecords(channel, fd); @@ -75,6 +71,17 @@ public class CIOCertificateDirectory { } } + protected byte[] executeSelect(CardChannel channel) throws CardException { + + CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, ISO7816Utils.P2_FCP, fid, 256); + ResponseAPDU resp = channel.transmit(cmd); + + byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCP); + byte[] fd = new TLVSequence(fcx).getValue(0x82); + + return fd; + } + protected void readCIOCertificatesFromRecords(CardChannel channel, byte[] fd) throws CardException, SignatureCardException, IOException { for (int r = 1; r < fd[fd.length - 1]; r++) { @@ -92,7 +99,6 @@ public class CIOCertificateDirectory { protected void readCIOCertificatesFromTransparentFile(CardChannel channel) throws CardException, SignatureCardException, IOException { -// byte[] ef = ISO7816Utils.readTransparentFile(channel, -1); byte[] ef = doReadTransparentFile(channel); int i = 0; @@ -132,7 +138,7 @@ public class CIOCertificateDirectory { cioCert.setiD(x509Certificate.getElementAt(1).getElementAt(0).gvByteArray()); //read CONTEXTSPECIFIC manually - byte[] ctxSpecific = x509Certificate.getElementAt(2).getEncoded(); + byte[] ctxSpecific = x509Certificate.getElementAt(x509Certificate.getSize()-1).getEncoded(); if ((ctxSpecific[0] & 0xff) == 0xa1) { int ll = ((ctxSpecific[1] & 0xf0) == 0x80) ? (ctxSpecific[1] & 0x0f) + 2 : 2; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java index 92ef7f11..858a6cc5 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java @@ -186,11 +186,13 @@ public class DNIeSecuredChannel extends T0CardChannel { super(channel); this.established = false; - + try { + this.establish(); + } catch (CardException e) { - + log.error("Error establishing secure channel with card.", e); } } @@ -199,6 +201,9 @@ public class DNIeSecuredChannel extends T0CardChannel { log.trace("Try to set up secure channel to card.."); + // select master file + executeSelectMasterFile(); + // get chip info this.snIcc = executeGetChipInfo(); @@ -230,42 +235,43 @@ public class DNIeSecuredChannel extends T0CardChannel { @Override public int transmit(ByteBuffer command, ByteBuffer response) throws CardException { - + byte[] commandAPDU = new byte[command.remaining()]; - for(int i=0; i 5) { - - log.error("Error securing APDU - invalid APDU length: " + apdu.length); + + if (apdu.length < 4 || apdu.length > 5) { + + log.error("Error securing APDU - invalid APDU length: " + + apdu.length); throw new CardException("Invalid APDU length."); } - + boolean leAvailable = apdu.length == 5; byte encCLA = (byte) (apdu[0] | (byte) 0x0C); - byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2], - apdu[3] }; + byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2], apdu[3] }; byte[] paddedHeader = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, encHeader); int leFieldLen; byte[] leField = null; - if(leAvailable) { + if (leAvailable) { leField = new byte[3]; leField[0] = (byte) 0x97; leField[1] = (byte) 0x01; leField[2] = apdu[4]; leFieldLen = leField.length; } else { - + leFieldLen = 0; } byte[] macData = new byte[paddedHeader.length + leFieldLen]; - System.arraycopy(paddedHeader, 0, macData, 0, - paddedHeader.length); - - if(leAvailable) { + System.arraycopy(paddedHeader, 0, macData, 0, paddedHeader.length); + + if (leAvailable) { System.arraycopy(leField, 0, macData, paddedHeader.length, leField.length); - - macData = DNIeCryptoUtil.applyPadding( - BLOCK_LENGTH, macData); + + macData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, macData); } incrementSSC(); - byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(macData, - kMac, this.ssc, BLOCK_LENGTH); + byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(macData, kMac, this.ssc, + BLOCK_LENGTH); byte[] encapsulatedMac = new byte[mac.length + 2]; encapsulatedMac[0] = (byte) 0x8E; @@ -852,28 +889,28 @@ public class DNIeSecuredChannel extends T0CardChannel { completeMessage[2] = apdu[2]; completeMessage[3] = apdu[3]; completeMessage[4] = (byte) (encapsulatedMac.length + leFieldLen); - - if(leAvailable) { - System - .arraycopy(leField, 0, completeMessage, 5, - leField.length); + + if (leAvailable) { + System.arraycopy(leField, 0, completeMessage, 5, leField.length); } - - System.arraycopy(encapsulatedMac, 0, completeMessage, - 5 + leFieldLen, encapsulatedMac.length); - return completeMessage; - + System.arraycopy(encapsulatedMac, 0, completeMessage, 5 + leFieldLen, + encapsulatedMac.length); + + return completeMessage; + } - + private byte[] secureAPDUWithData(byte[] apdu) throws CardException { - if(apdu.length < 6) { - - log.error("Error securing APDU - invalid APDU length: " + apdu.length); - throw new CardException("Error securing APDU - invalid APDU length: " + apdu.length); + if (apdu.length < 6) { + + log.error("Error securing APDU - invalid APDU length: " + + apdu.length); + throw new CardException( + "Error securing APDU - invalid APDU length: " + apdu.length); } - + byte cla = apdu[0]; byte ins = apdu[1]; byte p1 = apdu[2]; @@ -881,23 +918,24 @@ public class DNIeSecuredChannel extends T0CardChannel { byte lc = apdu[4]; boolean leAvailable; - if(apdu.length == lc + 5 + 1) { - + if (apdu.length == lc + 5 + 1) { + leAvailable = true; - } else if(apdu.length != lc + 5) { - - log.error("Error securing APDU - invalid APDU length: " + apdu.length); + } else if (apdu.length != lc + 5) { + + log.error("Error securing APDU - invalid APDU length: " + + apdu.length); throw new CardException("Invalid APDU length or format."); } else { - + leAvailable = false; } - + byte[] leField = null; - if(leAvailable) { - - byte le = apdu[apdu.length-1]; - + if (leAvailable) { + + byte le = apdu[apdu.length - 1]; + leField = new byte[3]; leField[0] = (byte) 0x97; leField[1] = (byte) 0x01; @@ -942,14 +980,17 @@ public class DNIeSecuredChannel extends T0CardChannel { System.arraycopy(encapsulated, 0, headerAndData, paddedHeader.length, encapsulated.length); - if(leAvailable) { + if (leAvailable) { byte[] macData = new byte[headerAndData.length + leField.length]; - System.arraycopy(headerAndData, 0, macData, 0, headerAndData.length); - System.arraycopy(leField, 0, macData, headerAndData.length, leField.length); - + System + .arraycopy(headerAndData, 0, macData, 0, + headerAndData.length); + System.arraycopy(leField, 0, macData, headerAndData.length, + leField.length); + headerAndData = macData; } - + byte[] paddedHeaderAndData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, headerAndData); @@ -964,34 +1005,35 @@ public class DNIeSecuredChannel extends T0CardChannel { System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length); int leFieldLen; - if(leAvailable) { + if (leAvailable) { leFieldLen = leField.length; } else { leFieldLen = 0; } - + byte[] completeMessage = new byte[5 + encapsulated.length + encapsulatedMac.length + leFieldLen]; completeMessage[0] = encCLA; completeMessage[1] = ins; completeMessage[2] = p1; completeMessage[3] = p2; - + completeMessage[4] = (byte) (encapsulated.length + leFieldLen + encapsulatedMac.length); System.arraycopy(encapsulated, 0, completeMessage, 5, encapsulated.length); - - if(leAvailable) { - System.arraycopy(leField, 0, completeMessage, 5 + encapsulated.length, leFieldLen); + + if (leAvailable) { + System.arraycopy(leField, 0, completeMessage, + 5 + encapsulated.length, leFieldLen); } - - System.arraycopy(encapsulatedMac, 0, completeMessage, - 5 + encapsulated.length + leFieldLen, encapsulatedMac.length); - return completeMessage; + System.arraycopy(encapsulatedMac, 0, completeMessage, 5 + + encapsulated.length + leFieldLen, encapsulatedMac.length); + + return completeMessage; } - + private byte[] secureAPDU(byte[] apdu) throws CardException { if (apdu == null || apdu.length < 4) { @@ -1005,12 +1047,12 @@ public class DNIeSecuredChannel extends T0CardChannel { return secureAPDUWithoutData(apdu); } - if(apdu.length > 5) { + if (apdu.length > 5) { return secureAPDUWithData(apdu); } - - throw new CardException("Error securing APDU - unexpected APDU length."); + + throw new CardException("Error securing APDU - unexpected APDU length."); } private byte[] verifyAndDecryptSecuredResponseAPDU(byte[] securedAPDU) @@ -1031,15 +1073,18 @@ public class DNIeSecuredChannel extends T0CardChannel { System.arraycopy(commandResponse, 0, macData, data.length, commandResponse.length); - byte[] paddedMacData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, macData); + byte[] paddedMacData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, + macData); incrementSSC(); - byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(paddedMacData, this.kMac, this.ssc, BLOCK_LENGTH); + byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(paddedMacData, this.kMac, + this.ssc, BLOCK_LENGTH); if (!Arrays.equals(mac, obtainedMac)) { - log.error("Error verifiying MAC of secured response. MAC values do not match."); + log + .error("Error verifiying MAC of secured response. MAC values do not match."); throw new CardException("Unable to verify MAC of Response APDU."); } @@ -1047,14 +1092,14 @@ public class DNIeSecuredChannel extends T0CardChannel { byte[] data2decrypt = new byte[data.length - DNIeCryptoUtil.getCutOffLength(data, BLOCK_LENGTH)]; - System.arraycopy(data, DNIeCryptoUtil.getCutOffLength(data, BLOCK_LENGTH), - data2decrypt, 0, data2decrypt.length); + System.arraycopy(data, DNIeCryptoUtil.getCutOffLength(data, + BLOCK_LENGTH), data2decrypt, 0, data2decrypt.length); byte[] plainData = null; try { - plainData = DNIeCryptoUtil.perform3DESCipherOperation(data2decrypt, this.kEnc, - Cipher.DECRYPT_MODE); + plainData = DNIeCryptoUtil.perform3DESCipherOperation( + data2decrypt, this.kEnc, Cipher.DECRYPT_MODE); } catch (Exception e) { log.error("Error decrypting data.", e); throw new CardException("Unable to decrypt data.", e); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java index 17a42e4d..dfa70d91 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java @@ -1,26 +1,30 @@ /* -* 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. -*/ + * 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.util.ISO7816Utils; import at.gv.egiz.smcc.util.TLV; import at.gv.egiz.smcc.util.TLVSequence; + +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; + import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; @@ -29,134 +33,158 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * + * * @author clemens */ public class EFObjectDirectory { - protected static final Logger log = LoggerFactory.getLogger(EFObjectDirectory.class); - - protected byte[] fid; - private byte[] ef_prkd; - private byte[] ef_pukd; - private byte[] ef_cd; - private byte[] ef_aod; - - public EFObjectDirectory() { - fid = new byte[] { (byte)0x50, (byte)0x31 }; - } - - public EFObjectDirectory(byte[] fid) { - this.fid = fid; - } - - /** - * assume DF.CIA selected - * EF.OD selected afterwards - * @param channel - * @throws CardException - * @throws SignatureCardException - */ - public void selectAndRead(CardChannel channel) throws CardException, SignatureCardException { - - log.trace("select EF.OD"); - CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, 0x00, fid, 256); - ResponseAPDU resp = channel.transmit(cmd); - - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("SELECT EF.OD failed: SW=0x" - + Integer.toHexString(resp.getSW())); - } - - log.trace("read EF.OD"); - byte[] efod = ISO7816Utils.readTransparentFile(channel, -1); - - for (TLV cio : new TLVSequence(efod)) { - int tag = cio.getTag(); - byte[] seq = cio.getValue(); - - if ((tag & 0xf0) == 0xa0 && seq.length >= 4) { - - byte[] path = Arrays.copyOfRange(seq, 4, 4+seq[3]); - - switch (cio.getTag() & 0x0f) { - case 0: - setEf_prkd(path); - break; - case 1: - setEf_pukd(path); - break; - case 4: - setEf_cd(path); - break; - case 8: - setEf_aod(path); - break; - default: - log.warn("CIOChoice 0x{} not supported: ", - (cio.getTag() & 0x0f)); - } - } else { - log.trace("ignoring invalid CIO reference entry: {}", seq); - } - } - } - - /** - * @return the ef_prkd - */ - public byte[] getEf_prkd() { - return ef_prkd; - } - - /** - * @param ef_prkd the ef_prkd to set - */ - public void setEf_prkd(byte[] ef_prkd) { - this.ef_prkd = ef_prkd; - } - - /** - * @return the ef_pukd - */ - public byte[] getEf_pukd() { - return ef_pukd; - } - - /** - * @param ef_pukd the ef_pukd to set - */ - public void setEf_pukd(byte[] ef_pukd) { - this.ef_pukd = ef_pukd; - } - - /** - * @return the ef_cd - */ - public byte[] getEf_cd() { - return ef_cd; - } - - /** - * @param ef_cd the ef_cd to set - */ - public void setEf_cd(byte[] ef_cd) { - this.ef_cd = ef_cd; - } - - /** - * @return the ef_aod - */ - public byte[] getEf_aod() { - return ef_aod; - } - - /** - * @param ef_aod the ef_aod to set - */ - public void setEf_aod(byte[] ef_aod) { - this.ef_aod = ef_aod; - } - + protected static final Logger log = LoggerFactory + .getLogger(EFObjectDirectory.class); + + protected byte[] fid; + private byte[] ef_prkd; + private byte[] ef_pukd; + private byte[] ef_aod; + + private List ef_cd_list = new ArrayList();; + + private Integer padding; + + public EFObjectDirectory() { + fid = new byte[] { (byte) 0x50, (byte) 0x31 }; + } + + public EFObjectDirectory(byte[] fid) { + this.fid = fid; + } + + public EFObjectDirectory(int padding) { + + fid = new byte[] { (byte) 0x50, (byte) 0x31 }; + this.padding = padding; + + } + + /** + * assume DF.CIA selected EF.OD selected afterwards + * + * @param channel + * @throws CardException + * @throws SignatureCardException + */ + public void selectAndRead(CardChannel channel) throws CardException, + SignatureCardException { + + executeSelect(channel); + + byte[] efod = ISO7816Utils.readTransparentFile(channel, -1); + + for (TLV cio : new TLVSequence(efod)) { + int tag = cio.getTag(); + + if (padding != null && tag == padding) { + // reached padding - quit record extraction + break; + } + + byte[] seq = cio.getValue(); + + if ((tag & 0xf0) == 0xa0 && seq.length >= 4) { + + byte[] path = Arrays.copyOfRange(seq, 4, 4 + seq[3]); + + switch (cio.getTag() & 0x0f) { + case 0: + setEf_prkd(path); + break; + case 1: + setEf_pukd(path); + break; + case 4: + addCdToEf_cd_list(path); + break; + case 8: + setEf_aod(path); + break; + default: + log.warn("CIOChoice 0x{} not supported: ", + (cio.getTag() & 0x0f)); + } + } else { + log.trace("ignoring invalid CIO reference entry: {}", seq); + } + } + } + + protected void executeSelect(CardChannel channel) + throws SignatureCardException, CardException { + + CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, 0x00, fid, 256); + ResponseAPDU resp = channel.transmit(cmd); + + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("SELECT EF.OD failed: SW=0x" + + Integer.toHexString(resp.getSW())); + } + + } + + /** + * @return the ef_prkd + */ + public byte[] getEf_prkd() { + return ef_prkd; + } + + /** + * @param ef_prkd + * the ef_prkd to set + */ + public void setEf_prkd(byte[] ef_prkd) { + this.ef_prkd = ef_prkd; + } + + /** + * @return the ef_pukd + */ + public byte[] getEf_pukd() { + return ef_pukd; + } + + /** + * @param ef_pukd + * the ef_pukd to set + */ + public void setEf_pukd(byte[] ef_pukd) { + this.ef_pukd = ef_pukd; + } + + /** + * @return the ef_aod + */ + public byte[] getEf_aod() { + return ef_aod; + } + + public List getEf_cd_list() { + return ef_cd_list; + } + + public void setEf_cd_list(List ef_cd_list) { + this.ef_cd_list = ef_cd_list; + } + + public void addCdToEf_cd_list(byte[] ef_cd) { + + this.ef_cd_list.add(ef_cd); + } + + /** + * @param ef_aod + * the ef_aod to set + */ + public void setEf_aod(byte[] ef_aod) { + this.ef_aod = ef_aod; + } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java index 1c30ca57..a0d426c7 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java @@ -21,7 +21,7 @@ import at.gv.egiz.smcc.util.SMCCHelper; public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard { - private final byte[] MASTER_FILE_ID = new byte[] { + public static final byte[] MASTER_FILE_ID = new byte[] { (byte) 0x4D, (byte) 0x61, (byte) 0x73, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x2E, (byte) 0x46, (byte) 0x69, (byte) 0x6C, @@ -42,20 +42,16 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard { "[0-9A-Za-z_<>!()?%\\-=&+\\.]", "at/gv/egiz/smcc/ESDNIeCard", "sig.pin", (byte) 0x00, new byte[] {}, PinInfo.UNKNOWN_RETRIES); - protected CardChannel channel; @Override protected CardChannel getCardChannel() { - - if (channel == null) { - - channel = new DNIeSecuredChannel(getCard().getBasicChannel()); - } - - return channel; + + // set up a new secure channel each time + return new DNIeSecuredChannel(getCard().getBasicChannel()); } - + @Override + @Exclusive public byte[] createSignature(InputStream input, KeyboxName keyboxName, PINGUI pinGUI, String alg) throws SignatureCardException, InterruptedException, IOException { @@ -63,7 +59,7 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard { CardChannel channel = getCardChannel(); try { - + // Select MF executeSelectMasterFile(channel); @@ -135,20 +131,22 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard { throw new SignatureCardException( "Error creating signature with DNIe card.", e); } + } @Override + @Exclusive public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI) throws SignatureCardException, InterruptedException { byte[] result = null; - + CardChannel channel = getCardChannel(); byte[] certId = null; try { - + // Select MF executeSelectMasterFile(channel); @@ -161,7 +159,7 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard { efOd.selectAndRead(channel); DNIeCIOCertificateDirectory efCd = new DNIeCIOCertificateDirectory( - efOd.getEf_cd()); + efOd.getEf_cd_list().get(0)); try { efCd.selectAndRead(channel); @@ -214,17 +212,18 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard { compressedWithoutHeader.length); result = decompressData(compressedWithoutHeader); - + } catch (CardException e) { log.error("Error reading certificate from card.", e); throw new SignatureCardException( "Error reading certificate from card.", e); } - + return result; } + @Override public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) throws SignatureCardException, InterruptedException { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java new file mode 100644 index 00000000..3fd1503c --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java @@ -0,0 +1,143 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.smcc; + +import iaik.me.asn1.ASN1; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.TLVSequence; + +public class FINEIDAODirectory { + + protected static final Logger log = LoggerFactory.getLogger(CIOCertificateDirectory.class); + protected byte[] fid; + protected List aos; + + public FINEIDAODirectory(byte[] fid) { + + this.fid = FINEIDUtil.removeMFPath(fid); + aos = new ArrayList(); + } + + /** + * assume DF.CIA selected + * CIO.CD selected afterwards + * + * @param channel + * @throws CardException + * @throws SignatureCardException + * @throws IOException if ASN.1 structure cannot be parsed + */ + public void selectAndRead(CardChannel channel) throws CardException, SignatureCardException, IOException { + + CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x08, ISO7816Utils.P2_FCI, fid, 256); + ResponseAPDU resp = channel.transmit(cmd); + + byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCI); + byte[] fd = new TLVSequence(fcx).getValue(0x82); + + if ((fd[0] & 0x05) == 0x01) { + + readAuthenticationObjectsFromTransparentFile(channel); + } + } + + protected byte[] doReadTransparentFile(CardChannel channel) throws CardException, SignatureCardException { + + return ISO7816Utils.readTransparentFile(channel, -1); + } + + protected void readAuthenticationObjectsFromTransparentFile(CardChannel channel) throws CardException, SignatureCardException, IOException { + + byte[] ef = doReadTransparentFile(channel); + + int i = 0; + int j; + + do { + int length = 0; + int ll = 0; + if ((ef[i + 1] & 0xf0) == 0x80) { + ll = ef[i + 1] & 0x7f; + for (int it = 0; it < ll; it++) { + length = (length << 8) + (ef[i + it + 2] & 0xff); + } + } else { + length = (ef[i + 1] & 0xff); + } + + log.trace("read transparent file entry: tag 0x{}, length 0x{}", Integer.toHexString(ef[i]), + Integer.toHexString(length)); + + j = i + 2 + ll + length; + addAuthenticationObject(Arrays.copyOfRange(ef, i, j)); + i = j; + } while (i < ef.length && ef[i] > 0); + + } + + protected void addAuthenticationObject(byte[] ao) throws IOException { + + ASN1 authenticationObjects = new ASN1(ao); + + FINEIDAuthenticationObject authObject = new FINEIDAuthenticationObject(); + authObject.setLabel(authenticationObjects.getElementAt(0).getElementAt(0).gvString()); + + authObject.setAuthId(authenticationObjects.getElementAt(1).getElementAt(0).gvByteArray()); + + //read CONTEXTSPECIFIC manually + byte[] ctxSpecific = authenticationObjects.getElementAt(authenticationObjects.getSize()-1).getEncoded(); + + if ((ctxSpecific[0] & 0xff) == 0xa1) { + int ll = ((ctxSpecific[1] & 0xf0) == 0x80) + ? (ctxSpecific[1] & 0x0f) + 2 : 2; + ASN1 aoAttributes = new ASN1(Arrays.copyOfRange(ctxSpecific, ll, ctxSpecific.length)); + + authObject.setPath(aoAttributes.getElementAt(aoAttributes.getSize()-1).getElementAt(0).gvByteArray()); + + // get pwdReference + byte[] ctxSpecific2 = aoAttributes.getElementAt(4).getEncoded(); + ASN1 pwdRef = new ASN1(ctxSpecific2); + + authObject.setPwdReference(pwdRef.gvByteArray()); + + } else { + log.warn("expected CONTEXTSPECIFIC, got 0x{}", + Integer.toHexString(ctxSpecific[0])); + } + + log.debug("adding {}", authObject); + aos.add(authObject); + } + + public List getAOs() { + return aos; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java new file mode 100644 index 00000000..d25946f1 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java @@ -0,0 +1,57 @@ +/* +* 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 FINEIDAuthenticationObject { + + private String label; + private byte[] authId; + private byte[] path; + private byte[] pwdReference; + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public byte[] getAuthId() { + return authId; + } + + public void setAuthId(byte[] authId) { + this.authId = authId; + } + + public byte[] getPath() { + return path; + } + + public void setPath(byte[] path) { + this.path = path; + } + + public byte[] getPwdReference() { + return pwdReference; + } + + public void setPwdReference(byte[] pwdReference) { + this.pwdReference = pwdReference; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java new file mode 100644 index 00000000..0de2b3c1 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java @@ -0,0 +1,56 @@ +/* + * 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 javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.TLVSequence; + +public class FINEIDCIOCertificateDirectory extends CIOCertificateDirectory { + + protected static final boolean RETRIEVE_AUTH_ID_FROM_ASN1 = Boolean.FALSE; + + public FINEIDCIOCertificateDirectory(byte[] fid) { + + super(fid); + this.fid = FINEIDUtil.removeMFPath(fid); + } + + @Override + protected byte[] executeSelect(CardChannel channel) throws CardException { + + CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x08, ISO7816Utils.P2_FCI, fid, 256); + ResponseAPDU resp = channel.transmit(cmd); + + byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCI); + byte[] fd = new TLVSequence(fcx).getValue(0x82); + + return fd; + } + + @Override + protected boolean retrieveAuthIdFromASN1() { + + return RETRIEVE_AUTH_ID_FROM_ASN1; + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java new file mode 100644 index 00000000..2d89e5bc --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java @@ -0,0 +1,33 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package at.gv.egiz.smcc; + +public class FINEIDCIOKeyDirectory extends FINEIDCIOCertificateDirectory { + + protected static final boolean RETRIEVE_AUTH_ID_FROM_ASN1 = Boolean.TRUE; + + public FINEIDCIOKeyDirectory(byte[] fid) { + + super(fid); + } + + protected boolean retrieveAuthIdFromASN1() { + + return RETRIEVE_AUTH_ID_FROM_ASN1; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java new file mode 100644 index 00000000..96c8e517 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java @@ -0,0 +1,364 @@ +/* + * 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 java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.TLVSequence; + +public class FINEIDCard extends AbstractSignatureCard implements SignatureCard { + + private static final int EF_OD_PADDING = 0xFF; + private static final String SIG_CERT_LABEL = "allekirjoitusvarmenne"; + private static final String SIG_KEY_LABEL = "allekirjoitusavain"; + + private final Logger log = LoggerFactory.getLogger(FINEIDCard.class); + + protected PinInfo pinInfo = new PinInfo(6, 8, "[0-9]", + "at/gv/egiz/smcc/FINEIDCard", "sig.pin", (byte) 0x00, + new byte[] {}, PinInfo.UNKNOWN_RETRIES); + + @Override + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINGUI pinGUI, String alg) throws SignatureCardException, + InterruptedException, IOException { + + CardChannel channel = getCardChannel(); + + try { + + FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory( + EF_OD_PADDING); + ef_od.selectAndRead(channel); + + // read PRKD to find correct key + FINEIDCIOKeyDirectory ef_prkd = new FINEIDCIOKeyDirectory(ef_od + .getEf_prkd()); + ef_prkd.selectAndRead(channel); + + byte[] efKey = null; + byte[] authID = null; + + for (CIOCertificate cioCertificate : ef_prkd.getCIOs()) { + String label = cioCertificate.getLabel(); + if (label != null + && label.toLowerCase().contains( + SIG_KEY_LABEL.toLowerCase())) { + + efKey = cioCertificate.getEfidOrPath(); + authID = cioCertificate.getAuthId(); + } + } + + if (efKey == null) { + + throw new SignatureCardException( + "Could not determine path to private key from PrKD."); + } + + if (authID == null) { + + throw new SignatureCardException( + "Could not determine authID of private key from PrKD."); + } + + // read AOD to find associated PIN (authId must match) + FINEIDAODirectory ef_aod = new FINEIDAODirectory(ef_od.getEf_aod()); + ef_aod.selectAndRead(channel); + + byte[] pinPath = null; + byte[] pwdRef = null; + for (FINEIDAuthenticationObject ao : ef_aod.getAOs()) { + + byte[] id = ao.getAuthId(); + if (id != null && Arrays.equals(id, authID)) { + pinPath = ao.getPath(); + pwdRef = ao.getPwdReference(); + } + } + + if (pinPath == null) { + + throw new SignatureCardException( + "Could not determine path to PIN from AOD."); + } + + if (pwdRef == null) { + + throw new SignatureCardException( + "Could not determine PIN reference from AOD."); + } + + // verify PIN + verifyPINLoop(channel, pinInfo, pinGUI, pinPath, + pwdRef[pwdRef.length - 1]); + + // Set MSE + CommandAPDU selectKeyPath = new CommandAPDU((byte) 0x00, + (byte) 0xA4, (byte) 0x08, (byte) 0x00, FINEIDUtil + .removeMFPath(efKey)); + ResponseAPDU resp = channel.transmit(selectKeyPath); + + if (resp.getSW() != 0x9000) { + + throw new SignatureCardException( + "Could not select private key file DF."); + } + + executeRestoreMSE(channel); + + byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x12, + (byte) 0x81, (byte) 0x02, efKey[efKey.length - 2], + efKey[efKey.length - 1] }; + + executeSetMSE(channel, dst); + + // SIGN + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get MessageDigest.", e); + throw new SignatureCardException(e); + } + // calculate message digest + byte[] digest = new byte[md.getDigestLength()]; + for (int l; (l = input.read(digest)) != -1;) { + md.update(digest, 0, l); + } + digest = md.digest(); + + byte[] sigVal = executeSign(channel, digest); + return sigVal; + + } catch (CardException e) { + + throw new SignatureCardException("Error creating signature.", e); + } + + } + + @Override + public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI) + throws SignatureCardException, InterruptedException { + + CardChannel channel = getCardChannel(); + + try { + FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory( + EF_OD_PADDING); + ef_od.selectAndRead(channel); + + byte[] certPath = null; + + for (int i = 0; i < ef_od.getEf_cd_list().size(); i++) { + + FINEIDCIOCertificateDirectory ef_cd = new FINEIDCIOCertificateDirectory( + ef_od.getEf_cd_list().get(i)); + + try { + ef_cd.selectAndRead(channel); + } catch (IOException e) { + log.debug("Cannot read EF.CD - try next one in list.."); + continue; + } + + for (CIOCertificate cioCertificate : ef_cd.getCIOs()) { + String label = cioCertificate.getLabel(); + if (label != null + && label.toLowerCase().contains( + SIG_CERT_LABEL.toLowerCase())) { + certPath = cioCertificate.getEfidOrPath(); + } + } + } + + if (certPath == null) { + + throw new SignatureCardException( + "Could not determine path to certificate."); + } + + log + .debug("Read certificate path: " + + SMCCHelper.toString(certPath)); + + certPath = FINEIDUtil.removeMFPath(certPath); + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, certPath); + ResponseAPDU resp = channel.transmit(apdu); + + byte[] fcx = new TLVSequence(resp.getBytes()) + .getValue(ISO7816Utils.TAG_FCI); + byte[] fileDataLength = new TLVSequence(fcx).getValue(0x81); + + return ISO7816Utils.readTransparentFile(channel, + computeLengthFromByteArray(fileDataLength)); + + } catch (CardException e) { + + throw new SignatureCardException( + "Error reading certificate from card.", e); + } + } + + @Override + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) + throws SignatureCardException, InterruptedException { + + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + protected void verifyPINLoop(CardChannel channel, PinInfo spec, + PINGUI provider, byte[] pinPath, byte keyID) + throws LockedException, NotActivatedException, + SignatureCardException, InterruptedException, CardException { + + CommandAPDU verifySelect = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, FINEIDUtil.removeMFPath(pinPath)); + ResponseAPDU response = channel.transmit(verifySelect); + + if (response.getSW() != 0x9000) { + + throw new SignatureCardException("Cannot select PIN path " + + SMCCHelper.toString(pinPath) + ": " + + Integer.toHexString(response.getSW())); + } + + int retries = -1; + + do { + retries = verifyPIN(channel, spec, provider, retries, keyID); + } while (retries > 0); + } + + protected int verifyPIN(CardChannel channel, PinInfo pinSpec, + PINGUI provider, int retries, byte keyID) + throws SignatureCardException, LockedException, + NotActivatedException, InterruptedException, CardException { + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00, + (byte) 0x20, (byte) 0x00, keyID, (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, provider, pinSpec, + 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(); + case 0x6984: + // reference data not usable + throw new NotActivatedException(); + case 0x6985: + // conditions of use not satisfied + throw new NotActivatedException(); + + default: + String msg = "VERIFY failed. SW=" + + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + + } + + private void executeRestoreMSE(CardChannel channel) throws CardException { + + CommandAPDU mseRestore = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0xF3, (byte) 0x00); + ResponseAPDU resp = channel.transmit(mseRestore); + + if (resp.getSW() != 0x9000) { + + throw new CardException("Error restoring MSE: " + + Integer.toHexString(resp.getSW())); + } + + } + + private void executeSetMSE(CardChannel channel, byte[] dst) + throws CardException { + + CommandAPDU mseSet = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0x41, (byte) 0xB6, dst); + ResponseAPDU resp = channel.transmit(mseSet); + + if (resp.getSW() != 0x9000) { + + throw new CardException("Error setting MSE: " + + Integer.toHexString(resp.getSW())); + } + + } + + private byte[] executeSign(CardChannel channel, byte[] hash) + throws CardException { + + CommandAPDU sign = new CommandAPDU((byte) 0x00, (byte) 0x2A, + (byte) 0x9E, (byte) 0x9A, hash); + ResponseAPDU resp = channel.transmit(sign); + + if (resp.getSW() != 0x9000) { + + throw new CardException("Error signing hash: " + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + } + + private int computeLengthFromByteArray(byte[] input) { + + int result = 0; + + for (int i = 0; i < input.length; i++) { + int current = input[input.length - 1 - i]; + result = result + (int) (current * Math.pow(256, i)); + } + return result; + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java new file mode 100644 index 00000000..2690d694 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java @@ -0,0 +1,44 @@ +/* + * 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 javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public class FINEIDEFObjectDirectory extends EFObjectDirectory { + + public FINEIDEFObjectDirectory(int padding) { + + super(padding); + } + + @Override + protected void executeSelect(CardChannel channel) + throws SignatureCardException, CardException { + + CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x00, 0x00, fid, 256); + ResponseAPDU resp = channel.transmit(cmd); + + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("SELECT EF.OD failed: SW=0x" + + Integer.toHexString(resp.getSW())); + } + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java new file mode 100644 index 00000000..11f37100 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java @@ -0,0 +1,37 @@ +/* +* 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 FINEIDUtil { + + public static byte[] removeMFPath(byte[] completePath) { + + byte[] result = null; + + // remove MF path + if (completePath.length >= 2 && completePath[0] == 0x3F + && completePath[1] == 0x00) { + result = new byte[completePath.length - 2]; + System.arraycopy(completePath, 2, result, 0, + completePath.length - 2); + } else { + result = completePath; + } + + return result; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java b/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java index c4324773..434f35a1 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java @@ -109,7 +109,7 @@ public class LIEZertifikatCard extends AbstractSignatureCard implements Signatur EFObjectDirectory ef_od = new EFObjectDirectory(); ef_od.selectAndRead(channel); - CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd()); + CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd_list().get(0)); ef_cd.selectAndRead(channel); byte[] ef_qcert = null; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java index 3a773ca2..22d295e0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -183,7 +183,7 @@ public class SignatureCardFactory { // e-card G3 supportedCards.add(new SupportedCard( // ATR - // (3b:dd:96:ff:81:b1:fe:45:1f:03:80:31:b0:52:02:03:64:04:1b:b4:22:81:05:18) + // (3b:dd:96:ff:81:b1:fe:45:1f:03:80:31:b0:52:02:03:64:04:1b:b4:22:81:05:18) new byte[] { (byte) 0x3b, (byte) 0xdd, (byte) 0x96, (byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe, (byte) 0x45, (byte) 0x1f, (byte) 0x03, (byte) 0x00, @@ -203,7 +203,7 @@ public class SignatureCardFactory { // a-sign premium (EPA) supportedCards.add(new SupportedCard( // ATR - // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00) + // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00) new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, (byte) 0x45, (byte) 0x50, (byte) 0x41, @@ -224,7 +224,7 @@ public class SignatureCardFactory { // a-sign premium (MCA) supportedCards.add(new SupportedCard( // ATR - // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00) + // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00) new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, (byte) 0x4D, (byte) 0x43, (byte) 0x41, @@ -309,6 +309,41 @@ public class SignatureCardFactory { (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0xff, (byte) 0xff }, "at.gv.egiz.smcc.ESDNIeCard")); + // FMNT card - ATR is correct, but implementation is NOT equal to DNIe +// supportedCards.add(new SupportedCard( +// // ATR +// // [3b:ef:00:00:40:14:80:25:43:45:52:45:53:57:01:16:01:01:03:90:00] +// new byte[] { (byte) 0x3b, (byte) 0xEF, (byte) 0x00, +// (byte) 0x00, (byte) 0x40, (byte) 0x14, (byte) 0x80, +// (byte) 0x25, (byte) 0x43, (byte) 0x45, (byte) 0x52, +// (byte) 0x45, (byte) 0x53, (byte) 0x57, (byte) 0x01, +// (byte) 0x16, (byte) 0x01, (byte) 0x01, (byte) 0x03, +// (byte) 0x90, (byte) 0x00 }, +// // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff) +// new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, +// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, +// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, +// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, +// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, +// (byte) 0xff, (byte) 0xff }, +// "at.gv.egiz.smcc.ESDNIeCard")); + + // FIN eID + supportedCards.add(new SupportedCard( + // ATR [3b:7B:94:00:00:80:62:12:51:56:46:69:6E:45:49:44] + new byte[] { (byte) 0x3b, (byte) 0x7B, (byte) 0x94, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x62, + (byte) 0x00, (byte) 0x51, (byte) 0x56, (byte) 0x46, + (byte) 0x69, (byte) 0x6E, (byte) 0x45, (byte) 0x49, + (byte) 0x44 }, + // mask (ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff) - + // ignore card OS minor version + new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff }, "at.gv.egiz.smcc.FINEIDCard")); + // ITCards supportedCards.add(new SupportedCard( // ATR = diff --git a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java index 4806b6c0..65bcd84b 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java @@ -49,7 +49,7 @@ public class T0CardChannel extends LogCardChannel { CommandAPDU command = new CommandAPDU(new byte[] { (byte) 0x00, (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) sw2 }); ResponseAPDU resp = channel.transmit(command); - + try { bof.write(resp.getData()); } catch (IOException e) { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java index f35086b6..44045d3c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java @@ -65,6 +65,11 @@ public class ISO7816Utils { @Override protected byte[] readBinary(int offset, int len) throws IOException { + if(len < 1) { + // nothing to read - return + return new byte[0]; + } + ResponseAPDU resp; try { resp = channel.transmit(new CommandAPDU(0x00, 0xB0, @@ -76,7 +81,7 @@ public class ISO7816Utils { // handle case: wrong number of bytes requested from card // card indicates correct number of bytes available in SW2 if (resp.getSW1() == 0x6c) { - + try { resp = channel.transmit(new CommandAPDU(0x00, 0xB0, 0x7F & (offset >> 8), offset & 0xFF, resp @@ -106,7 +111,6 @@ public class ISO7816Utils { }; return file; - } private static byte[] readFromInputStream(TransparentFileInputStream is) throws CardException, SignatureCardException { diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties new file mode 100644 index 00000000..7729e647 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties @@ -0,0 +1,3 @@ +#pin.name=PIN +sig.pin.name=PIN 2 +sig.pin.length=6-8 \ No newline at end of file diff --git a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java index 4c4a7b41..577979f2 100644 --- a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java +++ b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java @@ -386,16 +386,17 @@ public class ESCardTest extends AbstractSignatureCard { SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault()); try { - signatureCard.createSignature(null, null, null, null); +// signatureCard.createSignature(null, null, null, null); + signatureCard.getCertificate(null, null); } catch (SignatureCardException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); +// } catch (IOException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); } } diff --git a/smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java b/smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java new file mode 100644 index 00000000..eef6f611 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java @@ -0,0 +1,354 @@ +package at.gv.egiz.smcc; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Locale; + +import org.junit.Ignore; + +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.TLVSequence; + +import javax.smartcardio.*; + +@Ignore +public class FINEIDTest extends AbstractSignatureCard { + + /** + * @param args + */ + public static void main(String[] args) { + + FINEIDTest tester = new FINEIDTest(); + tester.runTest(); + + } + + public void runTest() { + + SMCCHelper helper = new SMCCHelper(); + + SignatureCard signatureCard = helper.getSignatureCard(Locale + .getDefault()); + + System.out.println("Found card: " + signatureCard.toString()); + + // TODO: replace this by already implemented getCardChannel() method + CardChannel channel = new T0CardChannel(signatureCard.getCard() + .getBasicChannel()); + + try { + + selectAID(channel); +// readCardInfo(channel); + testPIN(); + + } catch (Exception e) { + + e.printStackTrace(); + } + + } + + public void testPIN() { + + PinInfo pinInfo = new PinInfo(6, 8, "[0-9]", + "at/gv/egiz/smcc/FINEIDCard", "sig.pin", (byte) 0x00, + new byte[] {}, PinInfo.UNKNOWN_RETRIES); + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte)0x82 }, 0, + VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); + + CommandAPDU apdu = ISO7816Utils.createVerifyAPDU(apduSpec, new char[]{'1','2','3','4','5','6'}); + + System.out.println("APDU: " + SMCCHelper.toString(apdu.getBytes())); + + } + + public void selectAID(CardChannel channel) throws CardException { + + byte[] aid = new byte[] { (byte) 0xA0, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x63, (byte) 0x50, (byte) 0x4B, + (byte) 0x43, (byte) 0x53, (byte) 0x2D, (byte) 0x31, (byte) 0x35 }; + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x04, (byte) 0x00, aid); + + ResponseAPDU resp = channel.transmit(apdu); + + System.out.println("Response: " + SMCCHelper.toString(resp.getBytes())); + + } + + public void readCardInfo(CardChannel channel) throws CardException, + SignatureCardException, IOException { + + byte[] efQcert = null; + + FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory(0xFF); + ef_od.selectAndRead(channel); + + // **** READ CERT **** + + for (int i = 0; i < ef_od.getEf_cd_list().size(); i++) { + + FINEIDCIOCertificateDirectory ef_cd = new FINEIDCIOCertificateDirectory( + ef_od.getEf_cd_list().get(i)); + + try { + ef_cd.selectAndRead(channel); + } catch (IOException e) { + System.out + .println("Error reading EF.CD - try next if available."); + e.printStackTrace(); + continue; + } + + for (CIOCertificate cioCertificate : ef_cd.getCIOs()) { + String label = cioCertificate.getLabel(); + if (label != null + && label.toLowerCase().contains( + "allekirjoitusvarmenne".toLowerCase())) { + efQcert = cioCertificate.getEfidOrPath(); + } + } + } + + System.out.println("Read certificate path: " + + SMCCHelper.toString(efQcert)); + + byte[] certPath = null; + // remove MF path + if (efQcert[0] == 0x3F && efQcert[1] == 0x00) { + + certPath = new byte[efQcert.length - 2]; + System.arraycopy(efQcert, 2, certPath, 0, efQcert.length - 2); + } else { + + certPath = efQcert; + } + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, certPath); + ResponseAPDU resp = channel.transmit(apdu); + + System.out.println("Response: " + SMCCHelper.toString(resp.getBytes())); + + byte[] fcx = new TLVSequence(resp.getBytes()) + .getValue(ISO7816Utils.TAG_FCI); + byte[] fileDataLength = new TLVSequence(fcx).getValue(0x81); + + System.out.println("Data length: " + + SMCCHelper.toString(fileDataLength)); + + System.out.println("MaxSize: " + + computeLengthFromByteArray(fileDataLength)); + + byte[] cert = ISO7816Utils.readTransparentFile(channel, + computeLengthFromByteArray(fileDataLength)); + + System.out.println("Read cert: " + SMCCHelper.toString(cert)); + + toFile(cert, "F:/fin_cert.cer"); + + // **** VERIFY PIN **** + + byte[] prkdPath = ef_od.getEf_prkd(); + System.out.println("PRKD path: " + SMCCHelper.toString(prkdPath)); + + FINEIDCIOKeyDirectory ef_prkd = new FINEIDCIOKeyDirectory(ef_od + .getEf_prkd()); + ef_prkd.selectAndRead(channel); + + byte[] efKey = null; + byte[] authID = null; + for (CIOCertificate cioCertificate : ef_prkd.getCIOs()) { + String label = cioCertificate.getLabel(); + if (label != null + && label.toLowerCase().contains( + "allekirjoitusavain".toLowerCase())) { + efKey = cioCertificate.getEfidOrPath(); + System.out.println("AUTH ID of this key: " + + SMCCHelper.toString(cioCertificate.getAuthId())); + authID = cioCertificate.getAuthId(); + } + } + + System.out.println("Key path: " + SMCCHelper.toString(efKey)); + + byte[] aod = ef_od.getEf_aod(); + System.out.println("AOD path: " + SMCCHelper.toString(aod)); + + FINEIDAODirectory ef_aod = new FINEIDAODirectory(ef_od.getEf_aod()); + ef_aod.selectAndRead(channel); + + byte[] pinPath = null; + byte[] pwdRef = null; + for (FINEIDAuthenticationObject ao : ef_aod.getAOs()) { + + byte[] id = ao.getAuthId(); + if (id != null && Arrays.equals(id, authID)) { + pinPath = ao.getPath(); + pwdRef = ao.getPwdReference(); + } + } + + System.out.println("PIN path: " + SMCCHelper.toString(pinPath)); + System.out.println("PWD Ref: " + SMCCHelper.toString(pwdRef)); + + CommandAPDU verifySelect = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, removeMFFromPath(pinPath)); + ResponseAPDU r1 = channel.transmit(verifySelect); + + if (r1.getSW() != 0x9000) { + + System.out.println("Error executing Verify Select: " + + Integer.toHexString(r1.getSW())); + } + + CommandAPDU verify = new CommandAPDU((byte) 0x00, (byte) 0x20, + (byte) 0x00, pwdRef[pwdRef.length - 1], new byte[] { + (byte) 0x36, (byte) 0x35, (byte) 0x38, (byte) 0x30, + (byte) 0x36, (byte) 0x36, (byte) 0x00, (byte) 0x00 }); + ResponseAPDU r2 = channel.transmit(verify); + + if (r2.getSW() != 0x9000) { + + System.err.println("Error executing Verify: " + + Integer.toHexString(r2.getSW())); + } + + // **** SIGN **** + + CommandAPDU selectKeyPath = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, removeMFFromPath(efKey)); + ResponseAPDU r3 = channel.transmit(selectKeyPath); + + if (r3.getSW() != 0x9000) { + + System.err.println("Error executing select keypath: " + + Integer.toHexString(r3.getSW())); + } + + // MSE RESTORE + CommandAPDU mseRestore = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0xF3, (byte) 0x00); + ResponseAPDU r4 = channel.transmit(mseRestore); + + if (r4.getSW() != 0x9000) { + + System.err.println("Error executing restore mse: " + + Integer.toHexString(r4.getSW())); + } + + // MSE SET + + byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x12, + (byte) 0x81, (byte) 0x02, efKey[efKey.length - 2], + efKey[efKey.length - 1] }; + + CommandAPDU mseSet = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0x41, (byte) 0xB6, dst); + ResponseAPDU r5 = channel.transmit(mseSet); + + if (r5.getSW() != 0x9000) { + + System.err.println("Error executing set mse: " + + Integer.toHexString(r5.getSW())); + } + + // SIGN + + byte[] hash = new byte[] { (byte) 0x00, (byte) 0x01, (byte) 0x02, + (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, + (byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A, + (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, + (byte) 0x0F, (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13 }; + + CommandAPDU sign = new CommandAPDU((byte) 0x00, (byte) 0x2A, + (byte) 0x9E, (byte) 0x9A, hash); + ResponseAPDU r6 = channel.transmit(sign); + + if (r6.getSW() != 0x9000) { + + System.err.println("Error executing sign: " + + Integer.toHexString(r6.getSW())); + } + + System.out.println("Signature value: " + SMCCHelper.toString(r6.getData())); + + } + + @Override + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINGUI pinGUI, String alg) throws SignatureCardException, + InterruptedException, IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI) + throws SignatureCardException, InterruptedException { + // TODO Auto-generated method stub + return null; + } + + @Override + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) + throws SignatureCardException, InterruptedException { + // TODO Auto-generated method stub + return null; + } + + private int computeLengthFromByteArray(byte[] input) { + + int result = 0; + + for (int i = 0; i < input.length; i++) { + + int current = input[input.length - 1 - i]; + + result = result + (int) (current * Math.pow(256, i)); + } + + return result; + + } + + private void toFile(byte[] data, String filename) { + + try { + FileOutputStream fos = new FileOutputStream(filename); + + fos.write(data); + + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + private byte[] removeMFFromPath(byte[] path) { + + byte[] result = null; + + if (path[0] == 0x3F && path[1] == 0x00) { + + result = new byte[path.length - 2]; + System.arraycopy(path, 2, result, 0, path.length - 2); + } else { + + result = path; + } + + return result; + } + +} diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.der new file mode 100644 index 00000000..678534ec Binary files /dev/null and b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.der differ diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.der new file mode 100644 index 00000000..40518375 Binary files /dev/null and b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.der differ diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.der new file mode 100644 index 00000000..5dda6cc8 Binary files /dev/null and b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.der differ diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.der new file mode 100644 index 00000000..d48df411 Binary files /dev/null and b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.der differ -- cgit v1.2.3