From f1dcaf9d6d71998706535fb780b8e7ada15b652e Mon Sep 17 00:00:00 2001 From: tzefferer Date: Thu, 10 Mar 2011 13:41:13 +0000 Subject: Support for Lithuanian eID and Swedish eID git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@924 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../at/gv/egiz/smcc/CIOCertificateDirectory.java | 3 +- .../java/at/gv/egiz/smcc/LtEIDCIOCertificate.java | 79 ++++ .../gv/egiz/smcc/LtEIDCIOCertificateDirectory.java | 83 ++++ .../src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java | 53 +++ .../java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java | 46 ++ smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java | 526 +++++++++++++++++++++ .../java/at/gv/egiz/smcc/LtEIDObjectDirectory.java | 29 ++ .../main/java/at/gv/egiz/smcc/SEIdentityCard.java | 319 +++++++++++++ .../java/at/gv/egiz/smcc/SignatureCardFactory.java | 184 ++++--- .../main/java/at/gv/egiz/smcc/T0CardChannel.java | 9 +- .../java/at/gv/egiz/smcc/cio/ObjectDirectory.java | 17 +- .../resources/at/gv/egiz/smcc/LtEIDCard.properties | 1 + .../at/gv/egiz/smcc/SEIdentityCard.properties | 1 + 13 files changed, 1272 insertions(+), 78 deletions(-) create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/LtEIDCard.properties create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/SEIdentityCard.properties 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 122c4e7d..40471e4e 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java @@ -20,7 +20,6 @@ package at.gv.egiz.smcc; import at.gv.egiz.smcc.cio.CIOCertificate; import at.gv.egiz.smcc.util.ISO7816Utils; import at.gv.egiz.smcc.util.TLVSequence; -import iaik.me.asn1.ASN1; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -73,7 +72,9 @@ 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); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java new file mode 100644 index 00000000..9a8e5a06 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java @@ -0,0 +1,79 @@ +/* +* Copyright 2009 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 iaik.me.security.BigInteger; + +import java.io.IOException; +import java.util.Arrays; + +import at.gv.egiz.smcc.cio.CIOCertificate; + +public class LtEIDCIOCertificate extends CIOCertificate { + + // The Lithuanian eID card stores both certificates in one file. + // For each certificate, EF.CD contains an offset and a length that may be used + // to extract the certificates from the file. + private BigInteger offset; + private byte[] length; + + public LtEIDCIOCertificate(byte[] cio) throws IOException { + + super(cio); + + ASN1 x509Certificate = new ASN1(cio); + + //read CONTEXTSPECIFIC manually + byte[] ctxSpecific = x509Certificate.getElementAt(x509Certificate.getSize()-1).getEncoded(); + if ((ctxSpecific[0] & 0xff) == 0xa1) { + int ll = ((ctxSpecific[1] & 0xf0) == 0x80) + ? (ctxSpecific[1] & 0x0f) + 2 : 2; + ASN1 x509CertificateAttributes = new ASN1(Arrays.copyOfRange(ctxSpecific, ll, ctxSpecific.length)); + + offset = x509CertificateAttributes.getElementAt(0).getElementAt(1).gvBigInteger(); + + // first byte indicates number of relevant bytes in array + byte[] lengthValue = x509CertificateAttributes.getElementAt(0).getElementAt(2).gvByteArray(); + if(lengthValue == null || lengthValue[0] != lengthValue.length-1) { + + throw new IOException("Cannot extract certificate length information. Unexpected format."); + } + + length = new byte[lengthValue[0]]; + System.arraycopy(lengthValue, 1, length, 0, lengthValue[0]); + + } + } + + public BigInteger getOffset() { + return offset; + } + + public void setOffset(BigInteger offset) { + this.offset = offset; + } + + public byte[] getLength() { + return length; + } + + public void setLength(byte[] length) { + this.length = length; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java new file mode 100644 index 00000000..efff6cad --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java @@ -0,0 +1,83 @@ +/* +* Copyright 2009 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 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 LtEIDCIOCertificateDirectory extends CIOCertificateDirectory { + + public LtEIDCIOCertificateDirectory(byte[] fid) { + super(fid); + } + + protected void addCIOCertificate(byte[] cio) throws IOException { + + LtEIDCIOCertificate cioCert = new LtEIDCIOCertificate(cio); + + log.debug("adding {}", cioCert); + cios.add(cioCert); + } + + @Override + protected byte[] executeSelect(CardChannel channel) + throws CardException { + + byte[] finalPath = null; + + if (fid != null && fid.length > 2 && fid[0] == (byte) 0x3F + && fid[1] == (byte) 0x00) { + + // cut off MF identifier + finalPath = new byte[fid.length - 2]; + System.arraycopy(fid, 2, finalPath, 0, fid.length - 2); + } else { + finalPath = fid; + } + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, ISO7816Utils.P2_FCP, finalPath); + + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + + throw new CardException( + "Error selecting File - unexpected response from card: " + + Integer.toHexString(resp.getSW())); + } + + byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCP); + byte[] fd = new TLVSequence(fcx).getValue(0x82); + + return fd; + } + + @Override + protected byte[] doReadTransparentFile(CardChannel channel) throws CardException, SignatureCardException { + + return ISO7816Utils.readTransparentFile(channel, 0xEE); + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java new file mode 100644 index 00000000..cab5a491 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java @@ -0,0 +1,53 @@ +/* +* Copyright 2009 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 iaik.me.security.BigInteger; + +import java.io.IOException; + +public class LtEIDCIOKey { + + private byte[] iD; + private BigInteger keyReference; + + public LtEIDCIOKey(byte[] cio) throws IOException { + + ASN1 asn1 = new ASN1(cio); + + iD = asn1.getElementAt(1).getElementAt(0).gvByteArray(); + keyReference = asn1.getElementAt(1).getElementAt(3).gvBigInteger(); + } + + public byte[] getID() { + return iD; + } + + public void setID(byte[] id) { + iD = id; + } + + public BigInteger getKeyReference() { + return keyReference; + } + + public void setKeyReference(BigInteger keyReference) { + this.keyReference = keyReference; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java new file mode 100644 index 00000000..dfb8e9eb --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java @@ -0,0 +1,46 @@ +/* +* Copyright 2009 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.util.ArrayList; +import java.util.List; + +public class LtEIDCIOKeyDirectory extends LtEIDCIOCertificateDirectory { + + protected List keys; + + public LtEIDCIOKeyDirectory(byte[] fid) { + + super(fid); + keys = new ArrayList(); + } + + protected void addCIOCertificate(byte[] cio) throws IOException { + + LtEIDCIOKey cioKey = new LtEIDCIOKey(cio); + + log.debug("adding {}", cioKey); + keys.add(cioKey); + } + + public List getKeys() { + + return this.keys; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java new file mode 100644 index 00000000..e37f4018 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java @@ -0,0 +1,526 @@ +/* + * Copyright 2009 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.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +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.cio.CIOCertificate; +import at.gv.egiz.smcc.pin.gui.PINGUI; +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + +public class LtEIDCard extends AbstractSignatureCard implements SignatureCard { + + private static final byte[] AID = new byte[] { + + (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x18, + (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x63, + (byte) 0x42, (byte) 0x00 }; + + private static final String CERT_LABEL_IDENTIFIER = "DigitalSignature"; + + private static final PinInfo QS_PIN_SPEC = new PinInfo(8, 8, ".", + "at/gv/egiz/smcc/LtEIDCard", "qs.pin", (byte) 0x81, AID, + PinInfo.UNKNOWN_RETRIES); + + private final Logger log = LoggerFactory.getLogger(LtEIDCard.class); + + @Override + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINGUI pinGUI, String alg) throws SignatureCardException, + InterruptedException, IOException { + + CardChannel channel = getCardChannel(); + + // select AID + try { + selectApplication(channel); + } catch (CardException e) { + + throw new SignatureCardException("Error selecting AID.", e); + } + + try { + // read certificate info to get key id + byte[] signCertAndKeyID = null; + + LtEIDObjectDirectory efod = new LtEIDObjectDirectory(); + + efod.selectAndRead(channel); + + if (efod.getCDReferences() == null + || efod.getCDReferences().size() < 1) { + + throw new SignatureCardException( + "EF.CD not found - cannot get certificate information."); + } + + LtEIDCIOCertificateDirectory efcd = new LtEIDCIOCertificateDirectory( + efod.getCDReferences().get(0)); + efcd.selectAndRead(channel); + + List cioList = efcd.getCIOs(); + + LtEIDCIOCertificate sigCertInfo = null; + for (CIOCertificate cert : cioList) { + + if (cert instanceof LtEIDCIOCertificate + && cert.getLabel().contains(CERT_LABEL_IDENTIFIER)) { + + sigCertInfo = (LtEIDCIOCertificate) cert; + signCertAndKeyID = sigCertInfo.getiD(); + } + } + + // verify PIN + // Note: PIN verify is required prior to read PrKD + // verifyPIN(channel); + + log.debug("Starting real PIN Verification.."); + // TODO: Test real PIN Verification + verifyPINLoop(channel, QS_PIN_SPEC, pinGUI); + + if (efod.getPrKDReferences() == null + || efod.getPrKDReferences().size() < 1) { + + throw new SignatureCardException( + "EF.PrKD not found - cannot get key information."); + } + + List prKDReferences = efod.getPrKDReferences(); + + LtEIDCIOKeyDirectory efprkd = new LtEIDCIOKeyDirectory(efod + .getPrKDReferences().get(0)); + efprkd.selectAndRead(channel); + + LtEIDCIOKey signKey = null; + + for (LtEIDCIOKey key : efprkd.getKeys()) { + + if (signCertAndKeyID != null + && Arrays.equals(key.getID(), signCertAndKeyID)) { + + signKey = key; + } + } + + if (signKey == null) { + + throw new SignatureCardException( + "Unable to determine required key information."); + } + + execMSESet(channel, signKey.getKeyReference().intValue()); + execPSOHash(channel, input); + + return execPSOComputeDigitalSignature(channel); + + } catch (CardException e) { + + throw new SignatureCardException("Error creating signature.", e); + } + } + + @Override + public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI) + throws SignatureCardException, InterruptedException { + + CardChannel channel = getCardChannel(); + + // select AID + try { + selectApplication(channel); + } catch (CardException e) { + + throw new SignatureCardException("Error selecting AID.", e); + } + + LtEIDObjectDirectory efod = new LtEIDObjectDirectory(); + try { + efod.selectAndRead(channel); + + if (efod.getCDReferences() == null + || efod.getCDReferences().size() < 1) { + + throw new SignatureCardException( + "EF.CD not found - cannot get certificate information."); + } + + LtEIDCIOCertificateDirectory efcd = new LtEIDCIOCertificateDirectory( + efod.getCDReferences().get(0)); + efcd.selectAndRead(channel); + + List cioList = efcd.getCIOs(); + + LtEIDCIOCertificate sigCertInfo = null; + for (CIOCertificate cert : cioList) { + + if (cert instanceof LtEIDCIOCertificate + && cert.getLabel().contains(CERT_LABEL_IDENTIFIER)) { + + sigCertInfo = (LtEIDCIOCertificate) cert; + } + } + + if (sigCertInfo == null) { + + throw new SignatureCardException( + "Unable to determine signature certificate."); + } + + if (sigCertInfo.getOffset() == null + || sigCertInfo.getLength() == null + || sigCertInfo.getEfidOrPath() == null) { + + throw new SignatureCardException( + "Unable to retrieve required certificate information."); + } + + // select file with cert + byte[] fci = selectFile(channel, sigCertInfo.getEfidOrPath()); + + byte[] certFile = executeReadBinary(channel, ISO7816Utils + .getLengthFromFCx(fci)); + byte[] sigCert = new byte[toInt(sigCertInfo.getLength())]; + System.arraycopy(certFile, sigCertInfo.getOffset().intValue(), + sigCert, 0, sigCert.length); + + return sigCert; + + } catch (CardException e) { + throw new SignatureCardException( + "Unable to retrieve certificate from card.", e); + } catch (FileNotFoundException e) { + throw new SignatureCardException( + "Unable to retrieve certificate from card.", e); + } catch (IOException e) { + throw new SignatureCardException( + "Unable to retrieve certificate from card.", e); + } + + } + + private void execMSESet(CardChannel channel, int keyReference) + throws CardException { + + // Note: AlgoID (tag 0x80) has to be 0x12 + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0x41, (byte) 0xB6, new byte[] { (byte) 0x80, + (byte) 0x01, (byte) 0x12, (byte) 0x84, (byte) 0x01, + (byte) keyReference }); + + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + + throw new CardException( + "Error executing MSE-SET. Unexpected response: " + + Integer.toHexString(resp.getSW())); + } + } + + private void execPSOHash(CardChannel channel, InputStream input) + throws SignatureCardException { + + 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 + ByteArrayOutputStream data = new ByteArrayOutputStream(); + + try { + byte[] digest = new byte[md.getDigestLength()]; + for (int l; (l = input.read(digest)) != -1;) { + md.update(digest, 0, l); + } + digest = md.digest(); + + data.write(new byte[] { (byte) 0x90, (byte) 0x14 }); + data.write(digest); + + } catch (IOException e) { + throw new SignatureCardException("Error computing hash.", e); + } + + try { + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x2A, + (byte) 0x90, (byte) 0xA0, data.toByteArray()); + + ResponseAPDU resp = channel.transmit(apdu); + + log.debug("Answer to PSO-HASH: " + + Integer.toHexString(resp.getSW())); + + if (resp.getSW() != 0x9000) { + + throw new SignatureCardException( + "Error setting hash. Unexpected answer from card: " + + Integer.toHexString(resp.getSW())); + } + + } catch (CardException e) { + throw new SignatureCardException("Error setting hash.", e); + } + } + + private byte[] execPSOComputeDigitalSignature(CardChannel channel) + throws SignatureCardException { + + // Note: Le is mandatory to ensure correct functionality + CommandAPDU apdu = new CommandAPDU(new byte[] { (byte) 0x00, + (byte) 0x2A, (byte) 0x9E, (byte) 0x9A, (byte) 0x00 }); + + try { + ResponseAPDU resp = channel.transmit(apdu); + + log.debug("Answer to PSO-Compute Digital Signature: " + + Integer.toHexString(resp.getSW())); + + if (resp.getSW() != 0x9000) { + + throw new SignatureCardException( + "Error computing signature. Unexpected answer from card: " + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + + } catch (CardException e) { + throw new SignatureCardException("Error computing signature.", e); + } + + } + + protected void verifyPINLoop(CardChannel channel, PinInfo spec, + PINGUI provider) throws LockedException, NotActivatedException, + SignatureCardException, InterruptedException, CardException { + + int retries = -1; + do { + retries = verifyPIN(channel, spec, provider, retries); + } while (retries >= -1); + } + + protected int verifyPIN(CardChannel channel, PinInfo pinSpec, + PINGUI provider, int retries) throws SignatureCardException, + LockedException, NotActivatedException, InterruptedException, + CardException { + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00, + (byte) 0x20, (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, 16); + + ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec, + retries); + + if (resp.getSW() == 0x9000) { + return -2; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6300: + // incorrect PIN, number of retries not provided + return -1; + case 0x6400: + // ? + throw new TimeoutException(); + 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); + } + + } + + @Override + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) + throws SignatureCardException, InterruptedException { + + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + + } + + private void selectApplication(CardChannel channel) throws CardException { + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x04, (byte) 0x00, AID); + + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + + throw new CardException( + "Error selecting AID - unexpected response from card: " + + Integer.toHexString(resp.getSW())); + } + } + + private byte[] selectFile(CardChannel channel, byte[] path) + throws CardException { + + byte[] finalPath = null; + + if (path != null && path.length > 2 && path[0] == (byte) 0x3F + && path[1] == (byte) 0x00) { + + // cut off MF identifier + finalPath = new byte[path.length - 2]; + System.arraycopy(path, 2, finalPath, 0, path.length - 2); + } else { + finalPath = path; + } + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, finalPath); + + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + + throw new CardException( + "Error selecting File - unexpected response from card: " + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + + } + + private byte[] executeReadBinary(CardChannel channel, int bytes2read) + throws CardException { + + ByteArrayOutputStream bof = new ByteArrayOutputStream(); + + // int bytes2read = (lengthHi * 256) + lengthLo; + int bytesRead = 0; + + boolean done = false; + + int offset = 0; + int len = 0; + + while (!done) { + + if (bytes2read - bytesRead > 0xef) { + len = 0xef; + } else { + len = bytes2read - bytesRead; + } + + byte[] offsetBytes = SMCCHelper.toByteArray(offset); + ResponseAPDU resp = readFromCard(channel, offsetBytes[0], + offsetBytes[1], (byte) len); + + if (resp.getSW1() == (byte) 0x6C) { + + // handle case: card returns 6CXX (wrong number of bytes + // requested) + + resp = readFromCard(channel, offsetBytes[0], offsetBytes[1], + (byte) resp.getSW2()); + + // this has to be the final iteration + done = true; + } + + try { + bof.write(resp.getData()); + } catch (IOException e) { + log.error("Error executing secure read binary.", e); + throw new CardException("Error reading data from card", e); + } + + bytesRead = bytesRead + resp.getData().length; + offset = bytesRead; + + if (bytesRead == bytes2read) { + + done = true; + } + } + + return bof.toByteArray(); + } + + private ResponseAPDU readFromCard(CardChannel channel, byte offsetHi, + byte offsetLo, byte numBytes) throws CardException { + + byte[] apdu = new byte[] { + + (byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes }; + + CommandAPDU command = new CommandAPDU(apdu); + ResponseAPDU resp = channel.transmit(command); + + return resp; + + } + + private int toInt(byte[] array) { + + int len = array.length; + int result = 0; + + for (int i = len - 1; i >= 0; i--) { + + result = (int) (result + array[i] * Math.pow(256, len - i - 1)); + } + + return result; + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java new file mode 100644 index 00000000..624b8f3e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java @@ -0,0 +1,29 @@ +/* +* Copyright 2009 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.cio.ObjectDirectory; + +public class LtEIDObjectDirectory extends ObjectDirectory { + + public LtEIDObjectDirectory() { + + super(new byte[]{(byte)0x50, (byte)0x00, (byte)0x50, (byte)0x31}); + this.setP1((byte)0x08); + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java new file mode 100644 index 00000000..4538ecca --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java @@ -0,0 +1,319 @@ +package at.gv.egiz.smcc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; + +import at.gv.egiz.smcc.pin.gui.PINGUI; +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + +// TODO: This class uses predefined IDs and path to communicate with the Swedish e-ID card. +// Key and certificate IDs / paths should instead be read out from files defined by ISO 7816-15 + +public class SEIdentityCard extends AbstractSignatureCard implements + SignatureCard { + + private static final byte[] SIGDATA_PREFIX = 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) 0x14 }; + + private static final PinInfo PIN_SPEC = new PinInfo(6, 8, ".", + "at/gv/egiz/smcc/SEIdentityCard", "pin", (byte) 0x82, null, + PinInfo.UNKNOWN_RETRIES); + + private final Logger log = LoggerFactory.getLogger(SEIdentityCard.class); + + @Override + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINGUI pinGUI, String alg) throws SignatureCardException, + InterruptedException, IOException { + + log.debug("Trying to create signature.."); + + CardChannel channel = getCardChannel(); + + // SELECT FILE + try { + selectFile(channel, new byte[] { (byte) 0x50, (byte) 0x15, + (byte) 0x50, (byte) 0x16, (byte) 0x4B, (byte) 0x02 }); + } catch (CardException e) { + + throw new SignatureCardException("Error selecting file.", e); + } + + // VERIFY PIN + try { + verifyPINLoop(channel, PIN_SPEC, pinGUI); + } catch (CardException e1) { + + throw new SignatureCardException("Error verifying PIN.", e1); + } + + // SET MSE + setMSE(channel); + + // CREATE SIGNATURE + + 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 + try { + byte[] digest = new byte[md.getDigestLength()]; + for (int l; (l = input.read(digest)) != -1;) { + md.update(digest, 0, l); + } + digest = md.digest(); + + byte[] sigData = new byte[SIGDATA_PREFIX.length + digest.length]; + System.arraycopy(SIGDATA_PREFIX, 0, sigData, 0, SIGDATA_PREFIX.length); + System.arraycopy(digest, 0, sigData, SIGDATA_PREFIX.length, digest.length); + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x2A, + (byte) 0x9E, (byte) 0x9A, sigData); + + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + + throw new SignatureCardException("Error creating signature: " + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + + + } catch (IOException e) { + throw new SignatureCardException("Error creating signature.", e); + } catch (CardException e) { + throw new SignatureCardException("Error creating signature.", e); + } + } + + @Override + public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI) + throws SignatureCardException, InterruptedException { + + log.debug("Trying to fetch certificate.."); + + CardChannel channel = getCardChannel(); + + byte[] fci = null; + + try { + fci = selectFile(channel, new byte[] { (byte) 0x50, (byte) 0x15, + (byte) 0x50, (byte) 0x16, (byte) 0x43, (byte) 0x32 }); + } catch (CardException e) { + + throw new SignatureCardException("Error selecting card file.", e); + } + + if (fci == null) { + throw new SignatureCardException( + "Could not retireve FCI for certificate file."); + } + + byte[] cert = null; + + try { + cert = executeReadBinary(channel, ISO7816Utils + .getLengthFromFCx(fci)); + } catch (CardException e) { + throw new SignatureCardException( + "Error reading certificate from card.", e); + } + + return cert; + } + + @Override + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) + throws SignatureCardException, InterruptedException { + + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + private void setMSE(CardChannel channel) throws SignatureCardException { + + byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x02, + (byte) 0x81, (byte) 0x02, (byte) 0x4B, (byte) 0x02 }; + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0x41, (byte) 0xB6, dst); + + try { + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Error setting DST: " + + Integer.toHexString(resp.getSW())); + } + + } catch (CardException e) { + + throw new SignatureCardException("Error setting DST.", e); + } + + } + + private byte[] selectFile(CardChannel channel, byte[] fid) + throws CardException, SignatureCardException { + + CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4, + (byte) 0x08, (byte) 0x00, fid); + + ResponseAPDU resp = channel.transmit(apdu); + + if (resp.getSW() != 0x9000) { + + throw new SignatureCardException("Unexpected result from card: " + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + } + + private byte[] executeReadBinary(CardChannel channel, int bytes2read) + throws CardException { + + ByteArrayOutputStream bof = new ByteArrayOutputStream(); + + int bytesRead = 0; + + boolean done = false; + + int offset = 0; + int len = 0; + + while (!done) { + + if (bytes2read - bytesRead > 0xef) { + len = 0xef; + } else { + len = bytes2read - bytesRead; + } + + byte[] offsetBytes = SMCCHelper.toByteArray(offset); + ResponseAPDU resp = readFromCard(channel, offsetBytes[0], + offsetBytes[1], (byte) len); + + if (resp.getSW1() == (byte) 0x6C) { + + // handle case: card returns 6CXX (wrong number of bytes + // requested) + + resp = readFromCard(channel, offsetBytes[0], offsetBytes[1], + (byte) resp.getSW2()); + + // this has to be the final iteration + done = true; + } + + try { + bof.write(resp.getData()); + } catch (IOException e) { + log.error("Error executing secure read binary.", e); + throw new CardException("Error reading data from card", e); + } + + bytesRead = bytesRead + resp.getData().length; + offset = bytesRead; + + if (bytesRead == bytes2read) { + + done = true; + } + } + + return bof.toByteArray(); + } + + private ResponseAPDU readFromCard(CardChannel channel, byte offsetHi, + byte offsetLo, byte numBytes) throws CardException { + + byte[] apdu = new byte[] { + + (byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes }; + + CommandAPDU command = new CommandAPDU(apdu); + ResponseAPDU resp = channel.transmit(command); + + return resp; + + } + + protected void verifyPINLoop(CardChannel channel, PinInfo spec, + PINGUI provider) throws LockedException, NotActivatedException, + SignatureCardException, InterruptedException, CardException { + + int retries = -1; + do { + retries = verifyPIN(channel, spec, provider, retries); + } while (retries >= -1); + } + + protected int verifyPIN(CardChannel channel, PinInfo pinSpec, + PINGUI provider, int retries) throws SignatureCardException, + LockedException, NotActivatedException, InterruptedException, + CardException { + + 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, provider, pinSpec, + retries); + + if (resp.getSW() == 0x9000) { + return -2; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6300: + // incorrect PIN, number of retries not provided + return -1; + case 0x6400: + // ? + throw new TimeoutException(); + 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); + } + + } + +} 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 e189267c..156aca23 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -151,32 +151,33 @@ public class SignatureCardFactory { } /** - * Returns true if the historical bytes of the given ATR contain the historical bytes pattern of this - * SupportedCard object. + * Returns true if the historical bytes of the given ATR contain the + * historical bytes pattern of this SupportedCard object. * * @param atr * the ATR * - * @return true if the historical bytes of the given ATR contain the historical bytes pattern - * of this SupportedCard object, or false - * otherwise. + * @return true if the historical bytes of the given ATR + * contain the historical bytes pattern of this SupportedCard + * object, or false otherwise. */ public boolean matchesHistoricalBytesPattern(ATR atr) { - + byte[] historicalBytes = atr.getHistoricalBytes(); - if (historicalBytes == null || - this.historicalBytesPattern == null || - this.historicalBytesPattern.length > historicalBytes.length) { - + if (historicalBytes == null + || this.historicalBytesPattern == null + || this.historicalBytesPattern.length > historicalBytes.length) { + return false; } - + int[] failure = computeFailure(this.historicalBytesPattern); int j = 0; for (int i = 0; i < historicalBytes.length; i++) { - while (j > 0 && this.historicalBytesPattern[j] != historicalBytes[i]) { + while (j > 0 + && this.historicalBytesPattern[j] != historicalBytes[i]) { j = failure[j - 1]; } if (this.historicalBytesPattern[j] == historicalBytes[i]) { @@ -426,7 +427,7 @@ public class SignatureCardFactory { (byte) 0x69, (byte) 0x6E, (byte) 0x45, (byte) 0x49, (byte) 0x44 }, // historical bytes pattern - new byte[] {'F','i','n','E','I','D'}, + new byte[] { 'F', 'i', 'n', 'E', 'I', 'D' }, // 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, @@ -435,6 +436,44 @@ public class SignatureCardFactory { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }, "at.gv.egiz.smcc.FINEIDCard")); + // LT eID + supportedCards.add(new SupportedCard( + // ATR [3b:7D:94:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00] + new byte[] { (byte) 0x3b, (byte) 0x7D, (byte) 0x94, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x31, + (byte) 0x80, (byte) 0x65, (byte) 0xB0, (byte) 0x83, + (byte) 0x11, (byte) 0xC0, (byte) 0xA9, (byte) 0x83, + (byte) 0x00, (byte) 0x90, (byte) 0x00 }, + // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: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) 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.LtEIDCard")); + + // SE eID + supportedCards.add(new SupportedCard( + // ATR + // [3B:9F:94:80:1F:C3:00:68:10:44:05:01:46:49:53:45:31:C8:07:90:00:18] + new byte[] { (byte) 0x3b, (byte) 0x9F, (byte) 0x90, + (byte) 0x80, (byte) 0x1F, (byte) 0xC0, (byte) 0x00, + (byte) 0x68, (byte) 0x00, (byte) 0x00, (byte) 0x05, + (byte) 0x00, (byte) 0x46, (byte) 0x49, (byte) 0x53, + (byte) 0x45, (byte) 0x31, (byte) 0xC8, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00 }, + // mask + // (ff:ff:f0:ff:ff:f0:ff:ff:00:00:ff:f0:ff:ff:ff:ff:ff:ff:F0:00:00:00) + new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xf0, + (byte) 0xff, (byte) 0xff, (byte) 0xf0, (byte) 0xff, + (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0xff, + (byte) 0xf0, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf0, + (byte) 0x00, (byte) 0x00, (byte) 0x00 }, + "at.gv.egiz.smcc.SEIdentityCard")); + // ITCards supportedCards.add(new SupportedCard( // ATR = @@ -493,8 +532,8 @@ public class SignatureCardFactory { (byte) 0xff, 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', ' ', '1', '.', '0' }, // historical bytes pattern - new byte[] {'E', 's', 't', 'E', 'I', 'D', ' ', 'v', - 'e', 'r', ' ', '1', '.', '0'}, + new byte[] { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', + ' ', '1', '.', '0' }, // mask // (ff:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff) new byte[] { (byte) 0xff, (byte) 0x00, (byte) 0x00, @@ -513,8 +552,8 @@ public class SignatureCardFactory { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', ' ', '1', '.', '0', (byte) 0x2b }, // historical bytes pattern - new byte[] {'E', 's', 't', 'E', 'I', 'D', ' ', 'v', - 'e', 'r', ' ', '1', '.', '0'}, + new byte[] { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', + ' ', '1', '.', '0' }, // mask // (ff:ff:ff:ff:ff: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, @@ -534,8 +573,8 @@ public class SignatureCardFactory { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', ' ', '1', '.', '0', (byte) 0x43 }, // historical bytes pattern - new byte[] {'E', 's', 't', 'E', 'I', 'D', ' ', 'v', - 'e', 'r', ' ', '1', '.', '0'}, + new byte[] { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', + ' ', '1', '.', '0' }, // mask // (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00) new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, @@ -572,8 +611,8 @@ public class SignatureCardFactory { (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b, 'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n', (byte) 0x89 }, - // historical bytes pattern - new byte[]{'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n'}, + // historical bytes pattern + new byte[] { 'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n' }, // mask new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, @@ -647,44 +686,55 @@ public class SignatureCardFactory { SupportedCard supportedCard = cards.next(); if (supportedCard.matches(atr)) { - return instantiateSignatureCard(cardTerminal, card, supportedCard); + return instantiateSignatureCard(cardTerminal, card, + supportedCard); } } - - // if no matching implementation has been found yet, check for pattern match in historical bytes - log.trace("No card matching complete ATR found - checking candidates with historical bytes matches."); + + // if no matching implementation has been found yet, check for pattern + // match in historical bytes + log + .trace("No card matching complete ATR found - checking candidates with historical bytes matches."); Iterator cardsIterator = supportedCards.iterator(); List historicalBytesCandidates = new ArrayList(); while (cardsIterator.hasNext()) { SupportedCard supportedCard = cardsIterator.next(); - - if(supportedCard.matchesHistoricalBytesPattern(atr)) { - + + if (supportedCard.matchesHistoricalBytesPattern(atr)) { + historicalBytesCandidates.add(supportedCard); } - } + } historicalBytesCandidates = reduceCandidateList(historicalBytesCandidates); - if(historicalBytesCandidates.size() != 1) { - - log.warn("Found {} cards with matching historical bytes pattern.", historicalBytesCandidates.size()); + if (historicalBytesCandidates.size() != 1) { + + log.warn("Found {} cards with matching historical bytes pattern.", + historicalBytesCandidates.size()); } else { - - log.trace("Instantiating class " + historicalBytesCandidates.get(0).getImplementationClassName() + " according to historical bytes pattern match."); - return instantiateSignatureCard(cardTerminal, card, historicalBytesCandidates.get(0)); + + log.trace("Instantiating class " + + historicalBytesCandidates.get(0) + .getImplementationClassName() + + " according to historical bytes pattern match."); + return instantiateSignatureCard(cardTerminal, card, + historicalBytesCandidates.get(0)); } - + throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); } - private SignatureCard instantiateSignatureCard(CardTerminal cardTerminal, Card card, SupportedCard supportedCard) throws CardNotSupportedException { - + private SignatureCard instantiateSignatureCard(CardTerminal cardTerminal, + Card card, SupportedCard supportedCard) + throws CardNotSupportedException { + ClassLoader cl = SignatureCardFactory.class.getClassLoader(); SignatureCard sc; try { - Class scClass = cl.loadClass(supportedCard.getImplementationClassName()); + Class scClass = cl.loadClass(supportedCard + .getImplementationClassName()); sc = (SignatureCard) scClass.newInstance(); sc = ExclSignatureCardProxy.newInstance(sc); @@ -694,55 +744,45 @@ public class SignatureCardFactory { return sc; } catch (ClassNotFoundException e) { - log.warn( - "Cannot find signature card implementation class.", - e); + log.warn("Cannot find signature card implementation class.", e); throw new CardNotSupportedException( - "Cannot find signature card implementation class.", - e); + "Cannot find signature card implementation class.", e); } catch (InstantiationException e) { - log - .warn( - "Failed to instantiate signature card implementation.", - e); + log.warn("Failed to instantiate signature card implementation.", e); throw new CardNotSupportedException( - "Failed to instantiate signature card implementation.", - e); + "Failed to instantiate signature card implementation.", e); } catch (IllegalAccessException e) { - log - .warn( - "Failed to instantiate signature card implementation.", - e); + log.warn("Failed to instantiate signature card implementation.", e); throw new CardNotSupportedException( - "Failed to instantiate signature card implementation.", - e); - } + "Failed to instantiate signature card implementation.", e); + } } - - private List reduceCandidateList(List candidates) { - + + private List reduceCandidateList( + List candidates) { + List result = new ArrayList(); - - for(SupportedCard current : candidates) { - + + for (SupportedCard current : candidates) { + String implName = current.getImplementationClassName(); boolean alreadyPresent = false; - - for(SupportedCard card : result) { - - if(card.getImplementationClassName().equals(implName)) { - + + for (SupportedCard card : result) { + + if (card.getImplementationClassName().equals(implName)) { + alreadyPresent = true; } } - - if(!alreadyPresent) { + + if (!alreadyPresent) { result.add(current); } - } + } return result; } - + public static String toString(byte[] b) { StringBuffer sb = new StringBuffer(); if (b != null && b.length > 0) { 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 65bcd84b..5ec0b4b0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java @@ -8,6 +8,8 @@ import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; +import at.gv.egiz.smcc.util.SMCCHelper; + public class T0CardChannel extends LogCardChannel { public T0CardChannel(CardChannel channel) { @@ -22,7 +24,7 @@ public class T0CardChannel extends LogCardChannel { if(resp.getSW1() == (byte)0x61) { - byte[] data = executeGetResponse(channel, (byte)resp.getSW2()); + byte[] data = executeGetResponse((byte)resp.getSW2()); byte[] result = new byte[data.length + 2]; System.arraycopy(data, 0, result, 0, data.length); @@ -38,7 +40,7 @@ public class T0CardChannel extends LogCardChannel { } } - private byte[] executeGetResponse(CardChannel channel, byte sw2) + private byte[] executeGetResponse(byte sw2) throws CardException { boolean done = false; @@ -48,7 +50,8 @@ 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); +// ResponseAPDU resp = channel.transmit(command); + ResponseAPDU resp = super.transmit(command); try { bof.write(resp.getData()); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java index 3ab954ee..d1bd6144 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java @@ -56,7 +56,7 @@ public class ObjectDirectory { private List CD_refs; private Integer padding; - private int P1 = 0x02; + private int P1 = 0x02; public ObjectDirectory() { fid = new byte[] { (byte) 0x50, (byte) 0x31 }; @@ -99,6 +99,7 @@ public class ObjectDirectory { byte[] efod = ISO7816Utils.readTransparentFile(channel, -1); + PrKD_refs = new ArrayList(); PuKD_refs = new ArrayList(); AOD_refs = new ArrayList(); @@ -107,7 +108,7 @@ public class ObjectDirectory { for (TLV cio : new TLVSequence(efod)) { int tag = cio.getTag(); - //TODO FIN EID: check if unknown tag and tag length > array + //TODO FIN EID: check if unknown tag and tag length > array if (padding != null && tag == padding) { // reached padding - quit record extraction break; @@ -205,4 +206,16 @@ public class ObjectDirectory { public List getCDReferences() { return CD_refs; } + + public int getP1() { + return P1; + } + + public void setP1(int p1) { + P1 = p1; + } + + + + } diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/LtEIDCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/LtEIDCard.properties new file mode 100644 index 00000000..52fe8292 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/LtEIDCard.properties @@ -0,0 +1 @@ +qs.pin.name=User PIN \ No newline at end of file diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/SEIdentityCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/SEIdentityCard.properties new file mode 100644 index 00000000..70994cc7 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/SEIdentityCard.properties @@ -0,0 +1 @@ +pin.name=PIN \ No newline at end of file -- cgit v1.2.3