summaryrefslogtreecommitdiff
path: root/smcc/src/main/java/at
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java/at')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java87
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java475
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java1648
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java1040
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java831
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java38
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java379
7 files changed, 1273 insertions, 3225 deletions
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<CIOCertificate> 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<CIOCertificate> 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<lengthValues.length; i++) {
-
- result = (result + byteToInt(lengthValues[lengthValues.length-1-i]) * (int)Math.pow(256, i));
- }
-
- return result + startOfLength + lengthOfLength + 1; // defined length + tag byte + length bytes
-
- } else {
-
- return (int)lengthStartByte + startOfLength + 1; // defined length + tag byte + length byte
- }
-
- }
-
- private int byteToInt(byte b) {
-
- return b < 0 ? b + 256 : b;
-
- }
-
- private byte[] executeReadFile(CardChannel channel, byte[] path) throws CardException {
-
- log.debug("Executing secure read File command..");
-
- executeSecureSelectMasterFile(channel);
-
- // Select DF
- byte[] apdu_1 = new byte[] {
-
- (byte)0x00, // CLA
- (byte)0xA4, // INS
- (byte)0x00, // P1
- (byte)0x00, // P2
- (byte)0x02, // Lc
- path[0],
- path[1]
- };
-
- secureChannel.executeSecureSelect(channel, apdu_1);
-
- // Select EF
- byte[] apdu_2 = new byte[] {
-
- (byte)0x00, // CLA
- (byte)0xA4, // INS
- (byte)0x00, // P1
- (byte)0x00, // P2
- (byte)0x02, // Lc
- path[2],
- path[3]
- };
-
- byte[] fci = secureChannel.executeSecureSelect(channel, apdu_2);
- byte[] file = secureChannel.executeSecureReadBinary(channel, fci[7], fci[8]);
-
- return file;
- }
-
- private byte[] decompressData(byte[] input) throws CardException {
-
- Inflater decompresser = new Inflater();
- decompresser.setInput(input, 0, input.length);
- byte[] buffer = new byte[256];
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- try {
- while(!decompresser.finished()) {
-
- int numBytes = decompresser.inflate(buffer);
- bos.write(buffer, 0, numBytes);
- }
-
- decompresser.end();
-
- } catch (DataFormatException e) {
-
- throw new CardException("Error decompressing file.", e);
- }
-
- return bos.toByteArray();
- }
-}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java
deleted file mode 100644
index da63bec2..00000000
--- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java
+++ /dev/null
@@ -1,1648 +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 java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PublicKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.KeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Arrays;
-import java.util.Random;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.smartcardio.CardChannel;
-import javax.smartcardio.CardException;
-import javax.smartcardio.CommandAPDU;
-import javax.smartcardio.ResponseAPDU;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class DNIeCardSecureChannel {
-
- private final Logger log = LoggerFactory
- .getLogger(DNIeCardSecureChannel.class);
-
- private final byte[] APDU_DATA_MSE_LOAD_TERMINAL_CERTS = new byte[] {
- (byte) 0x83, (byte) 0x02, (byte) 0x02, (byte) 0x0F };
-
- private final String TERMINAL_MODULO = "DB2CB41E112BACFA2BD7C3D3D7967E84FB9434FC261F9D090A8983947DAF8488D3DF8FBDCC1F92493585E134A1B42DE519F463244D7ED384E26D516CC7A4FF7895B1992140043AACADFC12E856B202346AF8226B1A882137DC3C5A57F0D2815C1FCD4BB46FA9157FDFFD79EC3A10A824CCC1EB3CE0B6B4396AE236590016BA69";
- private final String TERMINAL_PRIVEXP = "18B44A3D155C61EBF4E3261C8BB157E36F63FE30E9AF28892B59E2ADEB18CC8C8BAD284B9165819CA4DEC94AA06B69BCE81706D1C1B668EB128695E5F7FEDE18A908A3011A646A481D3EA71D8A387D474609BD57A882B182E047DE80E04B4221416BD39DFA1FAC0300641962ADB109E28CAF50061B68C9CABD9B00313C0F46ED";
-
- private static final String ROOT_CA_MODULO = "EADEDA455332945039DAA404C8EBC4D3B7F5DC869283CDEA2F101E2AB54FB0D0B03D8F030DAF2458028288F54CE552F8FA57AB2FB103B112427E11131D1D27E10A5B500EAAE5D940301E30EB26C3E9066B257156ED639D70CCC090B863AFBB3BFED8C17BE7673034B9823E977ED657252927F9575B9FFF6691DB64F80B5E92CD";
- private static final String ROOT_CA_PUBEXP = "010001";
-
- private final byte[] C_CV_CA = new byte[] {
-
- (byte) 0x7F, (byte) 0x21, (byte) 0x81, (byte) 0xCE, (byte) 0x5F,
- (byte) 0x37, (byte) 0x81, (byte) 0x80, (byte) 0x3C, (byte) 0xBA,
- (byte) 0xDC, (byte) 0x36, (byte) 0x84, (byte) 0xBE, (byte) 0xF3,
- (byte) 0x20, (byte) 0x41, (byte) 0xAD, (byte) 0x15, (byte) 0x50,
- (byte) 0x89, (byte) 0x25, (byte) 0x8D, (byte) 0xFD, (byte) 0x20,
- (byte) 0xC6, (byte) 0x91, (byte) 0x15, (byte) 0xD7, (byte) 0x2F,
- (byte) 0x9C, (byte) 0x38, (byte) 0xAA, (byte) 0x99, (byte) 0xAD,
- (byte) 0x6C, (byte) 0x1A, (byte) 0xED, (byte) 0xFA, (byte) 0xB2,
- (byte) 0xBF, (byte) 0xAC, (byte) 0x90, (byte) 0x92, (byte) 0xFC,
- (byte) 0x70, (byte) 0xCC, (byte) 0xC0, (byte) 0x0C, (byte) 0xAF,
- (byte) 0x48, (byte) 0x2A, (byte) 0x4B, (byte) 0xE3, (byte) 0x1A,
- (byte) 0xFD, (byte) 0xBD, (byte) 0x3C, (byte) 0xBC, (byte) 0x8C,
- (byte) 0x83, (byte) 0x82, (byte) 0xCF, (byte) 0x06, (byte) 0xBC,
- (byte) 0x07, (byte) 0x19, (byte) 0xBA, (byte) 0xAB, (byte) 0xB5,
- (byte) 0x6B, (byte) 0x6E, (byte) 0xC8, (byte) 0x07, (byte) 0x60,
- (byte) 0xA4, (byte) 0xA9, (byte) 0x3F, (byte) 0xA2, (byte) 0xD7,
- (byte) 0xC3, (byte) 0x47, (byte) 0xF3, (byte) 0x44, (byte) 0x27,
- (byte) 0xF9, (byte) 0xFF, (byte) 0x5C, (byte) 0x8D, (byte) 0xE6,
- (byte) 0xD6, (byte) 0x5D, (byte) 0xAC, (byte) 0x95, (byte) 0xF2,
- (byte) 0xF1, (byte) 0x9D, (byte) 0xAC, (byte) 0x00, (byte) 0x53,
- (byte) 0xDF, (byte) 0x11, (byte) 0xA5, (byte) 0x07, (byte) 0xFB,
- (byte) 0x62, (byte) 0x5E, (byte) 0xEB, (byte) 0x8D, (byte) 0xA4,
- (byte) 0xC0, (byte) 0x29, (byte) 0x9E, (byte) 0x4A, (byte) 0x21,
- (byte) 0x12, (byte) 0xAB, (byte) 0x70, (byte) 0x47, (byte) 0x58,
- (byte) 0x8B, (byte) 0x8D, (byte) 0x6D, (byte) 0xA7, (byte) 0x59,
- (byte) 0x22, (byte) 0x14, (byte) 0xF2, (byte) 0xDB, (byte) 0xA1,
- (byte) 0x40, (byte) 0xC7, (byte) 0xD1, (byte) 0x22, (byte) 0x57,
- (byte) 0x9B, (byte) 0x5F, (byte) 0x38, (byte) 0x3D, (byte) 0x22,
- (byte) 0x53, (byte) 0xC8, (byte) 0xB9, (byte) 0xCB, (byte) 0x5B,
- (byte) 0xC3, (byte) 0x54, (byte) 0x3A, (byte) 0x55, (byte) 0x66,
- (byte) 0x0B, (byte) 0xDA, (byte) 0x80, (byte) 0x94, (byte) 0x6A,
- (byte) 0xFB, (byte) 0x05, (byte) 0x25, (byte) 0xE8, (byte) 0xE5,
- (byte) 0x58, (byte) 0x6B, (byte) 0x4E, (byte) 0x63, (byte) 0xE8,
- (byte) 0x92, (byte) 0x41, (byte) 0x49, (byte) 0x78, (byte) 0x36,
- (byte) 0xD8, (byte) 0xD3, (byte) 0xAB, (byte) 0x08, (byte) 0x8C,
- (byte) 0xD4, (byte) 0x4C, (byte) 0x21, (byte) 0x4D, (byte) 0x6A,
- (byte) 0xC8, (byte) 0x56, (byte) 0xE2, (byte) 0xA0, (byte) 0x07,
- (byte) 0xF4, (byte) 0x4F, (byte) 0x83, (byte) 0x74, (byte) 0x33,
- (byte) 0x37, (byte) 0x37, (byte) 0x1A, (byte) 0xDD, (byte) 0x8E,
- (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01,
- (byte) 0x42, (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x52,
- (byte) 0x44, (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 };
-
- private final byte[] CHR = new byte[] {
-
- (byte) 0x83, (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x53,
- (byte) 0x44, (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 };
-
- private final byte[] KEY_SELECTOR = new byte[] {
-
- (byte) 0x83, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x84,
- (byte) 0x02, (byte) 0x02, (byte) 0x1F };
-
- private final byte[] C_CV_IFD = new byte[] {
-
- (byte) 0x7f, (byte) 0x21, (byte) 0x81, (byte) 0xcd, (byte) 0x5f,
- (byte) 0x37, (byte) 0x81, (byte) 0x80, (byte) 0x82, (byte) 0x5b,
- (byte) 0x69, (byte) 0xc6, (byte) 0x45, (byte) 0x1e, (byte) 0x5f,
- (byte) 0x51, (byte) 0x70, (byte) 0x74, (byte) 0x38, (byte) 0x5f,
- (byte) 0x2f, (byte) 0x17, (byte) 0xd6, (byte) 0x4d, (byte) 0xfe,
- (byte) 0x2e, (byte) 0x68, (byte) 0x56, (byte) 0x75, (byte) 0x67,
- (byte) 0x09, (byte) 0x4b, (byte) 0x57, (byte) 0xf3, (byte) 0xc5,
- (byte) 0x78, (byte) 0xe8, (byte) 0x30, (byte) 0xe4, (byte) 0x25,
- (byte) 0x57, (byte) 0x2d, (byte) 0xe8, (byte) 0x28, (byte) 0xfa,
- (byte) 0xf4, (byte) 0xde, (byte) 0x1b, (byte) 0x01, (byte) 0xc3,
- (byte) 0x94, (byte) 0xe3, (byte) 0x45, (byte) 0xc2, (byte) 0xfb,
- (byte) 0x06, (byte) 0x29, (byte) 0xa3, (byte) 0x93, (byte) 0x49,
- (byte) 0x2f, (byte) 0x94, (byte) 0xf5, (byte) 0x70, (byte) 0xb0,
- (byte) 0x0b, (byte) 0x1d, (byte) 0x67, (byte) 0x77, (byte) 0x29,
- (byte) 0xf7, (byte) 0x55, (byte) 0xd1, (byte) 0x07, (byte) 0x02,
- (byte) 0x2b, (byte) 0xb0, (byte) 0xa1, (byte) 0x16, (byte) 0xe1,
- (byte) 0xd7, (byte) 0xd7, (byte) 0x65, (byte) 0x9d, (byte) 0xb5,
- (byte) 0xc4, (byte) 0xac, (byte) 0x0d, (byte) 0xde, (byte) 0xab,
- (byte) 0x07, (byte) 0xff, (byte) 0x04, (byte) 0x5f, (byte) 0x37,
- (byte) 0xb5, (byte) 0xda, (byte) 0xf1, (byte) 0x73, (byte) 0x2b,
- (byte) 0x54, (byte) 0xea, (byte) 0xb2, (byte) 0x38, (byte) 0xa2,
- (byte) 0xce, (byte) 0x17, (byte) 0xc9, (byte) 0x79, (byte) 0x41,
- (byte) 0x87, (byte) 0x75, (byte) 0x9c, (byte) 0xea, (byte) 0x9f,
- (byte) 0x92, (byte) 0xa1, (byte) 0x78, (byte) 0x05, (byte) 0xa2,
- (byte) 0x7c, (byte) 0x10, (byte) 0x15, (byte) 0xec, (byte) 0x56,
- (byte) 0xcc, (byte) 0x7e, (byte) 0x47, (byte) 0x1a, (byte) 0x48,
- (byte) 0x8e, (byte) 0x6f, (byte) 0x1b, (byte) 0x91, (byte) 0xf7,
- (byte) 0xaa, (byte) 0x5f, (byte) 0x38, (byte) 0x3c, (byte) 0xad,
- (byte) 0xfc, (byte) 0x12, (byte) 0xe8, (byte) 0x56, (byte) 0xb2,
- (byte) 0x02, (byte) 0x34, (byte) 0x6a, (byte) 0xf8, (byte) 0x22,
- (byte) 0x6b, (byte) 0x1a, (byte) 0x88, (byte) 0x21, (byte) 0x37,
- (byte) 0xdc, (byte) 0x3c, (byte) 0x5a, (byte) 0x57, (byte) 0xf0,
- (byte) 0xd2, (byte) 0x81, (byte) 0x5c, (byte) 0x1f, (byte) 0xcd,
- (byte) 0x4b, (byte) 0xb4, (byte) 0x6f, (byte) 0xa9, (byte) 0x15,
- (byte) 0x7f, (byte) 0xdf, (byte) 0xfd, (byte) 0x79, (byte) 0xec,
- (byte) 0x3a, (byte) 0x10, (byte) 0xa8, (byte) 0x24, (byte) 0xcc,
- (byte) 0xc1, (byte) 0xeb, (byte) 0x3c, (byte) 0xe0, (byte) 0xb6,
- (byte) 0xb4, (byte) 0x39, (byte) 0x6a, (byte) 0xe2, (byte) 0x36,
- (byte) 0x59, (byte) 0x00, (byte) 0x16, (byte) 0xba, (byte) 0x69,
- (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x42,
- (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x53, (byte) 0x44,
- (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06
-
- };
-
- private final byte[] APDU_GET_CHIP_INFO = new byte[] { (byte) 0x90,
- (byte) 0xB8, (byte) 0x00, (byte) 0x00, (byte) 0x07 };
-
- private final byte[] APDU_SELECT_EF_DF_HEADER = new byte[] { (byte) 0x00,
- (byte) 0xA4, (byte) 0x00, (byte) 0x00 };
-
- private final byte[] APDU_READ_BINARY = new byte[] { (byte) 0x00,
- (byte) 0xB0, (byte) 0x00, (byte) 0x00, (byte) 0xFF };
-
- private final byte[] SECURE_CHANNEL_COMP_CERT_ID = new byte[] {
- (byte) 0x60, (byte) 0x1F };
- private final byte[] SECURE_CHANNEL_INTERMEDIAT_CERT_ID = new byte[] {
- (byte) 0x60, (byte) 0x20 };
-
- private final byte[] TERMINAL_CHALLENGE_TAIL = new byte[] {
-
- (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x01 };
-
- private final byte[] KENC_COMPUTATION_TAIL = new byte[] {
-
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 };
-
- private final byte[] KMAC_COMPUTATION_TAIL = new byte[] {
-
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02 };
-
- private final int BLOCK_LENGTH = 8;
-
- private final byte[] IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00 };
-
- private final byte[] HASH_PADDING = new byte[] {
-
- (byte) 0x30, (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06,
- (byte) 0x05, (byte) 0x2B, (byte) 0x0E, (byte) 0x03, (byte) 0x02,
- (byte) 0x1A, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x14 };
-
- private byte[] snIcc;
- private byte[] componentCert;
- private byte[] intermediateCert;
-
- private byte[] rndIfd;
- private byte[] rndIcc;
- private int prndLength;
-
- private byte[] kicc;
- private byte[] kifd;
-
- private byte[] kEnc;
- private byte[] kMac;
- private byte[] ssc;
-
- private boolean established;
-
- public DNIeCardSecureChannel() {
-
- this.established = false;
- }
-
- public void establish(CardChannel channel) throws CardException {
-
- log.debug("Setting up secure channel to crd..");
-
- // get chip info
- this.snIcc = executeGetChipInfo(channel);
-
- // get card certificates to establish secure channel
- this.intermediateCert = executeReadSecureChannelCertificate(channel,
- SECURE_CHANNEL_INTERMEDIAT_CERT_ID);
- this.componentCert = executeReadSecureChannelCertificate(channel,
- SECURE_CHANNEL_COMP_CERT_ID);
-
- // verify card's secure channel certificates
- verifyCertificates();
-
- // load terminal secure channel certificates and select appropriate keys
- loadTerminalCertsAndSelectKeys(channel);
-
- // perform internal authentication
- performInternalAuthentication(channel);
-
- // perform external authentication
- performExternalAuthentication(channel);
-
- // derive channel keys
- calculateChannelKeys();
-
- // secure channel successfully established
- this.established = true;
- log.debug("Secure channel successfully established.");
-
- }
-
- public byte[] executeSecureSelect(CardChannel channel, byte[] apdu)
- throws CardException {
-
- log.debug("Executing secure select command..");
-
- 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[] data = resp.getData();
-
- byte[] response = verifyAndDecryptSecuredResponseAPDU(data);
-
- if (response.length >= 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 <code>atrPattern</code> or <code>atrMask</code> is
- * <code>null</code>.
- * @throws IllegalArgumentException
- * if the lengths of <code>atrPattern</code> and
- * <code>atrMask</code> 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 <code>true</code> if the given ATR matches the ATR pattern and
- * mask of this SupportedCard object, or <code>false</code>
- * 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<SupportedCard> 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<SupportedCard>();
-
- // 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 <code>null</code> if a software card should be
- * created
- * @param cardTerminal TODO
- *
- * @return a SignatureCard instance
- *
- * @throws CardNotSupportedException
- * if no implementation of the given <code>card</code> 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<SupportedCard> 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 <code>atrPattern</code> or <code>atrMask</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * if the lengths of <code>atrPattern</code> and
+ * <code>atrMask</code> 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 <code>true</code> if the given ATR matches the ATR pattern
+ * and mask of this SupportedCard object, or <code>false</code>
+ * 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<SupportedCard> 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<SupportedCard>();
+
+ // 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 <code>null</code> if a software card should
+ * be created
+ * @param cardTerminal
+ * TODO
+ *
+ * @return a SignatureCard instance
+ *
+ * @throws CardNotSupportedException
+ * if no implementation of the given <code>card</code> 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<SupportedCard> 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;
+
+ }
+
+ }
+
}