summaryrefslogtreecommitdiff
path: root/smcc/src
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CIOCertificate.java114
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java132
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java162
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java30
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java18
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/TLV.java84
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java80
7 files changed, 616 insertions, 4 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificate.java b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificate.java
new file mode 100644
index 00000000..6a778245
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificate.java
@@ -0,0 +1,114 @@
+/*
+* 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;
+
+/**
+ *
+ * @author clemens
+ */
+public class CIOCertificate {
+
+ /** CommonObjectAttributes */
+ private String label;
+ private byte[] authId;
+
+ /** CommonCertificateAttributes */
+ private byte[] iD;
+
+ /** X509CertificateAttributes*/
+ private byte[] efidOrPath;
+ private int serialNumber;
+
+ /**
+ * @return the label
+ */
+ public String getLabel() {
+ return label;
+ }
+
+ /**
+ * @param label the label to set
+ */
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ /**
+ * @return the authId
+ */
+ public byte[] getAuthId() {
+ return authId;
+ }
+
+ /**
+ * @param authId the authId to set
+ */
+ public void setAuthId(byte[] authId) {
+ this.authId = authId;
+ }
+
+ /**
+ * @return the iD
+ */
+ public byte[] getiD() {
+ return iD;
+ }
+
+ /**
+ * @param iD the iD to set
+ */
+ public void setiD(byte[] iD) {
+ this.iD = iD;
+ }
+
+ /**
+ * @return the efidOrPath
+ */
+ public byte[] getEfidOrPath() {
+ return efidOrPath;
+ }
+
+ /**
+ * @param efidOrPath the efidOrPath to set
+ */
+ public void setEfidOrPath(byte[] efidOrPath) {
+ this.efidOrPath = efidOrPath;
+ }
+
+ /**
+ * @return the serialNumber
+ */
+ public int getSerialNumber() {
+ return serialNumber;
+ }
+
+ /**
+ * @param serialNumber the serialNumber to set
+ */
+ public void setSerialNumber(int serialNumber) {
+ this.serialNumber = serialNumber;
+ }
+
+ @Override
+ public String toString() {
+ return "CIOCertificate " + label;
+ }
+
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
new file mode 100644
index 00000000..fd7746e6
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
@@ -0,0 +1,132 @@
+/*
+* 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.TLVSequence;
+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;
+
+/**
+ *
+ * @author clemens
+ */
+public class CIOCertificateDirectory {
+
+ protected static final Logger log = LoggerFactory.getLogger(CIOCertificateDirectory.class);
+ protected byte[] fid;
+ protected List<CIOCertificate> cios;
+
+ public CIOCertificateDirectory(byte[] fid) {
+ this.fid = fid;
+ cios = new ArrayList<CIOCertificate>();
+ }
+
+ /**
+ * 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, 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);
+
+ if ((fd[0] & 0x04) > 0) {
+ for (int r = 1; r < fd[fd.length - 1]; r++) {
+ log.trace("read CIO record {}", r);
+ byte[] record = ISO7816Utils.readRecord(channel, r);
+ log.trace("{} bytes", record.length);
+ addCIOCertificate(record);
+ }
+ } else if ((fd[0] & 0x05) == 0x01) {
+ byte[] ef = ISO7816Utils.readTransparentFile(channel, -1);
+
+ int i = 0;
+ int j;
+
+ do {
+ int length = 0;
+ int ll = 0;
+ if ((ef[i + 1] & 0xf0) == 0x80) {
+ ll = ef[i + 1] & 0x7f;
+ for (int it = 0; it < ll; it++) {
+ length = (length << 8) + (ef[i + it + 2] & 0xff);
+ }
+ } else {
+ length = (ef[i + 1] & 0xff);
+ }
+
+ log.trace("read transparent file entry: tag 0x{}, length 0x{}", Integer.toHexString(ef[i]),
+ Integer.toHexString(length));
+
+ j = i + 2 + ll + length;
+ addCIOCertificate(Arrays.copyOfRange(ef, i, j));
+ i = j;
+ } while (i < ef.length && ef[i] > 0);
+ }
+ }
+
+ protected void addCIOCertificate(byte[] cio) throws IOException {
+
+ ASN1 x509Certificate = new ASN1(cio);
+
+ CIOCertificate cioCert = new CIOCertificate();
+ cioCert.setLabel(x509Certificate.getElementAt(0).getElementAt(0).gvString());
+ cioCert.setAuthId(x509Certificate.getElementAt(0).getElementAt(2).gvByteArray());
+ cioCert.setiD(x509Certificate.getElementAt(1).getElementAt(0).gvByteArray());
+
+ //read CONTEXTSPECIFIC manually
+ byte[] ctxSpecific = x509Certificate.getElementAt(2).getEncoded();
+ if ((ctxSpecific[0] & 0xff) == 0xa1) {
+ int ll = ((ctxSpecific[1] & 0xf0) == 0x80)
+ ? (ctxSpecific[1] & 0x0f) + 2 : 2;
+ ASN1 x509CertificateAttributes = new ASN1(Arrays.copyOfRange(ctxSpecific, ll, ctxSpecific.length));
+
+ cioCert.setEfidOrPath(x509CertificateAttributes.getElementAt(0).getElementAt(0).gvByteArray());
+
+ } else {
+ log.warn("expected CONTEXTSPECIFIC, got 0x{}",
+ Integer.toHexString(ctxSpecific[0]));
+ }
+
+ log.debug("adding {}", cioCert);
+ cios.add(cioCert);
+
+ }
+
+ public List<CIOCertificate> getCIOs() {
+ return cios;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java
new file mode 100644
index 00000000..17a42e4d
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/EFObjectDirectory.java
@@ -0,0 +1,162 @@
+/*
+* 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.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;
+
+/**
+ *
+ * @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;
+ }
+
+
+}
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 b5ffa267..70a35685 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/LIEZertifikatCard.java
@@ -105,12 +105,29 @@ public class LIEZertifikatCard extends AbstractSignatureCard implements Signatur
CardChannel channel = getCardChannel();
// SELECT DF.CIA
execSELECT_AID(channel, AID_SIG);
- byte[] ef_qcert = getFID_QCERT(channel);
+
+ EFObjectDirectory ef_od = new EFObjectDirectory();
+ ef_od.selectAndRead(channel);
+
+ CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd());
+ ef_cd.selectAndRead(channel);
+
+ byte[] ef_qcert = null;
+ for (CIOCertificate cioCertificate : ef_cd.getCIOs()) {
+ String label = cioCertificate.getLabel();
+ //"TEST LLV APO 2s Liechtenstein Post Qualified CA ID"
+ if (label != null && label.toLowerCase()
+ .contains("liechtenstein post qualified ca id")) {
+ ef_qcert = cioCertificate.getEfidOrPath();
+ }
+ }
+
+
if (ef_qcert == null) {
throw new NotActivatedException();
}
- // SELECT CERT
+ // SELECT CERT, assume efid
execSELECT_EF(channel, ef_qcert);
// READ BINARY
@@ -122,6 +139,9 @@ public class LIEZertifikatCard extends AbstractSignatureCard implements Signatur
} catch (FileNotFoundException e) {
throw new NotActivatedException();
+ } catch (IOException ex) {
+ log.warn("failed to get certificate info", ex);
+ throw new SignatureCardException(ex);
} catch (CardException e) {
log.info("Failed to get certificate.", e);
throw new SignatureCardException(e);
@@ -294,7 +314,11 @@ public class LIEZertifikatCard extends AbstractSignatureCard implements Signatur
}
-
+
+ /**
+ *
+ * @return null if not found
+ */
protected byte[] getFID_QCERT(CardChannel channel)
throws SignatureCardException, CardException {
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 f7a8d58b..22a707c8 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
@@ -33,7 +33,23 @@ import at.gv.egiz.smcc.SignatureCardException;
import at.gv.egiz.smcc.VerifyAPDUSpec;
public class ISO7816Utils {
-
+
+ /**
+ * file control information templates
+ */
+ public static final byte TAG_FCP = 0x62;
+ public static final byte TAG_FMD = 0x64;
+ public static final byte TAG_FCI = 0x6f;
+
+ /**
+ * file control informatino bitmasks (SELECT P2)
+ */
+ public static final byte P2_FCI = 0x00;
+ public static final byte P2_FCP = 0x04;
+ public static final byte P2_FMD = 0x08;
+ public static final byte P2_NORESP = 0x0c;
+
+
public static TransparentFileInputStream openTransparentFileInputStream(
final CardChannel channel, int maxSize) {
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/TLV.java b/smcc/src/main/java/at/gv/egiz/smcc/util/TLV.java
new file mode 100644
index 00000000..b67fe3fa
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/TLV.java
@@ -0,0 +1,84 @@
+package at.gv.egiz.smcc.util;
+
+
+
+/*
+ * Copyright 2009 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class TLV {
+
+ private byte[] bytes;
+ private int start;
+
+ public TLV(byte[] bytes, int start) {
+ if (bytes.length - start < 2) {
+ throw new IllegalArgumentException("TLV must at least consit of tag and length.");
+ }
+ this.bytes = bytes;
+ this.start = start;
+ }
+
+ /**
+ * @return the tag
+ */
+ public int getTag() {
+ return 0xFF & bytes[start];
+ }
+
+ /**
+ * @return the length
+ */
+ public int getLength() {
+ return 0xFF & bytes[start + 1];
+ }
+
+ /**
+ * @return the value
+ */
+ public byte[] getValue() {
+ byte[] value = new byte[getLength()];
+ System.arraycopy(bytes, start + 2, value, 0, value.length);
+ return value;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Tag = " + Integer.toHexString(getTag()) + ", Length = " + getLength() + ", Value = " + toString(getValue());
+ }
+
+ public static String toString(byte[] b) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ for (int i = 1; i < b.length; i++) {
+ sb.append((i % 32 == 0) ? '\n' : ':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java b/smcc/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java
new file mode 100644
index 00000000..2409f212
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/TLVSequence.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2009 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package at.gv.egiz.smcc.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class TLVSequence implements Iterable<TLV> {
+
+ private byte[] bytes;
+
+ public TLVSequence(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public Iterator<TLV> iterator() {
+ return new TLVIterator();
+ }
+
+ public byte[] getValue(int tag) {
+ for (TLV tlv : this) {
+ if (tlv.getTag() == tag) {
+ return tlv.getValue();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (TLV tlv : this) {
+ sb.append(tlv).append('\n');
+ }
+ return sb.toString();
+ }
+
+ private class TLVIterator implements Iterator<TLV> {
+
+ private int pos = 0;
+
+ @Override
+ public boolean hasNext() {
+ return (bytes.length - pos > 2);
+ }
+
+ @Override
+ public TLV next() {
+ if (hasNext()) {
+ TLV tlv = new TLV(bytes, pos);
+ pos += tlv.getLength() + 2;
+ return tlv;
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}