diff options
Diffstat (limited to 'smcc/src/main/java')
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 { |