From dc1fbf4259cd7f997f782b3fdac37015564ab96f Mon Sep 17 00:00:00 2001 From: tzefferer Date: Tue, 16 Nov 2010 17:28:00 +0000 Subject: Refactoring of DNIe card integration git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@836 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../at/gv/egiz/smcc/CIOCertificateDirectory.java | 87 +- smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java | 475 ------ .../at/gv/egiz/smcc/DNIeCardSecureChannel.java | 1648 -------------------- .../java/at/gv/egiz/smcc/SignatureCardFactory.java | 1040 ++++++------ .../java/at/gv/egiz/smcc/util/ISO7816Utils.java | 831 +++++----- .../main/java/at/gv/egiz/smcc/util/SMCCHelper.java | 38 + .../egiz/smcc/util/TransparentFileInputStream.java | 379 ++--- .../resources/at/gv/egiz/smcc/DNIeCard.properties | 3 - smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java | 194 ++- 9 files changed, 1447 insertions(+), 3248 deletions(-) delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java delete mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties (limited to 'smcc') 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 fd7746e6..33dd99bb 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java @@ -37,6 +37,8 @@ import org.slf4j.LoggerFactory; */ public class CIOCertificateDirectory { + protected static final boolean RETRIEVE_AUTH_ID_FROM_ASN1 = Boolean.TRUE; + protected static final Logger log = LoggerFactory.getLogger(CIOCertificateDirectory.class); protected byte[] fid; protected List cios; @@ -64,47 +66,69 @@ public class CIOCertificateDirectory { byte[] fd = new TLVSequence(fcx).getValue(0x82); if ((fd[0] & 0x04) > 0) { - for (int r = 1; r < fd[fd.length - 1]; r++) { - log.trace("read CIO record {}", r); - byte[] record = ISO7816Utils.readRecord(channel, r); - log.trace("{} bytes", record.length); - addCIOCertificate(record); - } + + readCIOCertificatesFromRecords(channel, fd); + } else if ((fd[0] & 0x05) == 0x01) { - byte[] ef = ISO7816Utils.readTransparentFile(channel, -1); - - 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)); + readCIOCertificatesFromTransparentFile(channel); + } + } - j = i + 2 + ll + length; - addCIOCertificate(Arrays.copyOfRange(ef, i, j)); - i = j; - } while (i < ef.length && ef[i] > 0); + protected void readCIOCertificatesFromRecords(CardChannel channel, byte[] fd) throws CardException, SignatureCardException, IOException { + + for (int r = 1; r < fd[fd.length - 1]; r++) { + log.trace("read CIO record {}", r); + byte[] record = ISO7816Utils.readRecord(channel, r); + log.trace("{} bytes", record.length); + addCIOCertificate(record); } } + + protected byte[] doReadTransparentFile(CardChannel channel) throws CardException, SignatureCardException { + + return ISO7816Utils.readTransparentFile(channel, -1); + } + + protected void readCIOCertificatesFromTransparentFile(CardChannel channel) throws CardException, SignatureCardException, IOException { + +// byte[] ef = ISO7816Utils.readTransparentFile(channel, -1); + 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; + addCIOCertificate(Arrays.copyOfRange(ef, i, j)); + i = j; + } while (i < ef.length && ef[i] > 0); + + } + protected void addCIOCertificate(byte[] cio) throws IOException { ASN1 x509Certificate = new ASN1(cio); CIOCertificate cioCert = new CIOCertificate(); cioCert.setLabel(x509Certificate.getElementAt(0).getElementAt(0).gvString()); - cioCert.setAuthId(x509Certificate.getElementAt(0).getElementAt(2).gvByteArray()); + if(retrieveAuthIdFromASN1()) { + cioCert.setAuthId(x509Certificate.getElementAt(0).getElementAt(2).gvByteArray()); + } cioCert.setiD(x509Certificate.getElementAt(1).getElementAt(0).gvByteArray()); //read CONTEXTSPECIFIC manually @@ -129,4 +153,9 @@ public class CIOCertificateDirectory { public List getCIOs() { return cios; } + + protected boolean retrieveAuthIdFromASN1() { + + return RETRIEVE_AUTH_ID_FROM_ASN1; + } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java deleted file mode 100644 index ccb463a5..00000000 --- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java +++ /dev/null @@ -1,475 +0,0 @@ -/* -* 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 java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -import javax.smartcardio.CardChannel; -import javax.smartcardio.CardException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import at.gv.egiz.smcc.pin.gui.PINGUI; - -public class DNIeCard extends AbstractSignatureCard implements SignatureCard { - - private final Logger log = LoggerFactory.getLogger(DNIeCard.class); - - private 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, - (byte) 0x65 }; - - private final String SIG_KEY_NAME = "KprivFirmaDigital"; - private final String SIG_CERT_NAME = "CertFirmaDigital"; - - protected PinInfo pinInfo = new PinInfo(8, 16, - "[0-9A-Za-z_<>!()?%\\-=&+\\.]", "at/gv/egiz/smcc/DNIeCard", - "sig.pin", (byte) 0x00, new byte[] {}, PinInfo.UNKNOWN_RETRIES); - - DNIeCardSecureChannel secureChannel; - - public DNIeCard() { - this.secureChannel = new DNIeCardSecureChannel(); - - } - - @Override - public byte[] createSignature(InputStream input, KeyboxName keyboxName, - PINGUI pinGUI, String alg) throws SignatureCardException, - InterruptedException, IOException { - - CardChannel channel = getCardChannel(); - - if (!secureChannel.isEstablished()) - try { - secureChannel.establish(channel); - } catch (CardException e) { - - log.debug("Error establishing secure channel to card.", e); - } - - try { - - byte[] prKdf = executeReadPrKDF(channel); - byte[] keyId = getKeyIdFromASN1File(prKdf); - - verifyPINLoop(channel, pinInfo, pinGUI); - - secureChannel.executeSecureManageSecurityEnvironment(channel, keyId); - - 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(); - - return secureChannel.executeSecureCreateSignature(channel, digest); - - } catch (CardException e) { - - throw new SignatureCardException( - "Error creating signature with DNIe card.", e); - } - - } - - @Override - public byte[] getCertificate(KeyboxName keyboxName, PINGUI provider) - throws SignatureCardException, InterruptedException { - - byte[] result = null; - - CardChannel channel = getCardChannel(); - - if (!secureChannel.isEstablished()) { - try { - secureChannel.establish(channel); - } catch (CardException e) { - - log.debug("Error establishing secure channel to card.", e); - } - } - - byte[] certId = null; - - try { - // read CDF file - byte[] cdf = executeReadCDF(channel); - - // extract certificate id from ASN1 data structure - certId = getCertIdFromASN1File(cdf); - - } catch (CardException e1) { - - log.error("Error reading ASN.1 data!"); - e1.printStackTrace(); - } - - log.debug("Try to read certificate.."); - try { - - verifyPINLoop(channel, pinInfo, provider); - - // select master file - executeSecureSelectMasterFile(channel); - - // select 6081 - byte[] apdu = new byte[] { - - (byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02, - (byte) 0x60, (byte) 0x81 }; - - secureChannel.executeSecureSelect(channel, apdu); - - // select cert id - byte[] apdu2 = new byte[] { - - (byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02, - certId[certId.length-2], certId[certId.length-1] }; - - byte[] fci = secureChannel.executeSecureSelect(channel, apdu2); - - byte sizeHi = fci[7]; - byte sizeLo = fci[8]; - - byte[] data = secureChannel.executeSecureReadBinary(channel, - sizeHi, sizeLo); - - byte[] compressedWithoutHeader = new byte[data.length - 8]; - System.arraycopy(data, 8, compressedWithoutHeader, 0, - compressedWithoutHeader.length); - - result = decompressData(compressedWithoutHeader); - - } catch (CardException e) { - - throw new SignatureCardException("Error getting certificate.", e); - } - - return result; - } - - @Override - public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) - throws SignatureCardException, InterruptedException { - - log.debug("Attempting to read infobox from DNIe.."); - - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); - - } - - 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 > 0); - } - - protected int verifyPIN(CardChannel channel, PinInfo pinSpec, - PINGUI provider, int retries) throws SignatureCardException, - LockedException, NotActivatedException, InterruptedException, - CardException { - - char[] pin = provider.providePIN(pinSpec, retries); - - byte[] apdu = new byte[5 + pin.length]; - apdu[0] = (byte) 0x00; - apdu[1] = (byte) 0x20; - apdu[2] = (byte) 0x00; - apdu[3] = (byte) 0x00; - apdu[4] = (byte) pin.length; - - for (int i = 0; i < pin.length; i++) { - - apdu[i + 5] = (byte) pin[i]; - } - - int result = secureChannel.executeSecurePINVerify(channel, apdu); - - if (result == 0x9000) { - return -1; - } - if (result >> 4 == 0x63c) { - return 0x0f & result; - } - - switch (result) { - case 0x6983: - // authentication method blocked - throw new LockedException(); - - default: - String msg = "VERIFY failed. SW=" + Integer.toHexString(result); - log.info(msg); - throw new SignatureCardException(msg); - } - } - - private void executeSecureSelectMasterFile(CardChannel channel) - throws CardException { - - byte[] apdu = new byte[MASTER_FILE_ID.length + 5]; - apdu[0] = (byte) 0x00; - apdu[1] = (byte) 0xA4; - apdu[2] = (byte) 0x04; - apdu[3] = (byte) 0x00; - apdu[4] = (byte) MASTER_FILE_ID.length; - System.arraycopy(MASTER_FILE_ID, 0, apdu, 5, MASTER_FILE_ID.length); - - secureChannel.executeSecureSelect(channel, apdu); - } - - private byte[] executeReadCDF(CardChannel channel) throws CardException { - - return executeReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x04}); - } - - private byte[] executeReadPrKDF(CardChannel channel) throws CardException { - - return executeReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x01}); - } - - private byte[] getKeyIdFromASN1File(byte[] file) throws CardException { - - // split file in two records - int record1Length = getRecordLength(file, 1); - - byte[] record1 = new byte[record1Length]; - byte[] record2 = new byte[file.length - record1.length]; - - System.arraycopy(file, 0, record1, 0, record1.length); - System.arraycopy(file, record1.length, record2, 0, record2.length); - - byte[] keyId = new byte[2]; - - try { - ASN1 asn1_1 = new ASN1(record1); - ASN1 asn1_2 = new ASN1(record2); - - if(asn1_1.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_KEY_NAME)) { - - byte[] data = asn1_1.getElementAt(2).gvByteArray(); - - keyId[0] = data[9]; - keyId[1] = data[10]; - } - - else if(asn1_2.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_KEY_NAME)) { - - byte[] data = asn1_2.getElementAt(2).gvByteArray(); - - keyId[0] = data[9]; - keyId[1] = data[10]; - } - - } catch (Exception e) { - - throw new CardException("Error getting ASN1 data.", e); - } - - return keyId; - } - - private byte[] getCertIdFromASN1File(byte[] file) throws CardException { - - int record1Length = getRecordLength(file, 1); - - // split file in two records - byte[] record1 = new byte[record1Length]; - byte[] record2 = new byte[file.length - record1.length]; - - System.arraycopy(file, 0, record1, 0, record1.length); - System.arraycopy(file, record1.length, record2, 0, record2.length); - - byte[] certId = null; - - try { - ASN1 asn1_1 = new ASN1(record1); - ASN1 asn1_2 = new ASN1(record2); - - if(asn1_1.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_CERT_NAME)) { - - certId = retrieveCertId(asn1_1.getElementAt(2).gvByteArray()); - } - - if(asn1_2.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_CERT_NAME)) { - - certId = retrieveCertId(asn1_2.getElementAt(2).gvByteArray()); - } - - } catch (Exception e) { - - throw new CardException("Error getting ASN1 data.", e); - } - - return certId; - } - - private byte[] retrieveCertId(byte[] data) throws CardException { - - ASN1 contextSpecific = getASN1WithinContextSpecific(data); - try { - return contextSpecific.getElementAt(0).getElementAt(0).gvByteArray(); - } catch (IOException e) { - throw new CardException("Error retrieving certificate ID from ASN1 data.", e); - } - } - - private ASN1 getASN1WithinContextSpecific(byte[] data) throws CardException { - - byte first = data[0]; - byte lengthOfLength = 0; - - if(first < 0) { - - lengthOfLength = (byte)(first & (byte)0x7F); - lengthOfLength = (byte)(lengthOfLength +1); - } else { - - lengthOfLength = 1; - } - - byte[] asn1data = new byte[data.length - lengthOfLength]; - System.arraycopy(data, lengthOfLength, asn1data, 0, asn1data.length); - - try { - return new ASN1(asn1data); - } catch (IOException e) { - throw new CardException("Error getting ASN1 structure.", e); - } - } - - private int getRecordLength(byte[] data, int startOfLength) { - - byte lengthStartByte = data[startOfLength]; - - if(lengthStartByte < 0) { - // we have more than one length byte - byte lengthOfLength = (byte)(lengthStartByte & (byte)0x7F); - - byte[] lengthValues = new byte[lengthOfLength]; - System.arraycopy(data, startOfLength+1, lengthValues, 0, lengthOfLength); - - int result = 0; - - for(int i=0; i= 2 - && response[response.length - 2] == (byte) 0x90 - && response[response.length - 1] == (byte) 0x00) { - - log.debug("OK"); - } else { - - log.error("FAILED"); - throw new CardException("Unable to select file on card."); - } - - byte[] fci = new byte[response.length - 2]; - System.arraycopy(response, 0, fci, 0, response.length - 2); - - return fci; - - } - - public void executeSecureManageSecurityEnvironment(CardChannel channel, byte[] id) - throws CardException { - - log.debug("Manage Security Environment.."); - - byte[] apdu = new byte[7 + 2]; - apdu[0] = (byte) 0x00; - apdu[1] = (byte) 0x22; - apdu[2] = (byte) 0x41; - apdu[3] = (byte) 0xB6; - apdu[4] = (byte) (2 + 2); - apdu[5] = (byte) 0x84; // Tag - apdu[6] = (byte) 0x02; // Length - apdu[7] = id[0]; // ID - apdu[8] = id[1]; // ID - - byte[] securedAPDU = getSecuredAPDU(apdu); - - CommandAPDU command = new CommandAPDU(securedAPDU); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() == 0x9000) { - - byte[] response = resp.getData(); - - byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(response); - - if (decryptedResponse.length == 2 - && decryptedResponse[0] == (byte) 0x90 - && decryptedResponse[1] == (byte) 0x00) { - - log.debug("OK"); - } else { - - log.debug("FAILED"); - throw new CardException( - "Execution of command Manage Security Environment failed: " - + formatByteArray(decryptedResponse)); - } - - } - } - - public byte[] executeSecureCreateSignature(CardChannel channel, byte[] data) - throws CardException { - - log.debug("Compute electronic signature on card.."); - - byte[] apdu = new byte[5 + HASH_PADDING.length + data.length]; - apdu[0] = (byte) 0x00; - apdu[1] = (byte) 0x2A; - apdu[2] = (byte) 0x9E; - apdu[3] = (byte) 0x9A; - apdu[4] = (byte) (HASH_PADDING.length + data.length); - - System.arraycopy(HASH_PADDING, 0, apdu, 5, HASH_PADDING.length); - System.arraycopy(data, 0, apdu, 5 + HASH_PADDING.length, data.length); - - byte[] securedAPDU = getSecuredAPDU(apdu); - - CommandAPDU command = new CommandAPDU(securedAPDU); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() != 0x9000) { - - throw new CardException("Unexpected response from card: " - + Integer.toHexString(resp.getSW())); - } - - byte[] signatureValue = resp.getData(); - byte[] decryptedSignatureValueWithSW = verifyAndDecryptSecuredResponseAPDU(signatureValue); - - int len = decryptedSignatureValueWithSW.length; - if (decryptedSignatureValueWithSW[len - 2] == (byte) 0x90 - && decryptedSignatureValueWithSW[len - 1] == (byte) 0x00) { - - log.debug("OK"); - - byte[] sigVal = new byte[decryptedSignatureValueWithSW.length - 2]; - System.arraycopy(decryptedSignatureValueWithSW, 0, sigVal, 0, - decryptedSignatureValueWithSW.length - 2); - - log.debug("Computed signature value: " + formatByteArray(sigVal)); - return sigVal; - - } else { - - log.debug("FAILED"); - throw new CardException( - "Error creating signature on card: " - + Integer - .toHexString(decryptedSignatureValueWithSW[len - 2]) - + " " - + Integer - .toHexString(decryptedSignatureValueWithSW[len - 1])); - } - - } - - private byte[] getSecuredAPDU(byte[] apdu) throws CardException { - - // TODO: Handle APDU format: CLA INS P1 P2 Lc Data Le (not required by this implementation) - - if (apdu == null || apdu.length < 4) { - - throw new CardException("Invalid APDU to secure."); - } - - if (apdu.length < 6) { - - // TODO: Handle APDU format: CLA INS P1 P2 (not required by this implementation) - - if (apdu.length == 5) { - - // handle case CLA INS P1 P2 LE - - byte encCLA = (byte) (apdu[0] | (byte) 0x0C); - byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2], - apdu[3] }; - byte[] paddedHeader = applyPadding(BLOCK_LENGTH, encHeader); - - byte[] leField = new byte[3]; - leField[0] = (byte) 0x97; - leField[1] = (byte) 0x01; - leField[2] = apdu[4]; - - byte[] macData = new byte[paddedHeader.length + leField.length]; - System.arraycopy(paddedHeader, 0, macData, 0, - paddedHeader.length); - System.arraycopy(leField, 0, macData, paddedHeader.length, - leField.length); - - byte[] paddedMacData = applyPadding(BLOCK_LENGTH, macData); - - incrementSSC(); - - byte[] mac = calculateAPDUMAC(paddedMacData, kMac, this.ssc); - - byte[] encapsulatedMac = new byte[mac.length + 2]; - encapsulatedMac[0] = (byte) 0x8E; - encapsulatedMac[1] = (byte) mac.length; - System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length); - - byte[] completeMessage = new byte[5 + leField.length - + encapsulatedMac.length]; - completeMessage[0] = encCLA; - completeMessage[1] = apdu[1]; - completeMessage[2] = apdu[2]; - completeMessage[3] = apdu[3]; - completeMessage[4] = (byte) (encapsulatedMac.length + leField.length); - System - .arraycopy(leField, 0, completeMessage, 5, - leField.length); - System.arraycopy(encapsulatedMac, 0, completeMessage, - 5 + leField.length, encapsulatedMac.length); - - return completeMessage; - } - } - - // case data field available (assuming that Le field is missing) - - byte cla = apdu[0]; - byte ins = apdu[1]; - byte p1 = apdu[2]; - byte p2 = apdu[3]; - byte lc = apdu[4]; - - byte[] data = new byte[lc]; - System.arraycopy(apdu, 5, data, 0, lc); - - byte[] paddedData = applyPadding(BLOCK_LENGTH, data); - - byte[] encrypted = null; - - try { - - encrypted = perform3DESCipherOperation(paddedData, kEnc, - Cipher.ENCRYPT_MODE); - - } catch (Exception e) { - - throw new CardException("Error encrypting APDU.", e); - } - - byte[] encapsulated = new byte[encrypted.length + 3]; - encapsulated[0] = (byte) 0x87; - encapsulated[1] = (byte) (encrypted.length + 1); - encapsulated[2] = (byte) 0x01; - System.arraycopy(encrypted, 0, encapsulated, 3, encrypted.length); - - // calculate MAC - byte encCLA = (byte) (cla | (byte) 0x0C); - byte[] encHeader = new byte[] { encCLA, ins, p1, p2 }; - byte[] paddedHeader = applyPadding(BLOCK_LENGTH, encHeader); - - byte[] headerAndData = new byte[paddedHeader.length - + encapsulated.length]; - System - .arraycopy(paddedHeader, 0, headerAndData, 0, - paddedHeader.length); - System.arraycopy(encapsulated, 0, headerAndData, paddedHeader.length, - encapsulated.length); - - byte[] paddedHeaderAndData = applyPadding(BLOCK_LENGTH, headerAndData); - - incrementSSC(); - - byte[] mac = calculateAPDUMAC(paddedHeaderAndData, kMac, this.ssc); - - byte[] encapsulatedMac = new byte[mac.length + 2]; - encapsulatedMac[0] = (byte) 0x8E; - encapsulatedMac[1] = (byte) mac.length; - System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length); - - byte[] completeMessage = new byte[5 + encapsulated.length - + encapsulatedMac.length]; - completeMessage[0] = encCLA; - completeMessage[1] = ins; - completeMessage[2] = p1; - completeMessage[3] = p2; - completeMessage[4] = (byte) (encapsulated.length + encapsulatedMac.length); - System.arraycopy(encapsulated, 0, completeMessage, 5, - encapsulated.length); - System.arraycopy(encapsulatedMac, 0, completeMessage, - 5 + encapsulated.length, encapsulatedMac.length); - - return completeMessage; - } - - private byte[] verifyAndDecryptSecuredResponseAPDU(byte[] securedAPDU) - throws CardException { - - byte[] data = new byte[securedAPDU.length - 10]; - byte[] commandResponse = new byte[4]; - byte[] obtainedMac = new byte[4]; - - System.arraycopy(securedAPDU, 0, data, 0, data.length); - System.arraycopy(securedAPDU, data.length, commandResponse, 0, - commandResponse.length); - System.arraycopy(securedAPDU, data.length + commandResponse.length + 2, - obtainedMac, 0, obtainedMac.length); - - byte[] macData = new byte[data.length + commandResponse.length]; - System.arraycopy(data, 0, macData, 0, data.length); - System.arraycopy(commandResponse, 0, macData, data.length, - commandResponse.length); - - byte[] paddedMacData = applyPadding(BLOCK_LENGTH, macData); - - incrementSSC(); - - byte[] mac = calculateAPDUMAC(paddedMacData, this.kMac, this.ssc); - - if (!Arrays.equals(mac, obtainedMac)) { - - throw new CardException("Unable to verify MAC of Response APDU."); - } - - if (data.length > 0) { - - byte[] data2decrypt = new byte[data.length - - getCutOffLength(data, BLOCK_LENGTH)]; - System.arraycopy(data, getCutOffLength(data, BLOCK_LENGTH), - data2decrypt, 0, data2decrypt.length); - - byte[] plainData = null; - - try { - plainData = perform3DESCipherOperation(data2decrypt, this.kEnc, - Cipher.DECRYPT_MODE); - } catch (Exception e) { - throw new CardException("Unable to decrypt data.", e); - } - - byte[] unpaddedData = removePadding(plainData); - - byte[] result = new byte[unpaddedData.length + 2]; - System.arraycopy(unpaddedData, 0, result, 0, unpaddedData.length); - result[result.length - 2] = commandResponse[2]; - result[result.length - 1] = commandResponse[3]; - - return result; - } else { - - // no data in response - byte[] result = new byte[2]; - result[result.length - 2] = commandResponse[2]; - result[result.length - 1] = commandResponse[3]; - return result; - } - } - - private byte[] perform3DESCipherOperation(byte[] data, byte[] keyData, - int mode) throws NoSuchAlgorithmException, NoSuchProviderException, - NoSuchPaddingException, InvalidKeyException, - InvalidAlgorithmParameterException, ShortBufferException, - IllegalBlockSizeException, BadPaddingException { - - byte[] full3DESKey = new byte[24]; - System.arraycopy(keyData, 0, full3DESKey, 0, 16); - System.arraycopy(keyData, 0, full3DESKey, 16, 8); - - SecretKeySpec key = new SecretKeySpec(full3DESKey, "DESede"); - IvParameterSpec ivSpec = new IvParameterSpec(IV); - - Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); - - cipher.init(mode, key, ivSpec); - byte[] cipherText = new byte[cipher.getOutputSize(data.length)]; - int ctLength = cipher.update(data, 0, data.length, cipherText, 0); - ctLength += cipher.doFinal(cipherText, ctLength); - return cipherText; - - } - - private byte[] calculateAPDUMAC(byte[] data, byte[] key, byte[] ssc) - throws CardException { - - SecretKeySpec desSingleKey = new SecretKeySpec(key, 0, BLOCK_LENGTH, - "DES"); - Cipher singleDesCipher; - try { - singleDesCipher = Cipher.getInstance("DES/CBC/NoPadding"); - } catch (Exception e) { - - throw new CardException("Error creating DES cipher instance.", e); - } - - // Calculate the first n - 1 block. - IvParameterSpec ivSpec; - ivSpec = new IvParameterSpec(IV); - int dataLen = data.length; - - try { - singleDesCipher.init(Cipher.ENCRYPT_MODE, desSingleKey, ivSpec); - } catch (Exception e) { - throw new CardException("Error initializing DES cipher.", e); - } - byte[] result; - try { - result = singleDesCipher.doFinal(ssc); - } catch (Exception e) { - throw new CardException("Error applying DES cipher.", e); - } - - byte[] dataBlock = new byte[BLOCK_LENGTH]; - - for (int i = 0; i < dataLen - BLOCK_LENGTH; i = i + BLOCK_LENGTH) { - - System.arraycopy(data, i, dataBlock, 0, BLOCK_LENGTH); - byte[] input = xorByteArrays(result, dataBlock); - - try { - result = singleDesCipher.doFinal(input); - } catch (Exception e) { - throw new CardException("Error applying DES cipher.", e); - } - } - - // calculate the last block with 3DES - byte[] fullKey = new byte[24]; - System.arraycopy(key, 0, fullKey, 0, 16); - System.arraycopy(key, 0, fullKey, 16, 8); - - SecretKeySpec desKey = new SecretKeySpec(fullKey, "DESede"); - Cipher cipher; - try { - cipher = Cipher.getInstance("DESede/CBC/NoPadding"); - } catch (Exception e) { - throw new CardException("Error getting 3DES cipher instance.", e); - } - - ivSpec = new IvParameterSpec(IV); - try { - cipher.init(Cipher.ENCRYPT_MODE, desKey, ivSpec); - } catch (Exception e) { - throw new CardException("Error initializing 3DES cipher.", e); - } - - System.arraycopy(data, data.length - BLOCK_LENGTH, dataBlock, 0, - BLOCK_LENGTH); - byte[] input = xorByteArrays(result, dataBlock); - - byte[] mac = new byte[4]; - - try { - - result = cipher.doFinal(input); - - } catch (Exception e) { - throw new CardException("Error applying 3DES cipher.", e); - } - - System.arraycopy(result, 0, mac, 0, 4); - return mac; - } - - private byte[] executeSendTerminalChallenge(CardChannel channel, - byte[] challenge) throws CardException { - - // send challenge to card - CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x88, - (byte) 0x00, (byte) 0x00, challenge); - ResponseAPDU resp = channel.transmit(command); - - byte[] data = null; - - if (resp.getSW() == 0x9000) { - - data = resp.getData(); - - } else { - - throw new CardException("Invalid response to terminal challenge: " - + Integer.toHexString(resp.getSW())); - } - - return data; - } - - private byte[] readFromCard(CardChannel channel, byte offsetHi, - byte offsetLo, byte numBytes) throws CardException { - - byte[] apdu = new byte[] { - - (byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes }; - - byte[] securedAPDU = getSecuredAPDU(apdu); - - CommandAPDU command = new CommandAPDU(securedAPDU); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() != 0x9000) { - - throw new CardException("Unexpected reponse from card: " - + Integer.toHexString(resp.getSW())); - } - - byte[] data = resp.getData(); - - byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(data); - - return decryptedResponse; - - } - - public int executeSecurePINVerify(CardChannel channel, byte[] apdu) - throws CardException { - - byte[] securedAPDU = getSecuredAPDU(apdu); - - log.debug("Verifiying PIN.."); - - CommandAPDU command = new CommandAPDU(securedAPDU); - - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() == 0x9000) { - - byte[] securedResponseData = resp.getData(); - - byte[] plainData = verifyAndDecryptSecuredResponseAPDU(securedResponseData); - - if (plainData.length == 2) { - - return getSWAsInt(plainData); - } else { - - throw new CardException( - "Unexpected response to verify PIN APDU: " - + formatByteArray(plainData)); - } - } else { - - throw new CardException("Unexpected response to verify PIN APDU: " - + Integer.toHexString(resp.getSW())); - } - } - - public byte[] executeSecureReadBinary(CardChannel channel, byte lengthHi, - byte lengthLo) throws CardException { - - log.debug("Executing secure read binary.."); - - ByteArrayOutputStream bof = new ByteArrayOutputStream(); - - int bytes2read = (lengthHi * 256) + lengthLo; - int bytesRead = 0; - - boolean done = false; - boolean forceExit = false; - - int offset = 0; - int len = 0; - - while (!done) { - - if (bytes2read - bytesRead > 0xef) { - len = 0xef; - } else { - len = bytes2read - bytesRead; - } - - byte[] offsetBytes = intToHex(offset); - byte[] decryptedResponse = readFromCard(channel, offsetBytes[0], - offsetBytes[1], (byte) len); - - if (decryptedResponse.length == 2 - && decryptedResponse[0] == (byte) 0x6C) { - - // handle case: card returns 6CXX (wrong number of bytes - // requested) - // This happens sometimes with the DNIe in the final iteration - - decryptedResponse = readFromCard(channel, offsetBytes[0], - offsetBytes[1], decryptedResponse[1]); - - forceExit = true; - } - - byte[] decryptedData = new byte[decryptedResponse.length - 2]; - System.arraycopy(decryptedResponse, 0, decryptedData, 0, - decryptedResponse.length - 2); - - try { - bof.write(decryptedData); - } catch (IOException e) { - throw new CardException("Error reading data from card", e); - } - - bytesRead = bytesRead + decryptedData.length; - offset = bytesRead; - - if (bytesRead == bytes2read) { - - done = true; - } - - if (forceExit) { - - break; - } - } - - return bof.toByteArray(); - } - - private byte[] executeRequestCardChallenge(CardChannel channel) - throws CardException { - - CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x84, - (byte) 0x00, (byte) 0x00, (byte) BLOCK_LENGTH); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() != 0x9000) { - - throw new CardException( - "Invalid response from card upon challenge request: " - + Integer.toHexString(resp.getSW())); - } - - return resp.getData(); - - } - - private void performExternalAuthentication(CardChannel channel) - throws CardException { - - log.debug("Starting external authentication.."); - - byte[] cardChallenge = executeRequestCardChallenge(channel); - - this.rndIcc = cardChallenge; - - byte[] prnd2 = getRandomBytes(this.prndLength); - byte[] kIfd = getRandomBytes(32); - - // compute hash - byte[] hashData = new byte[prnd2.length + kIfd.length - + cardChallenge.length + BLOCK_LENGTH]; - - System.arraycopy(prnd2, 0, hashData, 0, prnd2.length); - System.arraycopy(kIfd, 0, hashData, prnd2.length, kIfd.length); - System.arraycopy(cardChallenge, 0, hashData, - prnd2.length + kIfd.length, cardChallenge.length); - - int snPadding = BLOCK_LENGTH - snIcc.length; - - for (int i = 0; i < snPadding; i++) { - - hashData[prnd2.length + kIfd.length + cardChallenge.length + i] = (byte) 0x00; - } - - System.arraycopy(snIcc, 0, hashData, prnd2.length + kIfd.length - + cardChallenge.length + snPadding, snIcc.length); - - byte[] digest = computeSHA1Hash(hashData); - - // prepare data to be encrypted - byte[] plain = new byte[2 + prnd2.length + kIfd.length + digest.length]; - - plain[0] = (byte) 0x6A; - - System.arraycopy(prnd2, 0, plain, 1, prnd2.length); - System.arraycopy(kIfd, 0, plain, 1 + prnd2.length, kIfd.length); - System.arraycopy(digest, 0, plain, 1 + prnd2.length + kIfd.length, - digest.length); - - plain[plain.length - 1] = (byte) 0xBC; - - // encrypt plain data - RSAPrivateKey terminalPrivateKey = createRSAPrivateKey(TERMINAL_MODULO, - TERMINAL_PRIVEXP); - - byte[] encResult = null; - try { - encResult = rsaEncrypt(terminalPrivateKey, plain); - } catch (Exception e) { - e.printStackTrace(); - throw new CardException("Error encrypting authentication data.", e); - } - - // apply MIN function - BigInteger sig = createUnsignedBigInteger(encResult); - BigInteger mod = new BigInteger(TERMINAL_MODULO, 16); - - BigInteger diff = mod.subtract(sig); - BigInteger sigMin = diff.min(sig); - - // encrypt with card public key - PublicKey cardPubKey = null; - - X509Certificate cert = createCertificate(componentCert); - cardPubKey = cert.getPublicKey(); - - byte[] authData = null; - try { - authData = rsaEncrypt(cardPubKey, sigMin.toByteArray()); - } catch (Exception e) { - e.printStackTrace(); - throw new CardException("Error encrypting authentication data."); - } - - log.debug("Sending computed cryptogram to card.."); - // send auth data to card - // BE CAREFUL WITH THAT! EXT-AUTH METHOD MAY GET BLOCKED! - if (executeExternalAuthenticate(channel, authData)) { - - this.kifd = kIfd; - log.debug("External authentication succeeded."); - } else { - log.error("External authentication failed."); - throw new CardException("External Authentication failed."); - } - - } - - private boolean executeExternalAuthenticate(CardChannel channel, - byte[] authData) throws CardException { - - CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x82, - (byte) 0x00, (byte) 0x00, authData); - ResponseAPDU resp = channel.transmit(command); - - return resp.getSW() == 0x9000; - } - - private void performInternalAuthentication(CardChannel channel) - throws CardException { - - log.debug("Starting internal authentication.."); - - byte[] randomBytes = getRandomBytes(BLOCK_LENGTH); - byte[] challengeData = new byte[randomBytes.length - + TERMINAL_CHALLENGE_TAIL.length]; - - this.rndIfd = randomBytes; - - System.arraycopy(randomBytes, 0, challengeData, 0, randomBytes.length); - System.arraycopy(TERMINAL_CHALLENGE_TAIL, 0, challengeData, - randomBytes.length, TERMINAL_CHALLENGE_TAIL.length); - - byte[] data = executeSendTerminalChallenge(channel, challengeData); - - // verify response - boolean ok = verifyCardResponse(data); - - log.debug("Internal Authentiction succeeded: " + ok); - - if (!ok) { - - log.debug("Internal Authentiction failed - cancel."); - throw new CardException("Internal authentication failed"); - } - - } - - private void verifyCertificates() throws CardException { - - // This method verifies the card's component and intermediate - // certificates cryptographically. - - RSAPublicKey rootPubKey = createRSAPublicKey(ROOT_CA_MODULO, - ROOT_CA_PUBEXP); - - X509Certificate intermediate = createCertificate(intermediateCert); - X509Certificate component = createCertificate(componentCert); - - try { - component.verify(intermediate.getPublicKey()); - intermediate.verify(rootPubKey); - } catch (Exception e) { - - throw new CardException("Certificate verification failed.", e); - } - } - - private boolean verifyCardResponse(byte[] resp) throws CardException { - - log.debug("Verifying card response.."); - - byte[] challenge = this.rndIfd; - byte[] response = resp; - - // decrypt response with terminal private key - byte[] plain = null; - RSAPrivateKey terminalPrivateKey = createRSAPrivateKey(TERMINAL_MODULO, - TERMINAL_PRIVEXP); - try { - plain = rsaDecrypt(terminalPrivateKey, response); - } catch (Exception e) { - throw new CardException("Error decrypting card response.", e); - } - - PublicKey pubKey = null; - - X509Certificate cert = createCertificate(componentCert); - pubKey = cert.getPublicKey(); - - byte[] sig = null; - - try { - sig = rsaDecrypt(pubKey, plain); - - } catch (Exception e) { - - throw new CardException( - "Error decrypting card response with card's public key", e); - } - - if (sig == null) { - - throw new CardException("Invalid decryption result: null."); - } else { - - if (sig[0] == (byte) 0x6A && sig[sig.length - 1] == (byte) 0xBC) { - - // Obtained response from card was obviously SIG - nothing else - // to do here so far - - } else { - - // Obtained response from card was probably N.ICC-SIG - - // compute N.ICC-SIG and decrypt result again - - RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey; - BigInteger mod = rsaPubKey.getModulus(); - BigInteger sigVal = createUnsignedBigInteger(plain); - - BigInteger substractionResult = mod.subtract(sigVal); - byte[] encrypted = substractionResult.toByteArray(); - - // necessary if substraction result contains leading - // zero byte - byte[] trimmed = new byte[128]; - System.arraycopy(encrypted, encrypted.length - 128, trimmed, 0, - 128); - - try { - sig = rsaDecrypt(pubKey, trimmed); - - } catch (Exception e) { - - throw new CardException("Error decrypting card response.", - e); - } - } - } - - // extract data from decrypted response - byte[] hash = new byte[20]; - byte[] kIcc = new byte[32]; - byte[] prnd1 = new byte[sig.length - 2 - 20 - 32]; - - this.prndLength = prnd1.length; - - System.arraycopy(sig, 1, prnd1, 0, prnd1.length); // 1 byte offset due - // to 6A padding - System.arraycopy(sig, prnd1.length + 1, kIcc, 0, kIcc.length); - System.arraycopy(sig, prnd1.length + kIcc.length + 1, hash, 0, - hash.length); - - // verify hash - byte[] hashData = new byte[prnd1.length + kIcc.length - + challenge.length + TERMINAL_CHALLENGE_TAIL.length]; - - System.arraycopy(prnd1, 0, hashData, 0, prnd1.length); - System.arraycopy(kIcc, 0, hashData, prnd1.length, kIcc.length); - System.arraycopy(challenge, 0, hashData, prnd1.length + kIcc.length, - challenge.length); - System.arraycopy(TERMINAL_CHALLENGE_TAIL, 0, hashData, prnd1.length - + kIcc.length + challenge.length, - TERMINAL_CHALLENGE_TAIL.length); - - byte[] digest = computeSHA1Hash(hashData); - - boolean internalAuthResult = Arrays.equals(hash, digest); - - if (internalAuthResult) { - - // if verification succeeded, remember kicc for subsequent channel - // key derivation - this.kicc = kIcc; - } - - return internalAuthResult; - - } - - private byte[] computeSHA1Hash(byte[] data) throws CardException { - - try { - MessageDigest sha = MessageDigest.getInstance("SHA"); - - sha.update(data); - return sha.digest(); - - } catch (NoSuchAlgorithmException e) { - throw new CardException("Error computing SHA1 hash.", e); - } - - } - - private byte[] executeGetChipInfo(CardChannel channel) throws CardException { - - // get chip info - read out card serial number - log.debug("Getting chip info.."); - CommandAPDU command = new CommandAPDU(APDU_GET_CHIP_INFO); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() == 0x9000) { - log.debug("OK."); - } else { - log.debug("FAILED: " + Integer.toHexString(resp.getSW())); - } - - return resp.getData(); - } - - private byte[] executeSelect(CardChannel channel, byte[] id) - throws CardException { - - log.debug("Selecting DF or EF.."); - - byte[] apdu = new byte[APDU_SELECT_EF_DF_HEADER.length + 1 + id.length]; - System.arraycopy(APDU_SELECT_EF_DF_HEADER, 0, apdu, 0, - APDU_SELECT_EF_DF_HEADER.length); - apdu[APDU_SELECT_EF_DF_HEADER.length] = (byte) id.length; - System.arraycopy(id, 0, apdu, APDU_SELECT_EF_DF_HEADER.length + 1, - id.length); - - CommandAPDU command = new CommandAPDU(apdu); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() != 0x9000) { - - throw new CardException("Unexpected response to Select Command: " - + Integer.toHexString(resp.getSW())); - } - - return resp.getData(); - - } - - private byte[] executeReadSecureChannelCertificate(CardChannel channel, - byte[] certId) throws CardException { - - log.debug("Reading certificate.."); - - byte[] fci = executeSelect(channel, certId); - - byte certLenHigh; - byte certLenLow; - - if (fci != null && fci.length >= 7) { - - certLenHigh = fci[7]; - certLenLow = fci[8]; - } else { - - throw new CardException("Invalid FCI obtained from card."); - } - - ByteArrayOutputStream bof = new ByteArrayOutputStream(); - - int bytes2read = (certLenHigh * 256) + certLenLow; - int bytesRead = 0; - - boolean done = false; - int offset = 0; - int len = 0; - - while (!done) { - - if (bytes2read - bytesRead > 255) { - len = 255; - } else { - len = bytes2read - bytesRead; - } - - byte[] offsetBytes = intToHex(offset); - - byte[] apdu = new byte[5]; - System.arraycopy(APDU_READ_BINARY, 0, apdu, 0, - APDU_READ_BINARY.length); - apdu[2] = offsetBytes[0]; - apdu[3] = offsetBytes[1]; - apdu[4] = (byte) len; - - CommandAPDU command = new CommandAPDU(apdu); - ResponseAPDU resp = channel.transmit(command); - - byte[] certData = resp.getData(); - - try { - bof.write(certData); - } catch (IOException e) { - throw new CardException("Error reading certificate from card", - e); - } - - bytesRead = bytesRead + certData.length; - offset = bytesRead; - - if (bytesRead == bytes2read) { - - done = true; - } - } - - log.debug("OK."); - - return bof.toByteArray(); - } - - private void executeManageSecurityEnvironment(CardChannel channel, byte p1, - byte p2, byte[] data) throws CardException { - - // MSE - CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x22, p1, p2, - data); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() != 0x9000) { - - throw new CardException( - "Unexpected response from card during preparation of secure channel credentials: " - + Integer.toHexString(resp.getSW())); - } - } - - private void executePerformSecurityOperation(CardChannel channel, - byte[] data) throws CardException { - - // PSO - load intermediate certificate - CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x2A, - (byte) 0x00, (byte) 0xAE, data); - ResponseAPDU resp = channel.transmit(command); - - if (resp.getSW() != 0x9000) { - - throw new CardException( - "Unexpected response from card during preparation of secure channel credentials: " - + Integer.toHexString(resp.getSW())); - } - - } - - private void loadTerminalCertsAndSelectKeys(CardChannel channel) - throws CardException { - - log - .debug("Loading terminal certificates and selecting appropriate keys to establish secure channel.."); - - // MSE - executeManageSecurityEnvironment(channel, (byte) 0x81, (byte) 0xB6, - APDU_DATA_MSE_LOAD_TERMINAL_CERTS); - - // PSO - load intermediate certificate - executePerformSecurityOperation(channel, C_CV_CA); - - // MSE - executeManageSecurityEnvironment(channel, (byte) 0x81, (byte) 0xB6, CHR); - - // PSO - load terminal certificate - executePerformSecurityOperation(channel, C_CV_IFD); - - // MSE - select keys - executeManageSecurityEnvironment(channel, (byte) 0xC1, (byte) 0xA4, - KEY_SELECTOR); - - log.debug("OK."); - - } - - private void calculateChannelKeys() throws CardException { - - log.debug("Generating channel keys.."); - - if (this.kicc == null || this.kifd == null) { - - throw new CardException( - "Required data for deriving keys not available."); - } - - if (this.kicc.length != this.kifd.length) { - - throw new CardException( - "Required data for deriving keys is invalid."); - } - - byte[] kifdicc = new byte[this.kicc.length]; - - for (int i = 0; i < kifdicc.length; i++) { - - kifdicc[i] = (byte) (this.kicc[i] ^ this.kifd[i]); - } - - byte[] kEncHashData = new byte[kifdicc.length - + KENC_COMPUTATION_TAIL.length]; - byte[] kMacHashData = new byte[kifdicc.length - + KMAC_COMPUTATION_TAIL.length]; - - System.arraycopy(kifdicc, 0, kEncHashData, 0, kifdicc.length); - System.arraycopy(kifdicc, 0, kMacHashData, 0, kifdicc.length); - - System.arraycopy(KENC_COMPUTATION_TAIL, 0, kEncHashData, - kifdicc.length, KENC_COMPUTATION_TAIL.length); - System.arraycopy(KMAC_COMPUTATION_TAIL, 0, kMacHashData, - kifdicc.length, KMAC_COMPUTATION_TAIL.length); - - byte[] hashEnc = computeSHA1Hash(kEncHashData); - byte[] hashMac = computeSHA1Hash(kMacHashData); - - this.kEnc = Arrays.copyOfRange(hashEnc, 0, 16); - this.kMac = Arrays.copyOfRange(hashMac, 0, 16); - - // compute sequence counter SSC - if (this.rndIcc == null || this.rndIfd == null - || this.rndIcc.length < 4 || this.rndIfd.length < 4) { - - throw new CardException("Data required to compute SSC not valid."); - } - - this.ssc = new byte[BLOCK_LENGTH]; - - System.arraycopy(this.rndIcc, this.rndIcc.length - 4, this.ssc, 0, 4); - System.arraycopy(this.rndIfd, this.rndIfd.length - 4, this.ssc, 4, 4); - - log.debug("OK."); - - } - - private String formatByteArray(byte[] data) { - - StringBuffer buf = new StringBuffer(); - - for (int i = 0; i < data.length; i++) { - - String s = Integer.toHexString(data[i]); - - if (s.length() == 1) { - s = "0" + s; - } - - if (s.length() > 2) { - s = s.substring(s.length() - 2); - } - - buf.append(s + " "); - } - - return buf.toString(); - } - - private byte[] intToHex(int val) throws CardException { - - String hexString = Integer.toHexString(val); - - if (hexString.length() > 4) { - throw new CardException( - "Unexpected input length to inToHex() utility method: " - + hexString.length()); - } - - byte high = 0x00; - byte low = 0x00; - - if (hexString.length() <= 2) { - - low = (byte) Integer.parseInt(hexString, 16); - } else { - - low = (byte) Integer.parseInt(hexString.substring(hexString - .length() - 2), 16); - high = (byte) Integer.parseInt(hexString.substring(0, hexString - .length() - 2), 16); - } - - return new byte[] { high, low }; - } - - private byte[] getRandomBytes(int length) { - - byte[] result = new byte[length]; - - for (int i = 0; i < length; i++) { - - Random rand = new Random(); - byte current = (byte) rand.nextInt(255); - result[i] = current; - } - - return result; - } - - private BigInteger createUnsignedBigInteger(byte[] data) { - - byte[] unsigned = new byte[data.length+1]; - unsigned[0] = (byte)0x00; - System.arraycopy(data, 0, unsigned, 1, data.length); - - return new BigInteger(unsigned); - - } - - private RSAPrivateKey createRSAPrivateKey(String mod, String privExponent) - throws CardException { - - BigInteger modulus = new BigInteger(mod, 16); - BigInteger privExp = new BigInteger(privExponent, 16); - - KeyFactory fac; - RSAPrivateKey key; - try { - fac = KeyFactory.getInstance("RSA"); - KeySpec spec = new RSAPrivateKeySpec(modulus, privExp); - key = (RSAPrivateKey) fac.generatePrivate(spec); - } catch (Exception e) { - - throw new CardException("Unable to create private key.", e); - } - - return key; - } - - private RSAPublicKey createRSAPublicKey(String mod, String pubExponent) - throws CardException { - - BigInteger modulus = new BigInteger(mod, 16); - BigInteger pubExp = new BigInteger(pubExponent, 16); - - KeyFactory fac; - RSAPublicKey key; - try { - fac = KeyFactory.getInstance("RSA"); - KeySpec spec = new RSAPublicKeySpec(modulus, pubExp); - key = (RSAPublicKey) fac.generatePublic(spec); - } catch (Exception e) { - - throw new CardException("Unable to create public key.", e); - } - - return key; - } - - private byte[] rsaEncrypt(Key key, byte[] data) - throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - - Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding"); - rsa.init(Cipher.ENCRYPT_MODE, key); - byte[] encrypted = rsa.doFinal(data); - - return encrypted; - - } - - private byte[] rsaDecrypt(Key key, byte[] cipher) - throws NoSuchAlgorithmException, NoSuchPaddingException, - InvalidKeyException, IllegalBlockSizeException, BadPaddingException { - - Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding"); - rsa.init(Cipher.DECRYPT_MODE, key); - byte[] decrypted = rsa.doFinal(cipher); - - return decrypted; - } - - private X509Certificate createCertificate(byte[] certData) - throws CardException { - - try { - InputStream inStream = new ByteArrayInputStream(certData); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - X509Certificate certificate = (X509Certificate) cf - .generateCertificate(inStream); - inStream.close(); - - return certificate; - - } catch (Exception e) { - - throw new CardException("Unable to create certificate.", e); - } - } - - private byte[] applyPadding(int blockSize, byte[] data) { - - // add mandatory 0x80 - byte[] extended = new byte[data.length + 1]; - System.arraycopy(data, 0, extended, 0, data.length); - extended[extended.length - 1] = (byte) 0x80; - - if (extended.length % blockSize == 0) { - - return extended; - } - - int requiredBlocks = ((int) (extended.length / blockSize) + 1); - - byte[] result = new byte[requiredBlocks * blockSize]; - Arrays.fill(result, (byte) 0x00); - System.arraycopy(extended, 0, result, 0, extended.length); - - return result; - - } - - private void incrementSSC() { - - BigInteger ssc = new BigInteger(this.ssc); - ssc = ssc.add(new BigInteger("1", 10)); - this.ssc = ssc.toByteArray(); - } - - private byte[] xorByteArrays(byte[] array1, byte[] array2) - throws CardException { - - if (array1 == null || array2 == null || array1.length != array2.length) { - - throw new CardException("Cannot xor byte arrays - invalid input."); - } - - byte[] result = new byte[array1.length]; - - for (int i = 0; i < array1.length; i++) { - - result[i] = (byte) (array1[i] ^ array2[i]); - } - - return result; - } - - private int getCutOffLength(byte[] data, int blockSize) - throws CardException { - - int len = data.length % blockSize; - - // verify - if (data[len - 1] == (byte) 0x01) { - - return len; - } else { - throw new CardException( - "Unable to reconstruct encrypted datablock."); - } - - } - - private byte[] removePadding(byte[] paddedData) throws CardException { - - for (int i = paddedData.length - 1; i >= 0; i--) { - - byte current = paddedData[i]; - - if (current == (byte) 0x00) { - - continue; - } - - if (current == (byte) 0x80) { - - // end of padding reached - byte[] data = new byte[i]; - System.arraycopy(paddedData, 0, data, 0, i); - return data; - - } else { - - throw new CardException("Wrong padding."); - } - - } - - throw new CardException( - "Error removing padding from data. Unexpected data format."); - - } - - public boolean isEstablished() { - return established; - } - - private int getSWAsInt(byte[] sw) throws CardException { - - if (sw.length != 2) { - - throw new CardException( - "Cannot transform SW to innteger - invalid input length."); - } - - int sw1 = (int) sw[0] < 0 ? (int) sw[0] + 256 : (int) sw[0]; - int sw2 = (int) sw[1] < 0 ? (int) sw[1] + 256 : (int) sw[1]; - - return (sw1 * 256) + sw2; - - } - -} 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 113f00cb..3a773ca2 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -1,19 +1,19 @@ /* -* 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; @@ -29,496 +29,532 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * A factory for creating {@link SignatureCard}s from {@link Card}s. + * A factory for creating {@link SignatureCard}s from {@link Card}s. */ public class SignatureCardFactory { - public static boolean ENFORCE_RECOMMENDED_PIN_LENGTH = false; - - /** - * This class represents a supported smart card. - */ - private class SupportedCard { - - /** - * The ATR pattern. - */ - private byte[] atrPattern; - - /** - * The ATR mask. - */ - private byte[] atrMask; - - /** - * The implementation class. - */ - private String impl; - - /** - * Creates a new SupportedCard instance with the given ATR pattern and mask - * und the corresponding implementation class. - * - * @param atrPattern - * the ATR pattern - * @param atrMask - * the ATR mask - * @param implementationClass - * the name of the implementation class - * - * @throws NullPointerException - * if atrPattern or atrMask is - * null. - * @throws IllegalArgumentException - * if the lengths of atrPattern and - * atrMask of not equal. - */ - public SupportedCard(byte[] atrPattern, byte[] atrMask, String implementationClass) { - if (atrPattern.length != atrMask.length) { - throw new IllegalArgumentException("Length of 'atr' and 'mask' must be equal."); - } - this.atrPattern = atrPattern; - this.atrMask = atrMask; - this.impl = implementationClass; - } - - /** - * Returns true if the given ATR matches the ATR pattern and mask this - * SupportedCard object. - * - * @param atr - * the ATR - * - * @return true if the given ATR matches the ATR pattern and - * mask of this SupportedCard object, or false - * otherwise. - */ - public boolean matches(ATR atr) { - - byte[] bytes = atr.getBytes(); - if (bytes == null) { - return false; - } - if (bytes.length < atrMask.length) { - // we cannot test for equal length here, as we get ATRs with - // additional bytes on systems using PCSClite (e.g. linux and OS X) sometimes - return false; - } - - int l = Math.min(atrMask.length, bytes.length); - for (int i = 0; i < l; i++) { - if ((bytes[i] & atrMask[i]) != atrPattern[i]) { - return false; - } - } - return true; - - } - - /** - * @return the corresponding implementation class. - */ - public String getImplementationClassName() { - return impl; - } - - } - - /** - * Logging facility. - */ - private final Logger log = LoggerFactory.getLogger(SignatureCardFactory.class); - - /** - * The instance to be returned by {@link #getInstance()}. - */ - private static SignatureCardFactory instance; - - /** - * The list of supported smart cards. - */ - private List supportedCards; - - /** - * @return an instance of this SignatureCardFactory. - */ - public static synchronized SignatureCardFactory getInstance() { - if (instance == null) { - instance = new SignatureCardFactory(); - } - return instance; - } - - /** - * Private constructor. - */ - private SignatureCardFactory() { - - supportedCards = new ArrayList(); - - // e-card - supportedCards.add(new SupportedCard( - // ATR (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00) - new byte[] { - (byte) 0x3b, (byte) 0xbd, (byte) 0x18, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, - (byte) 0x80, (byte) 0x51, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }, - // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00) - 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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }, - "at.gv.egiz.smcc.STARCOSCard")); - - // 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) - new byte[] { - (byte) 0x3b, (byte) 0xdd, (byte) 0x96, (byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe, (byte) 0x45, - (byte) 0x1f, (byte) 0x03, (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 - }, - // mask ( - new byte[] { - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (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 - }, - "at.gv.egiz.smcc.STARCOSCard")); - - // 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) - new byte[] { - (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, - (byte) 0x45, (byte) 0x50, (byte) 0x41, (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 - }, - // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00) - 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) 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 - }, - "at.gv.egiz.smcc.ACOSCard")); - - // 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) - new byte[] { - (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, - (byte) 0x4D, (byte) 0x43, (byte) 0x41, (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 - }, - // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00) - 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) 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 - }, - "at.gv.egiz.smcc.ACOSCard")); - - // BELPIC - supportedCards.add(new SupportedCard( - // ATR (3b:98:13:40:0A:A5:03:01:01:01:AD:13:11) - new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x13, - (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03, - (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, - (byte) 0x13, (byte) 0x11 }, - // mask (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 }, - "at.gv.egiz.smcc.BELPICCard")); - supportedCards.add(new SupportedCard( - // ATR [3b:98:_94_:40:_ff_:a5:03:01:01:01:ad:13:_10_] - new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94, - (byte) 0x40, (byte) 0xff, (byte) 0xa5, (byte) 0x03, - (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, - (byte) 0x13, (byte) 0x10 }, - // mask (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 }, - "at.gv.egiz.smcc.BELPICCard")); - supportedCards.add(new SupportedCard( - // ATR [3b:98:_94_:40:0a:a5:03:01:01:01:ad:13:_10_] - new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94, - (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03, - (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, - (byte) 0x13, (byte) 0x10 }, - // mask (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 }, - "at.gv.egiz.smcc.BELPICCard")); - supportedCards.add(new SupportedCard( - // ATR [3b:98:_95_:40:0a:a5:_07_:01:01:01:ad:13:_20_] - new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x95, - (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x07, - (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, - (byte) 0x13, (byte) 0x20 }, - // mask (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 }, - "at.gv.egiz.smcc.BELPICCard")); - - // DNIe - supportedCards.add(new SupportedCard( - // ATR [3b:7f:38:00:00:00:6a:44:4e:49:65:20:02:4c:34:01:13:03:90:00] - new byte[] { (byte) 0x3b, (byte) 0x7F, (byte) 0x38, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6A, - (byte) 0x44, (byte) 0x4E, (byte) 0x49, (byte) 0x65, - (byte) 0x00, (byte) 0x02, (byte) 0x4C, (byte) 0x34, (byte) 0x01, (byte) 0x13, (byte) 0x03, (byte) 0x90, (byte) 0x00 }, - // mask (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) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }, - "at.gv.egiz.smcc.DNIeCard")); - - // ITCards - supportedCards.add(new SupportedCard( - // ATR = - // [3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:11:01:43:4e:53:11:31:80:8e] - new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18, - (byte) 0x00, (byte) 0xff, (byte) 0x81, (byte) 0x31, - (byte) 0xfe, (byte) 0x55, (byte) 0x00, (byte) 0x6b, - (byte) 0x02, (byte) 0x09 /* - * , (byte) 0x02, (byte) 0x00, - * (byte) 0x01, (byte) 0x11, - * (byte) 0x01, (byte) 0x43, - * (byte) 0x4e, (byte) 0x53, - * (byte) 0x11, (byte) 0x31, - * (byte) 0x80, (byte) 0x8e - */ - }, - // mask (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, - * (byte) 0xff, (byte) 0xff, - * (byte) 0xff, (byte) 0xff - */ - }, "at.gv.egiz.smcc.ITCard")); - supportedCards.add(new SupportedCard( - // ATR - // (3B:FF:18:00:FF:C1:0A:31:FE:55:00:6B:05:08:C8:05:01:01:01:43:4E:53:10:31:80:1C) - new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18, - (byte) 0x00, (byte) 0xFF, (byte) 0xC1, (byte) 0x0a, - (byte) 0x31, (byte) 0xfe, (byte) 0x55, (byte) 0x00, - (byte) 0x6B, (byte) 0x05, (byte) 0x08, (byte) 0xC8, - (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01, - (byte) 0x43, (byte) 0x4E, (byte) 0x53, (byte) 0x10, - (byte) 0x31, (byte) 0x80, (byte) 0x1C }, - // mask - // (ff:ff:ff:00:00: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, - (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, (byte) 0xff, (byte) 0xff }, - "at.gv.egiz.smcc.ITCard")); - - // EstEID cards return different ATRs depending on the reader device - supportedCards.add(new SupportedCard( - // ATR - // (3B:5E:11:FF:45:73:74:45:49:44:20:76:65:72:20:31:2E:30) - new byte[] { (byte) 0x3b, (byte) 0x00, (byte) 0x00, - (byte) 0xff, (byte) 0x45, (byte) 0x73, (byte) 0x74, - (byte) 0x45, (byte) 0x49, (byte) 0x44, (byte) 0x20, - (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20, - (byte) 0x31, (byte) 0x2e, (byte) 0x30 }, - // 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, - (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.EstEIDCard")); - - // EstEID cards return different ATRs depending on the reader device - supportedCards.add(new SupportedCard( - // ATR - // (3B:DE:18:FF:C0:80:B1:FE:45:1F:03:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:2B) - new byte[] { (byte) 0x3b, (byte) 0xde, (byte) 0x18, - (byte) 0xff, (byte) 0xc0, (byte) 0x80, (byte) 0xb1, - (byte) 0xfe, (byte) 0x45, (byte) 0x1f, (byte) 0x03, - (byte) 0x45, (byte) 0x73, (byte) 0x74, (byte) 0x45, - (byte) 0x49, (byte) 0x44, (byte) 0x20, (byte) 0x76, - (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x31, - (byte) 0x2e, (byte) 0x30, (byte) 0x2b }, - // 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, - (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, (byte) 0xff, (byte) 0xff }, - "at.gv.egiz.smcc.EstEIDCard")); - - supportedCards.add(new SupportedCard( - // ATR (3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00 - - // 00:00:00:00) - new byte[] { (byte) 0x3b, (byte) 0x7d, (byte) 0x95, - (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:00:00: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, - (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0x00, (byte) 0xff, (byte) 0x00 }, - "at.gv.egiz.smcc.PtEidCard")); - - supportedCards.add(new SupportedCard( - // SwissSign ATR 3b:fa:18:00:02:c1:0a:31:fe:58:4b:53:77:69:73:73:53:69:67:6e:89 - new byte[] { (byte) 0x3b, (byte) 0xfa, (byte) 0x18, - (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a, - (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b, - 'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n', - (byte) 0x89}, - // mask - 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.SuisseIDCard")); - - supportedCards.add(new SupportedCard( - // QuoVadis ATR 3b:f2:18:00:02:c1:0a:31:fe:58:c8:08:74 - new byte[] { (byte) 0x3b, (byte) 0xf2, (byte) 0x18, - (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a, - (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0xc8, - (byte) 0x08, (byte) 0x74}, - // mask - 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}, - "at.gv.egiz.smcc.SuisseIDCard")); - - supportedCards.add(new SupportedCard( - // FL-Post card - // [3b:bb:18:00:c0:10:31:fe:45:80:67:04:12: b0:03:03:00:00:81:05:3c] - new byte[] { (byte) 0x3b, (byte) 0xbb, (byte) 0x18, - (byte) 0x00, (byte) 0xc0, (byte) 0x10, (byte) 0x31, - (byte) 0xfe, (byte) 0x45, (byte) 0x80, (byte) 0x67, - (byte) 0x04, (byte) 0x12, (byte) 0xb0, (byte) 0x03, - (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x81, - (byte) 0x05, (byte) 0x3c}, - // mask - 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.LIEZertifikatCard")); - - - } - - /** - * Creates a SignatureCard instance with the given smart card. - * - * @param card - * the smart card, or null if a software card should be - * created - * @param cardTerminal TODO - * - * @return a SignatureCard instance - * - * @throws CardNotSupportedException - * if no implementation of the given card could be - * found - */ - public SignatureCard createSignatureCard(Card card, CardTerminal cardTerminal) - throws CardNotSupportedException { - - if(card == null) { - SignatureCard sCard = new SWCard(); - sCard.init(card, cardTerminal); - return sCard; - } - - ATR atr = card.getATR(); - Iterator cards = supportedCards.iterator(); - while (cards.hasNext()) { - SupportedCard supportedCard = cards.next(); - if(supportedCard.matches(atr)) { - - ClassLoader cl = SignatureCardFactory.class.getClassLoader(); - SignatureCard sc; - try { - Class scClass = cl.loadClass(supportedCard.getImplementationClassName()); - sc = (SignatureCard) scClass.newInstance(); - - sc = ExclSignatureCardProxy.newInstance(sc); - - sc.init(card, cardTerminal); - - return sc; - - } catch (ClassNotFoundException e) { - log.warn("Cannot find signature card implementation class.", e); - throw new CardNotSupportedException("Cannot find signature card implementation class.", e); - } catch (InstantiationException e) { - log.warn("Failed to instantiate signature card implementation.", e); - throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e); - } catch (IllegalAccessException e) { - log.warn("Failed to instantiate signature card implementation.", e); - throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e); - } - - } - } - - throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); - - } - - public static String toString(byte[] b) { - StringBuffer sb = new StringBuffer(); - if (b != null && b.length > 0) { - sb.append(Integer.toHexString((b[0] & 240) >> 4)); - sb.append(Integer.toHexString(b[0] & 15)); - } - for(int i = 1; i < b.length; i++) { - sb.append(':'); - sb.append(Integer.toHexString((b[i] & 240) >> 4)); - sb.append(Integer.toHexString(b[i] & 15)); - } - return sb.toString(); - } + public static boolean ENFORCE_RECOMMENDED_PIN_LENGTH = false; + + /** + * This class represents a supported smart card. + */ + private class SupportedCard { + + /** + * The ATR pattern. + */ + private byte[] atrPattern; + + /** + * The ATR mask. + */ + private byte[] atrMask; + + /** + * The implementation class. + */ + private String impl; + + /** + * Creates a new SupportedCard instance with the given ATR pattern and + * mask und the corresponding implementation class. + * + * @param atrPattern + * the ATR pattern + * @param atrMask + * the ATR mask + * @param implementationClass + * the name of the implementation class + * + * @throws NullPointerException + * if atrPattern or atrMask is + * null. + * @throws IllegalArgumentException + * if the lengths of atrPattern and + * atrMask of not equal. + */ + public SupportedCard(byte[] atrPattern, byte[] atrMask, + String implementationClass) { + if (atrPattern.length != atrMask.length) { + throw new IllegalArgumentException( + "Length of 'atr' and 'mask' must be equal."); + } + this.atrPattern = atrPattern; + this.atrMask = atrMask; + this.impl = implementationClass; + } + + /** + * Returns true if the given ATR matches the ATR pattern and mask this + * SupportedCard object. + * + * @param atr + * the ATR + * + * @return true if the given ATR matches the ATR pattern + * and mask of this SupportedCard object, or false + * otherwise. + */ + public boolean matches(ATR atr) { + + byte[] bytes = atr.getBytes(); + if (bytes == null) { + return false; + } + if (bytes.length < atrMask.length) { + // we cannot test for equal length here, as we get ATRs with + // additional bytes on systems using PCSClite (e.g. linux and OS + // X) sometimes + return false; + } + + int l = Math.min(atrMask.length, bytes.length); + for (int i = 0; i < l; i++) { + if ((bytes[i] & atrMask[i]) != atrPattern[i]) { + return false; + } + } + return true; + + } + + /** + * @return the corresponding implementation class. + */ + public String getImplementationClassName() { + return impl; + } + + } + + /** + * Logging facility. + */ + private final Logger log = LoggerFactory + .getLogger(SignatureCardFactory.class); + + /** + * The instance to be returned by {@link #getInstance()}. + */ + private static SignatureCardFactory instance; + + /** + * The list of supported smart cards. + */ + private List supportedCards; + + /** + * @return an instance of this SignatureCardFactory. + */ + public static synchronized SignatureCardFactory getInstance() { + if (instance == null) { + instance = new SignatureCardFactory(); + } + return instance; + } + + /** + * Private constructor. + */ + private SignatureCardFactory() { + + supportedCards = new ArrayList(); + + // e-card + supportedCards.add(new SupportedCard( + // ATR + // (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00) + new byte[] { (byte) 0x3b, (byte) 0xbd, (byte) 0x18, + (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, + (byte) 0x45, (byte) 0x80, (byte) 0x51, (byte) 0x02, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00 }, + // mask + // (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00) + 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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00 }, + "at.gv.egiz.smcc.STARCOSCard")); + + // 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) + new byte[] { (byte) 0x3b, (byte) 0xdd, (byte) 0x96, + (byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe, + (byte) 0x45, (byte) 0x1f, (byte) 0x03, (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 }, + // mask ( + new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (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 }, "at.gv.egiz.smcc.STARCOSCard")); + + // 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) + new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11, + (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, + (byte) 0x45, (byte) 0x45, (byte) 0x50, (byte) 0x41, + (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 }, + // mask + // (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00) + 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) 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 }, "at.gv.egiz.smcc.ACOSCard")); + + // 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) + new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11, + (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, + (byte) 0x45, (byte) 0x4D, (byte) 0x43, (byte) 0x41, + (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 }, + // mask + // (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00) + 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) 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 }, "at.gv.egiz.smcc.ACOSCard")); + + // BELPIC + supportedCards.add(new SupportedCard( + // ATR (3b:98:13:40:0A:A5:03:01:01:01:AD:13:11) + new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x13, + (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, + (byte) 0x13, (byte) 0x11 }, + // mask (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 }, + "at.gv.egiz.smcc.BELPICCard")); + supportedCards.add(new SupportedCard( + // ATR [3b:98:_94_:40:_ff_:a5:03:01:01:01:ad:13:_10_] + new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94, + (byte) 0x40, (byte) 0xff, (byte) 0xa5, (byte) 0x03, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, + (byte) 0x13, (byte) 0x10 }, + // mask (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 }, + "at.gv.egiz.smcc.BELPICCard")); + supportedCards.add(new SupportedCard( + // ATR [3b:98:_94_:40:0a:a5:03:01:01:01:ad:13:_10_] + new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94, + (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, + (byte) 0x13, (byte) 0x10 }, + // mask (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 }, + "at.gv.egiz.smcc.BELPICCard")); + supportedCards.add(new SupportedCard( + // ATR [3b:98:_95_:40:0a:a5:_07_:01:01:01:ad:13:_20_] + new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x95, + (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x07, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, + (byte) 0x13, (byte) 0x20 }, + // mask (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 }, + "at.gv.egiz.smcc.BELPICCard")); + + // ES DNIe + supportedCards.add(new SupportedCard( + // ATR [3b:7f:38:00:00:00:6a:44:4e:49:65:20:02:4c:34:01:13:03:90:00] + new byte[] { (byte) 0x3b, (byte) 0x7F, (byte) 0x38, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6A, + (byte) 0x44, (byte) 0x4E, (byte) 0x49, (byte) 0x65, + (byte) 0x00, (byte) 0x02, (byte) 0x4C, (byte) 0x34, + (byte) 0x01, (byte) 0x13, (byte) 0x00, (byte) 0x90, + (byte) 0x00 }, + // mask (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) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0xff, + (byte) 0xff }, "at.gv.egiz.smcc.ESDNIeCard")); + + // ITCards + supportedCards.add(new SupportedCard( + // ATR = + // [3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:11:01:43:4e:53:11:31:80:8e] + new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18, + (byte) 0x00, (byte) 0xff, (byte) 0x81, (byte) 0x31, + (byte) 0xfe, (byte) 0x55, (byte) 0x00, (byte) 0x6b, + (byte) 0x02, (byte) 0x09 /* + * , (byte) 0x02, (byte) 0x00, + * (byte) 0x01, (byte) 0x11, + * (byte) 0x01, (byte) 0x43, + * (byte) 0x4e, (byte) 0x53, + * (byte) 0x11, (byte) 0x31, + * (byte) 0x80, (byte) 0x8e + */ + }, + // mask (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, + * (byte) 0xff, (byte) 0xff, + * (byte) 0xff, (byte) 0xff + */ + }, "at.gv.egiz.smcc.ITCard")); + supportedCards.add(new SupportedCard( + // ATR + // (3B:FF:18:00:FF:C1:0A:31:FE:55:00:6B:05:08:C8:05:01:01:01:43:4E:53:10:31:80:1C) + new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18, + (byte) 0x00, (byte) 0xFF, (byte) 0xC1, (byte) 0x0a, + (byte) 0x31, (byte) 0xfe, (byte) 0x55, (byte) 0x00, + (byte) 0x6B, (byte) 0x05, (byte) 0x08, (byte) 0xC8, + (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01, + (byte) 0x43, (byte) 0x4E, (byte) 0x53, (byte) 0x10, + (byte) 0x31, (byte) 0x80, (byte) 0x1C }, + // mask + // (ff:ff:ff:00:00: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, + (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, (byte) 0xff, (byte) 0xff }, + "at.gv.egiz.smcc.ITCard")); + + // EstEID cards return different ATRs depending on the reader device + supportedCards.add(new SupportedCard( + // ATR + // (3B:5E:11:FF:45:73:74:45:49:44:20:76:65:72:20:31:2E:30) + new byte[] { (byte) 0x3b, (byte) 0x00, (byte) 0x00, + (byte) 0xff, (byte) 0x45, (byte) 0x73, (byte) 0x74, + (byte) 0x45, (byte) 0x49, (byte) 0x44, (byte) 0x20, + (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20, + (byte) 0x31, (byte) 0x2e, (byte) 0x30 }, + // 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, + (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.EstEIDCard")); + + // EstEID cards return different ATRs depending on the reader device + supportedCards.add(new SupportedCard( + // ATR + // (3B:DE:18:FF:C0:80:B1:FE:45:1F:03:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:2B) + new byte[] { (byte) 0x3b, (byte) 0xde, (byte) 0x18, + (byte) 0xff, (byte) 0xc0, (byte) 0x80, (byte) 0xb1, + (byte) 0xfe, (byte) 0x45, (byte) 0x1f, (byte) 0x03, + (byte) 0x45, (byte) 0x73, (byte) 0x74, (byte) 0x45, + (byte) 0x49, (byte) 0x44, (byte) 0x20, (byte) 0x76, + (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x31, + (byte) 0x2e, (byte) 0x30, (byte) 0x2b }, + // 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, + (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, (byte) 0xff, (byte) 0xff }, + "at.gv.egiz.smcc.EstEIDCard")); + + supportedCards.add(new SupportedCard( + // ATR (3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00 - + // 00:00:00:00) + new byte[] { (byte) 0x3b, (byte) 0x7d, (byte) 0x95, + (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:00:00: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, + (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x00, (byte) 0xff, (byte) 0x00 }, + "at.gv.egiz.smcc.PtEidCard")); + + supportedCards.add(new SupportedCard( + // SwissSign ATR + // 3b:fa:18:00:02:c1:0a:31:fe:58:4b:53:77:69:73:73:53:69:67:6e:89 + new byte[] { (byte) 0x3b, (byte) 0xfa, (byte) 0x18, + (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a, + (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b, + 'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n', + (byte) 0x89 }, + // mask + 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.SuisseIDCard")); + + supportedCards.add(new SupportedCard( + // QuoVadis ATR 3b:f2:18:00:02:c1:0a:31:fe:58:c8:08:74 + new byte[] { (byte) 0x3b, (byte) 0xf2, (byte) 0x18, + (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a, + (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0xc8, + (byte) 0x08, (byte) 0x74 }, + // mask + 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 }, + "at.gv.egiz.smcc.SuisseIDCard")); + + supportedCards.add(new SupportedCard( + // FL-Post card + // [3b:bb:18:00:c0:10:31:fe:45:80:67:04:12: + // b0:03:03:00:00:81:05:3c] + new byte[] { (byte) 0x3b, (byte) 0xbb, (byte) 0x18, + (byte) 0x00, (byte) 0xc0, (byte) 0x10, (byte) 0x31, + (byte) 0xfe, (byte) 0x45, (byte) 0x80, (byte) 0x67, + (byte) 0x04, (byte) 0x12, (byte) 0xb0, (byte) 0x03, + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x81, + (byte) 0x05, (byte) 0x3c }, + // mask + 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.LIEZertifikatCard")); + + } + + /** + * Creates a SignatureCard instance with the given smart card. + * + * @param card + * the smart card, or null if a software card should + * be created + * @param cardTerminal + * TODO + * + * @return a SignatureCard instance + * + * @throws CardNotSupportedException + * if no implementation of the given card could be + * found + */ + public SignatureCard createSignatureCard(Card card, + CardTerminal cardTerminal) throws CardNotSupportedException { + + if (card == null) { + SignatureCard sCard = new SWCard(); + sCard.init(card, cardTerminal); + return sCard; + } + + ATR atr = card.getATR(); + Iterator cards = supportedCards.iterator(); + while (cards.hasNext()) { + SupportedCard supportedCard = cards.next(); + if (supportedCard.matches(atr)) { + + ClassLoader cl = SignatureCardFactory.class.getClassLoader(); + SignatureCard sc; + try { + Class scClass = cl.loadClass(supportedCard + .getImplementationClassName()); + sc = (SignatureCard) scClass.newInstance(); + + sc = ExclSignatureCardProxy.newInstance(sc); + + sc.init(card, cardTerminal); + + return sc; + + } catch (ClassNotFoundException e) { + log.warn( + "Cannot find signature card implementation class.", + e); + throw new CardNotSupportedException( + "Cannot find signature card implementation class.", + e); + } catch (InstantiationException e) { + log + .warn( + "Failed to instantiate signature card implementation.", + e); + throw new CardNotSupportedException( + "Failed to instantiate signature card implementation.", + e); + } catch (IllegalAccessException e) { + log + .warn( + "Failed to instantiate signature card implementation.", + e); + throw new CardNotSupportedException( + "Failed to instantiate signature card implementation.", + e); + } + + } + } + + throw new CardNotSupportedException("Card not supported: ATR=" + + toString(atr.getBytes())); + + } + public static String toString(byte[] b) { + StringBuffer sb = new StringBuffer(); + if (b != null && b.length > 0) { + sb.append(Integer.toHexString((b[0] & 240) >> 4)); + sb.append(Integer.toHexString(b[0] & 15)); + } + for (int i = 1; i < b.length; i++) { + sb.append(':'); + sb.append(Integer.toHexString((b[i] & 240) >> 4)); + sb.append(Integer.toHexString(b[i] & 15)); + } + return sb.toString(); + } } 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 22a707c8..f35086b6 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 @@ -1,19 +1,19 @@ /* -* 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.util; import java.io.ByteArrayOutputStream; @@ -34,374 +34,435 @@ import at.gv.egiz.smcc.VerifyAPDUSpec; public class ISO7816Utils { - /** - * file control information templates - */ - public static final byte TAG_FCP = 0x62; - public static final byte TAG_FMD = 0x64; - public static final byte TAG_FCI = 0x6f; - - /** - * file control informatino bitmasks (SELECT P2) - */ - public static final byte P2_FCI = 0x00; - public static final byte P2_FCP = 0x04; - public static final byte P2_FMD = 0x08; - public static final byte P2_NORESP = 0x0c; - - - public static TransparentFileInputStream openTransparentFileInputStream( - final CardChannel channel, int maxSize) { - - TransparentFileInputStream file = new TransparentFileInputStream(maxSize) { - - @Override - protected byte[] readBinary(int offset, int len) throws IOException { - - ResponseAPDU resp; - try { - resp = channel.transmit(new CommandAPDU(0x00, 0xB0, - 0x7F & (offset >> 8), offset & 0xFF, len)); - } catch (CardException e) { - throw new IOException(e); - } - - Throwable cause; - if (resp.getSW() == 0x9000) { - return resp.getData(); - } else if (resp.getSW() == 0x6982) { - cause = new SecurityStatusNotSatisfiedException(); - } else { - cause = new SignatureCardException("Failed to read bytes (offset=" + offset + ",len=" - + len + ") SW=" + Integer.toHexString(resp.getSW()) + "."); - } - throw new IOException(cause); - - } - - }; - - return file; - - } - - public static byte[] readTransparentFile(CardChannel channel, int maxSize) - throws CardException, SignatureCardException { - - TransparentFileInputStream is = openTransparentFileInputStream(channel, maxSize); - - try { - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - - int len; - for (byte[] b = new byte[256]; (len = is.read(b)) != -1;) { - os.write(b, 0, len); - } - - return os.toByteArray(); - - } catch (IOException e) { - Throwable cause = e.getCause(); - if (cause instanceof CardException) { - throw (CardException) cause; - } - if (cause instanceof SignatureCardException) { - throw (SignatureCardException) cause; - } - throw new SignatureCardException(e); - } - - } - - public static byte[] readTransparentFileTLV(CardChannel channel, int maxSize, - byte expectedType) throws CardException, SignatureCardException { - - TransparentFileInputStream is = openTransparentFileInputStream(channel, - maxSize); - - return readTransparentFileTLV(is, expectedType); - - } - - public static byte[] readTransparentFileTLV(TransparentFileInputStream is, - byte expectedType) throws CardException, SignatureCardException { - - - try { - - is.mark(256); - - // check expected type - int b = is.read(); - if (b == 0x00 || b == 0xFF) { - return null; - } - if (b == -1 || expectedType != (0xFF & b)) { - throw new SignatureCardException("Unexpected TLV type. Expected " - + Integer.toHexString(expectedType) + " but was " - + Integer.toHexString(b) + "."); - } - - // get actual length - int actualSize = 2; - b = is.read(); - if (b == -1) { - return null; - } else if ((0x80 & b) > 0) { - int octets = (0x0F & b); - actualSize += octets; - for (int i = 1; i <= octets; i++) { - b = is.read(); - if (b == -1) { - return null; - } - actualSize += (0xFF & b) << ((octets - i) * 8); - } - } else { - actualSize += 0xFF & b; - } - - // set limit to actual size and read into buffer - is.reset(); - is.setLimit(actualSize); - byte[] buf = new byte[actualSize]; - if (is.read(buf) == actualSize) { - return buf; - } else { - return null; - } - - } catch (IOException e) { - Throwable cause = e.getCause(); - if (cause instanceof CardException) { - throw (CardException) cause; - } - if (cause instanceof SignatureCardException) { - throw (SignatureCardException) cause; - } - throw new SignatureCardException(e); - } - - } - - public static int getLengthFromFCx(byte[] fcx) { - - int len = -1; - - if (fcx.length != 0 && (fcx[0] == (byte) 0x62 || fcx[0] == (byte) 0x6F)) { - int pos = 2; - while (pos < (fcx[1] - 2)) { - switch (fcx[pos]) { - - case (byte) 0x80: - case (byte) 0x81: { - len = 0xFF & fcx[pos + 2]; - for (int i = 1; i < fcx[pos + 1]; i++) { - len<<=8; - len+=0xFF & fcx[pos + i + 2]; - } - } - - default: - pos += 0xFF & fcx[pos + 1] + 2; - } - } - } - - return len; - - } - - public static byte[] readRecord(CardChannel channel, int record) throws CardException, SignatureCardException { - - ResponseAPDU resp = channel.transmit( - new CommandAPDU(0x00, 0xB2, record, 0x04, 256)); - if (resp.getSW() == 0x9000) { - return resp.getData(); - } else { - throw new SignatureCardException("Failed to read records. SW=" - + Integer.toHexString(resp.getSW())); - } - - } - - public static void formatPIN(int pinFormat, int pinJustification, byte[] fpin, byte[] mask, char[] pin) { - - boolean left = (pinJustification == VerifyAPDUSpec.PIN_JUSTIFICATION_LEFT); - - int j = (left) ? 0 : fpin.length - 1; - int step = (left) ? 1 : - 1; - switch (pinFormat) { - case VerifyAPDUSpec.PIN_FORMAT_BINARY: - if (fpin.length < pin.length) { - throw new IllegalArgumentException(); - } - for (int i = 0; i < pin.length; i++) { - fpin[j] = (byte) Character.digit(pin[i], 10); - mask[j] = (byte) 0xFF; - j += step; - } - break; - - case VerifyAPDUSpec.PIN_FORMAT_BCD: - if (fpin.length * 2 < pin.length) { - throw new IllegalArgumentException(); - } - for (int i = 0; i < pin.length; i++) { - int digit = Character.digit(pin[i], 10); - boolean h = (i % 2 == 0) ^ left; - fpin[j] |= h ? digit : digit << 4 ; - mask[j] |= h ? (byte) 0x0F : (byte) 0xF0; - j += (i % 2) * step; - } - break; - - case VerifyAPDUSpec.PIN_FORMAT_ASCII: - if (fpin.length < pin.length) { - throw new IllegalArgumentException(); - } - byte[] asciiPin = Charset.forName("ASCII").encode(CharBuffer.wrap(pin)).array(); - for (int i = 0; i < pin.length; i++) { - fpin[j] = asciiPin[i]; - mask[j] = (byte) 0xFF; - j += step; - } - break; - } - - } - - public static void insertPIN(byte[] apdu, int pos, byte[] fpin, byte[] mask) { - for (int i = 0; i < fpin.length; i++) { - apdu[pos + i] &= ~mask[i]; - apdu[pos + i] |= fpin[i]; - } - } - - public static void insertPINLength(byte[] apdu, int length, int lengthSize, int pos, int offset) { - - // use short (2 byte) to be able to shift the pin length - // by the number of bits given by the pin length position - short size = (short) (0x00FF & length); - short sMask = (short) ((1 << lengthSize) - 1); - // shift to the proper position - int shift = 16 - lengthSize - (pos % 8); - offset += (pos / 8) + 5; - size <<= shift; - sMask <<= shift; - // insert upper byte - apdu[offset] &= (0xFF & (~sMask >> 8)); - apdu[offset] |= (0xFF & (size >> 8)); - // insert lower byte - apdu[offset + 1] &= (0xFF & ~sMask); - apdu[offset + 1] |= (0xFF & size); - - } - - public static CommandAPDU createVerifyAPDU(VerifyAPDUSpec apduSpec, char[] pin) { - - // format pin - int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : pin.length; - byte[] fpin = new byte[l]; - byte[] mask = new byte[l]; - formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, pin); - - byte[] template = apduSpec.getApdu(); - byte[] apdu = new byte[Math.max(template.length, 5 + apduSpec.getPinPosition() + l)]; - System.arraycopy(template, 0, apdu, 0, template.length); - if (template.length < 5) { - apdu[4] = (byte) (apdu.length - 5); - } - - // insert formated pin - insertPIN(apdu, apduSpec.getPinPosition() + 5, fpin, mask); - - // insert pin length - if (apduSpec.getPinLengthSize() != 0) { - insertPINLength(apdu, pin.length, apduSpec.getPinLengthSize(), apduSpec.getPinLengthPos(), 0); - } - - return new CommandAPDU(apdu); - - } - - public static CommandAPDU createChangeReferenceDataAPDU( - ChangeReferenceDataAPDUSpec apduSpec, char[] oldPin, char[] newPin) { - - int lo = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : oldPin.length; - int ln = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : newPin.length; - - // format old pin - byte[] fpin = new byte[lo]; - byte[] mask = new byte[lo]; - formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, oldPin); - - byte[] template = apduSpec.getApdu(); - byte[] apdu = new byte[Math.max(template.length, - 5 + apduSpec.getPinPosition() - + Math.max(apduSpec.getPinInsertionOffsetOld() + lo, - apduSpec.getPinInsertionOffsetNew() + ln))]; - System.arraycopy(template, 0, apdu, 0, template.length); - if (template.length < 5) { - apdu[4] = (byte) (apdu.length - 5); - } - - // insert formated old pin - insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetOld() + 5, fpin, mask); - - // insert pin length - if (apduSpec.getPinLengthSize() != 0) { - insertPINLength(apdu, oldPin.length, apduSpec.getPinLengthSize(), - apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetOld()); - } - - // format new pin - fpin = new byte[ln]; - mask = new byte[ln]; - formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin); - - // insert formated new pin - insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask); - - // insert pin length - if (apduSpec.getPinLengthSize() != 0) { - insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(), - apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew()); - } - - return new CommandAPDU(apdu); - - } - - public static CommandAPDU createNewReferenceDataAPDU( - NewReferenceDataAPDUSpec apduSpec, char[] newPin) { - - // format old pin - int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : newPin.length; - byte[] fpin = new byte[l]; - byte[] mask = new byte[l]; - formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin); - - byte[] template = apduSpec.getApdu(); - byte[] apdu = new byte[Math.max(template.length, 5 + apduSpec.getPinPosition() + l)]; - System.arraycopy(template, 0, apdu, 0, template.length); - if (template.length < 5) { - apdu[4] = (byte) (apdu.length - 5); - } - - // insert formated new pin - insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask); - - // insert pin length - if (apduSpec.getPinLengthSize() != 0) { - insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(), - apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew()); - } - - return new CommandAPDU(apdu); - - } - - + /** + * file control information templates + */ + public static final byte TAG_FCP = 0x62; + public static final byte TAG_FMD = 0x64; + public static final byte TAG_FCI = 0x6f; + + /** + * file control informatino bitmasks (SELECT P2) + */ + public static final byte P2_FCI = 0x00; + public static final byte P2_FCP = 0x04; + public static final byte P2_FMD = 0x08; + public static final byte P2_NORESP = 0x0c; + + public static TransparentFileInputStream openTransparentFileInputStream( + final CardChannel channel, int maxSize) { + + // open stream with default chunkSize of 256 + return openTransparentFileInputStream(channel, maxSize, 256); + } + + public static TransparentFileInputStream openTransparentFileInputStream( + final CardChannel channel, int maxSize, int chunkSize) { + + TransparentFileInputStream file = new TransparentFileInputStream( + maxSize, chunkSize) { + + @Override + protected byte[] readBinary(int offset, int len) throws IOException { + + ResponseAPDU resp; + try { + resp = channel.transmit(new CommandAPDU(0x00, 0xB0, + 0x7F & (offset >> 8), offset & 0xFF, len)); + } catch (CardException e) { + throw new IOException(e); + } + + // 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 + .getSW2())); + } catch (CardException e) { + + throw new IOException("Error reading bytes from card.", + e); + } + } + + Throwable cause; + if (resp.getSW() == 0x9000) { + return resp.getData(); + } else if (resp.getSW() == 0x6982) { + cause = new SecurityStatusNotSatisfiedException(); + } else { + cause = new SignatureCardException( + "Failed to read bytes (offset=" + offset + ",len=" + + len + ") SW=" + + Integer.toHexString(resp.getSW()) + "."); + } + throw new IOException(cause); + + } + + }; + + return file; + + } + + private static byte[] readFromInputStream(TransparentFileInputStream is) throws CardException, SignatureCardException { + + try { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + int len; + for (byte[] b = new byte[256]; (len = is.read(b)) != -1;) { + os.write(b, 0, len); + } + + return os.toByteArray(); + + } catch (IOException e) { + Throwable cause = e.getCause(); + if (cause instanceof CardException) { + throw (CardException) cause; + } + if (cause instanceof SignatureCardException) { + throw (SignatureCardException) cause; + } + throw new SignatureCardException(e); + } + } + + public static byte[] readTransparentFile(CardChannel channel, int maxSize, int chunkSize) + throws CardException, SignatureCardException { + + TransparentFileInputStream is = openTransparentFileInputStream(channel, + maxSize, chunkSize); + + return readFromInputStream(is); + } + + public static byte[] readTransparentFile(CardChannel channel, int maxSize) + throws CardException, SignatureCardException { + + TransparentFileInputStream is = openTransparentFileInputStream(channel, + maxSize); + + return readFromInputStream(is); + } + + public static byte[] readTransparentFileTLV(CardChannel channel, + int maxSize, byte expectedType) throws CardException, + SignatureCardException { + + TransparentFileInputStream is = openTransparentFileInputStream(channel, + maxSize); + + return readTransparentFileTLV(is, expectedType); + + } + + public static byte[] readTransparentFileTLV(TransparentFileInputStream is, + byte expectedType) throws CardException, SignatureCardException { + + try { + + is.mark(256); + + // check expected type + int b = is.read(); + if (b == 0x00 || b == 0xFF) { + return null; + } + if (b == -1 || expectedType != (0xFF & b)) { + throw new SignatureCardException( + "Unexpected TLV type. Expected " + + Integer.toHexString(expectedType) + + " but was " + Integer.toHexString(b) + "."); + } + + // get actual length + int actualSize = 2; + b = is.read(); + if (b == -1) { + return null; + } else if ((0x80 & b) > 0) { + int octets = (0x0F & b); + actualSize += octets; + for (int i = 1; i <= octets; i++) { + b = is.read(); + if (b == -1) { + return null; + } + actualSize += (0xFF & b) << ((octets - i) * 8); + } + } else { + actualSize += 0xFF & b; + } + + // set limit to actual size and read into buffer + is.reset(); + is.setLimit(actualSize); + byte[] buf = new byte[actualSize]; + if (is.read(buf) == actualSize) { + return buf; + } else { + return null; + } + + } catch (IOException e) { + Throwable cause = e.getCause(); + if (cause instanceof CardException) { + throw (CardException) cause; + } + if (cause instanceof SignatureCardException) { + throw (SignatureCardException) cause; + } + throw new SignatureCardException(e); + } + + } + + public static int getLengthFromFCx(byte[] fcx) { + + int len = -1; + + if (fcx.length != 0 && (fcx[0] == (byte) 0x62 || fcx[0] == (byte) 0x6F)) { + int pos = 2; + while (pos < (fcx[1] - 2)) { + switch (fcx[pos]) { + + case (byte) 0x80: + case (byte) 0x81: { + len = 0xFF & fcx[pos + 2]; + for (int i = 1; i < fcx[pos + 1]; i++) { + len <<= 8; + len += 0xFF & fcx[pos + i + 2]; + } + } + + default: + pos += 0xFF & fcx[pos + 1] + 2; + } + } + } + + return len; + + } + + public static byte[] readRecord(CardChannel channel, int record) + throws CardException, SignatureCardException { + + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xB2, + record, 0x04, 256)); + if (resp.getSW() == 0x9000) { + return resp.getData(); + } else { + throw new SignatureCardException("Failed to read records. SW=" + + Integer.toHexString(resp.getSW())); + } + + } + + public static void formatPIN(int pinFormat, int pinJustification, + byte[] fpin, byte[] mask, char[] pin) { + + boolean left = (pinJustification == VerifyAPDUSpec.PIN_JUSTIFICATION_LEFT); + + int j = (left) ? 0 : fpin.length - 1; + int step = (left) ? 1 : -1; + switch (pinFormat) { + case VerifyAPDUSpec.PIN_FORMAT_BINARY: + if (fpin.length < pin.length) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < pin.length; i++) { + fpin[j] = (byte) Character.digit(pin[i], 10); + mask[j] = (byte) 0xFF; + j += step; + } + break; + + case VerifyAPDUSpec.PIN_FORMAT_BCD: + if (fpin.length * 2 < pin.length) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < pin.length; i++) { + int digit = Character.digit(pin[i], 10); + boolean h = (i % 2 == 0) ^ left; + fpin[j] |= h ? digit : digit << 4; + mask[j] |= h ? (byte) 0x0F : (byte) 0xF0; + j += (i % 2) * step; + } + break; + + case VerifyAPDUSpec.PIN_FORMAT_ASCII: + if (fpin.length < pin.length) { + throw new IllegalArgumentException(); + } + byte[] asciiPin = Charset.forName("ASCII").encode( + CharBuffer.wrap(pin)).array(); + for (int i = 0; i < pin.length; i++) { + fpin[j] = asciiPin[i]; + mask[j] = (byte) 0xFF; + j += step; + } + break; + } + + } + + public static void insertPIN(byte[] apdu, int pos, byte[] fpin, byte[] mask) { + for (int i = 0; i < fpin.length; i++) { + apdu[pos + i] &= ~mask[i]; + apdu[pos + i] |= fpin[i]; + } + } + + public static void insertPINLength(byte[] apdu, int length, int lengthSize, + int pos, int offset) { + + // use short (2 byte) to be able to shift the pin length + // by the number of bits given by the pin length position + short size = (short) (0x00FF & length); + short sMask = (short) ((1 << lengthSize) - 1); + // shift to the proper position + int shift = 16 - lengthSize - (pos % 8); + offset += (pos / 8) + 5; + size <<= shift; + sMask <<= shift; + // insert upper byte + apdu[offset] &= (0xFF & (~sMask >> 8)); + apdu[offset] |= (0xFF & (size >> 8)); + // insert lower byte + apdu[offset + 1] &= (0xFF & ~sMask); + apdu[offset + 1] |= (0xFF & size); + + } + + public static CommandAPDU createVerifyAPDU(VerifyAPDUSpec apduSpec, + char[] pin) { + + // format pin + int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() + : pin.length; + byte[] fpin = new byte[l]; + byte[] mask = new byte[l]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), + fpin, mask, pin); + + byte[] template = apduSpec.getApdu(); + byte[] apdu = new byte[Math.max(template.length, 5 + + apduSpec.getPinPosition() + l)]; + System.arraycopy(template, 0, apdu, 0, template.length); + if (template.length < 5) { + apdu[4] = (byte) (apdu.length - 5); + } + + // insert formated pin + insertPIN(apdu, apduSpec.getPinPosition() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, pin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), 0); + } + + return new CommandAPDU(apdu); + + } + + public static CommandAPDU createChangeReferenceDataAPDU( + ChangeReferenceDataAPDUSpec apduSpec, char[] oldPin, char[] newPin) { + + int lo = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() + : oldPin.length; + int ln = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() + : newPin.length; + + // format old pin + byte[] fpin = new byte[lo]; + byte[] mask = new byte[lo]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), + fpin, mask, oldPin); + + byte[] template = apduSpec.getApdu(); + byte[] apdu = new byte[Math.max(template.length, 5 + + apduSpec.getPinPosition() + + Math.max(apduSpec.getPinInsertionOffsetOld() + lo, apduSpec + .getPinInsertionOffsetNew() + + ln))]; + System.arraycopy(template, 0, apdu, 0, template.length); + if (template.length < 5) { + apdu[4] = (byte) (apdu.length - 5); + } + + // insert formated old pin + insertPIN(apdu, apduSpec.getPinPosition() + + apduSpec.getPinInsertionOffsetOld() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, oldPin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), apduSpec + .getPinInsertionOffsetOld()); + } + + // format new pin + fpin = new byte[ln]; + mask = new byte[ln]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), + fpin, mask, newPin); + + // insert formated new pin + insertPIN(apdu, apduSpec.getPinPosition() + + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), apduSpec + .getPinInsertionOffsetNew()); + } + + return new CommandAPDU(apdu); + + } + + public static CommandAPDU createNewReferenceDataAPDU( + NewReferenceDataAPDUSpec apduSpec, char[] newPin) { + + // format old pin + int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() + : newPin.length; + byte[] fpin = new byte[l]; + byte[] mask = new byte[l]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), + fpin, mask, newPin); + + byte[] template = apduSpec.getApdu(); + byte[] apdu = new byte[Math.max(template.length, 5 + + apduSpec.getPinPosition() + l)]; + System.arraycopy(template, 0, apdu, 0, template.length); + if (template.length < 5) { + apdu[4] = (byte) (apdu.length - 5); + } + + // insert formated new pin + insertPIN(apdu, apduSpec.getPinPosition() + + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), apduSpec + .getPinInsertionOffsetNew()); + } + + return new CommandAPDU(apdu); + + } + } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java index d9816746..fd58964d 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java @@ -16,10 +16,12 @@ */ package at.gv.egiz.smcc.util; +import java.math.BigInteger; import java.util.Locale; import java.util.Map; import javax.smartcardio.Card; +import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; import org.slf4j.Logger; @@ -142,6 +144,42 @@ public class SMCCHelper { return sb.toString(); } + public static byte[] toByteArray(int val) throws CardException { + + String hexString = Integer.toHexString(val); + + if (hexString.length() > 4) { + throw new CardException( + "Unexpected input length to toByteArray() utility method: " + + hexString.length()); + } + + byte high = 0x00; + byte low = 0x00; + + if (hexString.length() <= 2) { + + low = (byte) Integer.parseInt(hexString, 16); + } else { + + low = (byte) Integer.parseInt(hexString.substring(hexString + .length() - 2), 16); + high = (byte) Integer.parseInt(hexString.substring(0, hexString + .length() - 2), 16); + } + + return new byte[] { high, low }; + } + + public static BigInteger createUnsignedBigInteger(byte[] data) { + + byte[] unsigned = new byte[data.length + 1]; + unsigned[0] = (byte) 0x00; + System.arraycopy(data, 0, unsigned, 1, data.length); + + return new BigInteger(unsigned); + } + public static boolean isUseSWCard() { return useSWCard; } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java index 781f9137..2da17354 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java @@ -1,194 +1,201 @@ /* -* 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.util; import java.io.IOException; import java.io.InputStream; public abstract class TransparentFileInputStream extends InputStream { - - private final int chunkSize = 256; - - private byte[] buf = new byte[chunkSize]; - private int start = 0; - private int end = 0; - - private int offset = 0; - - private int length = -1; - - private int limit = -1; - - private int mark = -1; - - private int readlimit = -1; - - public TransparentFileInputStream() { - } - - public TransparentFileInputStream(int length) { - this.length = length; - } - - public void setLimit(int limit) { - this.limit = limit; - } - - private int fill() throws IOException { - if (start == end && (limit < 0 || offset < limit)) { - int l; - if (limit > 0 && limit - offset < chunkSize) { - l = limit - offset; - } else if (length > 0) { - if (length - offset < chunkSize) { - l = length - offset; - } else { - l = chunkSize - 1; - } - } else { - l = chunkSize; - } - byte[] b = readBinary(offset, l); - offset += b.length; - if (mark < 0) { - start = 0; - end = b.length; - System.arraycopy(b, 0, buf, start, b.length); - } else { - if (end - mark + b.length > buf.length) { - // double buffer size - byte[] nbuf = new byte[buf.length * 2]; - System.arraycopy(buf, mark, nbuf, 0, end - mark); - buf = nbuf; - } else { - System.arraycopy(buf, mark, buf, 0, end - mark); - } - start = start - mark; - end = end - mark + b.length; - mark = 0; - System.arraycopy(b, 0, buf, start, b.length); - } - if (l > b.length) { - // end of file reached - setLimit(offset); - } - } - return end - start; - } - - protected abstract byte[] readBinary(int offset, int len) throws IOException; - - @Override - public int read() throws IOException { - int b = (fill() > 0) ? 0xFF & buf[start++] : -1; - if (readlimit > 0 && start > readlimit) { - mark = -1; - readlimit = -1; - } - return b; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (b == null) { - throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > b.length - off) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - - int count = 0; - int l; - while (count < len) { - if (fill() > 0) { - l = Math.min(end - start, len - count); - System.arraycopy(buf, start, b, off, l); - start += l; - off += l; - count += l; - if (readlimit > 0 && start > readlimit) { - mark = -1; - readlimit = -1; - } - } else { - return (count > 0) ? count : -1; - } - } - - return count; - - } - - @Override - public synchronized void mark(int readlimit) { - this.readlimit = readlimit; - mark = start; - } - - @Override - public boolean markSupported() { - return true; - } - - @Override - public synchronized void reset() throws IOException { - if (mark < 0) { - throw new IOException(); - } else { - start = mark; - } - } - - @Override - public long skip(long n) throws IOException { - - if (n <= 0) { - return 0; - } - - if (n <= end - start) { - start += n; - return n; - } else { - - mark = -1; - - long remaining = n - (end - start); - start = end; - - if (limit >= 0 && limit < offset + remaining) { - remaining -= limit - offset; - offset = limit; - return n - remaining; - } - - if (length >= 0 && length < offset + remaining) { - remaining -= length - offset; - offset = length; - return n - remaining; - } - - offset += remaining; - - return n; - - } - - } - + + // private final int chunkSize = 256; + private int chunkSize = 256; + + private byte[] buf = new byte[chunkSize]; + private int start = 0; + private int end = 0; + + private int offset = 0; + + private int length = -1; + + private int limit = -1; + + private int mark = -1; + + private int readlimit = -1; + + public TransparentFileInputStream() { + } + + public TransparentFileInputStream(int length) { + this.length = length; + } + + public TransparentFileInputStream(int length, int chunkSize) { + this.length = length; + this.chunkSize = chunkSize; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + private int fill() throws IOException { + if (start == end && (limit < 0 || offset < limit)) { + int l; + if (limit > 0 && limit - offset < chunkSize) { + l = limit - offset; + } else if (length > 0) { + if (length - offset < chunkSize) { + l = length - offset; + } else { + l = chunkSize - 1; + } + } else { + l = chunkSize; + } + byte[] b = readBinary(offset, l); + offset += b.length; + if (mark < 0) { + start = 0; + end = b.length; + System.arraycopy(b, 0, buf, start, b.length); + } else { + if (end - mark + b.length > buf.length) { + // double buffer size + byte[] nbuf = new byte[buf.length * 2]; + System.arraycopy(buf, mark, nbuf, 0, end - mark); + buf = nbuf; + } else { + System.arraycopy(buf, mark, buf, 0, end - mark); + } + start = start - mark; + end = end - mark + b.length; + mark = 0; + System.arraycopy(b, 0, buf, start, b.length); + } + if (l > b.length) { + // end of file reached + setLimit(offset); + } + } + return end - start; + } + + protected abstract byte[] readBinary(int offset, int len) + throws IOException; + + @Override + public int read() throws IOException { + int b = (fill() > 0) ? 0xFF & buf[start++] : -1; + if (readlimit > 0 && start > readlimit) { + mark = -1; + readlimit = -1; + } + return b; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int count = 0; + int l; + while (count < len) { + if (fill() > 0) { + l = Math.min(end - start, len - count); + System.arraycopy(buf, start, b, off, l); + start += l; + off += l; + count += l; + if (readlimit > 0 && start > readlimit) { + mark = -1; + readlimit = -1; + } + } else { + return (count > 0) ? count : -1; + } + } + + return count; + + } + + @Override + public synchronized void mark(int readlimit) { + this.readlimit = readlimit; + mark = start; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void reset() throws IOException { + if (mark < 0) { + throw new IOException(); + } else { + start = mark; + } + } + + @Override + public long skip(long n) throws IOException { + + if (n <= 0) { + return 0; + } + + if (n <= end - start) { + start += n; + return n; + } else { + + mark = -1; + + long remaining = n - (end - start); + start = end; + + if (limit >= 0 && limit < offset + remaining) { + remaining -= limit - offset; + offset = limit; + return n - remaining; + } + + if (length >= 0 && length < offset + remaining) { + remaining -= length - offset; + offset = length; + return n - remaining; + } + + offset += remaining; + + return n; + + } + + } + } diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties deleted file mode 100644 index fc6157bf..00000000 --- a/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties +++ /dev/null @@ -1,3 +0,0 @@ -#pin.name=PIN -sig.pin.name=PIN -sig.pin.length=8-16 \ 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 a6dbfb8d..4c4a7b41 100644 --- a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java +++ b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; @@ -48,6 +49,7 @@ import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.Random; import java.util.zip.DataFormatException; import java.util.zip.Deflater; @@ -63,6 +65,8 @@ import javax.crypto.spec.SecretKeySpec; import javax.smartcardio.*; import at.gv.egiz.smcc.pin.gui.PINGUI; +import at.gv.egiz.smcc.util.SMCCHelper; + import org.junit.Ignore; @Ignore @@ -357,7 +361,9 @@ public class ESCardTest extends AbstractSignatureCard { ESCardTest tester = new ESCardTest(); - tester.testEchtCert(); + tester.cardTest(); +// tester.byteBufferTest(); +// tester.testEchtCert(); // try { // CardChannel channel = tester.setupCardChannel(); // @@ -369,8 +375,49 @@ public class ESCardTest extends AbstractSignatureCard { // e.printStackTrace(); // } + + } + private void cardTest() { + + SMCCHelper helper = new SMCCHelper(); + + SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault()); + + try { + signatureCard.createSignature(null, null, 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(); + } + + } + + private void byteBufferTest() { + + byte[] testarray = new byte[]{(byte)0x05,(byte)0x07,(byte)0x09,(byte)0x0B,(byte)0x0D}; + ByteBuffer buf = ByteBuffer.wrap(testarray); + + System.out.println("Position:" + buf.position()); + System.out.println("Remaining:" + buf.remaining()); + System.out.println("Get: " + buf.get()); + System.out.println("Position:" + buf.position()); + System.out.println("Remaining:" + buf.remaining()); + + buf.put((byte)0x11); + System.out.println("Position:" + buf.position()); + System.out.println("Remaining:" + buf.remaining()); + + printByteArray(buf.array()); + } + private void testEchtCert() { try { @@ -414,6 +461,62 @@ public class ESCardTest extends AbstractSignatureCard { } + private byte[] secure4ByteAPDU(byte[] apdu) throws CardException { + + if(apdu.length != 4) { + + throw new CardException("Invalid APDU length."); + } + + byte encCLA = (byte) (apdu[0] | (byte) 0x0C); + byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2], + apdu[3] }; + byte[] paddedHeader = DNIeCryptoUtil.applyPadding(8, + encHeader); + + byte[] macData = new byte[paddedHeader.length]; + System.arraycopy(paddedHeader, 0, macData, 0, + paddedHeader.length); + + +// byte[] paddedMacData = DNIeCryptoUtil.applyPadding( +// 8, macData); + + incrementSSC(); + + System.out.println("MAC data:"); + printByteArray(macData); + + byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(macData, + kMac, this.ssc, 8); + + System.out.println("MAC:"); + printByteArray(mac); + + byte[] encapsulatedMac = new byte[mac.length + 2]; + encapsulatedMac[0] = (byte) 0x8E; + encapsulatedMac[1] = (byte) mac.length; + System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length); + + byte[] completeMessage = new byte[5+ encapsulatedMac.length]; + completeMessage[0] = encCLA; + completeMessage[1] = apdu[1]; + completeMessage[2] = apdu[2]; + completeMessage[3] = apdu[3]; + completeMessage[4] = (byte) (encapsulatedMac.length); + + + System.arraycopy(encapsulatedMac, 0, completeMessage, + 5, encapsulatedMac.length); + + System.out.println("Secured 4 Byte APDU to: "); + printByteArray(completeMessage); + + return completeMessage; + + } + + private void testZLib() { try { @@ -686,8 +789,8 @@ public class ESCardTest extends AbstractSignatureCard { byte[] fci = executeSecureSelect(channel, apdu2); - // System.out.println("Obtained FCI:"); - // printByteArray(fci); + System.out.println("Obtained FCI:"); + printByteArray(fci); byte sizeHi = fci[7]; byte sizeLo = fci[8]; @@ -951,6 +1054,7 @@ public class ESCardTest extends AbstractSignatureCard { // (byte) 0xa0, (byte) 0xfe, (byte) 0x6e }; // + byte[] apdu = new byte[] { // PIN VERIFY (0 0 0 0 0 0 0 0) @@ -1002,6 +1106,22 @@ public class ESCardTest extends AbstractSignatureCard { } } + private void checkPIN(CardChannel channel) throws CardException { + + byte[] apdu = new byte[]{ + (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00 + }; + + byte[] securedAPDU = secure4ByteAPDU(apdu); + + + CommandAPDU command = new CommandAPDU(securedAPDU); + ResponseAPDU resp = channel.transmit(command); + + System.out.println("Response: " + Integer.toHexString(resp.getSW())); + + } + private byte[] readFromCard(CardChannel channel, byte offsetHi, byte offsetLo, byte numBytes) throws CardException { @@ -1283,10 +1403,43 @@ public class ESCardTest extends AbstractSignatureCard { byte[] file = executeSecureReadFile(channel, new byte[] { (byte) 0x50, (byte) 0x15, (byte) 0x60, (byte) 0x04 }); + writeDataToFile(file, "f:/CDF.bin"); getCertIdFromASN1File(file); + // NEW +// try { +// +// EFObjectDirectory ef_od = new EFObjectDirectory(new byte[]{(byte)0x50, (byte)0x15}); +// ef_od.selectAndRead(channel); +// +// CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd()); +// ef_cd.selectAndRead(channel); +// +// byte[] ef_qcert = null; +// for (CIOCertificate cioCertificate : ef_cd.getCIOs()) { +// String label = cioCertificate.getLabel(); +// //"TEST LLV APO 2s Liechtenstein Post Qualified CA ID" +// if (label != null && label.toLowerCase() +// .contains("liechtenstein post qualified ca id")) { +// ef_qcert = cioCertificate.getEfidOrPath(); +// } +// } +// +// } catch(SignatureCardException e) { +// +// System.out.println("Error getting CDF."); +// e.printStackTrace(); +// } +// +// catch (IOException e) { +// System.out.println("Error getting CDF."); +// e.printStackTrace(); +// } + // END NEW + + System.out.println("Reading CDF file successful."); } @@ -2245,24 +2398,25 @@ public class ESCardTest extends AbstractSignatureCard { // VERIFY PIN executeSecurePINVerify(channel); + checkPIN(channel); - // GET PrKDF - executeSecureReadPrKDF(channel); - - // Manage Security Environment - executeSecureManageSecurityEnvironment(channel); - - // Create signature - executeSecurePerformSecurityOperation(channel); - - // GET CDF - executeSecureReadCDF(channel); - - // Select certificate - executeSecureSelectCertificate(channel); - - // Verify signature - verifySignature(); +// // GET PrKDF +// executeSecureReadPrKDF(channel); +// +// // Manage Security Environment +// executeSecureManageSecurityEnvironment(channel); +// +// // Create signature +// executeSecurePerformSecurityOperation(channel); +// +// // GET CDF +// executeSecureReadCDF(channel); +// +// // Select certificate +// executeSecureSelectCertificate(channel); +// +// // Verify signature +// verifySignature(); } -- cgit v1.2.3