diff options
Diffstat (limited to 'smcc')
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(); +    } +     +  } +   +} | 
