/* * eID Applet Project. * Copyright (C) 2008-2009 FedICT. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ package test.be.fedict.eid.applet; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Locale; import javax.smartcardio.CardChannel; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.apache.commons.codec.binary.Hex; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; import be.fedict.eid.applet.Messages; import be.fedict.eid.applet.sc.PcscEid; public class Pkcs15Test { private static final Log LOG = LogFactory.getLog(Pkcs15Test.class); private PcscEid pcscEid; @Before public void setUp() throws Exception { this.messages = new Messages(Locale.getDefault()); this.pcscEid = new PcscEid(new TestView(), this.messages); if (false == this.pcscEid.isEidPresent()) { LOG.debug("insert eID card"); this.pcscEid.waitForEidPresent(); } } private Messages messages; @After public void tearDown() throws Exception { this.pcscEid.close(); } @Documented @Target({ ElementType.TYPE, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface Tag { byte value(); } @Tag(0x61) public static class Pkcs15ApplicationTemplate { @Tag(0x50) String label; @Tag(0x4f) byte[] id; @Tag(0x51) byte[] path; @Tag(0x73) byte[] discretionaryDataObjects; } public static class Pkcs15_EF_DIR { Pkcs15ApplicationTemplate[] applicationTemplates; } public static <T> T parsePkcs15File(byte[] data, Class<T> type) throws InstantiationException, IllegalAccessException { T result = type.newInstance(); int dataIdx = 0; Tag tagAnnotation = type.getAnnotation(Tag.class); if (null != tagAnnotation) { byte tag = tagAnnotation.value(); if (tag != data[0]) { throw new RuntimeException("incorrect tag: " + Integer.toHexString(tag)); } dataIdx++; int size = data[1]; dataIdx++; LOG.debug("size: " + size); if (data.length - dataIdx != size) { throw new RuntimeException("data size incorrect: " + size + " for tag " + Integer.toHexString(tag)); } } Field[] fields = type.getDeclaredFields(); if (1 == fields.length) { Field field = fields[0]; if (field.getType().isArray()) { Class<?> componentType = field.getType().getComponentType(); Object component = parsePkcs15File(data, componentType); Object array = Array.newInstance(componentType, 1); Array.set(array, 0, component); field.set(result, array); return result; } } while (dataIdx < data.length) { byte tag = data[dataIdx]; dataIdx++; int size = data[dataIdx]; dataIdx++; LOG.debug("tag: " + Integer.toHexString(tag) + "; size: " + size); for (Field field : fields) { Tag fieldTagAnnotation = field.getAnnotation(Tag.class); if (null != fieldTagAnnotation) { byte fieldTag = fieldTagAnnotation.value(); if (fieldTag == tag) { LOG.debug("field found for tag " + Integer.toHexString(tag) + ": " + field.getName()); Object value; if (String.class.equals(field.getType())) { value = new String(Arrays.copyOfRange(data, dataIdx, dataIdx + size)); } else if (byte[].class.equals(field.getType())) { value = Arrays.copyOfRange(data, dataIdx, dataIdx + size); } else { throw new RuntimeException("unsupported field type: " + field.getType().getName() + " for field " + field.getName()); } field.set(result, value); } } } dataIdx += size; } return result; } @Test public void EF_DIR() throws Exception { byte[] dir = this.pcscEid.readFile(new byte[] { 0x2f, 0x00 }); LOG.debug("size of EF(DIR): " + dir.length); LOG.debug("EF(DIR): " + new String(Hex.encodeHex(dir))); Pkcs15_EF_DIR result = parsePkcs15File(dir, Pkcs15_EF_DIR.class); assertNotNull(result); assertNotNull(result.applicationTemplates); assertEquals(1, result.applicationTemplates.length); Pkcs15ApplicationTemplate applicationTemplate = result.applicationTemplates[0]; assertNotNull(applicationTemplate); assertNotNull(applicationTemplate.label); LOG.debug("application label: " + applicationTemplate.label); LOG.debug("application id: " + new String(Hex.encodeHex(applicationTemplate.id))); LOG.debug("application path: " + new String(Hex.encodeHex(applicationTemplate.path))); LOG.debug("application discretionary data objects: " + new String(Hex.encodeHex(applicationTemplate.discretionaryDataObjects))); } private Pkcs15ApplicationTemplate getApplication(Pkcs15_EF_DIR dir, byte[] applicationId) { for (Pkcs15ApplicationTemplate applicationTemplate : dir.applicationTemplates) { if (Arrays.equals(applicationId, applicationTemplate.id)) { return applicationTemplate; } } throw new RuntimeException( "no application template found for application id: " + new String(Hex.encodeHex(applicationId))); } @Test public void EF_ODF() throws Exception { byte[] dirData = this.pcscEid.readFile(new byte[] { 0x2f, 0x00 }); Pkcs15_EF_DIR dir = parsePkcs15File(dirData, Pkcs15_EF_DIR.class); Pkcs15ApplicationTemplate applicationTemplate = getApplication(dir, new byte[] { (byte) 0xa0, 0x00, 0x00, 0x01, 0x77, 0x50, 0x4b, 0x43, 0x53, 0x2d, 0x31, 0x35 }); byte[] odfFileId = new byte[applicationTemplate.path.length + 2]; System.arraycopy(applicationTemplate.path, 0, odfFileId, 0, applicationTemplate.path.length); System.arraycopy(new byte[] { 0x50, 0x31 }, 0, odfFileId, 4, 2); byte[] odf = this.pcscEid.readFile(odfFileId); LOG.debug("size of EF(ODF): " + odf.length); LOG.debug("EF(ODF): " + new String(Hex.encodeHex(odf))); } @Test public void testSelectPkcs15Application() throws Exception { CardChannel cardChannel = this.pcscEid.getCardChannel(); byte[] aId = new byte[] { (byte) 0xa0, 0x00, 0x00, 0x01, 0x77, 0x50, 0x4b, 0x43, 0x53, 0x2d, 0x31, 0x35 }; CommandAPDU selectApplicationApdu = new CommandAPDU(0x00, 0xA4, 0x04, 0x0C, aId); ResponseAPDU responseApdu = cardChannel.transmit(selectApplicationApdu); assertEquals(0x9000, responseApdu.getSW()); } @Test public void testSelectBelpicApplication() throws Exception { CardChannel cardChannel = this.pcscEid.getCardChannel(); byte[] belpicAID = new byte[] { (byte) 0xA0, 0x00, 0x00, 0x00, 0x30, 0x29, 0x05, 0x70, 0x00, (byte) 0xAD, 0x13, 0x10, 0x01, 0x01, (byte) 0xFF }; CommandAPDU selectApplicationApdu = new CommandAPDU(0x00, 0xA4, 0x04, 0x0C, belpicAID); ResponseAPDU responseApdu = cardChannel.transmit(selectApplicationApdu); assertEquals(0x9000, responseApdu.getSW()); } }