diff options
22 files changed, 1485 insertions, 276 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 { diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties new file mode 100644 index 00000000..7729e647 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/FINEIDCard.properties @@ -0,0 +1,3 @@ +#pin.name=PIN
 +sig.pin.name=PIN 2
 +sig.pin.length=6-8
\ No newline at end of file diff --git a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java index 4c4a7b41..577979f2 100644 --- a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java +++ b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java @@ -386,16 +386,17 @@ public class ESCardTest extends AbstractSignatureCard {  	    SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault());
  	    try {
 -			signatureCard.createSignature(null, null, null, null);
 +//			signatureCard.createSignature(null, null, null, null);
 +	    	signatureCard.getCertificate(null, null);
  		} catch (SignatureCardException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		} catch (InterruptedException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
 -		} catch (IOException e) {
 -			// TODO Auto-generated catch block
 -			e.printStackTrace();
 +//		} catch (IOException e) {
 +//			// TODO Auto-generated catch block
 +//			e.printStackTrace();
  		}
  	}
 diff --git a/smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java b/smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java new file mode 100644 index 00000000..eef6f611 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/FINEIDTest.java @@ -0,0 +1,354 @@ +package at.gv.egiz.smcc;
 +
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.util.Arrays;
 +import java.util.Locale;
 +
 +import org.junit.Ignore;
 +
 +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;
 +
 +import javax.smartcardio.*;
 +
 +@Ignore
 +public class FINEIDTest extends AbstractSignatureCard {
 +
 +	/**
 +	 * @param args
 +	 */
 +	public static void main(String[] args) {
 +
 +		FINEIDTest tester = new FINEIDTest();
 +		tester.runTest();
 +
 +	}
 +
 +	public void runTest() {
 +
 +		SMCCHelper helper = new SMCCHelper();
 +
 +		SignatureCard signatureCard = helper.getSignatureCard(Locale
 +				.getDefault());
 +
 +		System.out.println("Found card: " + signatureCard.toString());
 +
 +		// TODO: replace this by already implemented getCardChannel() method
 +		CardChannel channel = new T0CardChannel(signatureCard.getCard()
 +				.getBasicChannel());
 +
 +		try {
 +
 +			selectAID(channel);
 +//			readCardInfo(channel);
 +			testPIN();
 +
 +		} catch (Exception e) {
 +
 +			e.printStackTrace();
 +		}
 +
 +	}
 +
 +	public void testPIN() {
 +		
 +		PinInfo pinInfo = new PinInfo(6, 8, "[0-9]",
 +				"at/gv/egiz/smcc/FINEIDCard", "sig.pin", (byte) 0x00,
 +				new byte[] {}, PinInfo.UNKNOWN_RETRIES);
 +		
 +		VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00,
 +				(byte) 0x20, (byte) 0x00, (byte)0x82 }, 0,
 +				VerifyAPDUSpec.PIN_FORMAT_ASCII, 8);
 +		
 +		CommandAPDU apdu = ISO7816Utils.createVerifyAPDU(apduSpec, new char[]{'1','2','3','4','5','6'});
 +		
 +		System.out.println("APDU: " + SMCCHelper.toString(apdu.getBytes()));
 +		
 +	}
 +	
 +	public void selectAID(CardChannel channel) throws CardException {
 +
 +		byte[] aid = new byte[] { (byte) 0xA0, (byte) 0x00, (byte) 0x00,
 +				(byte) 0x00, (byte) 0x63, (byte) 0x50, (byte) 0x4B,
 +				(byte) 0x43, (byte) 0x53, (byte) 0x2D, (byte) 0x31, (byte) 0x35 };
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x04, (byte) 0x00, aid);
 +
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		System.out.println("Response: " + SMCCHelper.toString(resp.getBytes()));
 +
 +	}
 +
 +	public void readCardInfo(CardChannel channel) throws CardException,
 +			SignatureCardException, IOException {
 +
 +		byte[] efQcert = null;
 +
 +		FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory(0xFF);
 +		ef_od.selectAndRead(channel);
 +
 +		// **** READ CERT ****
 +
 +		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) {
 +				System.out
 +						.println("Error reading EF.CD - try next if available.");
 +				e.printStackTrace();
 +				continue;
 +			}
 +
 +			for (CIOCertificate cioCertificate : ef_cd.getCIOs()) {
 +				String label = cioCertificate.getLabel();
 +				if (label != null
 +						&& label.toLowerCase().contains(
 +								"allekirjoitusvarmenne".toLowerCase())) {
 +					efQcert = cioCertificate.getEfidOrPath();
 +				}
 +			}
 +		}
 +
 +		System.out.println("Read certificate path: "
 +				+ SMCCHelper.toString(efQcert));
 +
 +		byte[] certPath = null;
 +		// remove MF path
 +		if (efQcert[0] == 0x3F && efQcert[1] == 0x00) {
 +
 +			certPath = new byte[efQcert.length - 2];
 +			System.arraycopy(efQcert, 2, certPath, 0, efQcert.length - 2);
 +		} else {
 +
 +			certPath = efQcert;
 +		}
 +
 +		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x08, (byte) 0x00, certPath);
 +		ResponseAPDU resp = channel.transmit(apdu);
 +
 +		System.out.println("Response: " + SMCCHelper.toString(resp.getBytes()));
 +
 +		byte[] fcx = new TLVSequence(resp.getBytes())
 +				.getValue(ISO7816Utils.TAG_FCI);
 +		byte[] fileDataLength = new TLVSequence(fcx).getValue(0x81);
 +
 +		System.out.println("Data length: "
 +				+ SMCCHelper.toString(fileDataLength));
 +
 +		System.out.println("MaxSize: "
 +				+ computeLengthFromByteArray(fileDataLength));
 +
 +		byte[] cert = ISO7816Utils.readTransparentFile(channel,
 +				computeLengthFromByteArray(fileDataLength));
 +
 +		System.out.println("Read cert: " + SMCCHelper.toString(cert));
 +
 +		toFile(cert, "F:/fin_cert.cer");
 +
 +		// **** VERIFY PIN ****
 +
 +		byte[] prkdPath = ef_od.getEf_prkd();
 +		System.out.println("PRKD path: " + SMCCHelper.toString(prkdPath));
 +
 +		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(
 +							"allekirjoitusavain".toLowerCase())) {
 +				efKey = cioCertificate.getEfidOrPath();
 +				System.out.println("AUTH ID of this key: "
 +						+ SMCCHelper.toString(cioCertificate.getAuthId()));
 +				authID = cioCertificate.getAuthId();
 +			}
 +		}
 +
 +		System.out.println("Key path: " + SMCCHelper.toString(efKey));
 +
 +		byte[] aod = ef_od.getEf_aod();
 +		System.out.println("AOD path: " + SMCCHelper.toString(aod));
 +
 +		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();
 +			}
 +		}
 +
 +		System.out.println("PIN path: " + SMCCHelper.toString(pinPath));
 +		System.out.println("PWD Ref: " + SMCCHelper.toString(pwdRef));
 +
 +		CommandAPDU verifySelect = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x08, (byte) 0x00, removeMFFromPath(pinPath));
 +		ResponseAPDU r1 = channel.transmit(verifySelect);
 +
 +		if (r1.getSW() != 0x9000) {
 +
 +			System.out.println("Error executing Verify Select: "
 +					+ Integer.toHexString(r1.getSW()));
 +		}
 +
 +		CommandAPDU verify = new CommandAPDU((byte) 0x00, (byte) 0x20,
 +				(byte) 0x00, pwdRef[pwdRef.length - 1], new byte[] {
 +						(byte) 0x36, (byte) 0x35, (byte) 0x38, (byte) 0x30,
 +						(byte) 0x36, (byte) 0x36, (byte) 0x00, (byte) 0x00 });
 +		ResponseAPDU r2 = channel.transmit(verify);
 +
 +		if (r2.getSW() != 0x9000) {
 +
 +			System.err.println("Error executing Verify: "
 +					+ Integer.toHexString(r2.getSW()));
 +		}
 +
 +		// **** SIGN ****
 +
 +		CommandAPDU selectKeyPath = new CommandAPDU((byte) 0x00, (byte) 0xA4,
 +				(byte) 0x08, (byte) 0x00, removeMFFromPath(efKey));
 +		ResponseAPDU r3 = channel.transmit(selectKeyPath);
 +
 +		if (r3.getSW() != 0x9000) {
 +
 +			System.err.println("Error executing select keypath: "
 +					+ Integer.toHexString(r3.getSW()));
 +		}
 +
 +		// MSE RESTORE
 +		CommandAPDU mseRestore = new CommandAPDU((byte) 0x00, (byte) 0x22,
 +				(byte) 0xF3, (byte) 0x00);
 +		ResponseAPDU r4 = channel.transmit(mseRestore);
 +
 +		if (r4.getSW() != 0x9000) {
 +
 +			System.err.println("Error executing restore mse: "
 +					+ Integer.toHexString(r4.getSW()));
 +		}
 +
 +		// MSE SET
 +
 +		byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x12,
 +				(byte) 0x81, (byte) 0x02, efKey[efKey.length - 2],
 +				efKey[efKey.length - 1] };
 +
 +		CommandAPDU mseSet = new CommandAPDU((byte) 0x00, (byte) 0x22,
 +				(byte) 0x41, (byte) 0xB6, dst);
 +		ResponseAPDU r5 = channel.transmit(mseSet);
 +
 +		if (r5.getSW() != 0x9000) {
 +
 +			System.err.println("Error executing set mse: "
 +					+ Integer.toHexString(r5.getSW()));
 +		}
 +
 +		// SIGN
 +
 +		byte[] hash = new byte[] { (byte) 0x00, (byte) 0x01, (byte) 0x02,
 +				(byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
 +				(byte) 0x07, (byte) 0x08, (byte) 0x09, (byte) 0x0A,
 +				(byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E,
 +				(byte) 0x0F, (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13 };
 +
 +		CommandAPDU sign = new CommandAPDU((byte) 0x00, (byte) 0x2A,
 +				(byte) 0x9E, (byte) 0x9A, hash);
 +		ResponseAPDU r6 = channel.transmit(sign);
 +
 +		if (r6.getSW() != 0x9000) {
 +
 +			System.err.println("Error executing sign: "
 +					+ Integer.toHexString(r6.getSW()));
 +		}
 +		
 +		System.out.println("Signature value: " + SMCCHelper.toString(r6.getData()));
 +		
 +	}
 +
 +	@Override
 +	public byte[] createSignature(InputStream input, KeyboxName keyboxName,
 +			PINGUI pinGUI, String alg) throws SignatureCardException,
 +			InterruptedException, IOException {
 +		// TODO Auto-generated method stub
 +		return null;
 +	}
 +
 +	@Override
 +	public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
 +			throws SignatureCardException, InterruptedException {
 +		// TODO Auto-generated method stub
 +		return null;
 +	}
 +
 +	@Override
 +	public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
 +			throws SignatureCardException, InterruptedException {
 +		// TODO Auto-generated method stub
 +		return null;
 +	}
 +
 +	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;
 +
 +	}
 +
 +	private void toFile(byte[] data, String filename) {
 +
 +		try {
 +			FileOutputStream fos = new FileOutputStream(filename);
 +
 +			fos.write(data);
 +
 +			fos.close();
 +		} catch (Exception e) {
 +			e.printStackTrace();
 +		}
 +
 +	}
 +
 +	private byte[] removeMFFromPath(byte[] path) {
 +
 +		byte[] result = null;
 +
 +		if (path[0] == 0x3F && path[1] == 0x00) {
 +
 +			result = new byte[path.length - 2];
 +			System.arraycopy(path, 2, result, 0, path.length - 2);
 +		} else {
 +
 +			result = path;
 +		}
 +
 +		return result;
 +	}
 +
 +}
 diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.derBinary files differ new file mode 100644 index 00000000..678534ec --- /dev/null +++ b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_cds.der diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.derBinary files differ new file mode 100644 index 00000000..40518375 --- /dev/null +++ b/smcc/src/test/resources/at/gv/egiz/smcc/test/es_dnie/es_prkd.der diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.derBinary files differ new file mode 100644 index 00000000..5dda6cc8 --- /dev/null +++ b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_cds.der diff --git a/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.der b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.derBinary files differ new file mode 100644 index 00000000..d48df411 --- /dev/null +++ b/smcc/src/test/resources/at/gv/egiz/smcc/test/fineid/fin_prkd.der | 
