diff options
| author | tzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2011-03-10 13:41:13 +0000 | 
|---|---|---|
| committer | tzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2011-03-10 13:41:13 +0000 | 
| commit | f1dcaf9d6d71998706535fb780b8e7ada15b652e (patch) | |
| tree | 2f7ddfe0aba58ed18da954cd2066bbda56e16885 /smcc/src/main/java | |
| parent | c6f4294a3c529b3e601c8071c144f203826188e2 (diff) | |
| download | mocca-f1dcaf9d6d71998706535fb780b8e7ada15b652e.tar.gz mocca-f1dcaf9d6d71998706535fb780b8e7ada15b652e.tar.bz2 mocca-f1dcaf9d6d71998706535fb780b8e7ada15b652e.zip | |
Support for Lithuanian eID and Swedish eID
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@924 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smcc/src/main/java')
11 files changed, 1270 insertions, 78 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 122c4e7d..40471e4e 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java @@ -20,7 +20,6 @@ package at.gv.egiz.smcc;  import at.gv.egiz.smcc.cio.CIOCertificate;  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; @@ -73,7 +72,9 @@ 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); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java new file mode 100644 index 00000000..9a8e5a06 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificate.java @@ -0,0 +1,79 @@ +/*
 +* Copyright 2009 Federal Chancellery Austria and
 +* Graz University of Technology
 +*
 +* Licensed under the Apache License, Version 2.0 (the "License");
 +* you may not use this file except in compliance with the License.
 +* You may obtain a copy of the License at
 +*
 +*     http://www.apache.org/licenses/LICENSE-2.0
 +*
 +* Unless required by applicable law or agreed to in writing, software
 +* distributed under the License is distributed on an "AS IS" BASIS,
 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +* See the License for the specific language governing permissions and
 +* limitations under the License.
 +*/
 +
 +package at.gv.egiz.smcc;
 +
 +import iaik.me.asn1.ASN1;
 +import iaik.me.security.BigInteger;
 +
 +import java.io.IOException;
 +import java.util.Arrays;
 +
 +import at.gv.egiz.smcc.cio.CIOCertificate;
 +
 +public class LtEIDCIOCertificate extends CIOCertificate {
 +
 +	// The Lithuanian eID card stores both certificates in one file.
 +	// For each certificate, EF.CD contains an offset and a length that may be used
 +	// to extract the certificates from the file.
 +	private BigInteger offset;
 +	private byte[] length;
 +	
 +	public LtEIDCIOCertificate(byte[] cio) throws IOException {
 +		
 +		super(cio);
 +		
 +		ASN1 x509Certificate = new ASN1(cio);
 +		
 +        //read CONTEXTSPECIFIC manually
 +        byte[] ctxSpecific = x509Certificate.getElementAt(x509Certificate.getSize()-1).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));
 +
 +            offset = x509CertificateAttributes.getElementAt(0).getElementAt(1).gvBigInteger();
 +            
 +            // first byte indicates number of relevant bytes in array 
 +            byte[] lengthValue = x509CertificateAttributes.getElementAt(0).getElementAt(2).gvByteArray();
 +            if(lengthValue == null || lengthValue[0] != lengthValue.length-1) {
 +            	
 +            	throw new IOException("Cannot extract certificate length information. Unexpected format.");
 +            }
 +            
 +            length = new byte[lengthValue[0]];
 +            System.arraycopy(lengthValue, 1, length, 0, lengthValue[0]);
 +            
 +        }		
 +	}
 +
 +	public BigInteger getOffset() {
 +		return offset;
 +	}
 +
 +	public void setOffset(BigInteger offset) {
 +		this.offset = offset;
 +	}
 +
 +	public byte[] getLength() {
 +		return length;
 +	}
 +
 +	public void setLength(byte[] length) {
 +		this.length = length;
 +	}	
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java new file mode 100644 index 00000000..efff6cad --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOCertificateDirectory.java @@ -0,0 +1,83 @@ +/*
 +* Copyright 2009 Federal Chancellery Austria and
 +* Graz University of Technology
 +*
 +* Licensed under the Apache License, Version 2.0 (the "License");
 +* you may not use this file except in compliance with the License.
 +* You may obtain a copy of the License at
 +*
 +*     http://www.apache.org/licenses/LICENSE-2.0
 +*
 +* Unless required by applicable law or agreed to in writing, software
 +* distributed under the License is distributed on an "AS IS" BASIS,
 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +* See the License for the specific language governing permissions and
 +* limitations under the License.
 +*/
 +
 +package at.gv.egiz.smcc;
 +
 +import java.io.IOException;
 +
 +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 LtEIDCIOCertificateDirectory extends CIOCertificateDirectory {
 +
 +	public LtEIDCIOCertificateDirectory(byte[] fid) {
 +		super(fid);
 +	}
 +
 +    protected void addCIOCertificate(byte[] cio) throws IOException {
 +
 +        LtEIDCIOCertificate cioCert = new LtEIDCIOCertificate(cio);
 +
 +        log.debug("adding {}", cioCert);
 +        cios.add(cioCert);
 +    }	
 +	
 +	@Override
 +	protected byte[] executeSelect(CardChannel channel)
 +			throws CardException {
 +
 +		byte[] finalPath = null;
 +
 +		if (fid != null && fid.length > 2 && fid[0] == (byte) 0x3F
 +				&& fid[1] == (byte) 0x00) {
 +
 +			// cut off MF identifier
 +			finalPath = new byte[fid.length - 2];
 +			System.arraycopy(fid, 2, finalPath, 0, fid.length - 2);
 +		} else {
 +			finalPath = fid;
 +		}
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x08, ISO7816Utils.P2_FCP, finalPath);
 +
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		if (resp.getSW() != 0x9000) {
 +
 +			throw new CardException(
 +					"Error selecting File - unexpected response from card: "
 +							+ Integer.toHexString(resp.getSW()));
 +		}
 +		
 +        byte[] fcx = new TLVSequence(resp.getBytes()).getValue(ISO7816Utils.TAG_FCP);
 +        byte[] fd = new TLVSequence(fcx).getValue(0x82);
 +        
 +        return fd;
 +	}
 +
 +	@Override
 +    protected byte[] doReadTransparentFile(CardChannel channel) throws CardException, SignatureCardException {
 +    	
 +    	return ISO7816Utils.readTransparentFile(channel, 0xEE);
 +    }		
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java new file mode 100644 index 00000000..cab5a491 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKey.java @@ -0,0 +1,53 @@ +/*
 +* Copyright 2009 Federal Chancellery Austria and
 +* Graz University of Technology
 +*
 +* Licensed under the Apache License, Version 2.0 (the "License");
 +* you may not use this file except in compliance with the License.
 +* You may obtain a copy of the License at
 +*
 +*     http://www.apache.org/licenses/LICENSE-2.0
 +*
 +* Unless required by applicable law or agreed to in writing, software
 +* distributed under the License is distributed on an "AS IS" BASIS,
 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +* See the License for the specific language governing permissions and
 +* limitations under the License.
 +*/
 +
 +package at.gv.egiz.smcc;
 +
 +import iaik.me.asn1.ASN1;
 +import iaik.me.security.BigInteger;
 +
 +import java.io.IOException;
 +
 +public class LtEIDCIOKey {
 +
 +	private byte[] iD;
 +	private BigInteger keyReference;
 +
 +	public LtEIDCIOKey(byte[] cio) throws IOException {
 +
 +		ASN1 asn1 = new ASN1(cio);
 +
 +		iD = asn1.getElementAt(1).getElementAt(0).gvByteArray();
 +		keyReference = asn1.getElementAt(1).getElementAt(3).gvBigInteger();
 +	}
 +
 +	public byte[] getID() {
 +		return iD;
 +	}
 +
 +	public void setID(byte[] id) {
 +		iD = id;
 +	}
 +
 +	public BigInteger getKeyReference() {
 +		return keyReference;
 +	}
 +
 +	public void setKeyReference(BigInteger keyReference) {
 +		this.keyReference = keyReference;
 +	}
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java new file mode 100644 index 00000000..dfb8e9eb --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCIOKeyDirectory.java @@ -0,0 +1,46 @@ +/*
 +* Copyright 2009 Federal Chancellery Austria and
 +* Graz University of Technology
 +*
 +* Licensed under the Apache License, Version 2.0 (the "License");
 +* you may not use this file except in compliance with the License.
 +* You may obtain a copy of the License at
 +*
 +*     http://www.apache.org/licenses/LICENSE-2.0
 +*
 +* Unless required by applicable law or agreed to in writing, software
 +* distributed under the License is distributed on an "AS IS" BASIS,
 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +* See the License for the specific language governing permissions and
 +* limitations under the License.
 +*/
 +
 +package at.gv.egiz.smcc;
 +
 +import java.io.IOException;
 +import java.util.ArrayList;
 +import java.util.List;
 +
 +public class LtEIDCIOKeyDirectory extends LtEIDCIOCertificateDirectory {
 +
 +	protected List<LtEIDCIOKey> keys;
 +	
 +	public LtEIDCIOKeyDirectory(byte[] fid) {
 +
 +		super(fid);
 +		keys = new ArrayList<LtEIDCIOKey>();
 +	}
 +
 +    protected void addCIOCertificate(byte[] cio) throws IOException {
 +    	
 +    	LtEIDCIOKey cioKey = new LtEIDCIOKey(cio);
 +
 +        log.debug("adding {}", cioKey);
 +        keys.add(cioKey);
 +    }
 +    
 +    public List<LtEIDCIOKey> getKeys() {
 +    	
 +    	return this.keys;
 +    }	
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java new file mode 100644 index 00000000..e37f4018 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDCard.java @@ -0,0 +1,526 @@ +/*
 + * Copyright 2009 Federal Chancellery Austria and
 + * Graz University of Technology
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +package at.gv.egiz.smcc;
 +
 +import java.io.ByteArrayOutputStream;
 +import java.io.FileNotFoundException;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.security.MessageDigest;
 +import java.security.NoSuchAlgorithmException;
 +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.cio.CIOCertificate;
 +import at.gv.egiz.smcc.pin.gui.PINGUI;
 +import at.gv.egiz.smcc.util.ISO7816Utils;
 +import at.gv.egiz.smcc.util.SMCCHelper;
 +
 +public class LtEIDCard extends AbstractSignatureCard implements SignatureCard {
 +
 +	private static final byte[] AID = new byte[] {
 +
 +	(byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x18,
 +			(byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x63,
 +			(byte) 0x42, (byte) 0x00 };
 +
 +	private static final String CERT_LABEL_IDENTIFIER = "DigitalSignature";
 +
 +	private static final PinInfo QS_PIN_SPEC = new PinInfo(8, 8, ".",
 +			"at/gv/egiz/smcc/LtEIDCard", "qs.pin", (byte) 0x81, AID,
 +			PinInfo.UNKNOWN_RETRIES);
 +
 +	private final Logger log = LoggerFactory.getLogger(LtEIDCard.class);
 +
 +	@Override
 +	public byte[] createSignature(InputStream input, KeyboxName keyboxName,
 +			PINGUI pinGUI, String alg) throws SignatureCardException,
 +			InterruptedException, IOException {
 +
 +		CardChannel channel = getCardChannel();
 +
 +		// select AID
 +		try {
 +			selectApplication(channel);
 +		} catch (CardException e) {
 +
 +			throw new SignatureCardException("Error selecting AID.", e);
 +		}
 +
 +		try {
 +			// read certificate info to get key id
 +			byte[] signCertAndKeyID = null;
 +
 +			LtEIDObjectDirectory efod = new LtEIDObjectDirectory();
 +
 +			efod.selectAndRead(channel);
 +
 +			if (efod.getCDReferences() == null
 +					|| efod.getCDReferences().size() < 1) {
 +
 +				throw new SignatureCardException(
 +						"EF.CD not found - cannot get certificate information.");
 +			}
 +
 +			LtEIDCIOCertificateDirectory efcd = new LtEIDCIOCertificateDirectory(
 +					efod.getCDReferences().get(0));
 +			efcd.selectAndRead(channel);
 +
 +			List<CIOCertificate> cioList = efcd.getCIOs();
 +
 +			LtEIDCIOCertificate sigCertInfo = null;
 +			for (CIOCertificate cert : cioList) {
 +
 +				if (cert instanceof LtEIDCIOCertificate
 +						&& cert.getLabel().contains(CERT_LABEL_IDENTIFIER)) {
 +
 +					sigCertInfo = (LtEIDCIOCertificate) cert;
 +					signCertAndKeyID = sigCertInfo.getiD();
 +				}
 +			}
 +
 +			// verify PIN
 +			// Note: PIN verify is required prior to read PrKD
 +			// verifyPIN(channel);
 +
 +			log.debug("Starting real PIN Verification..");
 +			// TODO: Test real PIN Verification
 +			verifyPINLoop(channel, QS_PIN_SPEC, pinGUI);
 +
 +			if (efod.getPrKDReferences() == null
 +					|| efod.getPrKDReferences().size() < 1) {
 +
 +				throw new SignatureCardException(
 +						"EF.PrKD not found - cannot get key information.");
 +			}
 +
 +			List<byte[]> prKDReferences = efod.getPrKDReferences();
 +
 +			LtEIDCIOKeyDirectory efprkd = new LtEIDCIOKeyDirectory(efod
 +					.getPrKDReferences().get(0));
 +			efprkd.selectAndRead(channel);
 +
 +			LtEIDCIOKey signKey = null;
 +
 +			for (LtEIDCIOKey key : efprkd.getKeys()) {
 +
 +				if (signCertAndKeyID != null
 +						&& Arrays.equals(key.getID(), signCertAndKeyID)) {
 +
 +					signKey = key;
 +				}
 +			}
 +
 +			if (signKey == null) {
 +
 +				throw new SignatureCardException(
 +						"Unable to determine required key information.");
 +			}
 +
 +			execMSESet(channel, signKey.getKeyReference().intValue());
 +			execPSOHash(channel, input);
 +
 +			return execPSOComputeDigitalSignature(channel);
 +
 +		} catch (CardException e) {
 +
 +			throw new SignatureCardException("Error creating signature.", e);
 +		}
 +	}
 +
 +	@Override
 +	public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
 +			throws SignatureCardException, InterruptedException {
 +
 +		CardChannel channel = getCardChannel();
 +
 +		// select AID
 +		try {
 +			selectApplication(channel);
 +		} catch (CardException e) {
 +
 +			throw new SignatureCardException("Error selecting AID.", e);
 +		}
 +
 +		LtEIDObjectDirectory efod = new LtEIDObjectDirectory();
 +		try {
 +			efod.selectAndRead(channel);
 +
 +			if (efod.getCDReferences() == null
 +					|| efod.getCDReferences().size() < 1) {
 +
 +				throw new SignatureCardException(
 +						"EF.CD not found - cannot get certificate information.");
 +			}
 +
 +			LtEIDCIOCertificateDirectory efcd = new LtEIDCIOCertificateDirectory(
 +					efod.getCDReferences().get(0));
 +			efcd.selectAndRead(channel);
 +
 +			List<CIOCertificate> cioList = efcd.getCIOs();
 +
 +			LtEIDCIOCertificate sigCertInfo = null;
 +			for (CIOCertificate cert : cioList) {
 +
 +				if (cert instanceof LtEIDCIOCertificate
 +						&& cert.getLabel().contains(CERT_LABEL_IDENTIFIER)) {
 +
 +					sigCertInfo = (LtEIDCIOCertificate) cert;
 +				}
 +			}
 +
 +			if (sigCertInfo == null) {
 +
 +				throw new SignatureCardException(
 +						"Unable to determine signature certificate.");
 +			}
 +
 +			if (sigCertInfo.getOffset() == null
 +					|| sigCertInfo.getLength() == null
 +					|| sigCertInfo.getEfidOrPath() == null) {
 +
 +				throw new SignatureCardException(
 +						"Unable to retrieve required certificate information.");
 +			}
 +
 +			// select file with cert
 +			byte[] fci = selectFile(channel, sigCertInfo.getEfidOrPath());
 +
 +			byte[] certFile = executeReadBinary(channel, ISO7816Utils
 +					.getLengthFromFCx(fci));
 +			byte[] sigCert = new byte[toInt(sigCertInfo.getLength())];
 +			System.arraycopy(certFile, sigCertInfo.getOffset().intValue(),
 +					sigCert, 0, sigCert.length);
 +
 +			return sigCert;
 +
 +		} catch (CardException e) {
 +			throw new SignatureCardException(
 +					"Unable to retrieve certificate from card.", e);
 +		} catch (FileNotFoundException e) {
 +			throw new SignatureCardException(
 +					"Unable to retrieve certificate from card.", e);
 +		} catch (IOException e) {
 +			throw new SignatureCardException(
 +					"Unable to retrieve certificate from card.", e);
 +		}
 +
 +	}
 +
 +	private void execMSESet(CardChannel channel, int keyReference)
 +			throws CardException {
 +
 +		// Note: AlgoID (tag 0x80) has to be 0x12
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x22,
 +				(byte) 0x41, (byte) 0xB6, new byte[] { (byte) 0x80,
 +						(byte) 0x01, (byte) 0x12, (byte) 0x84, (byte) 0x01,
 +						(byte) keyReference });
 +
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		if (resp.getSW() != 0x9000) {
 +
 +			throw new CardException(
 +					"Error executing MSE-SET. Unexpected response: "
 +							+ Integer.toHexString(resp.getSW()));
 +		}
 +	}
 +
 +	private void execPSOHash(CardChannel channel, InputStream input)
 +			throws SignatureCardException {
 +
 +		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
 +		ByteArrayOutputStream data = new ByteArrayOutputStream();
 +
 +		try {
 +			byte[] digest = new byte[md.getDigestLength()];
 +			for (int l; (l = input.read(digest)) != -1;) {
 +				md.update(digest, 0, l);
 +			}
 +			digest = md.digest();
 +
 +			data.write(new byte[] { (byte) 0x90, (byte) 0x14 });
 +			data.write(digest);
 +
 +		} catch (IOException e) {
 +			throw new SignatureCardException("Error computing hash.", e);
 +		}
 +
 +		try {
 +
 +			CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x2A,
 +					(byte) 0x90, (byte) 0xA0, data.toByteArray());
 +
 +			ResponseAPDU resp = channel.transmit(apdu);
 +
 +			log.debug("Answer to PSO-HASH: "
 +					+ Integer.toHexString(resp.getSW()));
 +
 +			if (resp.getSW() != 0x9000) {
 +
 +				throw new SignatureCardException(
 +						"Error setting hash. Unexpected answer from card: "
 +								+ Integer.toHexString(resp.getSW()));
 +			}
 +
 +		} catch (CardException e) {
 +			throw new SignatureCardException("Error setting hash.", e);
 +		}
 +	}
 +
 +	private byte[] execPSOComputeDigitalSignature(CardChannel channel)
 +			throws SignatureCardException {
 +
 +		// Note: Le is mandatory to ensure correct functionality
 +		CommandAPDU apdu = new CommandAPDU(new byte[] { (byte) 0x00,
 +				(byte) 0x2A, (byte) 0x9E, (byte) 0x9A, (byte) 0x00 });
 +
 +		try {
 +			ResponseAPDU resp = channel.transmit(apdu);
 +
 +			log.debug("Answer to PSO-Compute Digital Signature: "
 +					+ Integer.toHexString(resp.getSW()));
 +
 +			if (resp.getSW() != 0x9000) {
 +
 +				throw new SignatureCardException(
 +						"Error computing signature. Unexpected answer from card: "
 +								+ Integer.toHexString(resp.getSW()));
 +			}
 +
 +			return resp.getData();
 +
 +		} catch (CardException e) {
 +			throw new SignatureCardException("Error computing signature.", e);
 +		}
 +
 +	}
 +
 +	protected void verifyPINLoop(CardChannel channel, PinInfo spec,
 +			PINGUI provider) throws LockedException, NotActivatedException,
 +			SignatureCardException, InterruptedException, CardException {
 +
 +		int retries = -1;
 +		do {
 +			retries = verifyPIN(channel, spec, provider, retries);
 +		} while (retries >= -1);
 +	}
 +
 +	protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
 +			PINGUI provider, int retries) throws SignatureCardException,
 +			LockedException, NotActivatedException, InterruptedException,
 +			CardException {
 +
 +		VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00,
 +				(byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x10,
 +				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
 +				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
 +				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
 +				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }, 0,
 +				VerifyAPDUSpec.PIN_FORMAT_ASCII, 16);
 +
 +		ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec,
 +				retries);
 +
 +		if (resp.getSW() == 0x9000) {
 +			return -2;
 +		}
 +		if (resp.getSW() >> 4 == 0x63c) {
 +			return 0x0f & resp.getSW();
 +		}
 +
 +		switch (resp.getSW()) {
 +		case 0x6300:
 +			// incorrect PIN, number of retries not provided
 +			return -1;
 +		case 0x6400:
 +			// ?
 +			throw new TimeoutException();
 +		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);
 +		}
 +
 +	}
 +
 +	@Override
 +	public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
 +			throws SignatureCardException, InterruptedException {
 +
 +		throw new IllegalArgumentException("Infobox '" + infobox
 +				+ "' not supported.");
 +
 +	}
 +
 +	private void selectApplication(CardChannel channel) throws CardException {
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x04, (byte) 0x00, AID);
 +
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		if (resp.getSW() != 0x9000) {
 +
 +			throw new CardException(
 +					"Error selecting AID - unexpected response from card: "
 +							+ Integer.toHexString(resp.getSW()));
 +		}
 +	}
 +
 +	private byte[] selectFile(CardChannel channel, byte[] path)
 +			throws CardException {
 +
 +		byte[] finalPath = null;
 +
 +		if (path != null && path.length > 2 && path[0] == (byte) 0x3F
 +				&& path[1] == (byte) 0x00) {
 +
 +			// cut off MF identifier
 +			finalPath = new byte[path.length - 2];
 +			System.arraycopy(path, 2, finalPath, 0, path.length - 2);
 +		} else {
 +			finalPath = path;
 +		}
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x08, (byte) 0x00, finalPath);
 +
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		if (resp.getSW() != 0x9000) {
 +
 +			throw new CardException(
 +					"Error selecting File - unexpected response from card: "
 +							+ Integer.toHexString(resp.getSW()));
 +		}
 +
 +		return resp.getData();
 +
 +	}
 +
 +	private byte[] executeReadBinary(CardChannel channel, int bytes2read)
 +			throws CardException {
 +
 +		ByteArrayOutputStream bof = new ByteArrayOutputStream();
 +
 +		// int bytes2read = (lengthHi * 256) + lengthLo;
 +		int bytesRead = 0;
 +
 +		boolean done = false;
 +
 +		int offset = 0;
 +		int len = 0;
 +
 +		while (!done) {
 +
 +			if (bytes2read - bytesRead > 0xef) {
 +				len = 0xef;
 +			} else {
 +				len = bytes2read - bytesRead;
 +			}
 +
 +			byte[] offsetBytes = SMCCHelper.toByteArray(offset);
 +			ResponseAPDU resp = readFromCard(channel, offsetBytes[0],
 +					offsetBytes[1], (byte) len);
 +
 +			if (resp.getSW1() == (byte) 0x6C) {
 +
 +				// handle case: card returns 6CXX (wrong number of bytes
 +				// requested)
 +
 +				resp = readFromCard(channel, offsetBytes[0], offsetBytes[1],
 +						(byte) resp.getSW2());
 +
 +				// this has to be the final iteration
 +				done = true;
 +			}
 +
 +			try {
 +				bof.write(resp.getData());
 +			} catch (IOException e) {
 +				log.error("Error executing secure read binary.", e);
 +				throw new CardException("Error reading data from card", e);
 +			}
 +
 +			bytesRead = bytesRead + resp.getData().length;
 +			offset = bytesRead;
 +
 +			if (bytesRead == bytes2read) {
 +
 +				done = true;
 +			}
 +		}
 +
 +		return bof.toByteArray();
 +	}
 +
 +	private ResponseAPDU readFromCard(CardChannel channel, byte offsetHi,
 +			byte offsetLo, byte numBytes) throws CardException {
 +
 +		byte[] apdu = new byte[] {
 +
 +		(byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes };
 +
 +		CommandAPDU command = new CommandAPDU(apdu);
 +		ResponseAPDU resp = channel.transmit(command);
 +
 +		return resp;
 +
 +	}
 +
 +	private int toInt(byte[] array) {
 +
 +		int len = array.length;
 +		int result = 0;
 +
 +		for (int i = len - 1; i >= 0; i--) {
 +
 +			result = (int) (result + array[i] * Math.pow(256, len - i - 1));
 +		}
 +
 +		return result;
 +	}
 +
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java new file mode 100644 index 00000000..624b8f3e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LtEIDObjectDirectory.java @@ -0,0 +1,29 @@ +/*
 +* Copyright 2009 Federal Chancellery Austria and
 +* Graz University of Technology
 +*
 +* Licensed under the Apache License, Version 2.0 (the "License");
 +* you may not use this file except in compliance with the License.
 +* You may obtain a copy of the License at
 +*
 +*     http://www.apache.org/licenses/LICENSE-2.0
 +*
 +* Unless required by applicable law or agreed to in writing, software
 +* distributed under the License is distributed on an "AS IS" BASIS,
 +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 +* See the License for the specific language governing permissions and
 +* limitations under the License.
 +*/
 +
 +package at.gv.egiz.smcc;
 +
 +import at.gv.egiz.smcc.cio.ObjectDirectory;
 +
 +public class LtEIDObjectDirectory extends ObjectDirectory {
 +
 +	public LtEIDObjectDirectory() {
 +		
 +		super(new byte[]{(byte)0x50, (byte)0x00, (byte)0x50, (byte)0x31});	
 +		this.setP1((byte)0x08);		
 +	}
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java new file mode 100644 index 00000000..4538ecca --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SEIdentityCard.java @@ -0,0 +1,319 @@ +package at.gv.egiz.smcc;
 +
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.security.MessageDigest;
 +import java.security.NoSuchAlgorithmException;
 +
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +import javax.smartcardio.CommandAPDU;
 +import javax.smartcardio.ResponseAPDU;
 +import javax.smartcardio.CardChannel;
 +import javax.smartcardio.CardException;
 +
 +import at.gv.egiz.smcc.pin.gui.PINGUI;
 +import at.gv.egiz.smcc.util.ISO7816Utils;
 +import at.gv.egiz.smcc.util.SMCCHelper;
 +
 +// TODO: This class uses predefined IDs and path to communicate with the Swedish e-ID card.
 +// Key and certificate IDs / paths should instead be read out from files defined by ISO 7816-15
 +
 +public class SEIdentityCard extends AbstractSignatureCard implements
 +		SignatureCard {
 +
 +	private static final byte[] SIGDATA_PREFIX = new byte[] { (byte) 0x30, (byte) 0x21, (byte) 0x30,
 +			(byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2B,
 +			(byte) 0x0E, (byte) 0x03, (byte) 0x02, (byte) 0x1A,
 +			(byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x14 };
 +	
 +	private static final PinInfo PIN_SPEC = new PinInfo(6, 8, ".",
 +			"at/gv/egiz/smcc/SEIdentityCard", "pin", (byte) 0x82, null,
 +			PinInfo.UNKNOWN_RETRIES);
 +	
 +	private final Logger log = LoggerFactory.getLogger(SEIdentityCard.class);
 +
 +	@Override
 +	public byte[] createSignature(InputStream input, KeyboxName keyboxName,
 +			PINGUI pinGUI, String alg) throws SignatureCardException,
 +			InterruptedException, IOException {
 +
 +		log.debug("Trying to create signature..");
 +
 +		CardChannel channel = getCardChannel();
 +
 +		// SELECT FILE
 +		try {
 +			selectFile(channel, new byte[] { (byte) 0x50, (byte) 0x15,
 +					(byte) 0x50, (byte) 0x16, (byte) 0x4B, (byte) 0x02 });
 +		} catch (CardException e) {
 +			
 +			throw new SignatureCardException("Error selecting file.", e);
 +		}
 +
 +		// VERIFY PIN
 +		try {
 +			verifyPINLoop(channel, PIN_SPEC, pinGUI);
 +		} catch (CardException e1) {
 +
 +			throw new SignatureCardException("Error verifying PIN.", e1);
 +		}
 +
 +		// SET MSE
 +		setMSE(channel);
 +
 +		// CREATE SIGNATURE
 +
 +		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
 +		try {
 +			byte[] digest = new byte[md.getDigestLength()];
 +			for (int l; (l = input.read(digest)) != -1;) {
 +				md.update(digest, 0, l);
 +			}
 +			digest = md.digest();
 +
 +			byte[] sigData = new byte[SIGDATA_PREFIX.length + digest.length];
 +			System.arraycopy(SIGDATA_PREFIX, 0, sigData, 0, SIGDATA_PREFIX.length);
 +			System.arraycopy(digest, 0, sigData, SIGDATA_PREFIX.length, digest.length);
 +			
 +			CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x2A,
 +					(byte) 0x9E, (byte) 0x9A, sigData);			
 +			
 +			ResponseAPDU resp = channel.transmit(apdu);
 +
 +			if (resp.getSW() != 0x9000) {
 +
 +				throw new SignatureCardException("Error creating signature: "
 +						+ Integer.toHexString(resp.getSW()));
 +			}
 +
 +			return resp.getData();
 +			
 +			
 +		} catch (IOException e) {
 +			throw new SignatureCardException("Error creating signature.", e);
 +		} catch (CardException e) {
 +			throw new SignatureCardException("Error creating signature.", e);
 +		}
 +	}
 +
 +	@Override
 +	public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
 +			throws SignatureCardException, InterruptedException {
 +
 +		log.debug("Trying to fetch certificate..");
 +
 +		CardChannel channel = getCardChannel();
 +
 +		byte[] fci = null;
 +
 +		try {
 +			fci = selectFile(channel, new byte[] { (byte) 0x50, (byte) 0x15,
 +					(byte) 0x50, (byte) 0x16, (byte) 0x43, (byte) 0x32 });
 +		} catch (CardException e) {
 +
 +			throw new SignatureCardException("Error selecting card file.", e);
 +		}
 +
 +		if (fci == null) {
 +			throw new SignatureCardException(
 +					"Could not retireve FCI for certificate file.");
 +		}
 +
 +		byte[] cert = null;
 +
 +		try {
 +			cert = executeReadBinary(channel, ISO7816Utils
 +					.getLengthFromFCx(fci));
 +		} catch (CardException e) {
 +			throw new SignatureCardException(
 +					"Error reading certificate from card.", e);
 +		}
 +
 +		return cert;
 +	}
 +
 +	@Override
 +	public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
 +			throws SignatureCardException, InterruptedException {
 +
 +		throw new IllegalArgumentException("Infobox '" + infobox
 +				+ "' not supported.");
 +	}
 +
 +	private void setMSE(CardChannel channel) throws SignatureCardException {
 +
 +		byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x02,
 +				(byte) 0x81, (byte) 0x02, (byte) 0x4B, (byte) 0x02 };
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x22,
 +				(byte) 0x41, (byte) 0xB6, dst);
 +
 +		try {
 +			ResponseAPDU resp = channel.transmit(apdu);
 +
 +			if (resp.getSW() != 0x9000) {
 +				throw new SignatureCardException("Error setting DST: "
 +						+ Integer.toHexString(resp.getSW()));
 +			}
 +
 +		} catch (CardException e) {
 +
 +			throw new SignatureCardException("Error setting DST.", e);
 +		}
 +
 +	}
 +
 +	private byte[] selectFile(CardChannel channel, byte[] fid)
 +			throws CardException, SignatureCardException {
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x08, (byte) 0x00, fid);
 +
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		if (resp.getSW() != 0x9000) {
 +
 +			throw new SignatureCardException("Unexpected result from card: "
 +					+ Integer.toHexString(resp.getSW()));
 +		}
 +
 +		return resp.getData();
 +	}
 +
 +	private byte[] executeReadBinary(CardChannel channel, int bytes2read)
 +			throws CardException {
 +
 +		ByteArrayOutputStream bof = new ByteArrayOutputStream();
 +
 +		int bytesRead = 0;
 +
 +		boolean done = false;
 +
 +		int offset = 0;
 +		int len = 0;
 +
 +		while (!done) {
 +
 +			if (bytes2read - bytesRead > 0xef) {
 +				len = 0xef;
 +			} else {
 +				len = bytes2read - bytesRead;
 +			}
 +
 +			byte[] offsetBytes = SMCCHelper.toByteArray(offset);
 +			ResponseAPDU resp = readFromCard(channel, offsetBytes[0],
 +					offsetBytes[1], (byte) len);
 +
 +			if (resp.getSW1() == (byte) 0x6C) {
 +
 +				// handle case: card returns 6CXX (wrong number of bytes
 +				// requested)
 +
 +				resp = readFromCard(channel, offsetBytes[0], offsetBytes[1],
 +						(byte) resp.getSW2());
 +
 +				// this has to be the final iteration
 +				done = true;
 +			}
 +
 +			try {
 +				bof.write(resp.getData());
 +			} catch (IOException e) {
 +				log.error("Error executing secure read binary.", e);
 +				throw new CardException("Error reading data from card", e);
 +			}
 +
 +			bytesRead = bytesRead + resp.getData().length;
 +			offset = bytesRead;
 +
 +			if (bytesRead == bytes2read) {
 +
 +				done = true;
 +			}
 +		}
 +
 +		return bof.toByteArray();
 +	}
 +
 +	private ResponseAPDU readFromCard(CardChannel channel, byte offsetHi,
 +			byte offsetLo, byte numBytes) throws CardException {
 +
 +		byte[] apdu = new byte[] {
 +
 +		(byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes };
 +
 +		CommandAPDU command = new CommandAPDU(apdu);
 +		ResponseAPDU resp = channel.transmit(command);
 +
 +		return resp;
 +
 +	}
 +
 +	protected void verifyPINLoop(CardChannel channel, PinInfo spec,
 +			PINGUI provider) throws LockedException, NotActivatedException,
 +			SignatureCardException, InterruptedException, CardException {
 +
 +		int retries = -1;
 +		do {
 +			retries = verifyPIN(channel, spec, provider, retries);
 +		} while (retries >= -1);
 +	}
 +
 +	protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
 +			PINGUI provider, int retries) throws SignatureCardException,
 +			LockedException, NotActivatedException, InterruptedException,
 +			CardException {
 +
 +		VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00,
 +				(byte) 0x20, (byte) 0x00, pinSpec.getKID(), (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 -2;
 +		}
 +		if (resp.getSW() >> 4 == 0x63c) {
 +			return 0x0f & resp.getSW();
 +		}
 +
 +		switch (resp.getSW()) {
 +		case 0x6300:
 +			// incorrect PIN, number of retries not provided
 +			return -1;
 +		case 0x6400:
 +			// ?
 +			throw new TimeoutException();
 +		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);
 +		}
 +
 +	}
 +
 +}
 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 e189267c..156aca23 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -151,32 +151,33 @@ public class SignatureCardFactory {  		}  		/** -		 * Returns true if the historical bytes of the given ATR contain the historical bytes pattern of this -		 * SupportedCard object. +		 * Returns true if the historical bytes of the given ATR contain the +		 * historical bytes pattern of this SupportedCard object.  		 *   		 * @param atr  		 *            the ATR  		 *  -		 * @return <code>true</code> if the historical bytes of the given ATR contain the historical bytes pattern -		 *         of this SupportedCard object, or <code>false</code> -		 *         otherwise. +		 * @return <code>true</code> if the historical bytes of the given ATR +		 *         contain the historical bytes pattern of this SupportedCard +		 *         object, or <code>false</code> otherwise.  		 */  		public boolean matchesHistoricalBytesPattern(ATR atr) { -			 +  			byte[] historicalBytes = atr.getHistoricalBytes(); -			if (historicalBytes == null ||  -				this.historicalBytesPattern == null || -				this.historicalBytesPattern.length > historicalBytes.length) { -				 +			if (historicalBytes == null +					|| this.historicalBytesPattern == null +					|| this.historicalBytesPattern.length > historicalBytes.length) { +  				return false;  			} -			 +  			int[] failure = computeFailure(this.historicalBytesPattern);  			int j = 0;  			for (int i = 0; i < historicalBytes.length; i++) { -				while (j > 0 && this.historicalBytesPattern[j] != historicalBytes[i]) { +				while (j > 0 +						&& this.historicalBytesPattern[j] != historicalBytes[i]) {  					j = failure[j - 1];  				}  				if (this.historicalBytesPattern[j] == historicalBytes[i]) { @@ -426,7 +427,7 @@ public class SignatureCardFactory {  						(byte) 0x69, (byte) 0x6E, (byte) 0x45, (byte) 0x49,  						(byte) 0x44 },  				// historical bytes pattern -				new byte[] {'F','i','n','E','I','D'}, +				new byte[] { 'F', 'i', 'n', 'E', 'I', 'D' },  				// 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, @@ -435,6 +436,44 @@ public class SignatureCardFactory {  						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,  						(byte) 0xff }, "at.gv.egiz.smcc.FINEIDCard")); +		// LT eID +		supportedCards.add(new SupportedCard( +				// ATR [3b:7D:94:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00] +				new byte[] { (byte) 0x3b, (byte) 0x7D, (byte) 0x94, +						(byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x31, +						(byte) 0x80, (byte) 0x65, (byte) 0xB0, (byte) 0x83, +						(byte) 0x11, (byte) 0xC0, (byte) 0xA9, (byte) 0x83, +						(byte) 0x00, (byte) 0x90, (byte) 0x00 }, +				// mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff: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) 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.LtEIDCard")); + +		// SE eID +		supportedCards.add(new SupportedCard( +				// ATR +				// [3B:9F:94:80:1F:C3:00:68:10:44:05:01:46:49:53:45:31:C8:07:90:00:18] +				new byte[] { (byte) 0x3b, (byte) 0x9F, (byte) 0x90, +						(byte) 0x80, (byte) 0x1F, (byte) 0xC0, (byte) 0x00, +						(byte) 0x68, (byte) 0x00, (byte) 0x00, (byte) 0x05, +						(byte) 0x00, (byte) 0x46, (byte) 0x49, (byte) 0x53, +						(byte) 0x45, (byte) 0x31, (byte) 0xC8, (byte) 0x00, +						(byte) 0x00, (byte) 0x00, (byte) 0x00 }, +				// mask +				// (ff:ff:f0:ff:ff:f0:ff:ff:00:00:ff:f0:ff:ff:ff:ff:ff:ff:F0:00:00:00) +				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xf0, +						(byte) 0xff, (byte) 0xff, (byte) 0xf0, (byte) 0xff, +						(byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0xff, +						(byte) 0xf0, (byte) 0xff, (byte) 0xff, (byte) 0xff, +						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf0, +						(byte) 0x00, (byte) 0x00, (byte) 0x00 }, +				"at.gv.egiz.smcc.SEIdentityCard")); +  		// ITCards  		supportedCards.add(new SupportedCard(  		// ATR = @@ -493,8 +532,8 @@ public class SignatureCardFactory {  						(byte) 0xff, 'E', 's', 't', 'E', 'I', 'D', ' ', 'v',  						'e', 'r', ' ', '1', '.', '0' },  				// historical bytes pattern -				new byte[] {'E', 's', 't', 'E', 'I', 'D', ' ', 'v', -						'e', 'r', ' ', '1', '.', '0'},		 +				new byte[] { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', +						' ', '1', '.', '0' },  				// mask  				// (ff:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff)  				new byte[] { (byte) 0xff, (byte) 0x00, (byte) 0x00, @@ -513,8 +552,8 @@ public class SignatureCardFactory {  						'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', ' ',  						'1', '.', '0', (byte) 0x2b },  				// historical bytes pattern -				new byte[] {'E', 's', 't', 'E', 'I', 'D', ' ', 'v', -						'e', 'r', ' ', '1', '.', '0'},						 +				new byte[] { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', +						' ', '1', '.', '0' },  				// mask  				// (ff:ff:ff:ff:ff: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, @@ -534,8 +573,8 @@ public class SignatureCardFactory {  						'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', ' ',  						'1', '.', '0', (byte) 0x43 },  				// historical bytes pattern -				new byte[] {'E', 's', 't', 'E', 'I', 'D', ' ', 'v', -					'e', 'r', ' ', '1', '.', '0'},						 +				new byte[] { 'E', 's', 't', 'E', 'I', 'D', ' ', 'v', 'e', 'r', +						' ', '1', '.', '0' },  				// mask  				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)  				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, @@ -572,8 +611,8 @@ public class SignatureCardFactory {  						(byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b,  						'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n',  						(byte) 0x89 }, -				// historical bytes pattern						 -				new byte[]{'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n'}, +				// historical bytes pattern +				new byte[] { 'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n' },  				// mask  				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,  						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, @@ -647,44 +686,55 @@ public class SignatureCardFactory {  			SupportedCard supportedCard = cards.next();  			if (supportedCard.matches(atr)) { -				return instantiateSignatureCard(cardTerminal, card, supportedCard); +				return instantiateSignatureCard(cardTerminal, card, +						supportedCard);  			}  		} -		 -		// if no matching implementation has been found yet, check for pattern match in historical bytes -		log.trace("No card matching complete ATR found - checking candidates with historical bytes matches."); + +		// if no matching implementation has been found yet, check for pattern +		// match in historical bytes +		log +				.trace("No card matching complete ATR found - checking candidates with historical bytes matches.");  		Iterator<SupportedCard> cardsIterator = supportedCards.iterator();  		List<SupportedCard> historicalBytesCandidates = new ArrayList<SupportedCard>();  		while (cardsIterator.hasNext()) {  			SupportedCard supportedCard = cardsIterator.next(); -			 -			if(supportedCard.matchesHistoricalBytesPattern(atr)) { -				 + +			if (supportedCard.matchesHistoricalBytesPattern(atr)) { +  				historicalBytesCandidates.add(supportedCard);  			} -		}		 +		}  		historicalBytesCandidates = reduceCandidateList(historicalBytesCandidates); -		if(historicalBytesCandidates.size() != 1) { -			 -			log.warn("Found {} cards with matching historical bytes pattern.", historicalBytesCandidates.size()); +		if (historicalBytesCandidates.size() != 1) { + +			log.warn("Found {} cards with matching historical bytes pattern.", +					historicalBytesCandidates.size());  		} else { -			 -			log.trace("Instantiating class " + historicalBytesCandidates.get(0).getImplementationClassName() + " according to historical bytes pattern match."); -			return instantiateSignatureCard(cardTerminal, card, historicalBytesCandidates.get(0)); + +			log.trace("Instantiating class " +					+ historicalBytesCandidates.get(0) +							.getImplementationClassName() +					+ " according to historical bytes pattern match."); +			return instantiateSignatureCard(cardTerminal, card, +					historicalBytesCandidates.get(0));  		} -		 +  		throw new CardNotSupportedException("Card not supported: ATR="  				+ toString(atr.getBytes()));  	} -	private SignatureCard instantiateSignatureCard(CardTerminal cardTerminal, Card card, SupportedCard supportedCard) throws CardNotSupportedException { -		 +	private SignatureCard instantiateSignatureCard(CardTerminal cardTerminal, +			Card card, SupportedCard supportedCard) +			throws CardNotSupportedException { +  		ClassLoader cl = SignatureCardFactory.class.getClassLoader();  		SignatureCard sc;  		try { -			Class<?> scClass = cl.loadClass(supportedCard.getImplementationClassName()); +			Class<?> scClass = cl.loadClass(supportedCard +					.getImplementationClassName());  			sc = (SignatureCard) scClass.newInstance();  			sc = ExclSignatureCardProxy.newInstance(sc); @@ -694,55 +744,45 @@ public class SignatureCardFactory {  			return sc;  		} catch (ClassNotFoundException e) { -			log.warn( -					"Cannot find signature card implementation class.", -					e); +			log.warn("Cannot find signature card implementation class.", e);  			throw new CardNotSupportedException( -					"Cannot find signature card implementation class.", -					e); +					"Cannot find signature card implementation class.", e);  		} catch (InstantiationException e) { -			log -					.warn( -							"Failed to instantiate signature card implementation.", -							e); +			log.warn("Failed to instantiate signature card implementation.", e);  			throw new CardNotSupportedException( -					"Failed to instantiate signature card implementation.", -					e); +					"Failed to instantiate signature card implementation.", e);  		} catch (IllegalAccessException e) { -			log -					.warn( -							"Failed to instantiate signature card implementation.", -							e); +			log.warn("Failed to instantiate signature card implementation.", e);  			throw new CardNotSupportedException( -					"Failed to instantiate signature card implementation.", -					e); -		}		 +					"Failed to instantiate signature card implementation.", e); +		}  	} -	 -	private List<SupportedCard> reduceCandidateList(List<SupportedCard> candidates) { -		 + +	private List<SupportedCard> reduceCandidateList( +			List<SupportedCard> candidates) { +  		List<SupportedCard> result = new ArrayList<SupportedCard>(); -		 -		for(SupportedCard current : candidates) { -			 + +		for (SupportedCard current : candidates) { +  			String implName = current.getImplementationClassName();  			boolean alreadyPresent = false; -			 -			for(SupportedCard card : result) { -				 -				if(card.getImplementationClassName().equals(implName)) { -				 + +			for (SupportedCard card : result) { + +				if (card.getImplementationClassName().equals(implName)) { +  					alreadyPresent = true;  				}  			} -			 -			if(!alreadyPresent) { + +			if (!alreadyPresent) {  				result.add(current);  			} -		}				 +		}  		return result;  	} -	 +  	public static String toString(byte[] b) {  		StringBuffer sb = new StringBuffer();  		if (b != null && b.length > 0) { 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 65bcd84b..5ec0b4b0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java @@ -8,6 +8,8 @@ import javax.smartcardio.CardException;  import javax.smartcardio.CommandAPDU;
  import javax.smartcardio.ResponseAPDU;
 +import at.gv.egiz.smcc.util.SMCCHelper;
 +
  public class T0CardChannel extends LogCardChannel {
  	public T0CardChannel(CardChannel channel) {
 @@ -22,7 +24,7 @@ public class T0CardChannel extends LogCardChannel {  		 if(resp.getSW1() == (byte)0x61) {
 -			 byte[] data = executeGetResponse(channel, (byte)resp.getSW2());
 +			 byte[] data = executeGetResponse((byte)resp.getSW2());
  			 byte[] result = new byte[data.length + 2];
  			 System.arraycopy(data, 0, result, 0, data.length);
 @@ -38,7 +40,7 @@ public class T0CardChannel extends LogCardChannel {  		 }
  	 }
 -		private byte[] executeGetResponse(CardChannel channel, byte sw2)
 +		private byte[] executeGetResponse(byte sw2)
  		throws CardException {
  	boolean done = false;
 @@ -48,7 +50,8 @@ 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);
 +//		ResponseAPDU resp = channel.transmit(command);
 +		ResponseAPDU resp = super.transmit(command);
  		try {
  			bof.write(resp.getData());
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java index 3ab954ee..d1bd6144 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/cio/ObjectDirectory.java @@ -56,7 +56,7 @@ public class ObjectDirectory {  	private List<byte[]> CD_refs;  	private Integer padding; -        private int P1 = 0x02; +    private int P1 = 0x02;  	public ObjectDirectory() {  		fid = new byte[] { (byte) 0x50, (byte) 0x31 }; @@ -99,6 +99,7 @@ public class ObjectDirectory {  		byte[] efod = ISO7816Utils.readTransparentFile(channel, -1); +		                  PrKD_refs = new ArrayList<byte[]>();                  PuKD_refs = new ArrayList<byte[]>();                  AOD_refs = new ArrayList<byte[]>(); @@ -107,7 +108,7 @@ public class ObjectDirectory {  		for (TLV cio : new TLVSequence(efod)) {  			int tag = cio.getTag(); -                        //TODO FIN EID: check if unknown tag and tag length > array +            //TODO FIN EID: check if unknown tag and tag length > array  			if (padding != null && tag == padding) {  				// reached padding - quit record extraction  				break; @@ -205,4 +206,16 @@ public class ObjectDirectory {          public List<byte[]> getCDReferences() {  		return CD_refs;  	} + +	public int getP1() { +		return P1; +	} + +	public void setP1(int p1) { +		P1 = p1; +	} +         +        + +	  } | 
