summaryrefslogtreecommitdiff
path: root/smcc/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java22
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java247
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java308
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java31
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java143
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java57
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java56
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java33
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java364
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java44
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java37
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java2
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java41
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java2
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java8
15 files changed, 1123 insertions, 272 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 33dd99bb..a9886e80 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
@@ -59,12 +59,8 @@ public class CIOCertificateDirectory {
*/
public void selectAndRead(CardChannel channel) throws CardException, SignatureCardException, IOException {
- CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, ISO7816Utils.P2_FCP, fid, 256);
- ResponseAPDU resp = channel.transmit(cmd);
-
- byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCP);
- byte[] fd = new TLVSequence(fcx).getValue(0x82);
-
+ byte[] fd = executeSelect(channel);
+
if ((fd[0] & 0x04) > 0) {
readCIOCertificatesFromRecords(channel, fd);
@@ -75,6 +71,17 @@ public class CIOCertificateDirectory {
}
}
+ protected byte[] executeSelect(CardChannel channel) throws CardException {
+
+ CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, ISO7816Utils.P2_FCP, fid, 256);
+ ResponseAPDU resp = channel.transmit(cmd);
+
+ byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCP);
+ byte[] fd = new TLVSequence(fcx).getValue(0x82);
+
+ return fd;
+ }
+
protected void readCIOCertificatesFromRecords(CardChannel channel, byte[] fd) throws CardException, SignatureCardException, IOException {
for (int r = 1; r < fd[fd.length - 1]; r++) {
@@ -92,7 +99,6 @@ public class CIOCertificateDirectory {
protected void readCIOCertificatesFromTransparentFile(CardChannel channel) throws CardException, SignatureCardException, IOException {
-// byte[] ef = ISO7816Utils.readTransparentFile(channel, -1);
byte[] ef = doReadTransparentFile(channel);
int i = 0;
@@ -132,7 +138,7 @@ public class CIOCertificateDirectory {
cioCert.setiD(x509Certificate.getElementAt(1).getElementAt(0).gvByteArray());
//read CONTEXTSPECIFIC manually
- byte[] ctxSpecific = x509Certificate.getElementAt(2).getEncoded();
+ byte[] ctxSpecific = x509Certificate.getElementAt(x509Certificate.getSize()-1).getEncoded();
if ((ctxSpecific[0] & 0xff) == 0xa1) {
int ll = ((ctxSpecific[1] & 0xf0) == 0x80)
? (ctxSpecific[1] & 0x0f) + 2 : 2;
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java
index 92ef7f11..858a6cc5 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/DNIeSecuredChannel.java
@@ -186,11 +186,13 @@ public class DNIeSecuredChannel extends T0CardChannel {
super(channel);
this.established = false;
-
+
try {
+
this.establish();
+
} catch (CardException e) {
-
+
log.error("Error establishing secure channel with card.", e);
}
}
@@ -199,6 +201,9 @@ public class DNIeSecuredChannel extends T0CardChannel {
log.trace("Try to set up secure channel to card..");
+ // select master file
+ executeSelectMasterFile();
+
// get chip info
this.snIcc = executeGetChipInfo();
@@ -230,42 +235,43 @@ public class DNIeSecuredChannel extends T0CardChannel {
@Override
public int transmit(ByteBuffer command, ByteBuffer response)
throws CardException {
-
+
byte[] commandAPDU = new byte[command.remaining()];
- for(int i=0; i<commandAPDU.length; i++) {
-
+ for (int i = 0; i < commandAPDU.length; i++) {
+
commandAPDU[i] = command.get();
}
-
+
CommandAPDU apdu = new CommandAPDU(commandAPDU);
ResponseAPDU resp = transmit(apdu);
-
+
byte[] responseData = resp.getBytes();
- for(int i=0; i<responseData.length; i++) {
-
+ for (int i = 0; i < responseData.length; i++) {
+
response.put(responseData[i]);
}
-
+
return responseData.length;
}
@Override
public ResponseAPDU transmit(CommandAPDU apdu) throws CardException {
- if(!this.established) {
-
+ if (!this.established) {
+
this.establish();
}
-
+
byte[] plainAPDUData = apdu.getBytes();
byte[] securedAPDUData = secureAPDU(plainAPDUData);
CommandAPDU securedAPDU = new CommandAPDU(securedAPDUData);
ResponseAPDU securedResp = super.transmit(securedAPDU);
- byte[] respData = verifyAndDecryptSecuredResponseAPDU(securedResp.getData());
+ byte[] respData = verifyAndDecryptSecuredResponseAPDU(securedResp
+ .getData());
ResponseAPDU resp = new ResponseAPDU(respData);
-
+
return resp;
}
@@ -369,19 +375,42 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (resp.getSW() != 0x9000) {
- log.error("Error selecting DF or EF: " + Integer.toHexString(resp.getSW()));
+ log.error("Error selecting DF or EF: "
+ + Integer.toHexString(resp.getSW()));
throw new CardException("Unexpected response to Select Command: "
+ Integer.toHexString(resp.getSW()));
}
return resp.getData();
+ }
+
+ private void executeSelectMasterFile() throws CardException {
+ byte[] apdu = new byte[ESDNIeCard.MASTER_FILE_ID.length + 5];
+ apdu[0] = (byte) 0x00;
+ apdu[1] = (byte) 0xA4;
+ apdu[2] = (byte) 0x04;
+ apdu[3] = (byte) 0x00;
+ apdu[4] = (byte) ESDNIeCard.MASTER_FILE_ID.length;
+ System.arraycopy(ESDNIeCard.MASTER_FILE_ID, 0, apdu, 5,
+ ESDNIeCard.MASTER_FILE_ID.length);
+
+ CommandAPDU command = new CommandAPDU(apdu);
+ ResponseAPDU resp = super.transmit(command);
+
+ if (resp.getSW() != 0x9000) {
+
+ log.error("Error selecting master file: "
+ + Integer.toHexString(resp.getSW()));
+ throw new CardException("Error selecting master file: "
+ + Integer.toHexString(resp.getSW()));
+ }
}
private void verifyCertificates() throws CardException {
// This method verifies the card's component and intermediate
- // certificates cryptographically only.
+ // certificates cryptographically only (no revocation checking).
RSAPublicKey rootPubKey = DNIeCryptoUtil.createRSAPublicKey(
ROOT_CA_MODULO, ROOT_CA_PUBEXP);
@@ -417,7 +446,7 @@ public class DNIeSecuredChannel extends T0CardChannel {
executePerformSecurityOperation(C_CV_IFD);
// MSE - select keys
- executeManageSecurityEnvironment((byte) 0xC1, (byte) 0xA4, KEY_SELECTOR);
+ executeManageSecurityEnvironment((byte) 0xC1, (byte) 0xA4, KEY_SELECTOR);
}
@@ -431,7 +460,8 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (resp.getSW() != 0x9000) {
- log.error("Error executing Manage Security Environment: " + Integer.toHexString(resp.getSW()));
+ log.error("Error executing Manage Security Environment: "
+ + Integer.toHexString(resp.getSW()));
throw new CardException(
"Unexpected response from card during preparation of secure channel credentials: "
+ Integer.toHexString(resp.getSW()));
@@ -448,7 +478,8 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (resp.getSW() != 0x9000) {
- log.error("Error executing Perform Security Operation: " + Integer.toHexString(resp.getSW()));
+ log.error("Error executing Perform Security Operation: "
+ + Integer.toHexString(resp.getSW()));
throw new CardException(
"Unexpected response from card during preparation of secure channel credentials: "
+ Integer.toHexString(resp.getSW()));
@@ -477,8 +508,9 @@ public class DNIeSecuredChannel extends T0CardChannel {
log.trace("Internal Authentiction succeeded: " + ok);
if (!ok) {
-
- log.error("Internal authentication failed - unable to sucessfully verify card response.");
+
+ log
+ .error("Internal authentication failed - unable to sucessfully verify card response.");
throw new CardException("Internal authentication failed");
}
@@ -500,7 +532,8 @@ public class DNIeSecuredChannel extends T0CardChannel {
} else {
- log.error("Error sending terminal challenge to card: " + Integer.toHexString(resp.getSW()));
+ log.error("Error sending terminal challenge to card: "
+ + Integer.toHexString(resp.getSW()));
throw new CardException("Invalid response to terminal challenge: "
+ Integer.toHexString(resp.getSW()));
}
@@ -541,7 +574,8 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (sig == null) {
- log.error("Error verifying card response - decryption result is null");
+ log
+ .error("Error verifying card response - decryption result is null");
throw new CardException("Invalid decryption result: null.");
} else {
@@ -622,12 +656,13 @@ public class DNIeSecuredChannel extends T0CardChannel {
private void performExternalAuthentication() throws CardException {
log.trace("Performing external authentication.");
-
+
byte[] cardChallenge = executeRequestCardChallenge();
this.rndIcc = cardChallenge;
byte[] prnd2 = DNIeCryptoUtil.getRandomBytes(this.prndLength);
+
byte[] kIfd = DNIeCryptoUtil.getRandomBytes(32);
// compute hash
@@ -702,9 +737,9 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (executeExternalAuthenticate(authData)) {
log.trace("External authentication succeeded.");
- this.kifd = kIfd;
+ this.kifd = kIfd;
} else {
- log.error("Error performing external authentication.");
+ log.error("Error performing external authentication");
throw new CardException("External Authentication failed.");
}
@@ -718,7 +753,8 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (resp.getSW() != 0x9000) {
- log.error("Error requesting challenge from card: " + Integer.toHexString(resp.getSW()));
+ log.error("Error requesting challenge from card: "
+ + Integer.toHexString(resp.getSW()));
throw new CardException(
"Invalid response from card upon challenge request: "
+ Integer.toHexString(resp.getSW()));
@@ -734,6 +770,9 @@ public class DNIeSecuredChannel extends T0CardChannel {
(byte) 0x00, (byte) 0x00, authData);
ResponseAPDU resp = super.transmit(command);
+ log.trace("Card answer to EXTERNL AUTHENTICATE: "
+ + Integer.toHexString(resp.getSW()));
+
return resp.getSW() == 0x9000;
}
@@ -741,7 +780,8 @@ public class DNIeSecuredChannel extends T0CardChannel {
if (this.kicc == null || this.kifd == null) {
- log.error("Error generating channel keys - required key data is null.");
+ log
+ .error("Error generating channel keys - required key data is null.");
throw new CardException(
"Required data for deriving keys not available.");
}
@@ -793,52 +833,49 @@ public class DNIeSecuredChannel extends T0CardChannel {
System.arraycopy(this.rndIfd, this.rndIfd.length - 4, this.ssc, 4, 4);
}
-
private byte[] secureAPDUWithoutData(byte[] apdu) throws CardException {
-
- if(apdu.length < 4 || apdu.length > 5) {
-
- log.error("Error securing APDU - invalid APDU length: " + apdu.length);
+
+ if (apdu.length < 4 || apdu.length > 5) {
+
+ log.error("Error securing APDU - invalid APDU length: "
+ + apdu.length);
throw new CardException("Invalid APDU length.");
}
-
+
boolean leAvailable = apdu.length == 5;
byte encCLA = (byte) (apdu[0] | (byte) 0x0C);
- byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2],
- apdu[3] };
+ byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2], apdu[3] };
byte[] paddedHeader = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH,
encHeader);
int leFieldLen;
byte[] leField = null;
- if(leAvailable) {
+ if (leAvailable) {
leField = new byte[3];
leField[0] = (byte) 0x97;
leField[1] = (byte) 0x01;
leField[2] = apdu[4];
leFieldLen = leField.length;
} else {
-
+
leFieldLen = 0;
}
byte[] macData = new byte[paddedHeader.length + leFieldLen];
- System.arraycopy(paddedHeader, 0, macData, 0,
- paddedHeader.length);
-
- if(leAvailable) {
+ System.arraycopy(paddedHeader, 0, macData, 0, paddedHeader.length);
+
+ if (leAvailable) {
System.arraycopy(leField, 0, macData, paddedHeader.length,
leField.length);
-
- macData = DNIeCryptoUtil.applyPadding(
- BLOCK_LENGTH, macData);
+
+ macData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, macData);
}
incrementSSC();
- byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(macData,
- kMac, this.ssc, BLOCK_LENGTH);
+ byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(macData, kMac, this.ssc,
+ BLOCK_LENGTH);
byte[] encapsulatedMac = new byte[mac.length + 2];
encapsulatedMac[0] = (byte) 0x8E;
@@ -852,28 +889,28 @@ public class DNIeSecuredChannel extends T0CardChannel {
completeMessage[2] = apdu[2];
completeMessage[3] = apdu[3];
completeMessage[4] = (byte) (encapsulatedMac.length + leFieldLen);
-
- if(leAvailable) {
- System
- .arraycopy(leField, 0, completeMessage, 5,
- leField.length);
+
+ if (leAvailable) {
+ System.arraycopy(leField, 0, completeMessage, 5, leField.length);
}
-
- System.arraycopy(encapsulatedMac, 0, completeMessage,
- 5 + leFieldLen, encapsulatedMac.length);
- return completeMessage;
-
+ System.arraycopy(encapsulatedMac, 0, completeMessage, 5 + leFieldLen,
+ encapsulatedMac.length);
+
+ return completeMessage;
+
}
-
+
private byte[] secureAPDUWithData(byte[] apdu) throws CardException {
- if(apdu.length < 6) {
-
- log.error("Error securing APDU - invalid APDU length: " + apdu.length);
- throw new CardException("Error securing APDU - invalid APDU length: " + apdu.length);
+ if (apdu.length < 6) {
+
+ log.error("Error securing APDU - invalid APDU length: "
+ + apdu.length);
+ throw new CardException(
+ "Error securing APDU - invalid APDU length: " + apdu.length);
}
-
+
byte cla = apdu[0];
byte ins = apdu[1];
byte p1 = apdu[2];
@@ -881,23 +918,24 @@ public class DNIeSecuredChannel extends T0CardChannel {
byte lc = apdu[4];
boolean leAvailable;
- if(apdu.length == lc + 5 + 1) {
-
+ if (apdu.length == lc + 5 + 1) {
+
leAvailable = true;
- } else if(apdu.length != lc + 5) {
-
- log.error("Error securing APDU - invalid APDU length: " + apdu.length);
+ } else if (apdu.length != lc + 5) {
+
+ log.error("Error securing APDU - invalid APDU length: "
+ + apdu.length);
throw new CardException("Invalid APDU length or format.");
} else {
-
+
leAvailable = false;
}
-
+
byte[] leField = null;
- if(leAvailable) {
-
- byte le = apdu[apdu.length-1];
-
+ if (leAvailable) {
+
+ byte le = apdu[apdu.length - 1];
+
leField = new byte[3];
leField[0] = (byte) 0x97;
leField[1] = (byte) 0x01;
@@ -942,14 +980,17 @@ public class DNIeSecuredChannel extends T0CardChannel {
System.arraycopy(encapsulated, 0, headerAndData, paddedHeader.length,
encapsulated.length);
- if(leAvailable) {
+ if (leAvailable) {
byte[] macData = new byte[headerAndData.length + leField.length];
- System.arraycopy(headerAndData, 0, macData, 0, headerAndData.length);
- System.arraycopy(leField, 0, macData, headerAndData.length, leField.length);
-
+ System
+ .arraycopy(headerAndData, 0, macData, 0,
+ headerAndData.length);
+ System.arraycopy(leField, 0, macData, headerAndData.length,
+ leField.length);
+
headerAndData = macData;
}
-
+
byte[] paddedHeaderAndData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH,
headerAndData);
@@ -964,34 +1005,35 @@ public class DNIeSecuredChannel extends T0CardChannel {
System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length);
int leFieldLen;
- if(leAvailable) {
+ if (leAvailable) {
leFieldLen = leField.length;
} else {
leFieldLen = 0;
}
-
+
byte[] completeMessage = new byte[5 + encapsulated.length
+ encapsulatedMac.length + leFieldLen];
completeMessage[0] = encCLA;
completeMessage[1] = ins;
completeMessage[2] = p1;
completeMessage[3] = p2;
-
+
completeMessage[4] = (byte) (encapsulated.length + leFieldLen + encapsulatedMac.length);
System.arraycopy(encapsulated, 0, completeMessage, 5,
encapsulated.length);
-
- if(leAvailable) {
- System.arraycopy(leField, 0, completeMessage, 5 + encapsulated.length, leFieldLen);
+
+ if (leAvailable) {
+ System.arraycopy(leField, 0, completeMessage,
+ 5 + encapsulated.length, leFieldLen);
}
-
- System.arraycopy(encapsulatedMac, 0, completeMessage,
- 5 + encapsulated.length + leFieldLen, encapsulatedMac.length);
- return completeMessage;
+ System.arraycopy(encapsulatedMac, 0, completeMessage, 5
+ + encapsulated.length + leFieldLen, encapsulatedMac.length);
+
+ return completeMessage;
}
-
+
private byte[] secureAPDU(byte[] apdu) throws CardException {
if (apdu == null || apdu.length < 4) {
@@ -1005,12 +1047,12 @@ public class DNIeSecuredChannel extends T0CardChannel {
return secureAPDUWithoutData(apdu);
}
- if(apdu.length > 5) {
+ if (apdu.length > 5) {
return secureAPDUWithData(apdu);
}
-
- throw new CardException("Error securing APDU - unexpected APDU length.");
+
+ throw new CardException("Error securing APDU - unexpected APDU length.");
}
private byte[] verifyAndDecryptSecuredResponseAPDU(byte[] securedAPDU)
@@ -1031,15 +1073,18 @@ public class DNIeSecuredChannel extends T0CardChannel {
System.arraycopy(commandResponse, 0, macData, data.length,
commandResponse.length);
- byte[] paddedMacData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH, macData);
+ byte[] paddedMacData = DNIeCryptoUtil.applyPadding(BLOCK_LENGTH,
+ macData);
incrementSSC();
- byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(paddedMacData, this.kMac, this.ssc, BLOCK_LENGTH);
+ byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(paddedMacData, this.kMac,
+ this.ssc, BLOCK_LENGTH);
if (!Arrays.equals(mac, obtainedMac)) {
- log.error("Error verifiying MAC of secured response. MAC values do not match.");
+ log
+ .error("Error verifiying MAC of secured response. MAC values do not match.");
throw new CardException("Unable to verify MAC of Response APDU.");
}
@@ -1047,14 +1092,14 @@ public class DNIeSecuredChannel extends T0CardChannel {
byte[] data2decrypt = new byte[data.length
- DNIeCryptoUtil.getCutOffLength(data, BLOCK_LENGTH)];
- System.arraycopy(data, DNIeCryptoUtil.getCutOffLength(data, BLOCK_LENGTH),
- data2decrypt, 0, data2decrypt.length);
+ System.arraycopy(data, DNIeCryptoUtil.getCutOffLength(data,
+ BLOCK_LENGTH), data2decrypt, 0, data2decrypt.length);
byte[] plainData = null;
try {
- plainData = DNIeCryptoUtil.perform3DESCipherOperation(data2decrypt, this.kEnc,
- Cipher.DECRYPT_MODE);
+ plainData = DNIeCryptoUtil.perform3DESCipherOperation(
+ data2decrypt, this.kEnc, Cipher.DECRYPT_MODE);
} catch (Exception e) {
log.error("Error decrypting data.", e);
throw new CardException("Unable to decrypt data.", e);
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java
index 17a42e4d..dfa70d91 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java
@@ -1,26 +1,30 @@
/*
-* Copyright 2008 Federal Chancellery Austria and
-* Graz University of Technology
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package at.gv.egiz.smcc;
import at.gv.egiz.smcc.util.ISO7816Utils;
import at.gv.egiz.smcc.util.TLV;
import at.gv.egiz.smcc.util.TLVSequence;
+
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
@@ -29,134 +33,158 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- *
+ *
* @author clemens
*/
public class EFObjectDirectory {
- protected static final Logger log = LoggerFactory.getLogger(EFObjectDirectory.class);
-
- protected byte[] fid;
- private byte[] ef_prkd;
- private byte[] ef_pukd;
- private byte[] ef_cd;
- private byte[] ef_aod;
-
- public EFObjectDirectory() {
- fid = new byte[] { (byte)0x50, (byte)0x31 };
- }
-
- public EFObjectDirectory(byte[] fid) {
- this.fid = fid;
- }
-
- /**
- * assume DF.CIA selected
- * EF.OD selected afterwards
- * @param channel
- * @throws CardException
- * @throws SignatureCardException
- */
- public void selectAndRead(CardChannel channel) throws CardException, SignatureCardException {
-
- log.trace("select EF.OD");
- CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, 0x00, fid, 256);
- ResponseAPDU resp = channel.transmit(cmd);
-
- if (resp.getSW() != 0x9000) {
- throw new SignatureCardException("SELECT EF.OD failed: SW=0x"
- + Integer.toHexString(resp.getSW()));
- }
-
- log.trace("read EF.OD");
- byte[] efod = ISO7816Utils.readTransparentFile(channel, -1);
-
- for (TLV cio : new TLVSequence(efod)) {
- int tag = cio.getTag();
- byte[] seq = cio.getValue();
-
- if ((tag & 0xf0) == 0xa0 && seq.length >= 4) {
-
- byte[] path = Arrays.copyOfRange(seq, 4, 4+seq[3]);
-
- switch (cio.getTag() & 0x0f) {
- case 0:
- setEf_prkd(path);
- break;
- case 1:
- setEf_pukd(path);
- break;
- case 4:
- setEf_cd(path);
- break;
- case 8:
- setEf_aod(path);
- break;
- default:
- log.warn("CIOChoice 0x{} not supported: ",
- (cio.getTag() & 0x0f));
- }
- } else {
- log.trace("ignoring invalid CIO reference entry: {}", seq);
- }
- }
- }
-
- /**
- * @return the ef_prkd
- */
- public byte[] getEf_prkd() {
- return ef_prkd;
- }
-
- /**
- * @param ef_prkd the ef_prkd to set
- */
- public void setEf_prkd(byte[] ef_prkd) {
- this.ef_prkd = ef_prkd;
- }
-
- /**
- * @return the ef_pukd
- */
- public byte[] getEf_pukd() {
- return ef_pukd;
- }
-
- /**
- * @param ef_pukd the ef_pukd to set
- */
- public void setEf_pukd(byte[] ef_pukd) {
- this.ef_pukd = ef_pukd;
- }
-
- /**
- * @return the ef_cd
- */
- public byte[] getEf_cd() {
- return ef_cd;
- }
-
- /**
- * @param ef_cd the ef_cd to set
- */
- public void setEf_cd(byte[] ef_cd) {
- this.ef_cd = ef_cd;
- }
-
- /**
- * @return the ef_aod
- */
- public byte[] getEf_aod() {
- return ef_aod;
- }
-
- /**
- * @param ef_aod the ef_aod to set
- */
- public void setEf_aod(byte[] ef_aod) {
- this.ef_aod = ef_aod;
- }
-
+ protected static final Logger log = LoggerFactory
+ .getLogger(EFObjectDirectory.class);
+
+ protected byte[] fid;
+ private byte[] ef_prkd;
+ private byte[] ef_pukd;
+ private byte[] ef_aod;
+
+ private List<byte[]> ef_cd_list = new ArrayList<byte[]>();;
+
+ private Integer padding;
+
+ public EFObjectDirectory() {
+ fid = new byte[] { (byte) 0x50, (byte) 0x31 };
+ }
+
+ public EFObjectDirectory(byte[] fid) {
+ this.fid = fid;
+ }
+
+ public EFObjectDirectory(int padding) {
+
+ fid = new byte[] { (byte) 0x50, (byte) 0x31 };
+ this.padding = padding;
+
+ }
+
+ /**
+ * assume DF.CIA selected EF.OD selected afterwards
+ *
+ * @param channel
+ * @throws CardException
+ * @throws SignatureCardException
+ */
+ public void selectAndRead(CardChannel channel) throws CardException,
+ SignatureCardException {
+
+ executeSelect(channel);
+
+ byte[] efod = ISO7816Utils.readTransparentFile(channel, -1);
+
+ for (TLV cio : new TLVSequence(efod)) {
+ int tag = cio.getTag();
+
+ if (padding != null && tag == padding) {
+ // reached padding - quit record extraction
+ break;
+ }
+
+ byte[] seq = cio.getValue();
+
+ if ((tag & 0xf0) == 0xa0 && seq.length >= 4) {
+
+ byte[] path = Arrays.copyOfRange(seq, 4, 4 + seq[3]);
+
+ switch (cio.getTag() & 0x0f) {
+ case 0:
+ setEf_prkd(path);
+ break;
+ case 1:
+ setEf_pukd(path);
+ break;
+ case 4:
+ addCdToEf_cd_list(path);
+ break;
+ case 8:
+ setEf_aod(path);
+ break;
+ default:
+ log.warn("CIOChoice 0x{} not supported: ",
+ (cio.getTag() & 0x0f));
+ }
+ } else {
+ log.trace("ignoring invalid CIO reference entry: {}", seq);
+ }
+ }
+ }
+
+ protected void executeSelect(CardChannel channel)
+ throws SignatureCardException, CardException {
+
+ CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x02, 0x00, fid, 256);
+ ResponseAPDU resp = channel.transmit(cmd);
+
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("SELECT EF.OD failed: SW=0x"
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ /**
+ * @return the ef_prkd
+ */
+ public byte[] getEf_prkd() {
+ return ef_prkd;
+ }
+
+ /**
+ * @param ef_prkd
+ * the ef_prkd to set
+ */
+ public void setEf_prkd(byte[] ef_prkd) {
+ this.ef_prkd = ef_prkd;
+ }
+
+ /**
+ * @return the ef_pukd
+ */
+ public byte[] getEf_pukd() {
+ return ef_pukd;
+ }
+
+ /**
+ * @param ef_pukd
+ * the ef_pukd to set
+ */
+ public void setEf_pukd(byte[] ef_pukd) {
+ this.ef_pukd = ef_pukd;
+ }
+
+ /**
+ * @return the ef_aod
+ */
+ public byte[] getEf_aod() {
+ return ef_aod;
+ }
+
+ public List<byte[]> getEf_cd_list() {
+ return ef_cd_list;
+ }
+
+ public void setEf_cd_list(List<byte[]> ef_cd_list) {
+ this.ef_cd_list = ef_cd_list;
+ }
+
+ public void addCdToEf_cd_list(byte[] ef_cd) {
+
+ this.ef_cd_list.add(ef_cd);
+ }
+
+ /**
+ * @param ef_aod
+ * the ef_aod to set
+ */
+ public void setEf_aod(byte[] ef_aod) {
+ this.ef_aod = ef_aod;
+ }
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java
index 1c30ca57..a0d426c7 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/ESDNIeCard.java
@@ -21,7 +21,7 @@ import at.gv.egiz.smcc.util.SMCCHelper;
public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard {
- private final byte[] MASTER_FILE_ID = new byte[] {
+ public static final byte[] MASTER_FILE_ID = new byte[] {
(byte) 0x4D, (byte) 0x61, (byte) 0x73, (byte) 0x74, (byte) 0x65,
(byte) 0x72, (byte) 0x2E, (byte) 0x46, (byte) 0x69, (byte) 0x6C,
@@ -42,20 +42,16 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard {
"[0-9A-Za-z_<>!()?%\\-=&+\\.]", "at/gv/egiz/smcc/ESDNIeCard",
"sig.pin", (byte) 0x00, new byte[] {}, PinInfo.UNKNOWN_RETRIES);
- protected CardChannel channel;
@Override
protected CardChannel getCardChannel() {
-
- if (channel == null) {
-
- channel = new DNIeSecuredChannel(getCard().getBasicChannel());
- }
-
- return channel;
+
+ // set up a new secure channel each time
+ return new DNIeSecuredChannel(getCard().getBasicChannel());
}
-
+
@Override
+ @Exclusive
public byte[] createSignature(InputStream input, KeyboxName keyboxName,
PINGUI pinGUI, String alg) throws SignatureCardException,
InterruptedException, IOException {
@@ -63,7 +59,7 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard {
CardChannel channel = getCardChannel();
try {
-
+
// Select MF
executeSelectMasterFile(channel);
@@ -135,20 +131,22 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard {
throw new SignatureCardException(
"Error creating signature with DNIe card.", e);
}
+
}
@Override
+ @Exclusive
public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
throws SignatureCardException, InterruptedException {
byte[] result = null;
-
+
CardChannel channel = getCardChannel();
byte[] certId = null;
try {
-
+
// Select MF
executeSelectMasterFile(channel);
@@ -161,7 +159,7 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard {
efOd.selectAndRead(channel);
DNIeCIOCertificateDirectory efCd = new DNIeCIOCertificateDirectory(
- efOd.getEf_cd());
+ efOd.getEf_cd_list().get(0));
try {
efCd.selectAndRead(channel);
@@ -214,17 +212,18 @@ public class ESDNIeCard extends AbstractSignatureCard implements SignatureCard {
compressedWithoutHeader.length);
result = decompressData(compressedWithoutHeader);
-
+
} catch (CardException e) {
log.error("Error reading certificate from card.", e);
throw new SignatureCardException(
"Error reading certificate from card.", e);
}
-
+
return result;
}
+
@Override
public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
throws SignatureCardException, InterruptedException {
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java
new file mode 100644
index 00000000..3fd1503c
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAODirectory.java
@@ -0,0 +1,143 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc;
+
+import iaik.me.asn1.ASN1;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.smcc.util.ISO7816Utils;
+import at.gv.egiz.smcc.util.TLVSequence;
+
+public class FINEIDAODirectory {
+
+ protected static final Logger log = LoggerFactory.getLogger(CIOCertificateDirectory.class);
+ protected byte[] fid;
+ protected List<FINEIDAuthenticationObject> aos;
+
+ public FINEIDAODirectory(byte[] fid) {
+
+ this.fid = FINEIDUtil.removeMFPath(fid);
+ aos = new ArrayList<FINEIDAuthenticationObject>();
+ }
+
+ /**
+ * assume DF.CIA selected
+ * CIO.CD selected afterwards
+ *
+ * @param channel
+ * @throws CardException
+ * @throws SignatureCardException
+ * @throws IOException if ASN.1 structure cannot be parsed
+ */
+ public void selectAndRead(CardChannel channel) throws CardException, SignatureCardException, IOException {
+
+ CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x08, ISO7816Utils.P2_FCI, fid, 256);
+ ResponseAPDU resp = channel.transmit(cmd);
+
+ byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCI);
+ byte[] fd = new TLVSequence(fcx).getValue(0x82);
+
+ if ((fd[0] & 0x05) == 0x01) {
+
+ readAuthenticationObjectsFromTransparentFile(channel);
+ }
+ }
+
+ protected byte[] doReadTransparentFile(CardChannel channel) throws CardException, SignatureCardException {
+
+ return ISO7816Utils.readTransparentFile(channel, -1);
+ }
+
+ protected void readAuthenticationObjectsFromTransparentFile(CardChannel channel) throws CardException, SignatureCardException, IOException {
+
+ byte[] ef = doReadTransparentFile(channel);
+
+ int i = 0;
+ int j;
+
+ do {
+ int length = 0;
+ int ll = 0;
+ if ((ef[i + 1] & 0xf0) == 0x80) {
+ ll = ef[i + 1] & 0x7f;
+ for (int it = 0; it < ll; it++) {
+ length = (length << 8) + (ef[i + it + 2] & 0xff);
+ }
+ } else {
+ length = (ef[i + 1] & 0xff);
+ }
+
+ log.trace("read transparent file entry: tag 0x{}, length 0x{}", Integer.toHexString(ef[i]),
+ Integer.toHexString(length));
+
+ j = i + 2 + ll + length;
+ addAuthenticationObject(Arrays.copyOfRange(ef, i, j));
+ i = j;
+ } while (i < ef.length && ef[i] > 0);
+
+ }
+
+ protected void addAuthenticationObject(byte[] ao) throws IOException {
+
+ ASN1 authenticationObjects = new ASN1(ao);
+
+ FINEIDAuthenticationObject authObject = new FINEIDAuthenticationObject();
+ authObject.setLabel(authenticationObjects.getElementAt(0).getElementAt(0).gvString());
+
+ authObject.setAuthId(authenticationObjects.getElementAt(1).getElementAt(0).gvByteArray());
+
+ //read CONTEXTSPECIFIC manually
+ byte[] ctxSpecific = authenticationObjects.getElementAt(authenticationObjects.getSize()-1).getEncoded();
+
+ if ((ctxSpecific[0] & 0xff) == 0xa1) {
+ int ll = ((ctxSpecific[1] & 0xf0) == 0x80)
+ ? (ctxSpecific[1] & 0x0f) + 2 : 2;
+ ASN1 aoAttributes = new ASN1(Arrays.copyOfRange(ctxSpecific, ll, ctxSpecific.length));
+
+ authObject.setPath(aoAttributes.getElementAt(aoAttributes.getSize()-1).getElementAt(0).gvByteArray());
+
+ // get pwdReference
+ byte[] ctxSpecific2 = aoAttributes.getElementAt(4).getEncoded();
+ ASN1 pwdRef = new ASN1(ctxSpecific2);
+
+ authObject.setPwdReference(pwdRef.gvByteArray());
+
+ } else {
+ log.warn("expected CONTEXTSPECIFIC, got 0x{}",
+ Integer.toHexString(ctxSpecific[0]));
+ }
+
+ log.debug("adding {}", authObject);
+ aos.add(authObject);
+ }
+
+ public List<FINEIDAuthenticationObject> getAOs() {
+ return aos;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java
new file mode 100644
index 00000000..d25946f1
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDAuthenticationObject.java
@@ -0,0 +1,57 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc;
+
+public class FINEIDAuthenticationObject {
+
+ private String label;
+ private byte[] authId;
+ private byte[] path;
+ private byte[] pwdReference;
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public byte[] getAuthId() {
+ return authId;
+ }
+
+ public void setAuthId(byte[] authId) {
+ this.authId = authId;
+ }
+
+ public byte[] getPath() {
+ return path;
+ }
+
+ public void setPath(byte[] path) {
+ this.path = path;
+ }
+
+ public byte[] getPwdReference() {
+ return pwdReference;
+ }
+
+ public void setPwdReference(byte[] pwdReference) {
+ this.pwdReference = pwdReference;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java
new file mode 100644
index 00000000..0de2b3c1
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOCertificateDirectory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package at.gv.egiz.smcc;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import at.gv.egiz.smcc.util.ISO7816Utils;
+import at.gv.egiz.smcc.util.TLVSequence;
+
+public class FINEIDCIOCertificateDirectory extends CIOCertificateDirectory {
+
+ protected static final boolean RETRIEVE_AUTH_ID_FROM_ASN1 = Boolean.FALSE;
+
+ public FINEIDCIOCertificateDirectory(byte[] fid) {
+
+ super(fid);
+ this.fid = FINEIDUtil.removeMFPath(fid);
+ }
+
+ @Override
+ protected byte[] executeSelect(CardChannel channel) throws CardException {
+
+ CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x08, ISO7816Utils.P2_FCI, fid, 256);
+ ResponseAPDU resp = channel.transmit(cmd);
+
+ byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCI);
+ byte[] fd = new TLVSequence(fcx).getValue(0x82);
+
+ return fd;
+ }
+
+ @Override
+ protected boolean retrieveAuthIdFromASN1() {
+
+ return RETRIEVE_AUTH_ID_FROM_ASN1;
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java
new file mode 100644
index 00000000..2d89e5bc
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCIOKeyDirectory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package at.gv.egiz.smcc;
+
+public class FINEIDCIOKeyDirectory extends FINEIDCIOCertificateDirectory {
+
+ protected static final boolean RETRIEVE_AUTH_ID_FROM_ASN1 = Boolean.TRUE;
+
+ public FINEIDCIOKeyDirectory(byte[] fid) {
+
+ super(fid);
+ }
+
+ protected boolean retrieveAuthIdFromASN1() {
+
+ return RETRIEVE_AUTH_ID_FROM_ASN1;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java
new file mode 100644
index 00000000..96c8e517
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package at.gv.egiz.smcc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.smcc.pin.gui.PINGUI;
+import at.gv.egiz.smcc.util.ISO7816Utils;
+import at.gv.egiz.smcc.util.SMCCHelper;
+import at.gv.egiz.smcc.util.TLVSequence;
+
+public class FINEIDCard extends AbstractSignatureCard implements SignatureCard {
+
+ private static final int EF_OD_PADDING = 0xFF;
+ private static final String SIG_CERT_LABEL = "allekirjoitusvarmenne";
+ private static final String SIG_KEY_LABEL = "allekirjoitusavain";
+
+ private final Logger log = LoggerFactory.getLogger(FINEIDCard.class);
+
+ protected PinInfo pinInfo = new PinInfo(6, 8, "[0-9]",
+ "at/gv/egiz/smcc/FINEIDCard", "sig.pin", (byte) 0x00,
+ new byte[] {}, PinInfo.UNKNOWN_RETRIES);
+
+ @Override
+ public byte[] createSignature(InputStream input, KeyboxName keyboxName,
+ PINGUI pinGUI, String alg) throws SignatureCardException,
+ InterruptedException, IOException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+
+ FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory(
+ EF_OD_PADDING);
+ ef_od.selectAndRead(channel);
+
+ // read PRKD to find correct key
+ FINEIDCIOKeyDirectory ef_prkd = new FINEIDCIOKeyDirectory(ef_od
+ .getEf_prkd());
+ ef_prkd.selectAndRead(channel);
+
+ byte[] efKey = null;
+ byte[] authID = null;
+
+ for (CIOCertificate cioCertificate : ef_prkd.getCIOs()) {
+ String label = cioCertificate.getLabel();
+ if (label != null
+ && label.toLowerCase().contains(
+ SIG_KEY_LABEL.toLowerCase())) {
+
+ efKey = cioCertificate.getEfidOrPath();
+ authID = cioCertificate.getAuthId();
+ }
+ }
+
+ if (efKey == null) {
+
+ throw new SignatureCardException(
+ "Could not determine path to private key from PrKD.");
+ }
+
+ if (authID == null) {
+
+ throw new SignatureCardException(
+ "Could not determine authID of private key from PrKD.");
+ }
+
+ // read AOD to find associated PIN (authId must match)
+ FINEIDAODirectory ef_aod = new FINEIDAODirectory(ef_od.getEf_aod());
+ ef_aod.selectAndRead(channel);
+
+ byte[] pinPath = null;
+ byte[] pwdRef = null;
+ for (FINEIDAuthenticationObject ao : ef_aod.getAOs()) {
+
+ byte[] id = ao.getAuthId();
+ if (id != null && Arrays.equals(id, authID)) {
+ pinPath = ao.getPath();
+ pwdRef = ao.getPwdReference();
+ }
+ }
+
+ if (pinPath == null) {
+
+ throw new SignatureCardException(
+ "Could not determine path to PIN from AOD.");
+ }
+
+ if (pwdRef == null) {
+
+ throw new SignatureCardException(
+ "Could not determine PIN reference from AOD.");
+ }
+
+ // verify PIN
+ verifyPINLoop(channel, pinInfo, pinGUI, pinPath,
+ pwdRef[pwdRef.length - 1]);
+
+ // Set MSE
+ CommandAPDU selectKeyPath = new CommandAPDU((byte) 0x00,
+ (byte) 0xA4, (byte) 0x08, (byte) 0x00, FINEIDUtil
+ .removeMFPath(efKey));
+ ResponseAPDU resp = channel.transmit(selectKeyPath);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new SignatureCardException(
+ "Could not select private key file DF.");
+ }
+
+ executeRestoreMSE(channel);
+
+ byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x12,
+ (byte) 0x81, (byte) 0x02, efKey[efKey.length - 2],
+ efKey[efKey.length - 1] };
+
+ executeSetMSE(channel, dst);
+
+ // SIGN
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get MessageDigest.", e);
+ throw new SignatureCardException(e);
+ }
+ // calculate message digest
+ byte[] digest = new byte[md.getDigestLength()];
+ for (int l; (l = input.read(digest)) != -1;) {
+ md.update(digest, 0, l);
+ }
+ digest = md.digest();
+
+ byte[] sigVal = executeSign(channel, digest);
+ return sigVal;
+
+ } catch (CardException e) {
+
+ throw new SignatureCardException("Error creating signature.", e);
+ }
+
+ }
+
+ @Override
+ public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
+ throws SignatureCardException, InterruptedException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory(
+ EF_OD_PADDING);
+ ef_od.selectAndRead(channel);
+
+ byte[] certPath = null;
+
+ for (int i = 0; i < ef_od.getEf_cd_list().size(); i++) {
+
+ FINEIDCIOCertificateDirectory ef_cd = new FINEIDCIOCertificateDirectory(
+ ef_od.getEf_cd_list().get(i));
+
+ try {
+ ef_cd.selectAndRead(channel);
+ } catch (IOException e) {
+ log.debug("Cannot read EF.CD - try next one in list..");
+ continue;
+ }
+
+ for (CIOCertificate cioCertificate : ef_cd.getCIOs()) {
+ String label = cioCertificate.getLabel();
+ if (label != null
+ && label.toLowerCase().contains(
+ SIG_CERT_LABEL.toLowerCase())) {
+ certPath = cioCertificate.getEfidOrPath();
+ }
+ }
+ }
+
+ if (certPath == null) {
+
+ throw new SignatureCardException(
+ "Could not determine path to certificate.");
+ }
+
+ log
+ .debug("Read certificate path: "
+ + SMCCHelper.toString(certPath));
+
+ certPath = FINEIDUtil.removeMFPath(certPath);
+
+ CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+ (byte) 0x08, (byte) 0x00, certPath);
+ ResponseAPDU resp = channel.transmit(apdu);
+
+ byte[] fcx = new TLVSequence(resp.getBytes())
+ .getValue(ISO7816Utils.TAG_FCI);
+ byte[] fileDataLength = new TLVSequence(fcx).getValue(0x81);
+
+ return ISO7816Utils.readTransparentFile(channel,
+ computeLengthFromByteArray(fileDataLength));
+
+ } catch (CardException e) {
+
+ throw new SignatureCardException(
+ "Error reading certificate from card.", e);
+ }
+ }
+
+ @Override
+ public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
+ throws SignatureCardException, InterruptedException {
+
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ protected void verifyPINLoop(CardChannel channel, PinInfo spec,
+ PINGUI provider, byte[] pinPath, byte keyID)
+ throws LockedException, NotActivatedException,
+ SignatureCardException, InterruptedException, CardException {
+
+ CommandAPDU verifySelect = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+ (byte) 0x08, (byte) 0x00, FINEIDUtil.removeMFPath(pinPath));
+ ResponseAPDU response = channel.transmit(verifySelect);
+
+ if (response.getSW() != 0x9000) {
+
+ throw new SignatureCardException("Cannot select PIN path "
+ + SMCCHelper.toString(pinPath) + ": "
+ + Integer.toHexString(response.getSW()));
+ }
+
+ int retries = -1;
+
+ do {
+ retries = verifyPIN(channel, spec, provider, retries, keyID);
+ } while (retries > 0);
+ }
+
+ protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
+ PINGUI provider, int retries, byte keyID)
+ throws SignatureCardException, LockedException,
+ NotActivatedException, InterruptedException, CardException {
+
+ VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00,
+ (byte) 0x20, (byte) 0x00, keyID, (byte) 0x08, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00 }, 0,
+ VerifyAPDUSpec.PIN_FORMAT_ASCII, 8);
+
+ ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec,
+ retries);
+
+ if (resp.getSW() == 0x9000) {
+ return -1;
+ }
+ if (resp.getSW() >> 4 == 0x63c) {
+ return 0x0f & resp.getSW();
+ }
+
+ switch (resp.getSW()) {
+ case 0x6983:
+ // authentication method blocked
+ throw new LockedException();
+ case 0x6984:
+ // reference data not usable
+ throw new NotActivatedException();
+ case 0x6985:
+ // conditions of use not satisfied
+ throw new NotActivatedException();
+
+ default:
+ String msg = "VERIFY failed. SW="
+ + Integer.toHexString(resp.getSW());
+ log.info(msg);
+ throw new SignatureCardException(msg);
+ }
+
+ }
+
+ private void executeRestoreMSE(CardChannel channel) throws CardException {
+
+ CommandAPDU mseRestore = new CommandAPDU((byte) 0x00, (byte) 0x22,
+ (byte) 0xF3, (byte) 0x00);
+ ResponseAPDU resp = channel.transmit(mseRestore);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new CardException("Error restoring MSE: "
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ private void executeSetMSE(CardChannel channel, byte[] dst)
+ throws CardException {
+
+ CommandAPDU mseSet = new CommandAPDU((byte) 0x00, (byte) 0x22,
+ (byte) 0x41, (byte) 0xB6, dst);
+ ResponseAPDU resp = channel.transmit(mseSet);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new CardException("Error setting MSE: "
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ private byte[] executeSign(CardChannel channel, byte[] hash)
+ throws CardException {
+
+ CommandAPDU sign = new CommandAPDU((byte) 0x00, (byte) 0x2A,
+ (byte) 0x9E, (byte) 0x9A, hash);
+ ResponseAPDU resp = channel.transmit(sign);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new CardException("Error signing hash: "
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ return resp.getData();
+ }
+
+ private int computeLengthFromByteArray(byte[] input) {
+
+ int result = 0;
+
+ for (int i = 0; i < input.length; i++) {
+ int current = input[input.length - 1 - i];
+ result = result + (int) (current * Math.pow(256, i));
+ }
+ return result;
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java
new file mode 100644
index 00000000..2690d694
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDEFObjectDirectory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package at.gv.egiz.smcc;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+public class FINEIDEFObjectDirectory extends EFObjectDirectory {
+
+ public FINEIDEFObjectDirectory(int padding) {
+
+ super(padding);
+ }
+
+ @Override
+ protected void executeSelect(CardChannel channel)
+ throws SignatureCardException, CardException {
+
+ CommandAPDU cmd = new CommandAPDU(0x00, 0xA4, 0x00, 0x00, fid, 256);
+ ResponseAPDU resp = channel.transmit(cmd);
+
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("SELECT EF.OD failed: SW=0x"
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java
new file mode 100644
index 00000000..11f37100
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDUtil.java
@@ -0,0 +1,37 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc;
+
+public class FINEIDUtil {
+
+ public static byte[] removeMFPath(byte[] completePath) {
+
+ byte[] result = null;
+
+ // remove MF path
+ if (completePath.length >= 2 && completePath[0] == 0x3F
+ && completePath[1] == 0x00) {
+ result = new byte[completePath.length - 2];
+ System.arraycopy(completePath, 2, result, 0,
+ completePath.length - 2);
+ } else {
+ result = completePath;
+ }
+
+ return result;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java b/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java
index c4324773..434f35a1 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java
@@ -109,7 +109,7 @@ public class LIEZertifikatCard extends AbstractSignatureCard implements Signatur
EFObjectDirectory ef_od = new EFObjectDirectory();
ef_od.selectAndRead(channel);
- CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd());
+ CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd_list().get(0));
ef_cd.selectAndRead(channel);
byte[] ef_qcert = null;
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
index 3a773ca2..22d295e0 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
@@ -183,7 +183,7 @@ public class SignatureCardFactory {
// e-card G3
supportedCards.add(new SupportedCard(
// ATR
- // (3b:dd:96:ff:81:b1:fe:45:1f:03:80:31:b0:52:02:03:64:04:1b:b4:22:81:05:18)
+ // (3b:dd:96:ff:81:b1:fe:45:1f:03:80:31:b0:52:02:03:64:04:1b:b4:22:81:05:18)
new byte[] { (byte) 0x3b, (byte) 0xdd, (byte) 0x96,
(byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe,
(byte) 0x45, (byte) 0x1f, (byte) 0x03, (byte) 0x00,
@@ -203,7 +203,7 @@ public class SignatureCardFactory {
// a-sign premium (EPA)
supportedCards.add(new SupportedCard(
// ATR
- // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
+ // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11,
(byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe,
(byte) 0x45, (byte) 0x45, (byte) 0x50, (byte) 0x41,
@@ -224,7 +224,7 @@ public class SignatureCardFactory {
// a-sign premium (MCA)
supportedCards.add(new SupportedCard(
// ATR
- // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
+ // (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11,
(byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe,
(byte) 0x45, (byte) 0x4D, (byte) 0x43, (byte) 0x41,
@@ -309,6 +309,41 @@ public class SignatureCardFactory {
(byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0xff,
(byte) 0xff }, "at.gv.egiz.smcc.ESDNIeCard"));
+ // FMNT card - ATR is correct, but implementation is NOT equal to DNIe
+// supportedCards.add(new SupportedCard(
+// // ATR
+// // [3b:ef:00:00:40:14:80:25:43:45:52:45:53:57:01:16:01:01:03:90:00]
+// new byte[] { (byte) 0x3b, (byte) 0xEF, (byte) 0x00,
+// (byte) 0x00, (byte) 0x40, (byte) 0x14, (byte) 0x80,
+// (byte) 0x25, (byte) 0x43, (byte) 0x45, (byte) 0x52,
+// (byte) 0x45, (byte) 0x53, (byte) 0x57, (byte) 0x01,
+// (byte) 0x16, (byte) 0x01, (byte) 0x01, (byte) 0x03,
+// (byte) 0x90, (byte) 0x00 },
+// // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff)
+// new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
+// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+// (byte) 0xff, (byte) 0xff },
+// "at.gv.egiz.smcc.ESDNIeCard"));
+
+ // FIN eID
+ supportedCards.add(new SupportedCard(
+ // ATR [3b:7B:94:00:00:80:62:12:51:56:46:69:6E:45:49:44]
+ new byte[] { (byte) 0x3b, (byte) 0x7B, (byte) 0x94,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x62,
+ (byte) 0x00, (byte) 0x51, (byte) 0x56, (byte) 0x46,
+ (byte) 0x69, (byte) 0x6E, (byte) 0x45, (byte) 0x49,
+ (byte) 0x44 },
+ // mask (ff:ff:ff:ff:ff:ff:ff:00:ff:ff:ff:ff:ff:ff:ff:ff) -
+ // ignore card OS minor version
+ new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff }, "at.gv.egiz.smcc.FINEIDCard"));
+
// ITCards
supportedCards.add(new SupportedCard(
// ATR =
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java
index 4806b6c0..65bcd84b 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java
@@ -49,7 +49,7 @@ public class T0CardChannel extends LogCardChannel {
CommandAPDU command = new CommandAPDU(new byte[] { (byte) 0x00,
(byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) sw2 });
ResponseAPDU resp = channel.transmit(command);
-
+
try {
bof.write(resp.getData());
} catch (IOException e) {
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
index f35086b6..44045d3c 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
@@ -65,6 +65,11 @@ public class ISO7816Utils {
@Override
protected byte[] readBinary(int offset, int len) throws IOException {
+ if(len < 1) {
+ // nothing to read - return
+ return new byte[0];
+ }
+
ResponseAPDU resp;
try {
resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
@@ -76,7 +81,7 @@ public class ISO7816Utils {
// handle case: wrong number of bytes requested from card
// card indicates correct number of bytes available in SW2
if (resp.getSW1() == 0x6c) {
-
+
try {
resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
0x7F & (offset >> 8), offset & 0xFF, resp
@@ -106,7 +111,6 @@ public class ISO7816Utils {
};
return file;
-
}
private static byte[] readFromInputStream(TransparentFileInputStream is) throws CardException, SignatureCardException {